From 7c72824375fdd2c2b59af7549b3c1627ebb3626e Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 13:00:26 +1000 Subject: [PATCH 01/62] refactor: port MM probes to new api - Add concept of match certainty to new probe - Port CLIP Embed models to new API - Fiddle with stuff --- invokeai/app/invocations/flux_model_loader.py | 4 +- .../model_install/model_install_default.py | 7 +- .../model_records/model_records_base.py | 5 +- .../model_records/model_records_sql.py | 19 +- .../migrations/migration_22.py | 9 +- invokeai/backend/flux/util.py | 61 ++-- invokeai/backend/model_manager/config.py | 287 +++++++++++++++--- .../backend/model_manager/legacy_probe.py | 23 +- .../model_manager/load/model_loaders/flux.py | 37 ++- .../model_manager/single_file_config_files.py | 86 ++++++ invokeai/backend/model_manager/taxonomy.py | 13 +- .../flux_aitoolkit_lora_conversion_utils.py | 5 +- .../patches/lora_conversions/formats.py | 7 +- .../scripts/load_flux_model_bnb_llm_int8.py | 5 +- .../scripts/load_flux_model_bnb_nf4.py | 5 +- .../web/src/features/modelManagerV2/models.ts | 3 + .../web/src/features/nodes/types/common.ts | 2 +- .../frontend/web/src/services/api/schema.ts | 12 +- ...st_flux_aitoolkit_lora_conversion_utils.py | 5 +- .../test_flux_kohya_lora_conversion_utils.py | 5 +- tests/test_model_probe.py | 2 +- 21 files changed, 487 insertions(+), 115 deletions(-) create mode 100644 invokeai/backend/model_manager/single_file_config_files.py diff --git a/invokeai/app/invocations/flux_model_loader.py b/invokeai/app/invocations/flux_model_loader.py index e5a1966c659..4ed3b91bc61 100644 --- a/invokeai/app/invocations/flux_model_loader.py +++ b/invokeai/app/invocations/flux_model_loader.py @@ -13,7 +13,7 @@ preprocess_t5_encoder_model_identifier, preprocess_t5_tokenizer_model_identifier, ) -from invokeai.backend.flux.util import max_seq_lengths +from invokeai.backend.flux.util import get_flux_max_seq_length from invokeai.backend.model_manager.config import ( CheckpointConfigBase, ) @@ -94,5 +94,5 @@ def invoke(self, context: InvocationContext) -> FluxModelLoaderOutput: clip=CLIPField(tokenizer=tokenizer, text_encoder=clip_encoder, loras=[], skipped_layers=0), t5_encoder=T5EncoderField(tokenizer=tokenizer2, text_encoder=t5_encoder, loras=[]), vae=VAEField(vae=vae), - max_seq_len=max_seq_lengths[transformer_config.config_path], + max_seq_len=get_flux_max_seq_length(transformer_config.variant), ) diff --git a/invokeai/app/services/model_install/model_install_default.py b/invokeai/app/services/model_install/model_install_default.py index 454697ea5a1..5bc9af8e6b3 100644 --- a/invokeai/app/services/model_install/model_install_default.py +++ b/invokeai/app/services/model_install/model_install_default.py @@ -5,6 +5,7 @@ import re import threading import time +from copy import deepcopy from pathlib import Path from queue import Empty, Queue from shutil import move, rmtree @@ -370,6 +371,8 @@ def unconditionally_delete(self, key: str) -> None: # noqa D102 model_path = self.app_config.models_path / model.path if model_path.is_file() or model_path.is_symlink(): model_path.unlink() + assert model_path.parent != self.app_config.models_path + os.rmdir(model_path.parent) elif model_path.is_dir(): rmtree(model_path) self.unregister(key) @@ -607,9 +610,9 @@ def _probe(self, model_path: Path, config: Optional[ModelRecordChanges] = None): # any given model - eliminating ambiguity and removing reliance on order. # After implementing either of these fixes, remove @pytest.mark.xfail from `test_regression_against_model_probe` try: - return ModelProbe.probe(model_path=model_path, fields=fields, hash_algo=hash_algo) # type: ignore + return ModelProbe.probe(model_path=model_path, fields=deepcopy(fields), hash_algo=hash_algo) # type: ignore except InvalidModelConfigException: - return ModelConfigBase.classify(model_path, hash_algo, **fields) + return ModelConfigBase.classify(mod=model_path, hash_algo=hash_algo, **fields) def _register( self, model_path: Path, config: Optional[ModelRecordChanges] = None, info: Optional[AnyModelConfig] = None diff --git a/invokeai/app/services/model_records/model_records_base.py b/invokeai/app/services/model_records/model_records_base.py index 740d548a4a3..48f53175364 100644 --- a/invokeai/app/services/model_records/model_records_base.py +++ b/invokeai/app/services/model_records/model_records_base.py @@ -21,6 +21,7 @@ from invokeai.backend.model_manager.taxonomy import ( BaseModelType, ClipVariantType, + FluxVariantType, ModelFormat, ModelSourceType, ModelType, @@ -90,7 +91,9 @@ class ModelRecordChanges(BaseModelExcludeNull): # Checkpoint-specific changes # TODO(MM2): Should we expose these? Feels footgun-y... - variant: Optional[ModelVariantType | ClipVariantType] = Field(description="The variant of the model.", default=None) + variant: Optional[ModelVariantType | ClipVariantType | FluxVariantType] = 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/services/model_records/model_records_sql.py b/invokeai/app/services/model_records/model_records_sql.py index e3b24a6e626..9d4892a1416 100644 --- a/invokeai/app/services/model_records/model_records_sql.py +++ b/invokeai/app/services/model_records/model_records_sql.py @@ -141,10 +141,25 @@ def update_model(self, key: str, changes: ModelRecordChanges) -> AnyModelConfig: with self._db.transaction() as cursor: record = self.get_model(key) - # Model configs use pydantic's `validate_assignment`, so each change is validated by pydantic. + # The changes may mean the model config class changes. So we need to: + # + # 1. convert the existing record to a dict + # 2. apply the changes to the dict + # 3. create a new model config from the updated dict + # + # This way we ensure that the update does not inadvertently create an invalid model config. + + # 1. convert the existing record to a dict + record_as_dict = record.model_dump() + + # 2. apply the changes to the dict for field_name in changes.model_fields_set: - setattr(record, field_name, getattr(changes, field_name)) + record_as_dict[field_name] = getattr(changes, field_name) + # 3. create a new model config from the updated dict + record = ModelConfigFactory.make_config(record_as_dict) + + # If we get this far, the updated model config is valid, so we can save it to the database. json_serialized = record.model_dump_json() cursor.execute( diff --git a/invokeai/app/services/shared/sqlite_migrator/migrations/migration_22.py b/invokeai/app/services/shared/sqlite_migrator/migrations/migration_22.py index c79b58bf2ad..08b0e760686 100644 --- a/invokeai/app/services/shared/sqlite_migrator/migrations/migration_22.py +++ b/invokeai/app/services/shared/sqlite_migrator/migrations/migration_22.py @@ -8,7 +8,7 @@ from invokeai.app.services.config import InvokeAIAppConfig from invokeai.app.services.shared.sqlite_migrator.sqlite_migrator_common import Migration -from invokeai.backend.model_manager.config import AnyModelConfig, AnyModelConfigValidator +from invokeai.backend.model_manager.config import AnyModelConfigValidator class NormalizeResult(NamedTuple): @@ -30,7 +30,7 @@ def __call__(self, cursor: sqlite3.Cursor) -> None: for model_id, config_json in rows: try: # Get the model config as a pydantic object - config = self._load_model_config(config_json) + config = AnyModelConfigValidator.validate_json(config_json) except ValidationError: # This could happen if the config schema changed in a way that makes old configs invalid. Unlikely # for users, more likely for devs testing out migration paths. @@ -216,11 +216,6 @@ def _prune_empty_directories(self) -> None: self._logger.info("Pruned %d empty directories under %s", len(removed_dirs), self._models_dir) - def _load_model_config(self, config_json: str) -> AnyModelConfig: - # The typing of the validator says it returns Unknown, but it's really a AnyModelConfig. This utility function - # just makes that clear. - return AnyModelConfigValidator.validate_json(config_json) - def build_migration_22(app_config: InvokeAIAppConfig, logger: Logger) -> Migration: """Builds the migration object for migrating from version 21 to version 22. diff --git a/invokeai/backend/flux/util.py b/invokeai/backend/flux/util.py index 2a5261cb5c6..2cf52b6ec11 100644 --- a/invokeai/backend/flux/util.py +++ b/invokeai/backend/flux/util.py @@ -1,10 +1,11 @@ # Initially pulled from https://github.com/black-forest-labs/flux from dataclasses import dataclass -from typing import Dict, Literal +from typing import Literal from invokeai.backend.flux.model import FluxParams from invokeai.backend.flux.modules.autoencoder import AutoEncoderParams +from invokeai.backend.model_manager.taxonomy import AnyVariant, FluxVariantType @dataclass @@ -41,30 +42,39 @@ class ModelSpec: ] -max_seq_lengths: Dict[str, Literal[256, 512]] = { - "flux-dev": 512, - "flux-dev-fill": 512, - "flux-schnell": 256, +_flux_max_seq_lengths: dict[AnyVariant, Literal[256, 512]] = { + FluxVariantType.Dev: 512, + FluxVariantType.DevFill: 512, + FluxVariantType.Schnell: 256, } -ae_params = { - "flux": AutoEncoderParams( - resolution=256, - in_channels=3, - ch=128, - out_ch=3, - ch_mult=[1, 2, 4, 4], - num_res_blocks=2, - z_channels=16, - scale_factor=0.3611, - shift_factor=0.1159, - ) -} +def get_flux_max_seq_length(variant: AnyVariant): + try: + return _flux_max_seq_lengths[variant] + except KeyError: + raise ValueError(f"Unknown variant for FLUX max seq len: {variant}") + + +_flux_ae_params = AutoEncoderParams( + resolution=256, + in_channels=3, + ch=128, + out_ch=3, + ch_mult=[1, 2, 4, 4], + num_res_blocks=2, + z_channels=16, + scale_factor=0.3611, + shift_factor=0.1159, +) + +def get_flux_ae_params() -> AutoEncoderParams: + return _flux_ae_params -params = { - "flux-dev": FluxParams( + +_flux_transformer_params: dict[AnyVariant, FluxParams] = { + FluxVariantType.Dev: FluxParams( in_channels=64, vec_in_dim=768, context_in_dim=4096, @@ -78,7 +88,7 @@ class ModelSpec: qkv_bias=True, guidance_embed=True, ), - "flux-schnell": FluxParams( + FluxVariantType.Schnell: FluxParams( in_channels=64, vec_in_dim=768, context_in_dim=4096, @@ -92,7 +102,7 @@ class ModelSpec: qkv_bias=True, guidance_embed=False, ), - "flux-dev-fill": FluxParams( + FluxVariantType.DevFill: FluxParams( in_channels=384, out_channels=64, vec_in_dim=768, @@ -108,3 +118,10 @@ class ModelSpec: guidance_embed=True, ), } + + +def get_flux_transformers_params(variant: AnyVariant): + try: + return _flux_transformer_params[variant] + except KeyError: + raise ValueError(f"Unknown variant for FLUX transformer params: {variant}") diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 83f0c1d2bf5..b5523281539 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -44,6 +44,7 @@ BaseModelType, ClipVariantType, FluxLoRAFormat, + FluxVariantType, ModelFormat, ModelRepoVariant, ModelSourceType, @@ -51,6 +52,7 @@ ModelVariantType, SchedulerPredictionType, SubModelType, + variant_type_adapter, ) from invokeai.backend.model_manager.util.model_util import lora_token_vector_length from invokeai.backend.stable_diffusion.schedulers.schedulers import SCHEDULER_NAME_VALUES @@ -71,7 +73,7 @@ class InvalidModelConfigException(Exception): class SubmodelDefinition(BaseModel): path_or_prefix: str model_type: ModelType - variant: AnyVariant = None + variant: AnyVariant | None = None model_config = ConfigDict(protected_namespaces=()) @@ -111,6 +113,15 @@ class MatchSpeed(int, Enum): SLOW = 2 +class MatchCertainty(int, Enum): + """Represents the certainty of a config's 'matches' method.""" + + NEVER = 0 + MAYBE = 1 + EXACT = 2 + OVERRIDE = 3 + + class LegacyProbeMixin: """Mixin for classes using the legacy probe for model classification.""" @@ -194,20 +205,47 @@ def classify( Created to deprecate ModelProbe.probe """ if isinstance(mod, Path | str): - mod = ModelOnDisk(mod, hash_algo) + mod = ModelOnDisk(Path(mod), hash_algo) candidates = ModelConfigBase.USING_CLASSIFY_API sorted_by_match_speed = sorted(candidates, key=lambda cls: (cls._MATCH_SPEED, cls.__name__)) + overrides = overrides or {} + ModelConfigBase.cast_overrides(**overrides) + + matches: dict[Type[ModelConfigBase], MatchCertainty] = {} + for config_cls in sorted_by_match_speed: try: - if not config_cls.matches(mod): + score = config_cls.matches(mod, **overrides) + + # A score of 0 means "no match" + if score is MatchCertainty.NEVER: continue + + matches[config_cls] = score + + if score is MatchCertainty.EXACT or score is MatchCertainty.OVERRIDE: + # Perfect match - skip further checks + break except Exception as e: logger.warning(f"Unexpected exception while matching {mod.name} to '{config_cls.__name__}': {e}") continue - else: - return config_cls.from_model_on_disk(mod, **overrides) + + if matches: + # Select the config class with the highest score + sorted_by_score = sorted(matches.items(), key=lambda item: item[1].value) + # Check if there are multiple classes with the same top score + top_score = sorted_by_score[-1][1] + top_classes = [cls for cls, score in sorted_by_score if score is top_score] + if len(top_classes) > 1: + logger.warning( + f"Multiple model config classes matched with the same top score ({top_score}) for model {mod.name}: {[cls.__name__ for cls in top_classes]}. Using {top_classes[0].__name__}." + ) + config_cls = top_classes[0] + # Finally, create the config instance + logger.info(f"Model {mod.name} classified as {config_cls.__name__} with score {top_score.name}") + return config_cls.from_model_on_disk(mod, **overrides) if app_config.allow_unknown_models: try: @@ -234,13 +272,13 @@ def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: @classmethod @abstractmethod - def matches(cls, mod: ModelOnDisk) -> bool: + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: """Performs a quick check to determine if the config matches the model. - This doesn't need to be a perfect test - the aim is to eliminate unlikely matches quickly before parsing.""" + Returns a MatchCertainty score.""" pass @staticmethod - def cast_overrides(overrides: dict[str, Any]): + def cast_overrides(**overrides): """Casts user overrides from str to Enum""" if "type" in overrides: overrides["type"] = ModelType(overrides["type"]) @@ -255,13 +293,13 @@ def cast_overrides(overrides: dict[str, Any]): overrides["source_type"] = ModelSourceType(overrides["source_type"]) if "variant" in overrides: - overrides["variant"] = ModelVariantType(overrides["variant"]) + overrides["variant"] = variant_type_adapter.validate_strings(overrides["variant"]) @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, **overrides): """Creates an instance of this config or raises InvalidModelConfigException.""" fields = cls.parse(mod) - cls.cast_overrides(overrides) + cls.cast_overrides(**overrides) fields.update(overrides) fields["path"] = mod.path.as_posix() @@ -283,8 +321,8 @@ class UnknownModelConfig(ModelConfigBase): format: Literal[ModelFormat.Unknown] = ModelFormat.Unknown @classmethod - def matches(cls, mod: ModelOnDisk) -> bool: - return False + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + return MatchCertainty.NEVER @classmethod def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: @@ -370,17 +408,34 @@ class LoRAOmiConfig(LoRAConfigBase, ModelConfigBase): format: Literal[ModelFormat.OMI] = ModelFormat.OMI @classmethod - def matches(cls, mod: ModelOnDisk) -> bool: + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + is_lora_override = overrides.get("type") is ModelType.LoRA + is_omi_override = overrides.get("format") is ModelFormat.OMI + + # If both type and format are overridden, skip the heuristic checks + if is_lora_override and is_omi_override: + return MatchCertainty.OVERRIDE + + # OMI LoRAs are always files, never directories if mod.path.is_dir(): - return False + return MatchCertainty.NEVER + + # Avoid false positive match against ControlLoRA and Diffusers + if cls.flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: + return MatchCertainty.NEVER metadata = mod.metadata() - return ( + is_omi_lora_heuristic = ( bool(metadata.get("modelspec.sai_model_spec")) and metadata.get("ot_branch") == "omi_format" - and metadata["modelspec.architecture"].split("/")[1].lower() == "lora" + and metadata.get("modelspec.architecture", "").split("/")[1].lower() == "lora" ) + if is_omi_lora_heuristic: + return MatchCertainty.EXACT + + return MatchCertainty.NEVER + @classmethod def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: metadata = mod.metadata() @@ -402,27 +457,55 @@ class LoRALyCORISConfig(LoRAConfigBase, ModelConfigBase): format: Literal[ModelFormat.LyCORIS] = ModelFormat.LyCORIS @classmethod - def matches(cls, mod: ModelOnDisk) -> bool: + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + is_lora_override = overrides.get("type") is ModelType.LoRA + is_omi_override = overrides.get("format") is ModelFormat.LyCORIS + + # If both type and format are overridden, skip the heuristic checks and return a perfect score + if is_lora_override and is_omi_override: + return MatchCertainty.OVERRIDE + + # LyCORIS LoRAs are always files, never directories if mod.path.is_dir(): - return False + return MatchCertainty.NEVER # Avoid false positive match against ControlLoRA and Diffusers if cls.flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: - return False + return MatchCertainty.NEVER state_dict = mod.load_state_dict() for key in state_dict.keys(): if isinstance(key, int): continue - if key.startswith(("lora_te_", "lora_unet_", "lora_te1_", "lora_te2_", "lora_transformer_")): - return True + # Existence of these key prefixes/suffixes does not guarantee that this is a LoRA. + # Some main models have these keys, likely due to the creator merging in a LoRA. + + has_key_with_lora_prefix = key.startswith( + ( + "lora_te_", + "lora_unet_", + "lora_te1_", + "lora_te2_", + "lora_transformer_", + ) + ) + # "lora_A.weight" and "lora_B.weight" are associated with models in PEFT format. We don't support all PEFT # LoRA models, but as of the time of writing, we support Diffusers FLUX PEFT LoRA models. - if key.endswith(("to_k_lora.up.weight", "to_q_lora.down.weight", "lora_A.weight", "lora_B.weight")): - return True + has_key_with_lora_suffix = key.endswith( + ( + "to_k_lora.up.weight", + "to_q_lora.down.weight", + "lora_A.weight", + "lora_B.weight", + ) + ) - return False + if has_key_with_lora_prefix or has_key_with_lora_suffix: + return MatchCertainty.MAYBE + + return MatchCertainty.NEVER @classmethod def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: @@ -459,13 +542,31 @@ class LoRADiffusersConfig(LoRAConfigBase, ModelConfigBase): format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers @classmethod - def matches(cls, mod: ModelOnDisk) -> bool: + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + is_lora_override = overrides.get("type") is ModelType.LoRA + is_diffusers_override = overrides.get("format") is ModelFormat.Diffusers + + # If both type and format are overridden, skip the heuristic checks and return a perfect score + if is_lora_override and is_diffusers_override: + return MatchCertainty.OVERRIDE + + # Diffusers LoRAs are always directories, never files if mod.path.is_file(): - return cls.flux_lora_format(mod) == FluxLoRAFormat.Diffusers + return MatchCertainty.NEVER + + is_flux_lora_diffusers = cls.flux_lora_format(mod) == FluxLoRAFormat.Diffusers suffixes = ["bin", "safetensors"] weight_files = [mod.path / f"pytorch_lora_weights.{sfx}" for sfx in suffixes] - return any(wf.exists() for wf in weight_files) + has_lora_weight_file = any(wf.exists() for wf in weight_files) + + if is_flux_lora_diffusers and has_lora_weight_file: + return MatchCertainty.EXACT + + if is_flux_lora_diffusers or has_lora_weight_file: + return MatchCertainty.MAYBE + + return MatchCertainty.NEVER @classmethod def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: @@ -520,7 +621,7 @@ class MainConfigBase(ABC, BaseModel): default_settings: Optional[MainModelDefaultSettings] = Field( description="Default settings for this model", default=None ) - variant: AnyVariant = ModelVariantType.Normal + variant: ModelVariantType | FluxVariantType = ModelVariantType.Normal class VideoConfigBase(ABC, BaseModel): @@ -529,7 +630,7 @@ class VideoConfigBase(ABC, BaseModel): default_settings: Optional[MainModelDefaultSettings] = Field( description="Default settings for this model", default=None ) - variant: AnyVariant = ModelVariantType.Normal + variant: ModelVariantType = ModelVariantType.Normal class MainCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase): @@ -538,6 +639,14 @@ class MainCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixi prediction_type: SchedulerPredictionType = SchedulerPredictionType.Epsilon upcast_attention: bool = False + # @classmethod + # def matches(cls, mod: ModelOnDisk) -> bool: + # pass + + # @classmethod + # def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: + # pass + class MainBnbQuantized4bCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for main checkpoint models.""" @@ -583,12 +692,51 @@ class IPAdapterCheckpointConfig(IPAdapterConfigBase, LegacyProbeMixin, ModelConf class CLIPEmbedDiffusersConfig(DiffusersConfigBase): """Model config for Clip Embeddings.""" - variant: ClipVariantType = Field(description="Clip variant for this model") + variant: ClipVariantType = Field(...) type: Literal[ModelType.CLIPEmbed] = ModelType.CLIPEmbed format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers + base: Literal[BaseModelType.Any] = BaseModelType.Any + + @classmethod + def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: + clip_variant = cls.get_clip_variant_type(mod) + if clip_variant is None: + raise InvalidModelConfigException("Unable to determine CLIP variant type") + return {"variant": clip_variant} -class CLIPGEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, LegacyProbeMixin, ModelConfigBase): + @classmethod + def get_clip_variant_type(cls, mod: ModelOnDisk) -> ClipVariantType | None: + try: + with open(mod.path / "config.json") as file: + config = json.load(file) + hidden_size = config.get("hidden_size") + match hidden_size: + case 1280: + return ClipVariantType.G + case 768: + return ClipVariantType.L + case _: + return None + except Exception: + return None + + @classmethod + def is_clip_text_encoder(cls, mod: ModelOnDisk) -> bool: + try: + with open(mod.path / "config.json", "r") as file: + config = json.load(file) + architectures = config.get("architectures") + return architectures[0] in ( + "CLIPModel", + "CLIPTextModel", + "CLIPTextModelWithProjection", + ) + except Exception: + return False + + +class CLIPGEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): """Model config for CLIP-G Embeddings.""" variant: Literal[ClipVariantType.G] = ClipVariantType.G @@ -597,8 +745,28 @@ class CLIPGEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, LegacyProbeMixin, Mode def get_tag(cls) -> Tag: return Tag(f"{ModelType.CLIPEmbed.value}.{ModelFormat.Diffusers.value}.{ClipVariantType.G.value}") + @classmethod + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + is_clip_embed_override = overrides.get("type") is ModelType.CLIPEmbed + is_diffusers_override = overrides.get("format") is ModelFormat.Diffusers + has_clip_variant_override = overrides.get("variant") is ClipVariantType.G + + if is_clip_embed_override and is_diffusers_override and has_clip_variant_override: + return MatchCertainty.OVERRIDE + + if mod.path.is_file(): + return MatchCertainty.NEVER + + is_clip_embed = cls.is_clip_text_encoder(mod) + clip_variant = cls.get_clip_variant_type(mod) -class CLIPLEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, LegacyProbeMixin, ModelConfigBase): + if is_clip_embed and clip_variant is ClipVariantType.G: + return MatchCertainty.EXACT + + return MatchCertainty.NEVER + + +class CLIPLEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): """Model config for CLIP-L Embeddings.""" variant: Literal[ClipVariantType.L] = ClipVariantType.L @@ -607,6 +775,26 @@ class CLIPLEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, LegacyProbeMixin, Mode def get_tag(cls) -> Tag: return Tag(f"{ModelType.CLIPEmbed.value}.{ModelFormat.Diffusers.value}.{ClipVariantType.L.value}") + @classmethod + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + is_clip_embed_override = overrides.get("type") is ModelType.CLIPEmbed + is_diffusers_override = overrides.get("format") is ModelFormat.Diffusers + has_clip_variant_override = overrides.get("variant") is ClipVariantType.L + + if is_clip_embed_override and is_diffusers_override and has_clip_variant_override: + return MatchCertainty.OVERRIDE + + if mod.path.is_file(): + return MatchCertainty.NEVER + + is_clip_embed = cls.is_clip_text_encoder(mod) + clip_variant = cls.get_clip_variant_type(mod) + + if is_clip_embed and clip_variant is ClipVariantType.L: + return MatchCertainty.EXACT + + return MatchCertainty.NEVER + class CLIPVisionDiffusersConfig(DiffusersConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for CLIPVision.""" @@ -649,22 +837,30 @@ class LlavaOnevisionConfig(DiffusersConfigBase, ModelConfigBase): """Model config for Llava Onevision models.""" type: Literal[ModelType.LlavaOnevision] = ModelType.LlavaOnevision - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers @classmethod - def matches(cls, mod: ModelOnDisk) -> bool: + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + is_llava_override = overrides.get("type") is ModelType.LlavaOnevision + is_diffusers_override = overrides.get("format") is ModelFormat.Diffusers + + if is_llava_override and is_diffusers_override: + return MatchCertainty.OVERRIDE + if mod.path.is_file(): - return False + return MatchCertainty.NEVER config_path = mod.path / "config.json" try: with open(config_path, "r") as file: config = json.load(file) except FileNotFoundError: - return False + return MatchCertainty.NEVER architectures = config.get("architectures") - return architectures and architectures[0] == "LlavaOnevisionForConditionalGeneration" + if architectures and architectures[0] == "LlavaOnevisionForConditionalGeneration": + return MatchCertainty.EXACT + + return MatchCertainty.NEVER @classmethod def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: @@ -680,9 +876,9 @@ class ApiModelConfig(MainConfigBase, ModelConfigBase): format: Literal[ModelFormat.Api] = ModelFormat.Api @classmethod - def matches(cls, mod: ModelOnDisk) -> bool: + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: # API models are not stored on disk, so we can't match them. - return False + return MatchCertainty.NEVER @classmethod def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: @@ -695,9 +891,9 @@ class VideoApiModelConfig(VideoConfigBase, ModelConfigBase): format: Literal[ModelFormat.Api] = ModelFormat.Api @classmethod - def matches(cls, mod: ModelOnDisk) -> bool: + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: # API models are not stored on disk, so we can't match them. - return False + return MatchCertainty.NEVER @classmethod def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: @@ -735,8 +931,7 @@ def get_model_discriminator_value(v: Any) -> str: # Previously, CLIPEmbed did not have any variants, meaning older database entries lack a variant field. # To maintain compatibility, we default to ClipVariantType.L in this case. - if type_ == ModelType.CLIPEmbed.value and format_ == ModelFormat.Diffusers.value: - variant_ = variant_ or ClipVariantType.L.value + if type_ == ModelType.CLIPEmbed.value: return f"{type_}.{format_}.{variant_}" return f"{type_}.{format_}" @@ -779,7 +974,7 @@ def get_model_discriminator_value(v: Any) -> str: Discriminator(get_model_discriminator_value), ] -AnyModelConfigValidator = TypeAdapter(AnyModelConfig) +AnyModelConfigValidator = TypeAdapter[AnyModelConfig](AnyModelConfig) AnyDefaultSettings: TypeAlias = Union[MainModelDefaultSettings, LoraModelDefaultSettings, ControlAdapterDefaultSettings] @@ -787,8 +982,8 @@ class ModelConfigFactory: @staticmethod def make_config(model_data: Dict[str, Any], timestamp: Optional[float] = None) -> AnyModelConfig: """Return the appropriate config object from raw dict values.""" - model = AnyModelConfigValidator.validate_python(model_data) # type: ignore + model = AnyModelConfigValidator.validate_python(model_data) if isinstance(model, CheckpointConfigBase) and timestamp: model.converted_at = timestamp validate_hash(model.hash) - return model # type: ignore + return model diff --git a/invokeai/backend/model_manager/legacy_probe.py b/invokeai/backend/model_manager/legacy_probe.py index 36fd82667d7..3d3915353da 100644 --- a/invokeai/backend/model_manager/legacy_probe.py +++ b/invokeai/backend/model_manager/legacy_probe.py @@ -33,6 +33,7 @@ from invokeai.backend.model_manager.taxonomy import ( AnyVariant, BaseModelType, + FluxVariantType, ModelFormat, ModelRepoVariant, ModelSourceType, @@ -63,6 +64,7 @@ CkptType = Dict[str | int, Any] + LEGACY_CONFIGS: Dict[BaseModelType, Dict[ModelVariantType, Union[str, Dict[SchedulerPredictionType, str]]]] = { BaseModelType.StableDiffusion1: { ModelVariantType.Normal: { @@ -106,7 +108,7 @@ def get_format(self) -> ModelFormat: """Get model file format.""" raise NotImplementedError - def get_variant_type(self) -> Optional[ModelVariantType]: + def get_variant_type(self) -> AnyVariant | None: """Get model variant type.""" return None @@ -256,7 +258,7 @@ def probe( if fields["base"] == BaseModelType.StableDiffusion3 and callable(get_submodels): fields["submodels"] = get_submodels() - model_info = ModelConfigFactory.make_config(fields) # , key=fields.get("key", None)) + model_info = ModelConfigFactory.make_config(fields) return model_info @classmethod @@ -580,7 +582,7 @@ def get_format(self) -> ModelFormat: return ModelFormat.GGUFQuantized return ModelFormat("checkpoint") - def get_variant_type(self) -> ModelVariantType: + def get_variant_type(self) -> AnyVariant: model_type = ModelProbe.get_model_type_from_checkpoint(self.model_path, self.checkpoint) base_type = self.get_base_type() if model_type != ModelType.Main: @@ -597,19 +599,26 @@ def get_variant_type(self) -> ModelVariantType: ) return ModelVariantType.Normal + is_flux_dev = ( + "guidance_in.out_layer.weight" in state_dict + or "model.diffusion_model.guidance_in.out_layer.weight" in state_dict + ) + # FLUX Model variant types are distinguished by input channels: # - Unquantized Dev and Schnell have in_channels=64 # - BNB-NF4 Dev and Schnell have in_channels=1 # - FLUX Fill has in_channels=384 # - Unsure of quantized FLUX Fill models # - Unsure of GGUF-quantized models - if in_channels == 384: + if is_flux_dev and in_channels == 384: # This is a FLUX Fill model. FLUX Fill needs special handling throughout the application. The variant # type is used to determine whether to use the fill model or the base model. - return ModelVariantType.Inpaint - else: + return FluxVariantType.DevFill + elif is_flux_dev: # Fall back on "normal" variant type for all other FLUX models. - return ModelVariantType.Normal + return FluxVariantType.Dev + else: + return FluxVariantType.Schnell in_channels = state_dict["model.diffusion_model.input_blocks.0.0.weight"].shape[1] if in_channels == 9: diff --git a/invokeai/backend/model_manager/load/model_loaders/flux.py b/invokeai/backend/model_manager/load/model_loaders/flux.py index 6ea7b539252..ccb962e747f 100644 --- a/invokeai/backend/model_manager/load/model_loaders/flux.py +++ b/invokeai/backend/model_manager/load/model_loaders/flux.py @@ -33,10 +33,11 @@ from invokeai.backend.flux.model import Flux from invokeai.backend.flux.modules.autoencoder import AutoEncoder from invokeai.backend.flux.redux.flux_redux_model import FluxReduxModel -from invokeai.backend.flux.util import ae_params, params +from invokeai.backend.flux.util import get_flux_ae_params, get_flux_transformers_params from invokeai.backend.model_manager.config import ( AnyModelConfig, CheckpointConfigBase, + CLIPEmbedCheckpointConfig, CLIPEmbedDiffusersConfig, ControlNetCheckpointConfig, ControlNetDiffusersConfig, @@ -56,6 +57,7 @@ BaseModelType, ModelFormat, ModelType, + ModelVariantType, SubModelType, ) from invokeai.backend.model_manager.util.model_util import ( @@ -90,7 +92,7 @@ def _load_model( model_path = Path(config.path) with accelerate.init_empty_weights(): - model = AutoEncoder(ae_params[config.config_path]) + model = AutoEncoder(get_flux_ae_params()) sd = load_file(model_path) model.load_state_dict(sd, assign=True) # VAE is broken in float16, which mps defaults to @@ -107,7 +109,7 @@ def _load_model( @ModelLoaderRegistry.register(base=BaseModelType.Any, type=ModelType.CLIPEmbed, format=ModelFormat.Diffusers) -class ClipCheckpointModel(ModelLoader): +class CLIPDiffusersLoader(ModelLoader): """Class to load main models.""" def _load_model( @@ -129,6 +131,27 @@ def _load_model( ) +@ModelLoaderRegistry.register(base=BaseModelType.Any, type=ModelType.CLIPEmbed, format=ModelFormat.Checkpoint) +class CLIPCheckpointLoader(ModelLoader): + """Class to load main models.""" + + def _load_model( + self, + config: AnyModelConfig, + submodel_type: Optional[SubModelType] = None, + ) -> AnyModel: + if not isinstance(config, CLIPEmbedCheckpointConfig): + raise ValueError("Only CLIPEmbedCheckpointConfig models are currently supported here.") + + match submodel_type: + case SubModelType.TextEncoder: + return CLIPTextModel.from_pretrained(Path(config.path), use_safetensors=True) + case _: + raise ValueError( + f"Only TextEncoder submodels are currently supported. Received: {submodel_type.value if submodel_type else 'None'}" + ) + + @ModelLoaderRegistry.register(base=BaseModelType.Any, type=ModelType.T5Encoder, format=ModelFormat.BnbQuantizedLlmInt8b) class BnbQuantizedLlmInt8bCheckpointModel(ModelLoader): """Class to load main models.""" @@ -229,7 +252,7 @@ def _load_from_singlefile( model_path = Path(config.path) with accelerate.init_empty_weights(): - model = Flux(params[config.config_path]) + model = Flux(get_flux_transformers_params(config.variant)) sd = load_file(model_path) if "model.diffusion_model.double_blocks.0.img_attn.norm.key_norm.scale" in sd: @@ -271,7 +294,7 @@ def _load_from_singlefile( model_path = Path(config.path) with accelerate.init_empty_weights(): - model = Flux(params[config.config_path]) + model = Flux(get_flux_transformers_params(config.variant)) # HACK(ryand): We shouldn't be hard-coding the compute_dtype here. sd = gguf_sd_loader(model_path, compute_dtype=torch.bfloat16) @@ -322,7 +345,7 @@ def _load_from_singlefile( with SilenceWarnings(): with accelerate.init_empty_weights(): - model = Flux(params[config.config_path]) + model = Flux(get_flux_transformers_params(config.variant)) model = quantize_model_nf4(model, modules_to_not_convert=set(), compute_dtype=torch.bfloat16) sd = load_file(model_path) if "model.diffusion_model.double_blocks.0.img_attn.norm.key_norm.scale" in sd: @@ -362,7 +385,7 @@ def _load_model( def _load_xlabs_controlnet(self, sd: dict[str, torch.Tensor]) -> AnyModel: with accelerate.init_empty_weights(): # HACK(ryand): Is it safe to assume dev here? - model = XLabsControlNetFlux(params["flux-dev"]) + model = XLabsControlNetFlux(get_flux_transformers_params(ModelVariantType.FluxDev)) model.load_state_dict(sd, assign=True) return model diff --git a/invokeai/backend/model_manager/single_file_config_files.py b/invokeai/backend/model_manager/single_file_config_files.py new file mode 100644 index 00000000000..22fe646b550 --- /dev/null +++ b/invokeai/backend/model_manager/single_file_config_files.py @@ -0,0 +1,86 @@ +from dataclasses import dataclass + +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ModelType, + ModelVariantType, + SchedulerPredictionType, +) + + +@dataclass(frozen=True) +class LegacyConfigKey: + type: ModelType + base: BaseModelType + variant: ModelVariantType | None = None + pred: SchedulerPredictionType | None = None + + +LEGACY_CONFIG_MAP: dict[LegacyConfigKey, str] = { + LegacyConfigKey( + ModelType.Main, + BaseModelType.StableDiffusion1, + ModelVariantType.Normal, + SchedulerPredictionType.Epsilon, + ): "stable-diffusion/v1-inference.yaml", + LegacyConfigKey( + ModelType.Main, + BaseModelType.StableDiffusion1, + ModelVariantType.Normal, + SchedulerPredictionType.VPrediction, + ): "stable-diffusion/v1-inference-v.yaml", + LegacyConfigKey( + ModelType.Main, + BaseModelType.StableDiffusion1, + ModelVariantType.Inpaint, + ): "stable-diffusion/v1-inpainting-inference.yaml", + LegacyConfigKey( + ModelType.Main, + BaseModelType.StableDiffusion2, + ModelVariantType.Normal, + SchedulerPredictionType.Epsilon, + ): "stable-diffusion/v2-inference.yaml", + LegacyConfigKey( + ModelType.Main, + BaseModelType.StableDiffusion2, + ModelVariantType.Normal, + SchedulerPredictionType.VPrediction, + ): "stable-diffusion/v2-inference-v.yaml", + LegacyConfigKey( + ModelType.Main, + BaseModelType.StableDiffusion2, + ModelVariantType.Inpaint, + SchedulerPredictionType.Epsilon, + ): "stable-diffusion/v2-inpainting-inference.yaml", + LegacyConfigKey( + ModelType.Main, + BaseModelType.StableDiffusion2, + ModelVariantType.Inpaint, + SchedulerPredictionType.VPrediction, + ): "stable-diffusion/v2-inpainting-inference-v.yaml", + LegacyConfigKey( + ModelType.Main, + BaseModelType.StableDiffusion2, + ModelVariantType.Depth, + ): "stable-diffusion/v2-midas-inference.yaml", + LegacyConfigKey( + ModelType.Main, + BaseModelType.StableDiffusionXL, + ModelVariantType.Normal, + ): "stable-diffusion/sd_xl_base.yaml", + LegacyConfigKey( + ModelType.Main, + BaseModelType.StableDiffusionXL, + ModelVariantType.Inpaint, + ): "stable-diffusion/sd_xl_inpaint.yaml", + LegacyConfigKey( + ModelType.Main, + BaseModelType.StableDiffusionXLRefiner, + ModelVariantType.Normal, + ): "stable-diffusion/sd_xl_refiner.yaml", + LegacyConfigKey(ModelType.ControlNet, BaseModelType.StableDiffusion1): "controlnet/cldm_v15.yaml", + LegacyConfigKey(ModelType.ControlNet, BaseModelType.StableDiffusion2): "controlnet/cldm_v21.yaml", + LegacyConfigKey(ModelType.VAE, BaseModelType.StableDiffusion1): "stable-diffusion/v1-inference.yaml", + LegacyConfigKey(ModelType.VAE, BaseModelType.StableDiffusion2): "stable-diffusion/v2-inference.yaml", + LegacyConfigKey(ModelType.VAE, BaseModelType.StableDiffusionXL): "stable-diffusion/sd_xl_base.yaml", +} diff --git a/invokeai/backend/model_manager/taxonomy.py b/invokeai/backend/model_manager/taxonomy.py index 07f8c8f5def..15a3d3ce724 100644 --- a/invokeai/backend/model_manager/taxonomy.py +++ b/invokeai/backend/model_manager/taxonomy.py @@ -5,6 +5,7 @@ import onnxruntime as ort import torch from diffusers import ModelMixin +from pydantic import TypeAdapter from invokeai.backend.raw_model import RawModel @@ -30,6 +31,7 @@ class BaseModelType(str, Enum): Imagen4 = "imagen4" Gemini2_5 = "gemini-2.5" ChatGPT4o = "chatgpt-4o" + # This is actually the FLUX Kontext API model. Local FLUX Kontext is just BaseModelType.Flux. FluxKontext = "flux-kontext" Veo3 = "veo3" Runway = "runway" @@ -92,6 +94,12 @@ class ModelVariantType(str, Enum): Depth = "depth" +class FluxVariantType(str, Enum): + Schnell = "schnell" + Dev = "dev" + DevFill = "dev_fill" + + class ModelFormat(str, Enum): """Storage format of model.""" @@ -149,4 +157,7 @@ class FluxLoRAFormat(str, Enum): AIToolkit = "flux.aitoolkit" -AnyVariant: TypeAlias = Union[ModelVariantType, ClipVariantType, None] +AnyVariant: TypeAlias = Union[ModelVariantType, ClipVariantType, FluxVariantType] +variant_type_adapter = TypeAdapter[ModelVariantType | ClipVariantType | FluxVariantType]( + ModelVariantType | ClipVariantType | FluxVariantType +) diff --git a/invokeai/backend/patches/lora_conversions/flux_aitoolkit_lora_conversion_utils.py b/invokeai/backend/patches/lora_conversions/flux_aitoolkit_lora_conversion_utils.py index 6ca06a0355f..db218d14bbe 100644 --- a/invokeai/backend/patches/lora_conversions/flux_aitoolkit_lora_conversion_utils.py +++ b/invokeai/backend/patches/lora_conversions/flux_aitoolkit_lora_conversion_utils.py @@ -12,7 +12,10 @@ from invokeai.backend.util import InvokeAILogger -def is_state_dict_likely_in_flux_aitoolkit_format(state_dict: dict[str, Any], metadata: dict[str, Any] = None) -> bool: +def is_state_dict_likely_in_flux_aitoolkit_format( + state_dict: dict[str, Any], + metadata: dict[str, Any] | None = None, +) -> bool: if metadata: try: software = json.loads(metadata.get("software", "{}")) diff --git a/invokeai/backend/patches/lora_conversions/formats.py b/invokeai/backend/patches/lora_conversions/formats.py index 94f71e05ee6..4fe6eb8772c 100644 --- a/invokeai/backend/patches/lora_conversions/formats.py +++ b/invokeai/backend/patches/lora_conversions/formats.py @@ -1,3 +1,5 @@ +from typing import Any + from invokeai.backend.model_manager.taxonomy import FluxLoRAFormat from invokeai.backend.patches.lora_conversions.flux_aitoolkit_lora_conversion_utils import ( is_state_dict_likely_in_flux_aitoolkit_format, @@ -14,7 +16,10 @@ ) -def flux_format_from_state_dict(state_dict: dict, metadata: dict | None = None) -> FluxLoRAFormat | None: +def flux_format_from_state_dict( + state_dict: dict[str, Any], + metadata: dict[str, Any] | None = None, +) -> FluxLoRAFormat | None: if is_state_dict_likely_in_flux_kohya_format(state_dict): return FluxLoRAFormat.Kohya elif is_state_dict_likely_in_flux_onetrainer_format(state_dict): diff --git a/invokeai/backend/quantization/scripts/load_flux_model_bnb_llm_int8.py b/invokeai/backend/quantization/scripts/load_flux_model_bnb_llm_int8.py index 045ebbbf2c4..8231e313fdc 100644 --- a/invokeai/backend/quantization/scripts/load_flux_model_bnb_llm_int8.py +++ b/invokeai/backend/quantization/scripts/load_flux_model_bnb_llm_int8.py @@ -4,7 +4,8 @@ from safetensors.torch import load_file, save_file from invokeai.backend.flux.model import Flux -from invokeai.backend.flux.util import params +from invokeai.backend.flux.util import get_flux_transformers_params +from invokeai.backend.model_manager.taxonomy import ModelVariantType from invokeai.backend.quantization.bnb_llm_int8 import quantize_model_llm_int8 from invokeai.backend.quantization.scripts.load_flux_model_bnb_nf4 import log_time @@ -22,7 +23,7 @@ def main(): with log_time("Initialize FLUX transformer on meta device"): # TODO(ryand): Determine if this is a schnell model or a dev model and load the appropriate config. - p = params["flux-schnell"] + p = get_flux_transformers_params(ModelVariantType.FluxSchnell) # Initialize the model on the "meta" device. with accelerate.init_empty_weights(): diff --git a/invokeai/backend/quantization/scripts/load_flux_model_bnb_nf4.py b/invokeai/backend/quantization/scripts/load_flux_model_bnb_nf4.py index c8802b9e49e..6a4ee3abf93 100644 --- a/invokeai/backend/quantization/scripts/load_flux_model_bnb_nf4.py +++ b/invokeai/backend/quantization/scripts/load_flux_model_bnb_nf4.py @@ -7,7 +7,8 @@ from safetensors.torch import load_file, save_file from invokeai.backend.flux.model import Flux -from invokeai.backend.flux.util import params +from invokeai.backend.flux.util import get_flux_transformers_params +from invokeai.backend.model_manager.taxonomy import ModelVariantType from invokeai.backend.quantization.bnb_nf4 import quantize_model_nf4 @@ -35,7 +36,7 @@ def main(): # inference_dtype = torch.bfloat16 with log_time("Initialize FLUX transformer on meta device"): # TODO(ryand): Determine if this is a schnell model or a dev model and load the appropriate config. - p = params["flux-schnell"] + p = get_flux_transformers_params(ModelVariantType.FluxSchnell) # Initialize the model on the "meta" device. with accelerate.init_empty_weights(): diff --git a/invokeai/frontend/web/src/features/modelManagerV2/models.ts b/invokeai/frontend/web/src/features/modelManagerV2/models.ts index ec4ddf1a1d5..85ab9b126ee 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/models.ts +++ b/invokeai/frontend/web/src/features/modelManagerV2/models.ts @@ -223,6 +223,9 @@ export const MODEL_VARIANT_TO_LONG_NAME: Record = { normal: 'Normal', inpaint: 'Inpaint', depth: 'Depth', + dev: 'FLUX Dev', + dev_fill: 'FLUX Dev Fill', + schnell: 'FLUX Schnell', }; export const MODEL_FORMAT_TO_LONG_NAME: Record = { diff --git a/invokeai/frontend/web/src/features/nodes/types/common.ts b/invokeai/frontend/web/src/features/nodes/types/common.ts index 4b97c2145d8..96356f7cb4a 100644 --- a/invokeai/frontend/web/src/features/nodes/types/common.ts +++ b/invokeai/frontend/web/src/features/nodes/types/common.ts @@ -147,7 +147,7 @@ export const zSubModelType = z.enum([ ]); export const zClipVariantType = z.enum(['large', 'gigantic']); -export const zModelVariantType = z.enum(['normal', 'inpaint', 'depth']); +export const zModelVariantType = z.enum(['normal', 'inpaint', 'depth', 'dev', 'dev_fill', 'schnell']); export type ModelVariantType = z.infer; export const zModelFormat = z.enum([ 'omi', diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts index 4d9f9ab2ec3..eb27c4266dd 100644 --- a/invokeai/frontend/web/src/services/api/schema.ts +++ b/invokeai/frontend/web/src/services/api/schema.ts @@ -5489,7 +5489,7 @@ export type components = { * Config Path * @description path to the checkpoint model config file */ - config_path: string; + config_path?: string | null; /** * Converted At * @description When this model was last converted to diffusers @@ -14817,7 +14817,7 @@ export type components = { * Config Path * @description path to the checkpoint model config file */ - config_path: string; + config_path?: string | null; /** * Converted At * @description When this model was last converted to diffusers @@ -14926,7 +14926,7 @@ export type components = { * Config Path * @description path to the checkpoint model config file */ - config_path: string; + config_path?: string | null; /** * Converted At * @description When this model was last converted to diffusers @@ -15127,7 +15127,7 @@ export type components = { * Config Path * @description path to the checkpoint model config file */ - config_path: string; + config_path?: string | null; /** * Converted At * @description When this model was last converted to diffusers @@ -17486,7 +17486,7 @@ export type components = { * @description Variant type. * @enum {string} */ - ModelVariantType: "normal" | "inpaint" | "depth"; + ModelVariantType: "normal" | "inpaint" | "depth" | "flux_dev" | "flux_dev_fill" | "flux_schnell"; /** * ModelsList * @description Return list of configs. @@ -22198,7 +22198,7 @@ export type components = { * Config Path * @description path to the checkpoint model config file */ - config_path: string; + config_path?: string | null; /** * Converted At * @description When this model was last converted to diffusers diff --git a/tests/backend/patches/lora_conversions/test_flux_aitoolkit_lora_conversion_utils.py b/tests/backend/patches/lora_conversions/test_flux_aitoolkit_lora_conversion_utils.py index ed3e05a9b26..051ed210cd5 100644 --- a/tests/backend/patches/lora_conversions/test_flux_aitoolkit_lora_conversion_utils.py +++ b/tests/backend/patches/lora_conversions/test_flux_aitoolkit_lora_conversion_utils.py @@ -2,7 +2,8 @@ import pytest from invokeai.backend.flux.model import Flux -from invokeai.backend.flux.util import params +from invokeai.backend.flux.util import get_flux_transformers_params +from invokeai.backend.model_manager.taxonomy import ModelVariantType from invokeai.backend.patches.lora_conversions.flux_aitoolkit_lora_conversion_utils import ( _group_state_by_submodel, is_state_dict_likely_in_flux_aitoolkit_format, @@ -44,7 +45,7 @@ def test_flux_aitoolkit_transformer_state_dict_is_in_invoke_format(): # Initialize a FLUX model on the meta device. with accelerate.init_empty_weights(): - model = Flux(params["flux-schnell"]) + model = Flux(get_flux_transformers_params(ModelVariantType.FluxSchnell)) model_keys = set(model.state_dict().keys()) for converted_key_prefix in converted_key_prefixes: diff --git a/tests/backend/patches/lora_conversions/test_flux_kohya_lora_conversion_utils.py b/tests/backend/patches/lora_conversions/test_flux_kohya_lora_conversion_utils.py index 52b8ecc9c9c..eb8846f456b 100644 --- a/tests/backend/patches/lora_conversions/test_flux_kohya_lora_conversion_utils.py +++ b/tests/backend/patches/lora_conversions/test_flux_kohya_lora_conversion_utils.py @@ -3,7 +3,8 @@ import torch from invokeai.backend.flux.model import Flux -from invokeai.backend.flux.util import params +from invokeai.backend.flux.util import get_flux_transformers_params +from invokeai.backend.model_manager.taxonomy import ModelVariantType from invokeai.backend.patches.lora_conversions.flux_kohya_lora_conversion_utils import ( _convert_flux_transformer_kohya_state_dict_to_invoke_format, is_state_dict_likely_in_flux_kohya_format, @@ -63,7 +64,7 @@ def test_convert_flux_transformer_kohya_state_dict_to_invoke_format(): # Initialize a FLUX model on the meta device. with accelerate.init_empty_weights(): - model = Flux(params["flux-dev"]) + model = Flux(get_flux_transformers_params(ModelVariantType.FluxSchnell)) model_keys = set(model.state_dict().keys()) # Assert that the converted state dict matches the keys in the actual model. diff --git a/tests/test_model_probe.py b/tests/test_model_probe.py index 8ee4f8df1f5..8112ccdd19b 100644 --- a/tests/test_model_probe.py +++ b/tests/test_model_probe.py @@ -115,7 +115,7 @@ class MinimalConfigExample(ModelConfigBase): fun_quote: str @classmethod - def matches(cls, mod: ModelOnDisk) -> bool: + def matches(cls, mod: ModelOnDisk, **overrides) -> bool: return mod.path.suffix == ".json" @classmethod From 8ae9716738ec61df3351163be8597ca54b92e3dc Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 13:59:24 +1000 Subject: [PATCH 02/62] feat(mm): port TIs to new API --- invokeai/backend/model_manager/config.py | 127 ++++++++++++++++++++++- 1 file changed, 123 insertions(+), 4 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index b5523281539..5111db6cd43 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -30,6 +30,7 @@ from pathlib import Path from typing import ClassVar, Literal, Optional, Type, TypeAlias, Union +import torch from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag, TypeAdapter from typing_extensions import Annotated, Any, Dict @@ -601,19 +602,137 @@ class ControlNetCheckpointConfig(CheckpointConfigBase, ControlAdapterConfigBase, type: Literal[ModelType.ControlNet] = ModelType.ControlNet -class TextualInversionFileConfig(LegacyProbeMixin, ModelConfigBase): +class TextualInversionConfigBase(ABC, BaseModel): + type: Literal[ModelType.TextualInversion] = ModelType.TextualInversion + + @classmethod + def file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) -> bool: + p = path or mod.path + + if not p.exists(): + return False + + if p.is_dir(): + return False + + if p.name in {"learned_embeds.bin", "learned_embeds.safetensors"}: + return True + + state_dict = mod.load_state_dict(p) + + # Heuristic: textual inversion embeddings have these keys + if any(key in {"string_to_param", "emb_params"} for key in state_dict.keys()): + return True + + # Heuristic: small state dict with all tensor values + if (len(state_dict)) < 10 and all(isinstance(v, torch.Tensor) for v in state_dict.values()): + return True + + return False + + @classmethod + def get_base(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseModelType: + p = path or mod.path + + try: + state_dict = mod.load_state_dict(p) + if "string_to_token" in state_dict: + token_dim = list(state_dict["string_to_param"].values())[0].shape[-1] + elif "emb_params" in state_dict: + token_dim = state_dict["emb_params"].shape[-1] + elif "clip_g" in state_dict: + token_dim = state_dict["clip_g"].shape[-1] + else: + token_dim = list(state_dict.values())[0].shape[0] + + match token_dim: + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case 1280: + return BaseModelType.StableDiffusionXL + case _: + pass + except Exception: + pass + + raise InvalidModelConfigException(f"{p}: Could not determine base type") + + +class TextualInversionFileConfig(TextualInversionConfigBase, ModelConfigBase): """Model config for textual inversion embeddings.""" - type: Literal[ModelType.TextualInversion] = ModelType.TextualInversion format: Literal[ModelFormat.EmbeddingFile] = ModelFormat.EmbeddingFile + @classmethod + def get_tag(cls) -> Tag: + return Tag(f"{ModelType.TextualInversion.value}.{ModelFormat.EmbeddingFile.value}") + + @classmethod + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + is_embedding_override = overrides.get("type") is ModelType.TextualInversion + is_file_override = overrides.get("format") is ModelFormat.EmbeddingFile + + if is_embedding_override and is_file_override: + return MatchCertainty.OVERRIDE + + if mod.path.is_dir(): + return MatchCertainty.NEVER + + if cls.file_looks_like_embedding(mod): + return MatchCertainty.MAYBE + + return MatchCertainty.NEVER + + @classmethod + def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: + try: + base = cls.get_base(mod) + return {"base": base} + except Exception: + pass -class TextualInversionFolderConfig(LegacyProbeMixin, ModelConfigBase): + raise InvalidModelConfigException(f"{mod.path}: Could not determine base type") + + +class TextualInversionFolderConfig(TextualInversionConfigBase, ModelConfigBase): """Model config for textual inversion embeddings.""" - type: Literal[ModelType.TextualInversion] = ModelType.TextualInversion format: Literal[ModelFormat.EmbeddingFolder] = ModelFormat.EmbeddingFolder + @classmethod + def get_tag(cls) -> Tag: + return Tag(f"{ModelType.TextualInversion.value}.{ModelFormat.EmbeddingFolder.value}") + + @classmethod + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + is_embedding_override = overrides.get("type") is ModelType.TextualInversion + is_folder_override = overrides.get("format") is ModelFormat.EmbeddingFolder + + if is_embedding_override and is_folder_override: + return MatchCertainty.OVERRIDE + + if mod.path.is_file(): + return MatchCertainty.NEVER + + for filename in {"learned_embeds.bin", "learned_embeds.safetensors"}: + if cls.file_looks_like_embedding(mod, mod.path / filename): + return MatchCertainty.MAYBE + + return MatchCertainty.NEVER + + @classmethod + def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: + try: + for filename in {"learned_embeds.bin", "learned_embeds.safetensors"}: + base = cls.get_base(mod, mod.path / filename) + return {"base": base} + except Exception: + pass + + raise InvalidModelConfigException(f"{mod.path}: Could not determine base type") + class MainConfigBase(ABC, BaseModel): type: Literal[ModelType.Main] = ModelType.Main From 8b6fe5cb4f229425131c85046360454cf0efc751 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:01:13 +1000 Subject: [PATCH 03/62] tidy(mm): remove unused probes --- .../backend/model_manager/legacy_probe.py | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/invokeai/backend/model_manager/legacy_probe.py b/invokeai/backend/model_manager/legacy_probe.py index 3d3915353da..7d95e33081f 100644 --- a/invokeai/backend/model_manager/legacy_probe.py +++ b/invokeai/backend/model_manager/legacy_probe.py @@ -725,32 +725,6 @@ def get_base_type(self) -> BaseModelType: raise InvalidModelConfigException(f"Unknown LoRA type: {self.model_path}") -class TextualInversionCheckpointProbe(CheckpointProbeBase): - """Class for probing embeddings.""" - - def get_format(self) -> ModelFormat: - return ModelFormat.EmbeddingFile - - def get_base_type(self) -> BaseModelType: - checkpoint = self.checkpoint - if "string_to_token" in checkpoint: - token_dim = list(checkpoint["string_to_param"].values())[0].shape[-1] - elif "emb_params" in checkpoint: - token_dim = checkpoint["emb_params"].shape[-1] - elif "clip_g" in checkpoint: - token_dim = checkpoint["clip_g"].shape[-1] - else: - token_dim = list(checkpoint.values())[0].shape[0] - if token_dim == 768: - return BaseModelType.StableDiffusion1 - elif token_dim == 1024: - return BaseModelType.StableDiffusion2 - elif token_dim == 1280: - return BaseModelType.StableDiffusionXL - else: - raise InvalidModelConfigException(f"{self.model_path}: Could not determine base type") - - class ControlNetCheckpointProbe(CheckpointProbeBase): """Class for probing controlnets.""" @@ -973,19 +947,6 @@ def _guess_name(self) -> str: return name -class TextualInversionFolderProbe(FolderProbeBase): - def get_format(self) -> ModelFormat: - return ModelFormat.EmbeddingFolder - - def get_base_type(self) -> BaseModelType: - path = self.model_path / "learned_embeds.bin" - if not path.exists(): - raise InvalidModelConfigException( - f"{self.model_path.as_posix()} does not contain expected 'learned_embeds.bin' file" - ) - return TextualInversionCheckpointProbe(path).get_base_type() - - class T5EncoderFolderProbe(FolderProbeBase): def get_base_type(self) -> BaseModelType: return BaseModelType.Any @@ -1099,11 +1060,6 @@ def get_base_type(self) -> BaseModelType: return BaseModelType.Any -class CLIPEmbedFolderProbe(FolderProbeBase): - def get_base_type(self) -> BaseModelType: - return BaseModelType.Any - - class SpandrelImageToImageFolderProbe(FolderProbeBase): def get_base_type(self) -> BaseModelType: raise NotImplementedError() @@ -1149,11 +1105,9 @@ def get_base_type(self) -> BaseModelType: ModelProbe.register_probe("diffusers", ModelType.VAE, VaeFolderProbe) ModelProbe.register_probe("diffusers", ModelType.LoRA, LoRAFolderProbe) ModelProbe.register_probe("diffusers", ModelType.ControlLoRa, LoRAFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.TextualInversion, TextualInversionFolderProbe) ModelProbe.register_probe("diffusers", ModelType.T5Encoder, T5EncoderFolderProbe) ModelProbe.register_probe("diffusers", ModelType.ControlNet, ControlNetFolderProbe) ModelProbe.register_probe("diffusers", ModelType.IPAdapter, IPAdapterFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.CLIPEmbed, CLIPEmbedFolderProbe) ModelProbe.register_probe("diffusers", ModelType.CLIPVision, CLIPVisionFolderProbe) ModelProbe.register_probe("diffusers", ModelType.T2IAdapter, T2IAdapterFolderProbe) ModelProbe.register_probe("diffusers", ModelType.SpandrelImageToImage, SpandrelImageToImageFolderProbe) @@ -1165,7 +1119,6 @@ def get_base_type(self) -> BaseModelType: ModelProbe.register_probe("checkpoint", ModelType.VAE, VaeCheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.LoRA, LoRACheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.ControlLoRa, LoRACheckpointProbe) -ModelProbe.register_probe("checkpoint", ModelType.TextualInversion, TextualInversionCheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.ControlNet, ControlNetCheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.IPAdapter, IPAdapterCheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.CLIPVision, CLIPVisionCheckpointProbe) From cdcdecc49c89b055c8b54e93f9b3ad1c6f3da372 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:20:33 +1000 Subject: [PATCH 04/62] feat(mm): port spandrel to new API --- invokeai/backend/model_manager/config.py | 69 ++++++++++++++----- .../backend/model_manager/legacy_probe.py | 24 ------- 2 files changed, 51 insertions(+), 42 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 5111db6cd43..943641c968d 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -30,6 +30,7 @@ from pathlib import Path from typing import ClassVar, Literal, Optional, Type, TypeAlias, Union +import spandrel import torch from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag, TypeAdapter from typing_extensions import Annotated, Any, Dict @@ -56,6 +57,7 @@ variant_type_adapter, ) from invokeai.backend.model_manager.util.model_util import lora_token_vector_length +from invokeai.backend.spandrel_image_to_image_model import SpandrelImageToImageModel from invokeai.backend.stable_diffusion.schedulers.schedulers import SCHEDULER_NAME_VALUES logger = logging.getLogger(__name__) @@ -605,30 +607,36 @@ class ControlNetCheckpointConfig(CheckpointConfigBase, ControlAdapterConfigBase, class TextualInversionConfigBase(ABC, BaseModel): type: Literal[ModelType.TextualInversion] = ModelType.TextualInversion + KNOWN_SUFFIXES: ClassVar = {"bin", "safetensors", "pt", "ckpt"} + KNOWN_KEYS: ClassVar = {"string_to_param", "emb_params", "clip_g"} + @classmethod def file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) -> bool: - p = path or mod.path + try: + p = path or mod.path - if not p.exists(): - return False + if not p.exists(): + return False - if p.is_dir(): - return False + if p.is_dir(): + return False - if p.name in {"learned_embeds.bin", "learned_embeds.safetensors"}: - return True + if p.name in [f"learned_embeds.{s}" for s in cls.KNOWN_SUFFIXES]: + return True - state_dict = mod.load_state_dict(p) + state_dict = mod.load_state_dict(p) - # Heuristic: textual inversion embeddings have these keys - if any(key in {"string_to_param", "emb_params"} for key in state_dict.keys()): - return True + # Heuristic: textual inversion embeddings have these keys + if any(key in cls.KNOWN_KEYS for key in state_dict.keys()): + return True - # Heuristic: small state dict with all tensor values - if (len(state_dict)) < 10 and all(isinstance(v, torch.Tensor) for v in state_dict.values()): - return True + # Heuristic: small state dict with all tensor values + if (len(state_dict)) < 10 and all(isinstance(v, torch.Tensor) for v in state_dict.values()): + return True - return False + return False + except Exception: + return False @classmethod def get_base(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseModelType: @@ -716,8 +724,8 @@ def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: if mod.path.is_file(): return MatchCertainty.NEVER - for filename in {"learned_embeds.bin", "learned_embeds.safetensors"}: - if cls.file_looks_like_embedding(mod, mod.path / filename): + for p in mod.path.iterdir(): + if cls.file_looks_like_embedding(mod, p): return MatchCertainty.MAYBE return MatchCertainty.NEVER @@ -929,7 +937,7 @@ class T2IAdapterConfig(DiffusersConfigBase, ControlAdapterConfigBase, LegacyProb format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers -class SpandrelImageToImageConfig(LegacyProbeMixin, ModelConfigBase): +class SpandrelImageToImageConfig(ModelConfigBase): """Model config for Spandrel Image to Image models.""" _MATCH_SPEED: ClassVar[MatchSpeed] = MatchSpeed.SLOW # requires loading the model from disk @@ -937,6 +945,31 @@ class SpandrelImageToImageConfig(LegacyProbeMixin, ModelConfigBase): type: Literal[ModelType.SpandrelImageToImage] = ModelType.SpandrelImageToImage format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint + @classmethod + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + if not mod.path.is_file(): + return MatchCertainty.NEVER + + try: + # It would be nice to avoid having to load the Spandrel model from disk here. A couple of options were + # explored to avoid this: + # 1. Call `SpandrelImageToImageModel.load_from_state_dict(ckpt)`, where `ckpt` is a state_dict on the meta + # device. Unfortunately, some Spandrel models perform operations during initialization that are not + # supported on meta tensors. + # 2. Spandrel has internal logic to determine a model's type from its state_dict before loading the model. + # This logic is not exposed in spandrel's public API. We could copy the logic here, but then we have to + # maintain it, and the risk of false positive detections is higher. + SpandrelImageToImageModel.load_from_file(mod.path) + return MatchCertainty.EXACT + except spandrel.UnsupportedModelError: + pass + except Exception as e: + logger.warning( + f"Encountered error while probing to determine if {mod.path} is a Spandrel model. Ignoring. Error: {e}" + ) + + return MatchCertainty.NEVER + class SigLIPConfig(DiffusersConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for SigLIP.""" diff --git a/invokeai/backend/model_manager/legacy_probe.py b/invokeai/backend/model_manager/legacy_probe.py index 7d95e33081f..b6bda0c15f7 100644 --- a/invokeai/backend/model_manager/legacy_probe.py +++ b/invokeai/backend/model_manager/legacy_probe.py @@ -5,7 +5,6 @@ import picklescan.scanner as pscan import safetensors.torch -import spandrel import torch import invokeai.backend.util.logging as logger @@ -59,7 +58,6 @@ ) from invokeai.backend.quantization.gguf.ggml_tensor import GGMLTensor from invokeai.backend.quantization.gguf.loaders import gguf_sd_loader -from invokeai.backend.spandrel_image_to_image_model import SpandrelImageToImageModel from invokeai.backend.util.silence_warnings import SilenceWarnings CkptType = Dict[str | int, Any] @@ -340,26 +338,6 @@ def get_model_type_from_checkpoint(cls, model_path: Path, checkpoint: Optional[C if len(ckpt) < 10 and all(isinstance(v, torch.Tensor) for v in ckpt.values()): return ModelType.TextualInversion - # Check if the model can be loaded as a SpandrelImageToImageModel. - # This check is intentionally performed last, as it can be expensive (it requires loading the model from disk). - try: - # It would be nice to avoid having to load the Spandrel model from disk here. A couple of options were - # explored to avoid this: - # 1. Call `SpandrelImageToImageModel.load_from_state_dict(ckpt)`, where `ckpt` is a state_dict on the meta - # device. Unfortunately, some Spandrel models perform operations during initialization that are not - # supported on meta tensors. - # 2. Spandrel has internal logic to determine a model's type from its state_dict before loading the model. - # This logic is not exposed in spandrel's public API. We could copy the logic here, but then we have to - # maintain it, and the risk of false positive detections is higher. - SpandrelImageToImageModel.load_from_file(model_path) - return ModelType.SpandrelImageToImage - except spandrel.UnsupportedModelError: - pass - except Exception as e: - logger.warning( - f"Encountered error while probing to determine if {model_path} is a Spandrel model. Ignoring. Error: {e}" - ) - raise InvalidModelConfigException(f"Unable to determine model type for {model_path}") @classmethod @@ -1110,7 +1088,6 @@ def get_base_type(self) -> BaseModelType: ModelProbe.register_probe("diffusers", ModelType.IPAdapter, IPAdapterFolderProbe) ModelProbe.register_probe("diffusers", ModelType.CLIPVision, CLIPVisionFolderProbe) ModelProbe.register_probe("diffusers", ModelType.T2IAdapter, T2IAdapterFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.SpandrelImageToImage, SpandrelImageToImageFolderProbe) ModelProbe.register_probe("diffusers", ModelType.SigLIP, SigLIPFolderProbe) ModelProbe.register_probe("diffusers", ModelType.FluxRedux, FluxReduxFolderProbe) ModelProbe.register_probe("diffusers", ModelType.LlavaOnevision, LlaveOnevisionFolderProbe) @@ -1123,7 +1100,6 @@ def get_base_type(self) -> BaseModelType: ModelProbe.register_probe("checkpoint", ModelType.IPAdapter, IPAdapterCheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.CLIPVision, CLIPVisionCheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.T2IAdapter, T2IAdapterCheckpointProbe) -ModelProbe.register_probe("checkpoint", ModelType.SpandrelImageToImage, SpandrelImageToImageCheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.SigLIP, SigLIPCheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.FluxRedux, FluxReduxCheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.LlavaOnevision, LlavaOnevisionCheckpointProbe) From 12c3cbc7d020c16f49cc801021a068a0251fbe16 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:25:55 +1000 Subject: [PATCH 05/62] fix(mm): parsing for spandrel --- invokeai/backend/model_manager/config.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 943641c968d..2d5db0fdda2 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -942,6 +942,7 @@ class SpandrelImageToImageConfig(ModelConfigBase): _MATCH_SPEED: ClassVar[MatchSpeed] = MatchSpeed.SLOW # requires loading the model from disk + base: Literal[BaseModelType.Any] = BaseModelType.Any type: Literal[ModelType.SpandrelImageToImage] = ModelType.SpandrelImageToImage format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint @@ -970,6 +971,10 @@ def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: return MatchCertainty.NEVER + @classmethod + def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: + return {} + class SigLIPConfig(DiffusersConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for SigLIP.""" From 7ab60421a77d2e424d8c449eed641616e9b3409e Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:26:03 +1000 Subject: [PATCH 06/62] fix(mm): loader for clip embed --- .../model_manager/load/model_loaders/flux.py | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/invokeai/backend/model_manager/load/model_loaders/flux.py b/invokeai/backend/model_manager/load/model_loaders/flux.py index ccb962e747f..ca38f1bdca2 100644 --- a/invokeai/backend/model_manager/load/model_loaders/flux.py +++ b/invokeai/backend/model_manager/load/model_loaders/flux.py @@ -37,7 +37,6 @@ from invokeai.backend.model_manager.config import ( AnyModelConfig, CheckpointConfigBase, - CLIPEmbedCheckpointConfig, CLIPEmbedDiffusersConfig, ControlNetCheckpointConfig, ControlNetDiffusersConfig, @@ -131,27 +130,6 @@ def _load_model( ) -@ModelLoaderRegistry.register(base=BaseModelType.Any, type=ModelType.CLIPEmbed, format=ModelFormat.Checkpoint) -class CLIPCheckpointLoader(ModelLoader): - """Class to load main models.""" - - def _load_model( - self, - config: AnyModelConfig, - submodel_type: Optional[SubModelType] = None, - ) -> AnyModel: - if not isinstance(config, CLIPEmbedCheckpointConfig): - raise ValueError("Only CLIPEmbedCheckpointConfig models are currently supported here.") - - match submodel_type: - case SubModelType.TextEncoder: - return CLIPTextModel.from_pretrained(Path(config.path), use_safetensors=True) - case _: - raise ValueError( - f"Only TextEncoder submodels are currently supported. Received: {submodel_type.value if submodel_type else 'None'}" - ) - - @ModelLoaderRegistry.register(base=BaseModelType.Any, type=ModelType.T5Encoder, format=ModelFormat.BnbQuantizedLlmInt8b) class BnbQuantizedLlmInt8bCheckpointModel(ModelLoader): """Class to load main models.""" From 1db1264f28f530105263bb72909a0c58e1d5e75d Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:31:04 +1000 Subject: [PATCH 07/62] fix(mm): tis use existing weight_files method --- invokeai/backend/model_manager/config.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 2d5db0fdda2..7b0bcea8e54 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -607,7 +607,6 @@ class ControlNetCheckpointConfig(CheckpointConfigBase, ControlAdapterConfigBase, class TextualInversionConfigBase(ABC, BaseModel): type: Literal[ModelType.TextualInversion] = ModelType.TextualInversion - KNOWN_SUFFIXES: ClassVar = {"bin", "safetensors", "pt", "ckpt"} KNOWN_KEYS: ClassVar = {"string_to_param", "emb_params", "clip_g"} @classmethod @@ -621,7 +620,7 @@ def file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) - if p.is_dir(): return False - if p.name in [f"learned_embeds.{s}" for s in cls.KNOWN_SUFFIXES]: + if p.name in [f"learned_embeds.{s}" for s in mod.weight_files()]: return True state_dict = mod.load_state_dict(p) From 82ffb58f06a0365d0f6cabb06afd3d88bd4183ec Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:17:44 +1000 Subject: [PATCH 08/62] feat(mm): port vae to new API --- invokeai/backend/model_manager/config.py | 122 +++++++++++++++++- .../backend/model_manager/legacy_probe.py | 48 ------- .../backend/model_manager/model_on_disk.py | 16 +++ 3 files changed, 134 insertions(+), 52 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 7b0bcea8e54..bcc03390fc5 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -23,6 +23,7 @@ # pyright: reportIncompatibleVariableOverride=false import json import logging +import re import time from abc import ABC, abstractmethod from enum import Enum @@ -73,6 +74,15 @@ class InvalidModelConfigException(Exception): DEFAULTS_PRECISION = Literal["fp16", "fp32"] +def get_class_name_from_config(config: dict[str, Any]) -> Optional[str]: + if "_class_name" in config: + return config["_class_name"] + elif "architectures" in config: + return config["architectures"][0] + else: + return None + + class SubmodelDefinition(BaseModel): path_or_prefix: str model_type: ModelType @@ -578,18 +588,122 @@ def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: } -class VAECheckpointConfig(CheckpointConfigBase, LegacyProbeMixin, ModelConfigBase): +class VAEConfigBase(CheckpointConfigBase): + type: Literal[ModelType.VAE] = ModelType.VAE + + +class VAECheckpointConfig(VAEConfigBase, ModelConfigBase): """Model config for standalone VAE models.""" - type: Literal[ModelType.VAE] = ModelType.VAE + format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint + + KEY_PREFIXES: ClassVar = {"encoder.conv_in", "decoder.conv_in"} + + @classmethod + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + is_vae_override = overrides.get("type") is ModelType.VAE + is_checkpoint_override = overrides.get("format") is ModelFormat.Checkpoint + + if is_vae_override and is_checkpoint_override: + return MatchCertainty.OVERRIDE + + if mod.path.is_dir(): + return MatchCertainty.NEVER + + if mod.has_keys_starting_with(cls.KEY_PREFIXES): + return MatchCertainty.MAYBE + + return MatchCertainty.NEVER + + @classmethod + def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: + base = cls.get_base_type(mod) + return {"base": base} + + @classmethod + def get_base_type(cls, mod: ModelOnDisk) -> BaseModelType: + # Heuristic: VAEs of all architectures have a similar structure; the best we can do is guess based on name + for regexp, basetype in [ + (r"xl", BaseModelType.StableDiffusionXL), + (r"sd2", BaseModelType.StableDiffusion2), + (r"vae", BaseModelType.StableDiffusion1), + (r"FLUX.1-schnell_ae", BaseModelType.Flux), + ]: + if re.search(regexp, mod.path.name, re.IGNORECASE): + return basetype + + raise InvalidModelConfigException("Cannot determine base type") -class VAEDiffusersConfig(LegacyProbeMixin, ModelConfigBase): +class VAEDiffusersConfig(VAEConfigBase, ModelConfigBase): """Model config for standalone VAE models (diffusers version).""" - type: Literal[ModelType.VAE] = ModelType.VAE format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers + CLASS_NAMES: ClassVar = {"AutoencoderKL", "AutoencoderTiny"} + + @classmethod + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + is_vae_override = overrides.get("type") is ModelType.VAE + is_diffusers_override = overrides.get("format") is ModelFormat.Diffusers + + if is_vae_override and is_diffusers_override: + return MatchCertainty.OVERRIDE + + if mod.path.is_file(): + return MatchCertainty.NEVER + + try: + config = cls.get_config(mod) + class_name = get_class_name_from_config(config) + if class_name in cls.CLASS_NAMES: + return MatchCertainty.EXACT + except Exception: + pass + + return MatchCertainty.NEVER + + @classmethod + def get_config(cls, mod: ModelOnDisk) -> dict[str, Any]: + config_path = mod.path / "config.json" + with open(config_path, "r") as file: + return json.load(file) + + @classmethod + def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: + base = cls.get_base_type(mod) + return {"base": base} + + @classmethod + def get_base_type(cls, mod: ModelOnDisk) -> BaseModelType: + if cls._config_looks_like_sdxl(mod): + return BaseModelType.StableDiffusionXL + elif cls._name_looks_like_sdxl(mod): + return BaseModelType.StableDiffusionXL + else: + # We do not support diffusers VAEs for any other base model at this time... YOLO + return BaseModelType.StableDiffusion1 + + @classmethod + def _config_looks_like_sdxl(cls, mod: ModelOnDisk) -> bool: + config = cls.get_config(mod) + # Heuristic: These config values that distinguish Stability's SD 1.x VAE from their SDXL VAE. + return config.get("scaling_factor", 0) == 0.13025 and config.get("sample_size") in [512, 1024] + + @classmethod + def _name_looks_like_sdxl(cls, mod: ModelOnDisk) -> bool: + # Heuristic: SD and SDXL VAE are the same shape (3-channel RGB to 4-channel float scaled down + # by a factor of 8), so we can't necessarily tell them apart by config hyperparameters. Best + # we can do is guess based on name. + return bool(re.search(r"xl\b", cls._guess_name(mod), re.IGNORECASE)) + + @classmethod + def _guess_name(cls, mod: ModelOnDisk) -> str: + name = mod.path.name + if name == "vae": + name = mod.path.parent.name + return name + class ControlNetDiffusersConfig(DiffusersConfigBase, ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for ControlNet models (diffusers version).""" diff --git a/invokeai/backend/model_manager/legacy_probe.py b/invokeai/backend/model_manager/legacy_probe.py index b6bda0c15f7..5955c8af2c2 100644 --- a/invokeai/backend/model_manager/legacy_probe.py +++ b/invokeai/backend/model_manager/legacy_probe.py @@ -1,5 +1,4 @@ import json -import re from pathlib import Path from typing import Any, Callable, Dict, Literal, Optional, Union @@ -654,21 +653,6 @@ def get_scheduler_prediction_type(self) -> SchedulerPredictionType: return SchedulerPredictionType.Epsilon -class VaeCheckpointProbe(CheckpointProbeBase): - def get_base_type(self) -> BaseModelType: - # VAEs of all base types have the same structure, so we wimp out and - # guess using the name. - for regexp, basetype in [ - (r"xl", BaseModelType.StableDiffusionXL), - (r"sd2", BaseModelType.StableDiffusion2), - (r"vae", BaseModelType.StableDiffusion1), - (r"FLUX.1-schnell_ae", BaseModelType.Flux), - ]: - if re.search(regexp, self.model_path.name, re.IGNORECASE): - return basetype - raise InvalidModelConfigException("Cannot determine base type") - - class LoRACheckpointProbe(CheckpointProbeBase): """Class for LoRA checkpoints.""" @@ -895,36 +879,6 @@ def get_variant_type(self) -> ModelVariantType: return ModelVariantType.Normal -class VaeFolderProbe(FolderProbeBase): - def get_base_type(self) -> BaseModelType: - if self._config_looks_like_sdxl(): - return BaseModelType.StableDiffusionXL - elif self._name_looks_like_sdxl(): - # but SD and SDXL VAE are the same shape (3-channel RGB to 4-channel float scaled down - # by a factor of 8), we can't necessarily tell them apart by config hyperparameters. - return BaseModelType.StableDiffusionXL - else: - return BaseModelType.StableDiffusion1 - - def _config_looks_like_sdxl(self) -> bool: - # config values that distinguish Stability's SD 1.x VAE from their SDXL VAE. - config_file = self.model_path / "config.json" - if not config_file.exists(): - raise InvalidModelConfigException(f"Cannot determine base type for {self.model_path}") - with open(config_file, "r") as file: - config = json.load(file) - return config.get("scaling_factor", 0) == 0.13025 and config.get("sample_size") in [512, 1024] - - def _name_looks_like_sdxl(self) -> bool: - return bool(re.search(r"xl\b", self._guess_name(), re.IGNORECASE)) - - def _guess_name(self) -> str: - name = self.model_path.name - if name == "vae": - name = self.model_path.parent.name - return name - - class T5EncoderFolderProbe(FolderProbeBase): def get_base_type(self) -> BaseModelType: return BaseModelType.Any @@ -1080,7 +1034,6 @@ def get_base_type(self) -> BaseModelType: # Register probe classes ModelProbe.register_probe("diffusers", ModelType.Main, PipelineFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.VAE, VaeFolderProbe) ModelProbe.register_probe("diffusers", ModelType.LoRA, LoRAFolderProbe) ModelProbe.register_probe("diffusers", ModelType.ControlLoRa, LoRAFolderProbe) ModelProbe.register_probe("diffusers", ModelType.T5Encoder, T5EncoderFolderProbe) @@ -1093,7 +1046,6 @@ def get_base_type(self) -> BaseModelType: ModelProbe.register_probe("diffusers", ModelType.LlavaOnevision, LlaveOnevisionFolderProbe) ModelProbe.register_probe("checkpoint", ModelType.Main, PipelineCheckpointProbe) -ModelProbe.register_probe("checkpoint", ModelType.VAE, VaeCheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.LoRA, LoRACheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.ControlLoRa, LoRACheckpointProbe) ModelProbe.register_probe("checkpoint", ModelType.ControlNet, ControlNetCheckpointProbe) diff --git a/invokeai/backend/model_manager/model_on_disk.py b/invokeai/backend/model_manager/model_on_disk.py index 502ca596a62..9de78e53b32 100644 --- a/invokeai/backend/model_manager/model_on_disk.py +++ b/invokeai/backend/model_manager/model_on_disk.py @@ -128,3 +128,19 @@ def resolve_weight_file(self, path: Optional[Path] = None) -> Path: f"Please specify the intended file using the 'path' argument" ) return path + + def has_keys_exact(self, keys: set[str], path: Optional[Path] = None) -> bool: + state_dict = self.load_state_dict(path) + return keys.issubset({key for key in state_dict.keys() if isinstance(key, str)}) + + def has_keys_starting_with(self, prefixes: set[str], path: Optional[Path] = None) -> bool: + state_dict = self.load_state_dict(path) + return any( + any(key.startswith(prefix) for prefix in prefixes) for key in state_dict.keys() if isinstance(key, str) + ) + + def has_keys_ending_with(self, prefixes: set[str], path: Optional[Path] = None) -> bool: + state_dict = self.load_state_dict(path) + return any( + any(key.endswith(suffix) for suffix in prefixes) for key in state_dict.keys() if isinstance(key, str) + ) From 20a0231e80cac6522d1a49f423b888dd5b43e8d4 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:26:45 +1000 Subject: [PATCH 09/62] fix(mm): vae class inheritance and config_path --- invokeai/backend/model_manager/config.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index bcc03390fc5..822856e5201 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -588,11 +588,11 @@ def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: } -class VAEConfigBase(CheckpointConfigBase): +class VAEConfigBase(ABC, BaseModel): type: Literal[ModelType.VAE] = ModelType.VAE -class VAECheckpointConfig(VAEConfigBase, ModelConfigBase): +class VAECheckpointConfig(VAEConfigBase, CheckpointConfigBase, ModelConfigBase): """Model config for standalone VAE models.""" format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint @@ -618,7 +618,20 @@ def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: @classmethod def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: base = cls.get_base_type(mod) - return {"base": base} + config_path = ( + # For flux, this is a key in invokeai.backend.flux.util.ae_params + # Due to model type and format being the descriminator for model configs this + # is used rather than attempting to support flux with separate model types and format + # If changed in the future, please fix me + "flux" + if base is BaseModelType.Flux + else "stable-diffusion/v1-inference.yaml" + if base is BaseModelType.StableDiffusion1 + else "stable-diffusion/sd_xl_base.yaml" + if base is BaseModelType.StableDiffusionXL + else "stable-diffusion/v2-inference.yaml" + ) + return {"base": base, "config_path": config_path} @classmethod def get_base_type(cls, mod: ModelOnDisk) -> BaseModelType: @@ -635,7 +648,7 @@ def get_base_type(cls, mod: ModelOnDisk) -> BaseModelType: raise InvalidModelConfigException("Cannot determine base type") -class VAEDiffusersConfig(VAEConfigBase, ModelConfigBase): +class VAEDiffusersConfig(VAEConfigBase, DiffusersConfigBase, ModelConfigBase): """Model config for standalone VAE models (diffusers version).""" format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers From c88fee667a25009afb75d24170d0d34175d87238 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 18:26:38 +1000 Subject: [PATCH 10/62] tidy(mm): patcher types and import paths --- invokeai/backend/model_patcher.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/invokeai/backend/model_patcher.py b/invokeai/backend/model_patcher.py index a1d8bbed0a5..04f99495609 100644 --- a/invokeai/backend/model_patcher.py +++ b/invokeai/backend/model_patcher.py @@ -5,10 +5,10 @@ import pickle from contextlib import contextmanager -from typing import Any, Iterator, List, Optional, Tuple, Type, Union +from typing import Any, Generator, Iterator, List, Optional, Tuple, Type, Union import torch -from diffusers import UNet2DConditionModel +from diffusers.models.unets.unet_2d_condition import UNet2DConditionModel from transformers import CLIPTextModel, CLIPTextModelWithProjection, CLIPTokenizer from invokeai.app.shared.models import FreeUConfig @@ -146,7 +146,7 @@ def apply_clip_skip( cls, text_encoder: Union[CLIPTextModel, CLIPTextModelWithProjection], clip_skip: int, - ) -> None: + ) -> Generator[None, Any, Any]: skipped_layers = [] try: for _i in range(clip_skip): @@ -164,7 +164,7 @@ def apply_freeu( cls, unet: UNet2DConditionModel, freeu_config: Optional[FreeUConfig] = None, - ) -> None: + ) -> Generator[None, Any, Any]: did_apply_freeu = False try: assert hasattr(unet, "enable_freeu") # mypy doesn't pick up this attribute? From 5996e312d6e22b10d9ee33f558909f7de63763c3 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 18:26:57 +1000 Subject: [PATCH 11/62] feat(mm): better errors when invalid model config found in db --- invokeai/app/services/model_records/model_records_sql.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/invokeai/app/services/model_records/model_records_sql.py b/invokeai/app/services/model_records/model_records_sql.py index 9d4892a1416..7fad1761cce 100644 --- a/invokeai/app/services/model_records/model_records_sql.py +++ b/invokeai/app/services/model_records/model_records_sql.py @@ -292,14 +292,19 @@ def search_by_attr( for row in result: try: model_config = ModelConfigFactory.make_config(json.loads(row[0]), timestamp=row[1]) - except pydantic.ValidationError: + except pydantic.ValidationError as e: # We catch this error so that the app can still run if there are invalid model configs in the database. # One reason that an invalid model config might be in the database is if someone had to rollback from a # newer version of the app that added a new model type. row_data = f"{row[0][:64]}..." if len(row[0]) > 64 else row[0] + try: + name = json.loads(row[0]).get("name", "") + except Exception: + name = "" self._logger.warning( - f"Found an invalid model config in the database. Ignoring this model. ({row_data})" + f"Skipping invalid model config in the database with name {name}. Ignoring this model. ({row_data})" ) + self._logger.warning(f"Validation error: {e}") else: results.append(model_config) From 8217fd9e6c78998f52e9318e8c53a6fb4a833548 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 19:14:02 +1000 Subject: [PATCH 12/62] feat(mm): port t5 to new API --- invokeai/backend/model_manager/config.py | 82 ++++++++++++++++++- .../backend/model_manager/legacy_probe.py | 25 ------ .../backend/model_manager/model_on_disk.py | 15 ++-- 3 files changed, 89 insertions(+), 33 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 822856e5201..b109fa62972 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -406,16 +406,94 @@ def base_model(cls, mod: ModelOnDisk) -> BaseModelType: class T5EncoderConfigBase(ABC, BaseModel): """Base class for diffusers-style models.""" + base: Literal[BaseModelType.Any] = BaseModelType.Any type: Literal[ModelType.T5Encoder] = ModelType.T5Encoder + @classmethod + def get_config(cls, mod: ModelOnDisk) -> dict[str, Any]: + path = mod.path / "text_encoder_2" / "config.json" + with open(path, "r") as file: + return json.load(file) + + @classmethod + def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: + return {} + -class T5EncoderConfig(T5EncoderConfigBase, LegacyProbeMixin, ModelConfigBase): +class T5EncoderConfig(T5EncoderConfigBase, ModelConfigBase): format: Literal[ModelFormat.T5Encoder] = ModelFormat.T5Encoder + @classmethod + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + is_t5_type_override = overrides.get("type") is ModelType.T5Encoder + is_t5_format_override = overrides.get("format") is ModelFormat.T5Encoder + + if is_t5_type_override and is_t5_format_override: + return MatchCertainty.OVERRIDE + + if mod.path.is_file(): + return MatchCertainty.NEVER + + model_dir = mod.path / "text_encoder_2" + + if not model_dir.exists(): + return MatchCertainty.NEVER + + try: + config = cls.get_config(mod) + + is_t5_encoder_model = get_class_name_from_config(config) == "T5EncoderModel" + is_t5_format = (model_dir / "model.safetensors.index.json").exists() -class T5EncoderBnbQuantizedLlmInt8bConfig(T5EncoderConfigBase, LegacyProbeMixin, ModelConfigBase): + if is_t5_encoder_model and is_t5_format: + return MatchCertainty.EXACT + except Exception: + pass + + return MatchCertainty.NEVER + + +class T5EncoderBnbQuantizedLlmInt8bConfig(T5EncoderConfigBase, ModelConfigBase): format: Literal[ModelFormat.BnbQuantizedLlmInt8b] = ModelFormat.BnbQuantizedLlmInt8b + @classmethod + def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: + is_t5_type_override = overrides.get("type") is ModelType.T5Encoder + is_bnb_format_override = overrides.get("format") is ModelFormat.BnbQuantizedLlmInt8b + + if is_t5_type_override and is_bnb_format_override: + return MatchCertainty.OVERRIDE + + if mod.path.is_file(): + return MatchCertainty.NEVER + + model_dir = mod.path / "text_encoder_2" + + if not model_dir.exists(): + return MatchCertainty.NEVER + + try: + config = cls.get_config(mod) + + is_t5_encoder_model = get_class_name_from_config(config) == "T5EncoderModel" + + # Heuristic: look for the quantization in the name + files = model_dir.glob("*.safetensors") + filename_looks_like_bnb = any(x for x in files if "llm_int8" in x.as_posix()) + + if is_t5_encoder_model and filename_looks_like_bnb: + return MatchCertainty.EXACT + + # Heuristic: Look for the presence of "SCB" in state dict keys (typically a suffix) + has_scb_key = mod.has_keys_ending_with("SCB") + + if is_t5_encoder_model and has_scb_key: + return MatchCertainty.EXACT + except Exception: + pass + + return MatchCertainty.NEVER + class LoRAOmiConfig(LoRAConfigBase, ModelConfigBase): format: Literal[ModelFormat.OMI] = ModelFormat.OMI diff --git a/invokeai/backend/model_manager/legacy_probe.py b/invokeai/backend/model_manager/legacy_probe.py index 5955c8af2c2..85a39fd25ef 100644 --- a/invokeai/backend/model_manager/legacy_probe.py +++ b/invokeai/backend/model_manager/legacy_probe.py @@ -879,30 +879,6 @@ def get_variant_type(self) -> ModelVariantType: return ModelVariantType.Normal -class T5EncoderFolderProbe(FolderProbeBase): - def get_base_type(self) -> BaseModelType: - return BaseModelType.Any - - def get_format(self) -> ModelFormat: - path = self.model_path / "text_encoder_2" - if (path / "model.safetensors.index.json").exists(): - return ModelFormat.T5Encoder - files = list(path.glob("*.safetensors")) - if len(files) == 0: - raise InvalidModelConfigException(f"{self.model_path.as_posix()}: no .safetensors files found") - - # shortcut: look for the quantization in the name - if any(x for x in files if "llm_int8" in x.as_posix()): - return ModelFormat.BnbQuantizedLlmInt8b - - # more reliable path: probe contents for a 'SCB' key - ckpt = read_checkpoint_meta(files[0], scan=True) - if any("SCB" in x for x in ckpt.keys()): - return ModelFormat.BnbQuantizedLlmInt8b - - raise InvalidModelConfigException(f"{self.model_path.as_posix()}: unknown model format") - - class ONNXFolderProbe(PipelineFolderProbe): def get_base_type(self) -> BaseModelType: # Due to the way the installer is set up, the configuration file for safetensors @@ -1036,7 +1012,6 @@ def get_base_type(self) -> BaseModelType: ModelProbe.register_probe("diffusers", ModelType.Main, PipelineFolderProbe) ModelProbe.register_probe("diffusers", ModelType.LoRA, LoRAFolderProbe) ModelProbe.register_probe("diffusers", ModelType.ControlLoRa, LoRAFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.T5Encoder, T5EncoderFolderProbe) ModelProbe.register_probe("diffusers", ModelType.ControlNet, ControlNetFolderProbe) ModelProbe.register_probe("diffusers", ModelType.IPAdapter, IPAdapterFolderProbe) ModelProbe.register_probe("diffusers", ModelType.CLIPVision, CLIPVisionFolderProbe) diff --git a/invokeai/backend/model_manager/model_on_disk.py b/invokeai/backend/model_manager/model_on_disk.py index 9de78e53b32..1f7625e09c0 100644 --- a/invokeai/backend/model_manager/model_on_disk.py +++ b/invokeai/backend/model_manager/model_on_disk.py @@ -129,18 +129,21 @@ def resolve_weight_file(self, path: Optional[Path] = None) -> Path: ) return path - def has_keys_exact(self, keys: set[str], path: Optional[Path] = None) -> bool: + def has_keys_exact(self, keys: str | set[str], path: Optional[Path] = None) -> bool: + _keys = {keys} if isinstance(keys, str) else keys state_dict = self.load_state_dict(path) - return keys.issubset({key for key in state_dict.keys() if isinstance(key, str)}) + return _keys.issubset({key for key in state_dict.keys() if isinstance(key, str)}) - def has_keys_starting_with(self, prefixes: set[str], path: Optional[Path] = None) -> bool: + def has_keys_starting_with(self, prefixes: str | set[str], path: Optional[Path] = None) -> bool: + _prefixes = {prefixes} if isinstance(prefixes, str) else prefixes state_dict = self.load_state_dict(path) return any( - any(key.startswith(prefix) for prefix in prefixes) for key in state_dict.keys() if isinstance(key, str) + any(key.startswith(prefix) for prefix in _prefixes) for key in state_dict.keys() if isinstance(key, str) ) - def has_keys_ending_with(self, prefixes: set[str], path: Optional[Path] = None) -> bool: + def has_keys_ending_with(self, suffixes: str | set[str], path: Optional[Path] = None) -> bool: + _suffixes = {suffixes} if isinstance(suffixes, str) else suffixes state_dict = self.load_state_dict(path) return any( - any(key.endswith(suffix) for suffix in prefixes) for key in state_dict.keys() if isinstance(key, str) + any(key.endswith(suffix) for suffix in _suffixes) for key in state_dict.keys() if isinstance(key, str) ) From 1d3f6c495bf4aa588852b8e081a057721caa0d20 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 23 Sep 2025 19:27:29 +1000 Subject: [PATCH 13/62] feat(mm): make config_path optional --- invokeai/app/invocations/flux_denoise.py | 6 +++--- invokeai/backend/model_manager/config.py | 14 +++++++++----- .../MainModelPicker.tsx | 2 +- .../ui/layouts/InitialStateMainModelPicker.tsx | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/invokeai/app/invocations/flux_denoise.py b/invokeai/app/invocations/flux_denoise.py index 35d095e2799..1599e8428cb 100644 --- a/invokeai/app/invocations/flux_denoise.py +++ b/invokeai/app/invocations/flux_denoise.py @@ -48,7 +48,7 @@ unpack, ) from invokeai.backend.flux.text_conditioning import FluxReduxConditioning, FluxTextConditioning -from invokeai.backend.model_manager.taxonomy import ModelFormat, ModelVariantType +from invokeai.backend.model_manager.taxonomy import FluxVariantType, ModelFormat from invokeai.backend.patches.layer_patcher import LayerPatcher from invokeai.backend.patches.lora_conversions.flux_lora_constants import FLUX_LORA_TRANSFORMER_PREFIX from invokeai.backend.patches.model_patch_raw import ModelPatchRaw @@ -232,7 +232,7 @@ def _run_diffusion( ) transformer_config = context.models.get_config(self.transformer.transformer) - is_schnell = "schnell" in getattr(transformer_config, "config_path", "") + is_schnell = transformer_config.variant is FluxVariantType.Schnell # Calculate the timestep schedule. timesteps = get_schedule( @@ -277,7 +277,7 @@ def _run_diffusion( # Prepare the extra image conditioning tensor (img_cond) for either FLUX structural control or FLUX Fill. img_cond: torch.Tensor | None = None - is_flux_fill = transformer_config.variant == ModelVariantType.Inpaint # type: ignore + is_flux_fill = transformer_config.variant is FluxVariantType.DevFill if is_flux_fill: img_cond = self._prep_flux_fill_img_cond( context, device=TorchDevice.choose_torch_device(), dtype=inference_dtype diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index b109fa62972..9ab1cdcc0f6 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -198,7 +198,6 @@ def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) if issubclass(cls, LegacyProbeMixin): ModelConfigBase.USING_LEGACY_PROBE.add(cls) - # Cannot use `elif isinstance(cls, UnknownModelConfig)` because UnknownModelConfig is not defined yet else: ModelConfigBase.USING_CLASSIFY_API.add(cls) @@ -346,11 +345,16 @@ class CheckpointConfigBase(ABC, BaseModel): """Base class for checkpoint-style models.""" format: Literal[ModelFormat.Checkpoint, ModelFormat.BnbQuantizednf4b, ModelFormat.GGUFQuantized] = Field( - description="Format of the provided checkpoint model", default=ModelFormat.Checkpoint + description="Format of the provided checkpoint model", + default=ModelFormat.Checkpoint, ) - config_path: str = Field(description="path to the checkpoint model config file") - converted_at: Optional[float] = Field( - description="When this model was last converted to diffusers", default_factory=time.time + config_path: str | None = Field( + description="path to the checkpoint model config file", + default=None, + ) + converted_at: float | None = Field( + description="When this model was last converted to diffusers", + default_factory=time.time, ) diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx index aa527f29342..27194efbd55 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx @@ -27,7 +27,7 @@ export const MainModelPicker = memo(() => { () => selectedModelConfig && isCheckpointMainModelConfig(selectedModelConfig) && - selectedModelConfig.config_path === 'flux-dev', + selectedModelConfig.variant === 'flux_dev', [selectedModelConfig] ); diff --git a/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx b/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx index 9807ae9e690..b98cbd8067e 100644 --- a/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx +++ b/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx @@ -26,7 +26,7 @@ export const InitialStateMainModelPicker = memo(() => { () => selectedModelConfig && isCheckpointMainModelConfig(selectedModelConfig) && - selectedModelConfig.config_path === 'flux-dev', + selectedModelConfig.variant === 'flux_dev', [selectedModelConfig] ); From 881f0632c1e95da6c36257947bc8f98337495a39 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:33:03 +1000 Subject: [PATCH 14/62] refactor(mm): simplify model classification process Previously, we had a multi-phase strategy to identify models from their files on disk: 1. Run each model config classes' `matches()` method on the files. It checks if the model could possibly be an identified as the candidate model type. This was intended to be a quick check. Break on the first match. 2. If we have a match, run the config class's `parse()` method. It derive some additional model config attrs from the model files. This was intended to encapsulate heavier operations that may require loading the model into memory. 3. Derive the common model config attrs, like name, description, calculate the hash, etc. Some of these are also heavier operations. This strategy has some issues: - It is not clear how the pieces fit together. There is some back-and-forth between different methods and the config base class. It is hard to trace the flow of logic until you fully wrap your head around the system and therefore difficult to add a model architecture to the probe. - The assumption that we could do quick, lightweight checks before heavier checks is incorrect. We often _must_ load the model state dict in the `matches()` method. So there is no practical perf benefit to splitting up the responsibility of `matches()` and `parse()`. - Sometimes we need to do the same checks in `matches()` and `parse()`. In these cases, splitting the logic is has a negative perf impact because we are doing the same work twice. - As we introduce the concept of an "unknown" model config (i.e. a model that we cannot identify, but still record in the db; see #8582), we will _always_ run _all_ the checks for every model. Therefore we need not try to defer heavier checks or resource-intensive ops like hashing. We are going to do them anyways. - There are situations where a model may match multiple configs. One known case are SD pipeline models with merged LoRAs. In the old probe API, we relied on the implicit order of checks to know that if a model matched for pipeline _and_ LoRA, we prefer the pipeline match. But, in the new API, we do not have this implicit ordering of checks. To resolve this in a resilient way, we need to get all matches up front, then use tie-breaker logic to figure out which should win (or add "differential diagnosis" logic to the matchers). - Field overrides weren't handled well by this strategy. They were only applied at the very end, if a model matched successfully. This means we cannot tell the system "Hey, this model is type X with base Y. Trust me bro.". We cannot override the match logic. As we move towards letting users correct mis-identified models (see #8582), this is a requirement. We can simplify the process significantly and better support "unknown" models. Firstly, model config classes now have a single `from_model_on_disk()` method that attempts to construct an instance of the class from the model files. This replaces the `matches()` and `parse()` methods. If we fail to create the config instance, a special exception is raised that indicates why we think the files cannot be identified as the given model config class. Next, the flow for model identification is a bit simpler: - Derive all the common fields up-front (name, desc, hash, etc). - Merge in overrides. - Call `from_model_on_disk()` for every config class, passing in the fields. Overrides are handled in this method. - Record the results for each config class and choose the best one. The identification logic is a bit more verbose, with the special exceptions and handling of overrides, but it is very clear what is happening. The one downside I can think of for this strategy is we do need to check every model type, instead of stopping at the first match. It's a bit less efficient. In practice, however, this isn't a hot code path, and the improved clarity is worth far more than perf optimizations that the end user will likely never notice. --- .../model_install/model_install_default.py | 2 +- invokeai/backend/model_manager/config.py | 667 +++++++++++++++++- 2 files changed, 663 insertions(+), 6 deletions(-) diff --git a/invokeai/app/services/model_install/model_install_default.py b/invokeai/app/services/model_install/model_install_default.py index 5bc9af8e6b3..cae423afe33 100644 --- a/invokeai/app/services/model_install/model_install_default.py +++ b/invokeai/app/services/model_install/model_install_default.py @@ -612,7 +612,7 @@ def _probe(self, model_path: Path, config: Optional[ModelRecordChanges] = None): try: return ModelProbe.probe(model_path=model_path, fields=deepcopy(fields), hash_algo=hash_algo) # type: ignore except InvalidModelConfigException: - return ModelConfigBase.classify(mod=model_path, hash_algo=hash_algo, **fields) + return ModelConfigBase.classify(mod=model_path, fields=deepcopy(fields), hash_algo=hash_algo) def _register( self, model_path: Path, config: Optional[ModelRecordChanges] = None, info: Optional[AnyModelConfig] = None diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 9ab1cdcc0f6..a5c2058e1b9 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -21,6 +21,7 @@ """ # pyright: reportIncompatibleVariableOverride=false +from dataclasses import dataclass import json import logging import re @@ -29,11 +30,19 @@ from enum import Enum from inspect import isabstract from pathlib import Path -from typing import ClassVar, Literal, Optional, Type, TypeAlias, Union +from typing import ( + ClassVar, + Literal, + Optional, + Self, + Type, + TypeAlias, + Union, +) import spandrel import torch -from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag, TypeAdapter +from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag, TypeAdapter, ValidationError from typing_extensions import Annotated, Any, Dict from invokeai.app.services.config.config_default import get_config @@ -71,6 +80,18 @@ class InvalidModelConfigException(Exception): pass +class NotAMatch(Exception): + """Exception for when a model does not match a config class. + + Args: + config_class: The config class that was being tested. + reason: The reason why the model did not match. + """ + + def __init__(self, config_class: "Type[ModelConfigBase]", reason: str): + super().__init__(f"{config_class.__name__} does not match: {reason}") + + DEFAULTS_PRECISION = Literal["fp16", "fp32"] @@ -190,8 +211,8 @@ def json_schema_extra(schema: dict[str, Any]) -> None: ) usage_info: Optional[str] = Field(default=None, description="Usage information for this model") - USING_LEGACY_PROBE: ClassVar[set[Type["ModelConfigBase"]]] = set() - USING_CLASSIFY_API: ClassVar[set[Type["ModelConfigBase"]]] = set() + USING_LEGACY_PROBE: ClassVar[set[Type["AnyModelConfig"]]] = set() + USING_CLASSIFY_API: ClassVar[set[Type["AnyModelConfig"]]] = set() _MATCH_SPEED: ClassVar[MatchSpeed] = MatchSpeed.MED def __init_subclass__(cls, **kwargs): @@ -289,6 +310,13 @@ def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: Returns a MatchCertainty score.""" pass + @classmethod + @abstractmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + """Performs a quick check to determine if the config matches the model. + Returns a MatchCertainty score.""" + pass + @staticmethod def cast_overrides(**overrides): """Casts user overrides from str to Enum""" @@ -308,7 +336,7 @@ def cast_overrides(**overrides): overrides["variant"] = variant_type_adapter.validate_strings(overrides["variant"]) @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, **overrides): + def from_model_on_disk_2(cls, mod: ModelOnDisk, **overrides): """Creates an instance of this config or raises InvalidModelConfigException.""" fields = cls.parse(mod) cls.cast_overrides(**overrides) @@ -424,6 +452,11 @@ def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: return {} +def load_json(path: Path) -> dict[str, Any]: + with open(path, "r") as file: + return json.load(file) + + class T5EncoderConfig(T5EncoderConfigBase, ModelConfigBase): format: Literal[ModelFormat.T5Encoder] = ModelFormat.T5Encoder @@ -456,6 +489,45 @@ def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: return MatchCertainty.NEVER + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + + if type_override is not None and type_override is not ModelType.T5Encoder: + raise NotAMatch(cls, f"type override is {type_override}, not T5Encoder") + + if format_override is not None and format_override is not ModelFormat.T5Encoder: + raise NotAMatch(cls, f"format override is {format_override}, not T5Encoder") + + if type_override is ModelType.T5Encoder and format_override is ModelFormat.T5Encoder: + return cls(**fields) + + if mod.path.is_file(): + raise NotAMatch(cls, "model path is a file, not a directory") + + # Heuristic: Look for the T5EncoderModel class name in the config + try: + config = load_json(mod.path / "text_encoder_2" / "config.json") + except Exception as e: + raise NotAMatch(cls, "unable to load text_encoder_2/config.json") from e + + try: + config_class_name = get_class_name_from_config(config) + except Exception as e: + raise NotAMatch(cls, "unable to determine class name from text_encoder_2/config.json") from e + + if config_class_name != "T5EncoderModel": + raise NotAMatch(cls, "model class is not T5EncoderModel") + + # Heuristic: Look for the presence of the unquantized config file (not present for bnb-quantized models) + has_unquantized_config = (mod.path / "text_encoder_2" / "model.safetensors.index.json").exists() + + if not has_unquantized_config: + raise NotAMatch(cls, "missing text_encoder_2/model.safetensors.index.json") + + return cls(**fields) + class T5EncoderBnbQuantizedLlmInt8bConfig(T5EncoderConfigBase, ModelConfigBase): format: Literal[ModelFormat.BnbQuantizedLlmInt8b] = ModelFormat.BnbQuantizedLlmInt8b @@ -498,10 +570,98 @@ def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: return MatchCertainty.NEVER + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + + if type_override is not None and type_override is not ModelType.T5Encoder: + raise NotAMatch(cls, f"type override is {type_override}, not T5Encoder") + + if format_override is not None and format_override is not ModelFormat.BnbQuantizedLlmInt8b: + raise NotAMatch(cls, f"format override is {format_override}, not BnbQuantizedLlmInt8b") + + if type_override is ModelType.T5Encoder and format_override is ModelFormat.BnbQuantizedLlmInt8b: + return cls(**fields) + + # Heuristic: Look for the T5EncoderModel class name in the config + try: + config = load_json(mod.path / "text_encoder_2" / "config.json") + except Exception as e: + raise NotAMatch(cls, "unable to load text_encoder_2/config.json") from e + + try: + config_class_name = get_class_name_from_config(config) + except Exception as e: + raise NotAMatch(cls, "unable to determine class name from text_encoder_2/config.json") from e + + if config_class_name != "T5EncoderModel": + raise NotAMatch(cls, "model class is not T5EncoderModel") + + # Heuristic: look for the quantization in the filename name + filename_looks_like_bnb = any(x for x in mod.weight_files() if "llm_int8" in x.as_posix()) + + # Heuristic: Look for the presence of "SCB" suffixes in state dict keys + has_scb_key_suffix = mod.has_keys_ending_with("SCB") + + if not filename_looks_like_bnb and not has_scb_key_suffix: + raise NotAMatch(cls, "missing bnb quantization indicators") + + return cls(**fields) + class LoRAOmiConfig(LoRAConfigBase, ModelConfigBase): format: Literal[ModelFormat.OMI] = ModelFormat.OMI + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + + if type_override is not None and type_override is not ModelType.LoRA: + raise NotAMatch(cls, f"type override is {type_override}, not LoRA") + + if format_override is not None and format_override is not ModelFormat.OMI: + raise NotAMatch(cls, f"format override is {format_override}, not OMI") + + if type_override is ModelType.LoRA and format_override is ModelFormat.OMI: + return cls(**fields) + + # Heuristic: OMI LoRAs are always files, never directories + if mod.path.is_dir(): + raise NotAMatch(cls, "model path is a directory, not a file") + + # Heuristic: differential diagnosis vs ControlLoRA and Diffusers + if cls.flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: + raise NotAMatch(cls, "model is a ControlLoRA or Diffusers LoRA") + + # Heuristic: Look for OMI LoRA metadata + metadata = mod.metadata() + is_omi_lora_heuristic = ( + bool(metadata.get("modelspec.sai_model_spec")) + and metadata.get("ot_branch") == "omi_format" + and metadata.get("modelspec.architecture", "").split("/")[1].lower() == "lora" + ) + + if not is_omi_lora_heuristic: + raise NotAMatch(cls, "model does not match OMI LoRA heuristics") + + base = fields.get("base") or cls.get_base_or_raise(mod) + + return cls(**fields, base=base) + + @classmethod + def get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType + metadata = mod.metadata() + architecture = metadata["modelspec.architecture"] + + if architecture == stable_diffusion_xl_1_lora: + return BaseModelType.StableDiffusionXL + elif architecture == flux_dev_1_lora: + return BaseModelType.Flux + else: + raise NotAMatch(cls, f"unrecognised/unsupported architecture for OMI LoRA: {architecture}") + @classmethod def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: is_lora_override = overrides.get("type") is ModelType.LoRA @@ -608,6 +768,54 @@ def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: "base": cls.base_model(mod), } + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + + if type_override is not None and type_override is not ModelType.LoRA: + raise NotAMatch(cls, f"type override is {type_override}, not LoRA") + + if format_override is not None and format_override is not ModelFormat.LyCORIS: + raise NotAMatch(cls, f"format override is {format_override}, not LyCORIS") + + if type_override is ModelType.LoRA and format_override is ModelFormat.LyCORIS: + return cls(**fields) + + # Heuristic: LyCORIS LoRAs are always files, never directories + if mod.path.is_dir(): + raise NotAMatch(cls, "model path is a directory, not a file") + + # Heuristic: differential diagnosis vs ControlLoRA and Diffusers + if cls.flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: + raise NotAMatch(cls, "model is a ControlLoRA or Diffusers LoRA") + + # Note: Existence of these key prefixes/suffixes does not guarantee that this is a LoRA. + # Some main models have these keys, likely due to the creator merging in a LoRA. + has_key_with_lora_prefix = mod.has_keys_starting_with( + { + "lora_te_", + "lora_unet_", + "lora_te1_", + "lora_te2_", + "lora_transformer_", + } + ) + + has_key_with_lora_suffix = mod.has_keys_ending_with( + { + "to_k_lora.up.weight", + "to_q_lora.down.weight", + "lora_A.weight", + "lora_B.weight", + } + ) + + if not has_key_with_lora_prefix and not has_key_with_lora_suffix: + raise NotAMatch(cls, "model does not match LyCORIS LoRA heuristics") + + return cls(**fields) + class ControlAdapterConfigBase(ABC, BaseModel): default_settings: Optional[ControlAdapterDefaultSettings] = Field( @@ -669,6 +877,35 @@ def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: "base": cls.base_model(mod), } + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + + if type_override is not None and type_override is not ModelType.LoRA: + raise NotAMatch(cls, f"type override is {type_override}, not LoRA") + + if format_override is not None and format_override is not ModelFormat.Diffusers: + raise NotAMatch(cls, f"format override is {format_override}, not Diffusers") + + if type_override is ModelType.LoRA and format_override is ModelFormat.Diffusers: + return cls(**fields) + + # Heuristic: Diffusers LoRAs are always directories, never files + if mod.path.is_file(): + raise NotAMatch(cls, "model path is a file, not a directory") + + is_flux_lora_diffusers = cls.flux_lora_format(mod) == FluxLoRAFormat.Diffusers + + suffixes = ["bin", "safetensors"] + weight_files = [mod.path / f"pytorch_lora_weights.{sfx}" for sfx in suffixes] + has_lora_weight_file = any(wf.exists() for wf in weight_files) + + if not is_flux_lora_diffusers and not has_lora_weight_file: + raise NotAMatch(cls, "model does not match Diffusers LoRA heuristics") + + return cls(**fields) + class VAEConfigBase(ABC, BaseModel): type: Literal[ModelType.VAE] = ModelType.VAE @@ -729,6 +966,43 @@ def get_base_type(cls, mod: ModelOnDisk) -> BaseModelType: raise InvalidModelConfigException("Cannot determine base type") + @classmethod + def get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + # Heuristic: VAEs of all architectures have a similar structure; the best we can do is guess based on name + for regexp, basetype in [ + (r"xl", BaseModelType.StableDiffusionXL), + (r"sd2", BaseModelType.StableDiffusion2), + (r"vae", BaseModelType.StableDiffusion1), + (r"FLUX.1-schnell_ae", BaseModelType.Flux), + ]: + if re.search(regexp, mod.path.name, re.IGNORECASE): + return basetype + + raise NotAMatch(cls, "cannot determine base type") + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + + if type_override is not None and type_override is not ModelType.VAE: + raise NotAMatch(cls, f"type override is {type_override}, not VAE") + + if format_override is not None and format_override is not ModelFormat.Checkpoint: + raise NotAMatch(cls, f"format override is {format_override}, not Checkpoint") + + if type_override is ModelType.VAE and format_override is ModelFormat.Checkpoint: + return cls(**fields) + + if mod.path.is_dir(): + raise NotAMatch(cls, "model path is a directory, not a file") + + if not mod.has_keys_starting_with({"encoder.conv_in", "decoder.conv_in"}): + raise NotAMatch(cls, "model does not match Checkpoint VAE heuristics") + + base = fields.get("base") or cls.get_base_or_raise(mod) + return cls(**fields, base=base) + class VAEDiffusersConfig(VAEConfigBase, DiffusersConfigBase, ModelConfigBase): """Model config for standalone VAE models (diffusers version).""" @@ -799,6 +1073,49 @@ def _guess_name(cls, mod: ModelOnDisk) -> str: name = mod.path.parent.name return name + @classmethod + def get_base(cls, mod: ModelOnDisk) -> BaseModelType: + if cls._config_looks_like_sdxl(mod): + return BaseModelType.StableDiffusionXL + elif cls._name_looks_like_sdxl(mod): + return BaseModelType.StableDiffusionXL + else: + # TODO(psyche): Figure out how to positively identify SD1 here, and raise if we can't. Until then, YOLO. + return BaseModelType.StableDiffusion1 + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + + if type_override is not None and type_override is not ModelType.VAE: + raise NotAMatch(cls, f"type override is {type_override}, not VAE") + + if format_override is not None and format_override is not ModelFormat.Diffusers: + raise NotAMatch(cls, f"format override is {format_override}, not Diffusers") + + if type_override is ModelType.VAE and format_override is ModelFormat.Diffusers: + return cls(**fields) + + if mod.path.is_file(): + raise NotAMatch(cls, "model path is a file, not a directory") + + try: + config = load_json(mod.path / "config.json") + except Exception as e: + raise NotAMatch(cls, "unable to load config.json") from e + + try: + config_class_name = get_class_name_from_config(config) + except Exception as e: + raise NotAMatch(cls, "unable to determine class name from config") from e + + if config_class_name not in cls.CLASS_NAMES: + raise NotAMatch(cls, f"model class is not one of {cls.CLASS_NAMES}") + + base = fields.get("base") or cls.get_base(mod) + return cls(**fields, base=base) + class ControlNetDiffusersConfig(DiffusersConfigBase, ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for ControlNet models (diffusers version).""" @@ -875,6 +1192,35 @@ def get_base(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseModelType: raise InvalidModelConfigException(f"{p}: Could not determine base type") + @classmethod + def get_base_or_raise(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseModelType: + p = path or mod.path + + try: + state_dict = mod.load_state_dict(p) + if "string_to_token" in state_dict: + token_dim = list(state_dict["string_to_param"].values())[0].shape[-1] + elif "emb_params" in state_dict: + token_dim = state_dict["emb_params"].shape[-1] + elif "clip_g" in state_dict: + token_dim = state_dict["clip_g"].shape[-1] + else: + token_dim = list(state_dict.values())[0].shape[0] + + match token_dim: + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case 1280: + return BaseModelType.StableDiffusionXL + case _: + pass + except Exception: + pass + + raise InvalidModelConfigException(f"{p}: Could not determine base type") + class TextualInversionFileConfig(TextualInversionConfigBase, ModelConfigBase): """Model config for textual inversion embeddings.""" @@ -911,6 +1257,29 @@ def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: raise InvalidModelConfigException(f"{mod.path}: Could not determine base type") + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + + if type_override is not None and type_override is not ModelType.TextualInversion: + raise NotAMatch(cls, f"type override is {type_override}, not TextualInversion") + + if format_override is not None and format_override is not ModelFormat.EmbeddingFile: + raise NotAMatch(cls, f"format override is {format_override}, not EmbeddingFile") + + if type_override is ModelType.TextualInversion and format_override is ModelFormat.EmbeddingFile: + return cls(**fields) + + if mod.path.is_dir(): + raise NotAMatch(cls, "model path is a directory, not a file") + + if not cls.file_looks_like_embedding(mod): + raise NotAMatch(cls, "model does not look like a textual inversion embedding file") + + base = fields.get("base") or cls.get_base_or_raise(mod) + return cls(**fields, base=base) + class TextualInversionFolderConfig(TextualInversionConfigBase, ModelConfigBase): """Model config for textual inversion embeddings.""" @@ -949,6 +1318,30 @@ def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: raise InvalidModelConfigException(f"{mod.path}: Could not determine base type") + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + + if type_override is not None and type_override is not ModelType.TextualInversion: + raise NotAMatch(cls, f"type override is {type_override}, not TextualInversion") + + if format_override is not None and format_override is not ModelFormat.EmbeddingFolder: + raise NotAMatch(cls, f"format override is {format_override}, not EmbeddingFolder") + + if type_override is ModelType.TextualInversion and format_override is ModelFormat.EmbeddingFolder: + return cls(**fields) + + if mod.path.is_file(): + raise NotAMatch(cls, "model path is a file, not a directory") + + for p in mod.weight_files(): + if cls.file_looks_like_embedding(mod, p): + base = fields.get("base") or cls.get_base_or_raise(mod, p) + return cls(**fields, base=base) + + raise NotAMatch(cls, "model does not look like a textual inversion embedding folder") + class MainConfigBase(ABC, BaseModel): type: Literal[ModelType.Main] = ModelType.Main @@ -1100,6 +1493,39 @@ def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: return MatchCertainty.NEVER + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + variant_override = fields.get("variant") + + if type_override is not None and type_override is not ModelType.CLIPEmbed: + raise NotAMatch(cls, f"type override is {type_override}, not CLIPEmbed") + + if format_override is not None and format_override is not ModelFormat.Diffusers: + raise NotAMatch(cls, f"format override is {format_override}, not Diffusers") + + if variant_override is not None and variant_override is not ClipVariantType.G: + raise NotAMatch(cls, f"variant override is {variant_override}, not G") + + if ( + type_override is ModelType.CLIPEmbed + and format_override is ModelFormat.Diffusers + and variant_override is ClipVariantType.G + ): + return cls(**fields) + + if mod.path.is_file(): + raise NotAMatch(cls, "model path is a file, not a directory") + + is_clip_embed = cls.is_clip_text_encoder(mod) + clip_variant = cls.get_clip_variant_type(mod) + + if not is_clip_embed or clip_variant is not ClipVariantType.G: + raise NotAMatch(cls, "model does not match CLIP-G heuristics") + + return cls(**fields) + class CLIPLEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): """Model config for CLIP-L Embeddings.""" @@ -1130,6 +1556,39 @@ def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: return MatchCertainty.NEVER + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + variant_override = fields.get("variant") + + if type_override is not None and type_override is not ModelType.CLIPEmbed: + raise NotAMatch(cls, f"type override is {type_override}, not CLIPEmbed") + + if format_override is not None and format_override is not ModelFormat.Diffusers: + raise NotAMatch(cls, f"format override is {format_override}, not Diffusers") + + if variant_override is not None and variant_override is not ClipVariantType.L: + raise NotAMatch(cls, f"variant override is {variant_override}, not L") + + if ( + type_override is ModelType.CLIPEmbed + and format_override is ModelFormat.Diffusers + and variant_override is ClipVariantType.L + ): + return cls(**fields) + + if mod.path.is_file(): + raise NotAMatch(cls, "model path is a file, not a directory") + + is_clip_embed = cls.is_clip_text_encoder(mod) + clip_variant = cls.get_clip_variant_type(mod) + + if not is_clip_embed or clip_variant is not ClipVariantType.L: + raise NotAMatch(cls, "model does not match CLIP-L heuristics") + + return cls(**fields) + class CLIPVisionDiffusersConfig(DiffusersConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for CLIPVision.""" @@ -1183,6 +1642,46 @@ def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: return {} + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + base_override = fields.get("base") + + if type_override is not None and type_override is not ModelType.SpandrelImageToImage: + raise NotAMatch(cls, f"type override is {type_override}, not SpandrelImageToImage") + + if format_override is not None and format_override is not ModelFormat.Checkpoint: + raise NotAMatch(cls, f"format override is {format_override}, not Checkpoint") + + if base_override is not None and base_override is not BaseModelType.Any: + raise NotAMatch(cls, f"base override is {base_override}, not Any") + + if ( + type_override is ModelType.SpandrelImageToImage + and format_override is ModelFormat.Checkpoint + and base_override is BaseModelType.Any + ): + return cls(**fields) + + if not mod.path.is_file(): + raise NotAMatch(cls, "model path is a directory, not a file") + + try: + # It would be nice to avoid having to load the Spandrel model from disk here. A couple of options were + # explored to avoid this: + # 1. Call `SpandrelImageToImageModel.load_from_state_dict(ckpt)`, where `ckpt` is a state_dict on the meta + # device. Unfortunately, some Spandrel models perform operations during initialization that are not + # supported on meta tensors. + # 2. Spandrel has internal logic to determine a model's type from its state_dict before loading the model. + # This logic is not exposed in spandrel's public API. We could copy the logic here, but then we have to + # maintain it, and the risk of false positive detections is higher. + SpandrelImageToImageModel.load_from_file(mod.path) + base = fields.get("base") or BaseModelType.Any + return cls(**fields, base=base) + except Exception as e: + raise NotAMatch(cls, "model does not match SpandrelImageToImage heuristics") from e + class SigLIPConfig(DiffusersConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for SigLIP.""" @@ -1202,6 +1701,8 @@ class LlavaOnevisionConfig(DiffusersConfigBase, ModelConfigBase): """Model config for Llava Onevision models.""" type: Literal[ModelType.LlavaOnevision] = ModelType.LlavaOnevision + base: Literal[BaseModelType.Any] = BaseModelType.Any + variant: Literal[ModelVariantType.Normal] = ModelVariantType.Normal @classmethod def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: @@ -1234,6 +1735,41 @@ def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: "variant": ModelVariantType.Normal, } + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + type_override = fields.get("type") + format_override = fields.get("format") + + if type_override is not None and type_override is not ModelType.LlavaOnevision: + raise NotAMatch(cls, f"type override is {type_override}, not LlavaOnevision") + + if format_override is not None and format_override is not ModelFormat.Diffusers: + raise NotAMatch(cls, f"format override is {format_override}, not Diffusers") + + if type_override is ModelType.LlavaOnevision and format_override is ModelFormat.Diffusers: + return cls(**fields) + + if mod.path.is_file(): + raise NotAMatch(cls, "model path is a file, not a directory") + + # Heuristic: Look for the LlavaOnevisionForConditionalGeneration class name in the config + try: + config = load_json(mod.path / "config.json") + except Exception as e: + raise NotAMatch(cls, "unable to load config.json") from e + + try: + config_class_name = get_class_name_from_config(config) + except Exception as e: + raise NotAMatch(cls, "unable to determine class name from config.json") from e + + if config_class_name != "LlavaOnevisionForConditionalGeneration": + raise NotAMatch(cls, "model class is not LlavaOnevisionForConditionalGeneration") + + base = fields.get("base") or BaseModelType.Any + variant = fields.get("variant") or ModelVariantType.Normal + return cls(**fields, base=base, variant=variant) + class ApiModelConfig(MainConfigBase, ModelConfigBase): """Model config for API-based models.""" @@ -1249,6 +1785,9 @@ def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: raise NotImplementedError("API models are not parsed from disk.") + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + raise NotAMatch(cls, "API models cannot be built from disk") class VideoApiModelConfig(VideoConfigBase, ModelConfigBase): """Model config for API-based video models.""" @@ -1264,6 +1803,10 @@ def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: raise NotImplementedError("API models are not parsed from disk.") + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + raise NotAMatch(cls, "API models cannot be built from disk") + def get_model_discriminator_value(v: Any) -> str: """ @@ -1342,6 +1885,15 @@ def get_model_discriminator_value(v: Any) -> str: AnyModelConfigValidator = TypeAdapter[AnyModelConfig](AnyModelConfig) AnyDefaultSettings: TypeAlias = Union[MainModelDefaultSettings, LoraModelDefaultSettings, ControlAdapterDefaultSettings] +@dataclass +class ModelClassificationResultSuccess: + model: AnyModelConfig + +@dataclass +class ModelClassificationResultFailure: + error: Exception + +ModelClassificationResult = ModelClassificationResultSuccess | ModelClassificationResultFailure class ModelConfigFactory: @staticmethod @@ -1352,3 +1904,108 @@ def make_config(model_data: Dict[str, Any], timestamp: Optional[float] = None) - model.converted_at = timestamp validate_hash(model.hash) return model + + @staticmethod + def build_common_fields( + mod: ModelOnDisk, + overrides: dict[str, Any] | None = None, + ) -> dict[str, Any]: + """Builds the common fields for all model configs. + + Args: + mod: The model on disk to extract fields from. + overrides: A optional dictionary of fields to override. These fields will take precedence over the values + extracted from the model on disk. + + - Casts string fields to their Enum types. + - Does not validate the fields against the model config schema. + """ + + _overrides: dict[str, Any] = overrides or {} + fields: dict[str, Any] = {} + + if "type" in _overrides: + fields["type"] = ModelType(_overrides["type"]) + + if "format" in _overrides: + fields["format"] = ModelFormat(_overrides["format"]) + + if "base" in _overrides: + fields["base"] = BaseModelType(_overrides["base"]) + + if "source_type" in _overrides: + fields["source_type"] = ModelSourceType(_overrides["source_type"]) + + if "variant" in _overrides: + fields["variant"] = variant_type_adapter.validate_strings(_overrides["variant"]) + + fields["path"] = mod.path.as_posix() + fields["source"] = _overrides.get("source") or fields["path"] + fields["source_type"] = _overrides.get("source_type") or ModelSourceType.Path + fields["name"] = _overrides.get("name") or mod.name + fields["hash"] = _overrides.get("hash") or mod.hash() + fields["key"] = _overrides.get("key") or uuid_string() + fields["description"] = _overrides.get("description") + fields["repo_variant"] = _overrides.get("repo_variant") or mod.repo_variant() + fields["file_size"] = _overrides.get("file_size") or mod.size() + + return fields + + @staticmethod + def from_model_on_disk( + mod: str | Path | ModelOnDisk, + overrides: dict[str, Any] | None = None, + hash_algo: HASHING_ALGORITHMS = "blake3_single", + ) -> AnyModelConfig: + """ + Returns the best matching ModelConfig instance from a model's file/folder path. + Raises InvalidModelConfigException if no valid configuration is found. + Created to deprecate ModelProbe.probe + """ + if isinstance(mod, Path | str): + mod = ModelOnDisk(Path(mod), hash_algo) + + # We will always need these fields to build any model config. + fields = ModelConfigFactory.build_common_fields(mod, overrides) + + # Store results as a mapping of config class to either an instance of that class or an exception + # that was raised when trying to build it. + results: dict[type[AnyModelConfig], AnyModelConfig | Exception] = {} + + # Try to build an instance of each model config class that uses the classify API. + # Each class will either return an instance of itself or raise NotAMatch if it doesn't match. + # Other exceptions may be raised if something unexpected happens during matching or building. + for config_class in ModelConfigBase.USING_CLASSIFY_API: + try: + instance = config_class.from_model_on_disk(mod, fields) + results[config_class] = instance + except NotAMatch as e: + results[config_class] = e + logger.debug(f"No match for {config_class.__name__} on model {mod.name}") + except ValidationError as e: + # This means the model matched, but we couldn't create the pydantic model instance for the config. + # Maybe invalid overrides were provided? + results[config_class] = e + logger.warning(f"Schema validation error for {config_class.__name__} on model {mod.name}: {e}") + except Exception as e: + results[config_class] = e + logger.warning(f"Unexpected exception while matching {mod.name} to {config_class.__name__}: {e}") + + matches = [r for r in results.values() if isinstance(r, ModelConfigBase)] + + if not matches and app_config.allow_unknown_models: + logger.warning(f"Unable to identify model {mod.name}, classifying as UnknownModelConfig") + return UnknownModelConfig.from_model_on_disk(mod, fields) + + instance = next(iter(matches)) + if len(matches) > 1: + # TODO(psyche): When we get multiple matches, at most only 1 will be correct. We should disambiguate the + # matches, probably on a case-by-case basis. + # + # One known case is certain SD main (pipeline) models can look like a LoRA. This could happen if the model + # contains merged in LoRA weights. + logger.warning( + f"Multiple model config classes matched for model {mod.name}: {[type(m).__name__ for m in matches]}. Using {type(instance).__name__}." + ) + logger.info(f"Model {mod.name} classified as {type(instance).__name__}") + return instance From 049e9f25e2a55880d34820319f000eb4f55a42c3 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:49:59 +1000 Subject: [PATCH 15/62] refactor(mm): remove unused methods in config.py --- invokeai/backend/model_manager/config.py | 748 +++-------------------- 1 file changed, 69 insertions(+), 679 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index a5c2058e1b9..8f5f7c89f6e 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -21,7 +21,6 @@ """ # pyright: reportIncompatibleVariableOverride=false -from dataclasses import dataclass import json import logging import re @@ -40,7 +39,6 @@ Union, ) -import spandrel import torch from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag, TypeAdapter, ValidationError from typing_extensions import Annotated, Any, Dict @@ -88,7 +86,7 @@ class NotAMatch(Exception): reason: The reason why the model did not match. """ - def __init__(self, config_class: "Type[ModelConfigBase]", reason: str): + def __init__(self, config_class: "Type[AnyModelConfig]", reason: str): super().__init__(f"{config_class.__name__} does not match: {reason}") @@ -104,6 +102,19 @@ def get_class_name_from_config(config: dict[str, Any]) -> Optional[str]: return None +def validate_overrides( + config_class: "Type[AnyModelConfig]", overrides: dict[str, Any], allowed: dict[str, Any] +) -> None: + for key, value in allowed.items(): + if key not in overrides: + continue + if overrides[key] != value: + raise NotAMatch( + config_class, + f"override {key}={overrides[key]} does not match required value {key}={value}", + ) + + class SubmodelDefinition(BaseModel): path_or_prefix: str model_type: ModelType @@ -139,23 +150,6 @@ class ControlAdapterDefaultSettings(BaseModel): model_config = ConfigDict(extra="forbid") -class MatchSpeed(int, Enum): - """Represents the estimated runtime speed of a config's 'matches' method.""" - - FAST = 0 - MED = 1 - SLOW = 2 - - -class MatchCertainty(int, Enum): - """Represents the certainty of a config's 'matches' method.""" - - NEVER = 0 - MAYBE = 1 - EXACT = 2 - OVERRIDE = 3 - - class LegacyProbeMixin: """Mixin for classes using the legacy probe for model classification.""" @@ -213,7 +207,6 @@ def json_schema_extra(schema: dict[str, Any]) -> None: USING_LEGACY_PROBE: ClassVar[set[Type["AnyModelConfig"]]] = set() USING_CLASSIFY_API: ClassVar[set[Type["AnyModelConfig"]]] = set() - _MATCH_SPEED: ClassVar[MatchSpeed] = MatchSpeed.MED def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) @@ -228,131 +221,19 @@ def all_config_classes(): concrete = {cls for cls in subclasses if not isabstract(cls)} return concrete - @staticmethod - def classify( - mod: str | Path | ModelOnDisk, hash_algo: HASHING_ALGORITHMS = "blake3_single", **overrides - ) -> "AnyModelConfig": - """ - Returns the best matching ModelConfig instance from a model's file/folder path. - Raises InvalidModelConfigException if no valid configuration is found. - Created to deprecate ModelProbe.probe - """ - if isinstance(mod, Path | str): - mod = ModelOnDisk(Path(mod), hash_algo) - - candidates = ModelConfigBase.USING_CLASSIFY_API - sorted_by_match_speed = sorted(candidates, key=lambda cls: (cls._MATCH_SPEED, cls.__name__)) - - overrides = overrides or {} - ModelConfigBase.cast_overrides(**overrides) - - matches: dict[Type[ModelConfigBase], MatchCertainty] = {} - - for config_cls in sorted_by_match_speed: - try: - score = config_cls.matches(mod, **overrides) - - # A score of 0 means "no match" - if score is MatchCertainty.NEVER: - continue - - matches[config_cls] = score - - if score is MatchCertainty.EXACT or score is MatchCertainty.OVERRIDE: - # Perfect match - skip further checks - break - except Exception as e: - logger.warning(f"Unexpected exception while matching {mod.name} to '{config_cls.__name__}': {e}") - continue - - if matches: - # Select the config class with the highest score - sorted_by_score = sorted(matches.items(), key=lambda item: item[1].value) - # Check if there are multiple classes with the same top score - top_score = sorted_by_score[-1][1] - top_classes = [cls for cls, score in sorted_by_score if score is top_score] - if len(top_classes) > 1: - logger.warning( - f"Multiple model config classes matched with the same top score ({top_score}) for model {mod.name}: {[cls.__name__ for cls in top_classes]}. Using {top_classes[0].__name__}." - ) - config_cls = top_classes[0] - # Finally, create the config instance - logger.info(f"Model {mod.name} classified as {config_cls.__name__} with score {top_score.name}") - return config_cls.from_model_on_disk(mod, **overrides) - - if app_config.allow_unknown_models: - try: - return UnknownModelConfig.from_model_on_disk(mod, **overrides) - except Exception: - # Fall through to raising the exception below - pass - - raise InvalidModelConfigException("Unable to determine model type") - @classmethod def get_tag(cls) -> Tag: type = cls.model_fields["type"].default.value format = cls.model_fields["format"].default.value return Tag(f"{type}.{format}") - @classmethod - @abstractmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - """Returns a dictionary with the fields needed to construct the model. - Raises InvalidModelConfigException if the model is invalid. - """ - pass - - @classmethod - @abstractmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - """Performs a quick check to determine if the config matches the model. - Returns a MatchCertainty score.""" - pass - @classmethod @abstractmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - """Performs a quick check to determine if the config matches the model. - Returns a MatchCertainty score.""" - pass + """Given the model on disk and any overrides, return an instance of this config class. - @staticmethod - def cast_overrides(**overrides): - """Casts user overrides from str to Enum""" - if "type" in overrides: - overrides["type"] = ModelType(overrides["type"]) - - if "format" in overrides: - overrides["format"] = ModelFormat(overrides["format"]) - - if "base" in overrides: - overrides["base"] = BaseModelType(overrides["base"]) - - if "source_type" in overrides: - overrides["source_type"] = ModelSourceType(overrides["source_type"]) - - if "variant" in overrides: - overrides["variant"] = variant_type_adapter.validate_strings(overrides["variant"]) - - @classmethod - def from_model_on_disk_2(cls, mod: ModelOnDisk, **overrides): - """Creates an instance of this config or raises InvalidModelConfigException.""" - fields = cls.parse(mod) - cls.cast_overrides(**overrides) - fields.update(overrides) - - fields["path"] = mod.path.as_posix() - fields["source"] = fields.get("source") or fields["path"] - fields["source_type"] = fields.get("source_type") or ModelSourceType.Path - fields["name"] = fields.get("name") or mod.name - fields["hash"] = fields.get("hash") or mod.hash() - fields["key"] = fields.get("key") or uuid_string() - fields["description"] = fields.get("description") - fields["repo_variant"] = fields.get("repo_variant") or mod.repo_variant() - fields["file_size"] = fields.get("file_size") or mod.size() - - return cls(**fields) + Implementations should raise NotAMatch if the model does not match this config class.""" + pass class UnknownModelConfig(ModelConfigBase): @@ -361,12 +242,8 @@ class UnknownModelConfig(ModelConfigBase): format: Literal[ModelFormat.Unknown] = ModelFormat.Unknown @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - return MatchCertainty.NEVER - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - return {} + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + raise NotAMatch(cls, "unknown model config cannot match any model") class CheckpointConfigBase(ABC, BaseModel): @@ -441,16 +318,6 @@ class T5EncoderConfigBase(ABC, BaseModel): base: Literal[BaseModelType.Any] = BaseModelType.Any type: Literal[ModelType.T5Encoder] = ModelType.T5Encoder - @classmethod - def get_config(cls, mod: ModelOnDisk) -> dict[str, Any]: - path = mod.path / "text_encoder_2" / "config.json" - with open(path, "r") as file: - return json.load(file) - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - return {} - def load_json(path: Path) -> dict[str, Any]: with open(path, "r") as file: @@ -460,35 +327,6 @@ def load_json(path: Path) -> dict[str, Any]: class T5EncoderConfig(T5EncoderConfigBase, ModelConfigBase): format: Literal[ModelFormat.T5Encoder] = ModelFormat.T5Encoder - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - is_t5_type_override = overrides.get("type") is ModelType.T5Encoder - is_t5_format_override = overrides.get("format") is ModelFormat.T5Encoder - - if is_t5_type_override and is_t5_format_override: - return MatchCertainty.OVERRIDE - - if mod.path.is_file(): - return MatchCertainty.NEVER - - model_dir = mod.path / "text_encoder_2" - - if not model_dir.exists(): - return MatchCertainty.NEVER - - try: - config = cls.get_config(mod) - - is_t5_encoder_model = get_class_name_from_config(config) == "T5EncoderModel" - is_t5_format = (model_dir / "model.safetensors.index.json").exists() - - if is_t5_encoder_model and is_t5_format: - return MatchCertainty.EXACT - except Exception: - pass - - return MatchCertainty.NEVER - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: type_override = fields.get("type") @@ -532,44 +370,6 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class T5EncoderBnbQuantizedLlmInt8bConfig(T5EncoderConfigBase, ModelConfigBase): format: Literal[ModelFormat.BnbQuantizedLlmInt8b] = ModelFormat.BnbQuantizedLlmInt8b - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - is_t5_type_override = overrides.get("type") is ModelType.T5Encoder - is_bnb_format_override = overrides.get("format") is ModelFormat.BnbQuantizedLlmInt8b - - if is_t5_type_override and is_bnb_format_override: - return MatchCertainty.OVERRIDE - - if mod.path.is_file(): - return MatchCertainty.NEVER - - model_dir = mod.path / "text_encoder_2" - - if not model_dir.exists(): - return MatchCertainty.NEVER - - try: - config = cls.get_config(mod) - - is_t5_encoder_model = get_class_name_from_config(config) == "T5EncoderModel" - - # Heuristic: look for the quantization in the name - files = model_dir.glob("*.safetensors") - filename_looks_like_bnb = any(x for x in files if "llm_int8" in x.as_posix()) - - if is_t5_encoder_model and filename_looks_like_bnb: - return MatchCertainty.EXACT - - # Heuristic: Look for the presence of "SCB" in state dict keys (typically a suffix) - has_scb_key = mod.has_keys_ending_with("SCB") - - if is_t5_encoder_model and has_scb_key: - return MatchCertainty.EXACT - except Exception: - pass - - return MatchCertainty.NEVER - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: type_override = fields.get("type") @@ -651,7 +451,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields, base=base) @classmethod - def get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType + def get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: metadata = mod.metadata() architecture = metadata["modelspec.architecture"] @@ -662,112 +462,12 @@ def get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType else: raise NotAMatch(cls, f"unrecognised/unsupported architecture for OMI LoRA: {architecture}") - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - is_lora_override = overrides.get("type") is ModelType.LoRA - is_omi_override = overrides.get("format") is ModelFormat.OMI - - # If both type and format are overridden, skip the heuristic checks - if is_lora_override and is_omi_override: - return MatchCertainty.OVERRIDE - - # OMI LoRAs are always files, never directories - if mod.path.is_dir(): - return MatchCertainty.NEVER - - # Avoid false positive match against ControlLoRA and Diffusers - if cls.flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: - return MatchCertainty.NEVER - - metadata = mod.metadata() - is_omi_lora_heuristic = ( - bool(metadata.get("modelspec.sai_model_spec")) - and metadata.get("ot_branch") == "omi_format" - and metadata.get("modelspec.architecture", "").split("/")[1].lower() == "lora" - ) - - if is_omi_lora_heuristic: - return MatchCertainty.EXACT - - return MatchCertainty.NEVER - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - metadata = mod.metadata() - architecture = metadata["modelspec.architecture"] - - if architecture == stable_diffusion_xl_1_lora: - base = BaseModelType.StableDiffusionXL - elif architecture == flux_dev_1_lora: - base = BaseModelType.Flux - else: - raise InvalidModelConfigException(f"Unrecognised/unsupported architecture for OMI LoRA: {architecture}") - - return {"base": base} - class LoRALyCORISConfig(LoRAConfigBase, ModelConfigBase): """Model config for LoRA/Lycoris models.""" format: Literal[ModelFormat.LyCORIS] = ModelFormat.LyCORIS - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - is_lora_override = overrides.get("type") is ModelType.LoRA - is_omi_override = overrides.get("format") is ModelFormat.LyCORIS - - # If both type and format are overridden, skip the heuristic checks and return a perfect score - if is_lora_override and is_omi_override: - return MatchCertainty.OVERRIDE - - # LyCORIS LoRAs are always files, never directories - if mod.path.is_dir(): - return MatchCertainty.NEVER - - # Avoid false positive match against ControlLoRA and Diffusers - if cls.flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: - return MatchCertainty.NEVER - - state_dict = mod.load_state_dict() - for key in state_dict.keys(): - if isinstance(key, int): - continue - - # Existence of these key prefixes/suffixes does not guarantee that this is a LoRA. - # Some main models have these keys, likely due to the creator merging in a LoRA. - - has_key_with_lora_prefix = key.startswith( - ( - "lora_te_", - "lora_unet_", - "lora_te1_", - "lora_te2_", - "lora_transformer_", - ) - ) - - # "lora_A.weight" and "lora_B.weight" are associated with models in PEFT format. We don't support all PEFT - # LoRA models, but as of the time of writing, we support Diffusers FLUX PEFT LoRA models. - has_key_with_lora_suffix = key.endswith( - ( - "to_k_lora.up.weight", - "to_q_lora.down.weight", - "lora_A.weight", - "lora_B.weight", - ) - ) - - if has_key_with_lora_prefix or has_key_with_lora_suffix: - return MatchCertainty.MAYBE - - return MatchCertainty.NEVER - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - return { - "base": cls.base_model(mod), - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: type_override = fields.get("type") @@ -844,39 +544,6 @@ class LoRADiffusersConfig(LoRAConfigBase, ModelConfigBase): format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - is_lora_override = overrides.get("type") is ModelType.LoRA - is_diffusers_override = overrides.get("format") is ModelFormat.Diffusers - - # If both type and format are overridden, skip the heuristic checks and return a perfect score - if is_lora_override and is_diffusers_override: - return MatchCertainty.OVERRIDE - - # Diffusers LoRAs are always directories, never files - if mod.path.is_file(): - return MatchCertainty.NEVER - - is_flux_lora_diffusers = cls.flux_lora_format(mod) == FluxLoRAFormat.Diffusers - - suffixes = ["bin", "safetensors"] - weight_files = [mod.path / f"pytorch_lora_weights.{sfx}" for sfx in suffixes] - has_lora_weight_file = any(wf.exists() for wf in weight_files) - - if is_flux_lora_diffusers and has_lora_weight_file: - return MatchCertainty.EXACT - - if is_flux_lora_diffusers or has_lora_weight_file: - return MatchCertainty.MAYBE - - return MatchCertainty.NEVER - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - return { - "base": cls.base_model(mod), - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: type_override = fields.get("type") @@ -916,56 +583,6 @@ class VAECheckpointConfig(VAEConfigBase, CheckpointConfigBase, ModelConfigBase): format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint - KEY_PREFIXES: ClassVar = {"encoder.conv_in", "decoder.conv_in"} - - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - is_vae_override = overrides.get("type") is ModelType.VAE - is_checkpoint_override = overrides.get("format") is ModelFormat.Checkpoint - - if is_vae_override and is_checkpoint_override: - return MatchCertainty.OVERRIDE - - if mod.path.is_dir(): - return MatchCertainty.NEVER - - if mod.has_keys_starting_with(cls.KEY_PREFIXES): - return MatchCertainty.MAYBE - - return MatchCertainty.NEVER - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - base = cls.get_base_type(mod) - config_path = ( - # For flux, this is a key in invokeai.backend.flux.util.ae_params - # Due to model type and format being the descriminator for model configs this - # is used rather than attempting to support flux with separate model types and format - # If changed in the future, please fix me - "flux" - if base is BaseModelType.Flux - else "stable-diffusion/v1-inference.yaml" - if base is BaseModelType.StableDiffusion1 - else "stable-diffusion/sd_xl_base.yaml" - if base is BaseModelType.StableDiffusionXL - else "stable-diffusion/v2-inference.yaml" - ) - return {"base": base, "config_path": config_path} - - @classmethod - def get_base_type(cls, mod: ModelOnDisk) -> BaseModelType: - # Heuristic: VAEs of all architectures have a similar structure; the best we can do is guess based on name - for regexp, basetype in [ - (r"xl", BaseModelType.StableDiffusionXL), - (r"sd2", BaseModelType.StableDiffusion2), - (r"vae", BaseModelType.StableDiffusion1), - (r"FLUX.1-schnell_ae", BaseModelType.Flux), - ]: - if re.search(regexp, mod.path.name, re.IGNORECASE): - return basetype - - raise InvalidModelConfigException("Cannot determine base type") - @classmethod def get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: # Heuristic: VAEs of all architectures have a similar structure; the best we can do is guess based on name @@ -1012,50 +629,7 @@ class VAEDiffusersConfig(VAEConfigBase, DiffusersConfigBase, ModelConfigBase): CLASS_NAMES: ClassVar = {"AutoencoderKL", "AutoencoderTiny"} @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - is_vae_override = overrides.get("type") is ModelType.VAE - is_diffusers_override = overrides.get("format") is ModelFormat.Diffusers - - if is_vae_override and is_diffusers_override: - return MatchCertainty.OVERRIDE - - if mod.path.is_file(): - return MatchCertainty.NEVER - - try: - config = cls.get_config(mod) - class_name = get_class_name_from_config(config) - if class_name in cls.CLASS_NAMES: - return MatchCertainty.EXACT - except Exception: - pass - - return MatchCertainty.NEVER - - @classmethod - def get_config(cls, mod: ModelOnDisk) -> dict[str, Any]: - config_path = mod.path / "config.json" - with open(config_path, "r") as file: - return json.load(file) - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - base = cls.get_base_type(mod) - return {"base": base} - - @classmethod - def get_base_type(cls, mod: ModelOnDisk) -> BaseModelType: - if cls._config_looks_like_sdxl(mod): - return BaseModelType.StableDiffusionXL - elif cls._name_looks_like_sdxl(mod): - return BaseModelType.StableDiffusionXL - else: - # We do not support diffusers VAEs for any other base model at this time... YOLO - return BaseModelType.StableDiffusion1 - - @classmethod - def _config_looks_like_sdxl(cls, mod: ModelOnDisk) -> bool: - config = cls.get_config(mod) + def _config_looks_like_sdxl(cls, config: dict[str, Any]) -> bool: # Heuristic: These config values that distinguish Stability's SD 1.x VAE from their SDXL VAE. return config.get("scaling_factor", 0) == 0.13025 and config.get("sample_size") in [512, 1024] @@ -1074,8 +648,8 @@ def _guess_name(cls, mod: ModelOnDisk) -> str: return name @classmethod - def get_base(cls, mod: ModelOnDisk) -> BaseModelType: - if cls._config_looks_like_sdxl(mod): + def get_base(cls, mod: ModelOnDisk, config: dict[str, Any]) -> BaseModelType: + if cls._config_looks_like_sdxl(config): return BaseModelType.StableDiffusionXL elif cls._name_looks_like_sdxl(mod): return BaseModelType.StableDiffusionXL @@ -1113,7 +687,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: if config_class_name not in cls.CLASS_NAMES: raise NotAMatch(cls, f"model class is not one of {cls.CLASS_NAMES}") - base = fields.get("base") or cls.get_base(mod) + base = fields.get("base") or cls.get_base(mod, config) return cls(**fields, base=base) @@ -1231,32 +805,6 @@ class TextualInversionFileConfig(TextualInversionConfigBase, ModelConfigBase): def get_tag(cls) -> Tag: return Tag(f"{ModelType.TextualInversion.value}.{ModelFormat.EmbeddingFile.value}") - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - is_embedding_override = overrides.get("type") is ModelType.TextualInversion - is_file_override = overrides.get("format") is ModelFormat.EmbeddingFile - - if is_embedding_override and is_file_override: - return MatchCertainty.OVERRIDE - - if mod.path.is_dir(): - return MatchCertainty.NEVER - - if cls.file_looks_like_embedding(mod): - return MatchCertainty.MAYBE - - return MatchCertainty.NEVER - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - try: - base = cls.get_base(mod) - return {"base": base} - except Exception: - pass - - raise InvalidModelConfigException(f"{mod.path}: Could not determine base type") - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: type_override = fields.get("type") @@ -1290,34 +838,6 @@ class TextualInversionFolderConfig(TextualInversionConfigBase, ModelConfigBase): def get_tag(cls) -> Tag: return Tag(f"{ModelType.TextualInversion.value}.{ModelFormat.EmbeddingFolder.value}") - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - is_embedding_override = overrides.get("type") is ModelType.TextualInversion - is_folder_override = overrides.get("format") is ModelFormat.EmbeddingFolder - - if is_embedding_override and is_folder_override: - return MatchCertainty.OVERRIDE - - if mod.path.is_file(): - return MatchCertainty.NEVER - - for p in mod.path.iterdir(): - if cls.file_looks_like_embedding(mod, p): - return MatchCertainty.MAYBE - - return MatchCertainty.NEVER - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - try: - for filename in {"learned_embeds.bin", "learned_embeds.safetensors"}: - base = cls.get_base(mod, mod.path / filename) - return {"base": base} - except Exception: - pass - - raise InvalidModelConfigException(f"{mod.path}: Could not determine base type") - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: type_override = fields.get("type") @@ -1367,14 +887,6 @@ class MainCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixi prediction_type: SchedulerPredictionType = SchedulerPredictionType.Epsilon upcast_attention: bool = False - # @classmethod - # def matches(cls, mod: ModelOnDisk) -> bool: - # pass - - # @classmethod - # def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - # pass - class MainBnbQuantized4bCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for main checkpoint models.""" @@ -1425,44 +937,26 @@ class CLIPEmbedDiffusersConfig(DiffusersConfigBase): format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers base: Literal[BaseModelType.Any] = BaseModelType.Any - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - clip_variant = cls.get_clip_variant_type(mod) - if clip_variant is None: - raise InvalidModelConfigException("Unable to determine CLIP variant type") - - return {"variant": clip_variant} + CLASS_NAMES: ClassVar = { + "CLIPModel", + "CLIPTextModel", + "CLIPTextModelWithProjection", + } @classmethod - def get_clip_variant_type(cls, mod: ModelOnDisk) -> ClipVariantType | None: + def get_clip_variant_type(cls, config: dict[str, Any]) -> ClipVariantType | None: try: - with open(mod.path / "config.json") as file: - config = json.load(file) - hidden_size = config.get("hidden_size") - match hidden_size: - case 1280: - return ClipVariantType.G - case 768: - return ClipVariantType.L - case _: - return None + hidden_size = config.get("hidden_size") + match hidden_size: + case 1280: + return ClipVariantType.G + case 768: + return ClipVariantType.L + case _: + return None except Exception: return None - @classmethod - def is_clip_text_encoder(cls, mod: ModelOnDisk) -> bool: - try: - with open(mod.path / "config.json", "r") as file: - config = json.load(file) - architectures = config.get("architectures") - return architectures[0] in ( - "CLIPModel", - "CLIPTextModel", - "CLIPTextModelWithProjection", - ) - except Exception: - return False - class CLIPGEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): """Model config for CLIP-G Embeddings.""" @@ -1473,26 +967,6 @@ class CLIPGEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): def get_tag(cls) -> Tag: return Tag(f"{ModelType.CLIPEmbed.value}.{ModelFormat.Diffusers.value}.{ClipVariantType.G.value}") - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - is_clip_embed_override = overrides.get("type") is ModelType.CLIPEmbed - is_diffusers_override = overrides.get("format") is ModelFormat.Diffusers - has_clip_variant_override = overrides.get("variant") is ClipVariantType.G - - if is_clip_embed_override and is_diffusers_override and has_clip_variant_override: - return MatchCertainty.OVERRIDE - - if mod.path.is_file(): - return MatchCertainty.NEVER - - is_clip_embed = cls.is_clip_text_encoder(mod) - clip_variant = cls.get_clip_variant_type(mod) - - if is_clip_embed and clip_variant is ClipVariantType.G: - return MatchCertainty.EXACT - - return MatchCertainty.NEVER - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: type_override = fields.get("type") @@ -1518,10 +992,22 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: if mod.path.is_file(): raise NotAMatch(cls, "model path is a file, not a directory") - is_clip_embed = cls.is_clip_text_encoder(mod) - clip_variant = cls.get_clip_variant_type(mod) + try: + config = load_json(mod.path / "config.json") + except Exception as e: + raise NotAMatch(cls, "unable to load config.json") from e + + try: + config_class_name = get_class_name_from_config(config) + except Exception as e: + raise NotAMatch(cls, "unable to determine class name from config") from e + + if config_class_name not in cls.CLASS_NAMES: + raise NotAMatch(cls, f"model class is not one of {cls.CLASS_NAMES}") - if not is_clip_embed or clip_variant is not ClipVariantType.G: + clip_variant = cls.get_clip_variant_type(config) + + if clip_variant is not ClipVariantType.G: raise NotAMatch(cls, "model does not match CLIP-G heuristics") return cls(**fields) @@ -1536,26 +1022,6 @@ class CLIPLEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): def get_tag(cls) -> Tag: return Tag(f"{ModelType.CLIPEmbed.value}.{ModelFormat.Diffusers.value}.{ClipVariantType.L.value}") - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - is_clip_embed_override = overrides.get("type") is ModelType.CLIPEmbed - is_diffusers_override = overrides.get("format") is ModelFormat.Diffusers - has_clip_variant_override = overrides.get("variant") is ClipVariantType.L - - if is_clip_embed_override and is_diffusers_override and has_clip_variant_override: - return MatchCertainty.OVERRIDE - - if mod.path.is_file(): - return MatchCertainty.NEVER - - is_clip_embed = cls.is_clip_text_encoder(mod) - clip_variant = cls.get_clip_variant_type(mod) - - if is_clip_embed and clip_variant is ClipVariantType.L: - return MatchCertainty.EXACT - - return MatchCertainty.NEVER - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: type_override = fields.get("type") @@ -1581,10 +1047,22 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: if mod.path.is_file(): raise NotAMatch(cls, "model path is a file, not a directory") - is_clip_embed = cls.is_clip_text_encoder(mod) - clip_variant = cls.get_clip_variant_type(mod) + try: + config = load_json(mod.path / "config.json") + except Exception as e: + raise NotAMatch(cls, "unable to load config.json") from e + + try: + config_class_name = get_class_name_from_config(config) + except Exception as e: + raise NotAMatch(cls, "unable to determine class name from config") from e + + if config_class_name not in cls.CLASS_NAMES: + raise NotAMatch(cls, f"model class is not one of {cls.CLASS_NAMES}") + + clip_variant = cls.get_clip_variant_type(config) - if not is_clip_embed or clip_variant is not ClipVariantType.L: + if clip_variant is not ClipVariantType.L: raise NotAMatch(cls, "model does not match CLIP-L heuristics") return cls(**fields) @@ -1607,41 +1085,10 @@ class T2IAdapterConfig(DiffusersConfigBase, ControlAdapterConfigBase, LegacyProb class SpandrelImageToImageConfig(ModelConfigBase): """Model config for Spandrel Image to Image models.""" - _MATCH_SPEED: ClassVar[MatchSpeed] = MatchSpeed.SLOW # requires loading the model from disk - base: Literal[BaseModelType.Any] = BaseModelType.Any type: Literal[ModelType.SpandrelImageToImage] = ModelType.SpandrelImageToImage format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - if not mod.path.is_file(): - return MatchCertainty.NEVER - - try: - # It would be nice to avoid having to load the Spandrel model from disk here. A couple of options were - # explored to avoid this: - # 1. Call `SpandrelImageToImageModel.load_from_state_dict(ckpt)`, where `ckpt` is a state_dict on the meta - # device. Unfortunately, some Spandrel models perform operations during initialization that are not - # supported on meta tensors. - # 2. Spandrel has internal logic to determine a model's type from its state_dict before loading the model. - # This logic is not exposed in spandrel's public API. We could copy the logic here, but then we have to - # maintain it, and the risk of false positive detections is higher. - SpandrelImageToImageModel.load_from_file(mod.path) - return MatchCertainty.EXACT - except spandrel.UnsupportedModelError: - pass - except Exception as e: - logger.warning( - f"Encountered error while probing to determine if {mod.path} is a Spandrel model. Ignoring. Error: {e}" - ) - - return MatchCertainty.NEVER - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - return {} - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: type_override = fields.get("type") @@ -1704,37 +1151,6 @@ class LlavaOnevisionConfig(DiffusersConfigBase, ModelConfigBase): base: Literal[BaseModelType.Any] = BaseModelType.Any variant: Literal[ModelVariantType.Normal] = ModelVariantType.Normal - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - is_llava_override = overrides.get("type") is ModelType.LlavaOnevision - is_diffusers_override = overrides.get("format") is ModelFormat.Diffusers - - if is_llava_override and is_diffusers_override: - return MatchCertainty.OVERRIDE - - if mod.path.is_file(): - return MatchCertainty.NEVER - - config_path = mod.path / "config.json" - try: - with open(config_path, "r") as file: - config = json.load(file) - except FileNotFoundError: - return MatchCertainty.NEVER - - architectures = config.get("architectures") - if architectures and architectures[0] == "LlavaOnevisionForConditionalGeneration": - return MatchCertainty.EXACT - - return MatchCertainty.NEVER - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - return { - "base": BaseModelType.Any, - "variant": ModelVariantType.Normal, - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: type_override = fields.get("type") @@ -1776,33 +1192,16 @@ class ApiModelConfig(MainConfigBase, ModelConfigBase): format: Literal[ModelFormat.Api] = ModelFormat.Api - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - # API models are not stored on disk, so we can't match them. - return MatchCertainty.NEVER - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - raise NotImplementedError("API models are not parsed from disk.") - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: raise NotAMatch(cls, "API models cannot be built from disk") + class VideoApiModelConfig(VideoConfigBase, ModelConfigBase): """Model config for API-based video models.""" format: Literal[ModelFormat.Api] = ModelFormat.Api - @classmethod - def matches(cls, mod: ModelOnDisk, **overrides) -> MatchCertainty: - # API models are not stored on disk, so we can't match them. - return MatchCertainty.NEVER - - @classmethod - def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: - raise NotImplementedError("API models are not parsed from disk.") - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: raise NotAMatch(cls, "API models cannot be built from disk") @@ -1885,15 +1284,6 @@ def get_model_discriminator_value(v: Any) -> str: AnyModelConfigValidator = TypeAdapter[AnyModelConfig](AnyModelConfig) AnyDefaultSettings: TypeAlias = Union[MainModelDefaultSettings, LoraModelDefaultSettings, ControlAdapterDefaultSettings] -@dataclass -class ModelClassificationResultSuccess: - model: AnyModelConfig - -@dataclass -class ModelClassificationResultFailure: - error: Exception - -ModelClassificationResult = ModelClassificationResultSuccess | ModelClassificationResultFailure class ModelConfigFactory: @staticmethod From 8b6929bef40c6fd8cfc0bcd1d12acff3e7819adf Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 24 Sep 2025 17:42:56 +1000 Subject: [PATCH 16/62] refactor(mm): add model config parsing utils --- .../model_install/model_install_default.py | 10 +- invokeai/backend/model_manager/config.py | 606 +++++++++--------- scripts/classify-model.py | 5 +- tests/test_model_probe.py | 10 +- 4 files changed, 312 insertions(+), 319 deletions(-) diff --git a/invokeai/app/services/model_install/model_install_default.py b/invokeai/app/services/model_install/model_install_default.py index cae423afe33..4cae15b8e36 100644 --- a/invokeai/app/services/model_install/model_install_default.py +++ b/invokeai/app/services/model_install/model_install_default.py @@ -39,7 +39,7 @@ AnyModelConfig, CheckpointConfigBase, InvalidModelConfigException, - ModelConfigBase, + ModelConfigFactory, ) from invokeai.backend.model_manager.legacy_probe import ModelProbe from invokeai.backend.model_manager.metadata import ( @@ -612,7 +612,11 @@ def _probe(self, model_path: Path, config: Optional[ModelRecordChanges] = None): try: return ModelProbe.probe(model_path=model_path, fields=deepcopy(fields), hash_algo=hash_algo) # type: ignore except InvalidModelConfigException: - return ModelConfigBase.classify(mod=model_path, fields=deepcopy(fields), hash_algo=hash_algo) + return ModelConfigFactory.from_model_on_disk( + mod=model_path, + overrides=deepcopy(fields), + hash_algo=hash_algo, + ) def _register( self, model_path: Path, config: Optional[ModelRecordChanges] = None, info: Optional[AnyModelConfig] = None @@ -633,7 +637,7 @@ def _register( info.path = model_path.as_posix() - if isinstance(info, CheckpointConfigBase): + if isinstance(info, CheckpointConfigBase) and info.config_path is not None: # Checkpoints have a config file needed for conversion. Same handling as the model weights - if it's in the # invoke-managed legacy config dir, we use a relative path. legacy_config_path = self.app_config.legacy_conf_path / info.config_path diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 8f5f7c89f6e..bb77cf6fdfd 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -86,34 +86,86 @@ class NotAMatch(Exception): reason: The reason why the model did not match. """ - def __init__(self, config_class: "Type[AnyModelConfig]", reason: str): + def __init__( + self, + config_class: type, + reason: str, + ): super().__init__(f"{config_class.__name__} does not match: {reason}") DEFAULTS_PRECISION = Literal["fp16", "fp32"] -def get_class_name_from_config(config: dict[str, Any]) -> Optional[str]: - if "_class_name" in config: - return config["_class_name"] - elif "architectures" in config: - return config["architectures"][0] - else: - return None +def get_config_or_raise( + config_class: type, + config_path: Path, +) -> dict[str, Any]: + """Load the config file at the given path, or raise NotAMatch if it cannot be loaded.""" + if not config_path.exists(): + raise NotAMatch(config_class, f"missing config file: {config_path}") + + try: + config = load_json(config_path) + return config + except Exception as e: + raise NotAMatch(config_class, f"unable to load config file: {config_path}") from e -def validate_overrides( - config_class: "Type[AnyModelConfig]", overrides: dict[str, Any], allowed: dict[str, Any] +def raise_for_class_names( + config_class: type, + config_path: Path, + valid_class_names: set[str], ) -> None: - for key, value in allowed.items(): - if key not in overrides: + """Raise NotAMatch if the config file is missing or does not contain a valid class name.""" + + config = get_config_or_raise(config_class, config_path) + + try: + if "_class_name" in config: + config_class_name = config["_class_name"] + elif "architectures" in config: + config_class_name = config["architectures"][0] + else: + raise ValueError("missing _class_name or architectures field") + except Exception as e: + raise NotAMatch(config_class, f"unable to determine class name from config file: {config_path}") from e + + if config_class_name not in valid_class_names: + raise NotAMatch(config_class, f"model class is not one of {valid_class_names}, got {config_class_name}") + + +def matches_overrides( + config_class: "Type[AnyModelConfig]", + provided_overrides: dict[str, Any], + valid_overrides: dict[str, Any], +) -> bool: + """Check if the provided overrides match the valid overrides for this config class. + + Args: + config_class: The config class that is being tested. + provided_overrides: The overrides provided by the user. + valid_overrides: The overrides that are valid for this config class. + + Returns: + True if all provided overrides match the valid overrides, False if some valid overrides are missing. + + Raises: + NotAMatch if any override does not match the allowed value. + """ + is_perfect_match = True + for key, value in valid_overrides.items(): + if key not in provided_overrides: + is_perfect_match = False continue - if overrides[key] != value: + if provided_overrides[key] != value: raise NotAMatch( config_class, - f"override {key}={overrides[key]} does not match required value {key}={value}", + f"override {key}={provided_overrides[key]} does not match required value {key}={value}", ) + return is_perfect_match + class SubmodelDefinition(BaseModel): path_or_prefix: str @@ -327,36 +379,32 @@ def load_json(path: Path) -> dict[str, Any]: class T5EncoderConfig(T5EncoderConfigBase, ModelConfigBase): format: Literal[ModelFormat.T5Encoder] = ModelFormat.T5Encoder - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - - if type_override is not None and type_override is not ModelType.T5Encoder: - raise NotAMatch(cls, f"type override is {type_override}, not T5Encoder") + VALID_OVERRIDES: ClassVar = { + "type": ModelType.T5Encoder, + "format": ModelFormat.T5Encoder, + } - if format_override is not None and format_override is not ModelFormat.T5Encoder: - raise NotAMatch(cls, f"format override is {format_override}, not T5Encoder") + VALID_CLASS_NAMES: ClassVar = { + "T5EncoderModel", + } - if type_override is ModelType.T5Encoder and format_override is ModelFormat.T5Encoder: + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, + ): return cls(**fields) if mod.path.is_file(): raise NotAMatch(cls, "model path is a file, not a directory") - # Heuristic: Look for the T5EncoderModel class name in the config - try: - config = load_json(mod.path / "text_encoder_2" / "config.json") - except Exception as e: - raise NotAMatch(cls, "unable to load text_encoder_2/config.json") from e - - try: - config_class_name = get_class_name_from_config(config) - except Exception as e: - raise NotAMatch(cls, "unable to determine class name from text_encoder_2/config.json") from e - - if config_class_name != "T5EncoderModel": - raise NotAMatch(cls, "model class is not T5EncoderModel") + raise_for_class_names( + config_class=cls, + config_path=mod.path / "text_encoder_2" / "config.json", + valid_class_names=cls.VALID_CLASS_NAMES, + ) # Heuristic: Look for the presence of the unquantized config file (not present for bnb-quantized models) has_unquantized_config = (mod.path / "text_encoder_2" / "model.safetensors.index.json").exists() @@ -370,33 +418,30 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class T5EncoderBnbQuantizedLlmInt8bConfig(T5EncoderConfigBase, ModelConfigBase): format: Literal[ModelFormat.BnbQuantizedLlmInt8b] = ModelFormat.BnbQuantizedLlmInt8b - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - - if type_override is not None and type_override is not ModelType.T5Encoder: - raise NotAMatch(cls, f"type override is {type_override}, not T5Encoder") + VALID_OVERRIDES: ClassVar = { + "type": ModelType.T5Encoder, + "format": ModelFormat.BnbQuantizedLlmInt8b, + } - if format_override is not None and format_override is not ModelFormat.BnbQuantizedLlmInt8b: - raise NotAMatch(cls, f"format override is {format_override}, not BnbQuantizedLlmInt8b") + VALID_CLASS_NAMES: ClassVar = { + "T5EncoderModel", + } - if type_override is ModelType.T5Encoder and format_override is ModelFormat.BnbQuantizedLlmInt8b: + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, + ): return cls(**fields) # Heuristic: Look for the T5EncoderModel class name in the config - try: - config = load_json(mod.path / "text_encoder_2" / "config.json") - except Exception as e: - raise NotAMatch(cls, "unable to load text_encoder_2/config.json") from e - - try: - config_class_name = get_class_name_from_config(config) - except Exception as e: - raise NotAMatch(cls, "unable to determine class name from text_encoder_2/config.json") from e - - if config_class_name != "T5EncoderModel": - raise NotAMatch(cls, "model class is not T5EncoderModel") + raise_for_class_names( + config_class=cls, + config_path=mod.path / "text_encoder_2" / "config.json", + valid_class_names=cls.VALID_CLASS_NAMES, + ) # Heuristic: look for the quantization in the filename name filename_looks_like_bnb = any(x for x in mod.weight_files() if "llm_int8" in x.as_posix()) @@ -413,18 +458,18 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class LoRAOmiConfig(LoRAConfigBase, ModelConfigBase): format: Literal[ModelFormat.OMI] = ModelFormat.OMI + VALID_OVERRIDES: ClassVar = { + "type": ModelType.LoRA, + "format": ModelFormat.OMI, + } + @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - - if type_override is not None and type_override is not ModelType.LoRA: - raise NotAMatch(cls, f"type override is {type_override}, not LoRA") - - if format_override is not None and format_override is not ModelFormat.OMI: - raise NotAMatch(cls, f"format override is {format_override}, not OMI") - - if type_override is ModelType.LoRA and format_override is ModelFormat.OMI: + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, + ): return cls(**fields) # Heuristic: OMI LoRAs are always files, never directories @@ -446,12 +491,12 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: if not is_omi_lora_heuristic: raise NotAMatch(cls, "model does not match OMI LoRA heuristics") - base = fields.get("base") or cls.get_base_or_raise(mod) + base = fields.get("base") or cls._get_base_or_raise(mod) return cls(**fields, base=base) @classmethod - def get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: metadata = mod.metadata() architecture = metadata["modelspec.architecture"] @@ -468,18 +513,18 @@ class LoRALyCORISConfig(LoRAConfigBase, ModelConfigBase): format: Literal[ModelFormat.LyCORIS] = ModelFormat.LyCORIS + VALID_OVERRIDES: ClassVar = { + "type": ModelType.LoRA, + "format": ModelFormat.LyCORIS, + } + @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - - if type_override is not None and type_override is not ModelType.LoRA: - raise NotAMatch(cls, f"type override is {type_override}, not LoRA") - - if format_override is not None and format_override is not ModelFormat.LyCORIS: - raise NotAMatch(cls, f"format override is {format_override}, not LyCORIS") - - if type_override is ModelType.LoRA and format_override is ModelFormat.LyCORIS: + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, + ): return cls(**fields) # Heuristic: LyCORIS LoRAs are always files, never directories @@ -544,18 +589,18 @@ class LoRADiffusersConfig(LoRAConfigBase, ModelConfigBase): format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers + VALID_OVERRIDES: ClassVar = { + "type": ModelType.LoRA, + "format": ModelFormat.Diffusers, + } + @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - - if type_override is not None and type_override is not ModelType.LoRA: - raise NotAMatch(cls, f"type override is {type_override}, not LoRA") - - if format_override is not None and format_override is not ModelFormat.Diffusers: - raise NotAMatch(cls, f"format override is {format_override}, not Diffusers") - - if type_override is ModelType.LoRA and format_override is ModelFormat.Diffusers: + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, + ): return cls(**fields) # Heuristic: Diffusers LoRAs are always directories, never files @@ -583,8 +628,31 @@ class VAECheckpointConfig(VAEConfigBase, CheckpointConfigBase, ModelConfigBase): format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint + VALID_OVERRIDES: ClassVar = { + "type": ModelType.VAE, + "format": ModelFormat.Checkpoint, + } + @classmethod - def get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, + ): + return cls(**fields) + + if mod.path.is_dir(): + raise NotAMatch(cls, "model path is a directory, not a file") + + if not mod.has_keys_starting_with({"encoder.conv_in", "decoder.conv_in"}): + raise NotAMatch(cls, "model does not match Checkpoint VAE heuristics") + + base = fields.get("base") or cls._get_base_or_raise(mod) + return cls(**fields, base=base) + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: # Heuristic: VAEs of all architectures have a similar structure; the best we can do is guess based on name for regexp, basetype in [ (r"xl", BaseModelType.StableDiffusionXL), @@ -597,37 +665,42 @@ def get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, "cannot determine base type") - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - if type_override is not None and type_override is not ModelType.VAE: - raise NotAMatch(cls, f"type override is {type_override}, not VAE") +class VAEDiffusersConfig(VAEConfigBase, DiffusersConfigBase, ModelConfigBase): + """Model config for standalone VAE models (diffusers version).""" - if format_override is not None and format_override is not ModelFormat.Checkpoint: - raise NotAMatch(cls, f"format override is {format_override}, not Checkpoint") + format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers - if type_override is ModelType.VAE and format_override is ModelFormat.Checkpoint: + VALID_OVERRIDES: ClassVar = { + "type": ModelType.VAE, + "format": ModelFormat.Diffusers, + } + VALID_CLASS_NAMES: ClassVar = { + "AutoencoderKL", + "AutoencoderTiny", + } + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, + ): return cls(**fields) - if mod.path.is_dir(): - raise NotAMatch(cls, "model path is a directory, not a file") + if mod.path.is_file(): + raise NotAMatch(cls, "model path is a file, not a directory") - if not mod.has_keys_starting_with({"encoder.conv_in", "decoder.conv_in"}): - raise NotAMatch(cls, "model does not match Checkpoint VAE heuristics") + raise_for_class_names( + config_class=cls, + config_path=mod.path / "config.json", + valid_class_names=cls.VALID_CLASS_NAMES, + ) - base = fields.get("base") or cls.get_base_or_raise(mod) + base = fields.get("base") or cls._get_base_or_raise(mod) return cls(**fields, base=base) - -class VAEDiffusersConfig(VAEConfigBase, DiffusersConfigBase, ModelConfigBase): - """Model config for standalone VAE models (diffusers version).""" - - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers - - CLASS_NAMES: ClassVar = {"AutoencoderKL", "AutoencoderTiny"} - @classmethod def _config_looks_like_sdxl(cls, config: dict[str, Any]) -> bool: # Heuristic: These config values that distinguish Stability's SD 1.x VAE from their SDXL VAE. @@ -648,7 +721,8 @@ def _guess_name(cls, mod: ModelOnDisk) -> str: return name @classmethod - def get_base(cls, mod: ModelOnDisk, config: dict[str, Any]) -> BaseModelType: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + config = get_config_or_raise(cls, mod.path / "config.json") if cls._config_looks_like_sdxl(config): return BaseModelType.StableDiffusionXL elif cls._name_looks_like_sdxl(mod): @@ -657,39 +731,6 @@ def get_base(cls, mod: ModelOnDisk, config: dict[str, Any]) -> BaseModelType: # TODO(psyche): Figure out how to positively identify SD1 here, and raise if we can't. Until then, YOLO. return BaseModelType.StableDiffusion1 - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - - if type_override is not None and type_override is not ModelType.VAE: - raise NotAMatch(cls, f"type override is {type_override}, not VAE") - - if format_override is not None and format_override is not ModelFormat.Diffusers: - raise NotAMatch(cls, f"format override is {format_override}, not Diffusers") - - if type_override is ModelType.VAE and format_override is ModelFormat.Diffusers: - return cls(**fields) - - if mod.path.is_file(): - raise NotAMatch(cls, "model path is a file, not a directory") - - try: - config = load_json(mod.path / "config.json") - except Exception as e: - raise NotAMatch(cls, "unable to load config.json") from e - - try: - config_class_name = get_class_name_from_config(config) - except Exception as e: - raise NotAMatch(cls, "unable to determine class name from config") from e - - if config_class_name not in cls.CLASS_NAMES: - raise NotAMatch(cls, f"model class is not one of {cls.CLASS_NAMES}") - - base = fields.get("base") or cls.get_base(mod, config) - return cls(**fields, base=base) - class ControlNetDiffusersConfig(DiffusersConfigBase, ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for ControlNet models (diffusers version).""" @@ -710,7 +751,7 @@ class TextualInversionConfigBase(ABC, BaseModel): KNOWN_KEYS: ClassVar = {"string_to_param", "emb_params", "clip_g"} @classmethod - def file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) -> bool: + def _file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) -> bool: try: p = path or mod.path @@ -738,40 +779,15 @@ def file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) - return False @classmethod - def get_base(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseModelType: + def _get_base_or_raise(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseModelType: p = path or mod.path try: state_dict = mod.load_state_dict(p) - if "string_to_token" in state_dict: - token_dim = list(state_dict["string_to_param"].values())[0].shape[-1] - elif "emb_params" in state_dict: - token_dim = state_dict["emb_params"].shape[-1] - elif "clip_g" in state_dict: - token_dim = state_dict["clip_g"].shape[-1] - else: - token_dim = list(state_dict.values())[0].shape[0] - - match token_dim: - case 768: - return BaseModelType.StableDiffusion1 - case 1024: - return BaseModelType.StableDiffusion2 - case 1280: - return BaseModelType.StableDiffusionXL - case _: - pass - except Exception: - pass - - raise InvalidModelConfigException(f"{p}: Could not determine base type") - - @classmethod - def get_base_or_raise(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseModelType: - p = path or mod.path + except Exception as e: + raise NotAMatch(cls, f"unable to load state dict from {p}: {e}") from e try: - state_dict = mod.load_state_dict(p) if "string_to_token" in state_dict: token_dim = list(state_dict["string_to_param"].values())[0].shape[-1] elif "emb_params" in state_dict: @@ -780,20 +796,18 @@ def get_base_or_raise(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseMo token_dim = state_dict["clip_g"].shape[-1] else: token_dim = list(state_dict.values())[0].shape[0] + except Exception as e: + raise NotAMatch(cls, f"unable to determine token dimension from state dict in {p}: {e}") from e - match token_dim: - case 768: - return BaseModelType.StableDiffusion1 - case 1024: - return BaseModelType.StableDiffusion2 - case 1280: - return BaseModelType.StableDiffusionXL - case _: - pass - except Exception: - pass - - raise InvalidModelConfigException(f"{p}: Could not determine base type") + match token_dim: + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case 1280: + return BaseModelType.StableDiffusionXL + case _: + raise NotAMatch(cls, f"unrecognized token dimension {token_dim}") class TextualInversionFileConfig(TextualInversionConfigBase, ModelConfigBase): @@ -801,31 +815,31 @@ class TextualInversionFileConfig(TextualInversionConfigBase, ModelConfigBase): format: Literal[ModelFormat.EmbeddingFile] = ModelFormat.EmbeddingFile + VALID_OVERRIDES: ClassVar = { + "type": ModelType.TextualInversion, + "format": ModelFormat.EmbeddingFile, + } + @classmethod def get_tag(cls) -> Tag: return Tag(f"{ModelType.TextualInversion.value}.{ModelFormat.EmbeddingFile.value}") @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - - if type_override is not None and type_override is not ModelType.TextualInversion: - raise NotAMatch(cls, f"type override is {type_override}, not TextualInversion") - - if format_override is not None and format_override is not ModelFormat.EmbeddingFile: - raise NotAMatch(cls, f"format override is {format_override}, not EmbeddingFile") - - if type_override is ModelType.TextualInversion and format_override is ModelFormat.EmbeddingFile: + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, + ): return cls(**fields) if mod.path.is_dir(): raise NotAMatch(cls, "model path is a directory, not a file") - if not cls.file_looks_like_embedding(mod): + if not cls._file_looks_like_embedding(mod): raise NotAMatch(cls, "model does not look like a textual inversion embedding file") - base = fields.get("base") or cls.get_base_or_raise(mod) + base = fields.get("base") or cls._get_base_or_raise(mod) return cls(**fields, base=base) @@ -834,30 +848,30 @@ class TextualInversionFolderConfig(TextualInversionConfigBase, ModelConfigBase): format: Literal[ModelFormat.EmbeddingFolder] = ModelFormat.EmbeddingFolder + VALID_OVERRIDES: ClassVar = { + "type": ModelType.TextualInversion, + "format": ModelFormat.EmbeddingFolder, + } + @classmethod def get_tag(cls) -> Tag: return Tag(f"{ModelType.TextualInversion.value}.{ModelFormat.EmbeddingFolder.value}") @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - - if type_override is not None and type_override is not ModelType.TextualInversion: - raise NotAMatch(cls, f"type override is {type_override}, not TextualInversion") - - if format_override is not None and format_override is not ModelFormat.EmbeddingFolder: - raise NotAMatch(cls, f"format override is {format_override}, not EmbeddingFolder") - - if type_override is ModelType.TextualInversion and format_override is ModelFormat.EmbeddingFolder: + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, + ): return cls(**fields) if mod.path.is_file(): raise NotAMatch(cls, "model path is a file, not a directory") for p in mod.weight_files(): - if cls.file_looks_like_embedding(mod, p): - base = fields.get("base") or cls.get_base_or_raise(mod, p) + if cls._file_looks_like_embedding(mod, p): + base = fields.get("base") or cls._get_base_or_raise(mod, p) return cls(**fields, base=base) raise NotAMatch(cls, "model does not look like a textual inversion embedding folder") @@ -937,7 +951,7 @@ class CLIPEmbedDiffusersConfig(DiffusersConfigBase): format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers base: Literal[BaseModelType.Any] = BaseModelType.Any - CLASS_NAMES: ClassVar = { + VALID_CLASS_NAMES: ClassVar = { "CLIPModel", "CLIPTextModel", "CLIPTextModelWithProjection", @@ -963,47 +977,37 @@ class CLIPGEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): variant: Literal[ClipVariantType.G] = ClipVariantType.G + VALID_OVERRIDES: ClassVar = { + "type": ModelType.CLIPEmbed, + "format": ModelFormat.Diffusers, + "variant": ClipVariantType.G, + } + @classmethod def get_tag(cls) -> Tag: return Tag(f"{ModelType.CLIPEmbed.value}.{ModelFormat.Diffusers.value}.{ClipVariantType.G.value}") @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - variant_override = fields.get("variant") - - if type_override is not None and type_override is not ModelType.CLIPEmbed: - raise NotAMatch(cls, f"type override is {type_override}, not CLIPEmbed") - - if format_override is not None and format_override is not ModelFormat.Diffusers: - raise NotAMatch(cls, f"format override is {format_override}, not Diffusers") - - if variant_override is not None and variant_override is not ClipVariantType.G: - raise NotAMatch(cls, f"variant override is {variant_override}, not G") - - if ( - type_override is ModelType.CLIPEmbed - and format_override is ModelFormat.Diffusers - and variant_override is ClipVariantType.G + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, ): return cls(**fields) if mod.path.is_file(): raise NotAMatch(cls, "model path is a file, not a directory") - try: - config = load_json(mod.path / "config.json") - except Exception as e: - raise NotAMatch(cls, "unable to load config.json") from e + config_path = mod.path / "config.json" - try: - config_class_name = get_class_name_from_config(config) - except Exception as e: - raise NotAMatch(cls, "unable to determine class name from config") from e + raise_for_class_names( + config_class=cls, + config_path=config_path, + valid_class_names=cls.VALID_CLASS_NAMES, + ) - if config_class_name not in cls.CLASS_NAMES: - raise NotAMatch(cls, f"model class is not one of {cls.CLASS_NAMES}") + config = get_config_or_raise(cls, config_path) clip_variant = cls.get_clip_variant_type(config) @@ -1018,48 +1022,37 @@ class CLIPLEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): variant: Literal[ClipVariantType.L] = ClipVariantType.L + VALID_OVERRIDES: ClassVar = { + "type": ModelType.CLIPEmbed, + "format": ModelFormat.Diffusers, + "variant": ClipVariantType.L, + } + @classmethod def get_tag(cls) -> Tag: return Tag(f"{ModelType.CLIPEmbed.value}.{ModelFormat.Diffusers.value}.{ClipVariantType.L.value}") @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - variant_override = fields.get("variant") - - if type_override is not None and type_override is not ModelType.CLIPEmbed: - raise NotAMatch(cls, f"type override is {type_override}, not CLIPEmbed") - - if format_override is not None and format_override is not ModelFormat.Diffusers: - raise NotAMatch(cls, f"format override is {format_override}, not Diffusers") - - if variant_override is not None and variant_override is not ClipVariantType.L: - raise NotAMatch(cls, f"variant override is {variant_override}, not L") - - if ( - type_override is ModelType.CLIPEmbed - and format_override is ModelFormat.Diffusers - and variant_override is ClipVariantType.L + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, ): return cls(**fields) if mod.path.is_file(): raise NotAMatch(cls, "model path is a file, not a directory") - try: - config = load_json(mod.path / "config.json") - except Exception as e: - raise NotAMatch(cls, "unable to load config.json") from e - - try: - config_class_name = get_class_name_from_config(config) - except Exception as e: - raise NotAMatch(cls, "unable to determine class name from config") from e + config_path = mod.path / "config.json" - if config_class_name not in cls.CLASS_NAMES: - raise NotAMatch(cls, f"model class is not one of {cls.CLASS_NAMES}") + raise_for_class_names( + config_class=cls, + config_path=config_path, + valid_class_names=cls.VALID_CLASS_NAMES, + ) + config = get_config_or_raise(cls, config_path) clip_variant = cls.get_clip_variant_type(config) if clip_variant is not ClipVariantType.L: @@ -1089,25 +1082,18 @@ class SpandrelImageToImageConfig(ModelConfigBase): type: Literal[ModelType.SpandrelImageToImage] = ModelType.SpandrelImageToImage format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint + VALID_OVERRIDES: ClassVar = { + "type": ModelType.SpandrelImageToImage, + "format": ModelFormat.Checkpoint, + "base": BaseModelType.Any, + } + @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - base_override = fields.get("base") - - if type_override is not None and type_override is not ModelType.SpandrelImageToImage: - raise NotAMatch(cls, f"type override is {type_override}, not SpandrelImageToImage") - - if format_override is not None and format_override is not ModelFormat.Checkpoint: - raise NotAMatch(cls, f"format override is {format_override}, not Checkpoint") - - if base_override is not None and base_override is not BaseModelType.Any: - raise NotAMatch(cls, f"base override is {base_override}, not Any") - - if ( - type_override is ModelType.SpandrelImageToImage - and format_override is ModelFormat.Checkpoint - and base_override is BaseModelType.Any + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, ): return cls(**fields) @@ -1151,40 +1137,36 @@ class LlavaOnevisionConfig(DiffusersConfigBase, ModelConfigBase): base: Literal[BaseModelType.Any] = BaseModelType.Any variant: Literal[ModelVariantType.Normal] = ModelVariantType.Normal - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - type_override = fields.get("type") - format_override = fields.get("format") - - if type_override is not None and type_override is not ModelType.LlavaOnevision: - raise NotAMatch(cls, f"type override is {type_override}, not LlavaOnevision") + VALID_OVERRIDES: ClassVar = { + "type": ModelType.LlavaOnevision, + "format": ModelFormat.Diffusers, + } - if format_override is not None and format_override is not ModelFormat.Diffusers: - raise NotAMatch(cls, f"format override is {format_override}, not Diffusers") + VALID_CLASS_NAMES: ClassVar = { + "LlavaOnevisionForConditionalGeneration", + } - if type_override is ModelType.LlavaOnevision and format_override is ModelFormat.Diffusers: + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + if matches_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, + ): return cls(**fields) if mod.path.is_file(): raise NotAMatch(cls, "model path is a file, not a directory") - # Heuristic: Look for the LlavaOnevisionForConditionalGeneration class name in the config - try: - config = load_json(mod.path / "config.json") - except Exception as e: - raise NotAMatch(cls, "unable to load config.json") from e - - try: - config_class_name = get_class_name_from_config(config) - except Exception as e: - raise NotAMatch(cls, "unable to determine class name from config.json") from e + config_path = mod.path / "config.json" - if config_class_name != "LlavaOnevisionForConditionalGeneration": - raise NotAMatch(cls, "model class is not LlavaOnevisionForConditionalGeneration") + raise_for_class_names( + config_class=cls, + config_path=config_path, + valid_class_names=cls.VALID_CLASS_NAMES, + ) - base = fields.get("base") or BaseModelType.Any - variant = fields.get("variant") or ModelVariantType.Normal - return cls(**fields, base=base, variant=variant) + return cls(**fields) class ApiModelConfig(MainConfigBase, ModelConfigBase): diff --git a/scripts/classify-model.py b/scripts/classify-model.py index 6411b4c7055..78bbd5a2f68 100755 --- a/scripts/classify-model.py +++ b/scripts/classify-model.py @@ -7,7 +7,8 @@ from typing import get_args from invokeai.backend.model_hash.model_hash import HASHING_ALGORITHMS -from invokeai.backend.model_manager import InvalidModelConfigException, ModelConfigBase, ModelProbe +from invokeai.backend.model_manager import InvalidModelConfigException, ModelProbe +from invokeai.backend.model_manager.config import ModelConfigFactory algos = ", ".join(set(get_args(HASHING_ALGORITHMS))) @@ -30,7 +31,7 @@ def classify_with_fallback(path: Path, hash_algo: HASHING_ALGORITHMS): try: return ModelProbe.probe(path, hash_algo=hash_algo) except InvalidModelConfigException: - return ModelConfigBase.classify(path, hash_algo) + return ModelConfigFactory.from_model_on_disk(mod=path, hash_algo=hash_algo,) for path in args.model_path: diff --git a/tests/test_model_probe.py b/tests/test_model_probe.py index 8112ccdd19b..e24a3ac8bd7 100644 --- a/tests/test_model_probe.py +++ b/tests/test_model_probe.py @@ -132,7 +132,10 @@ def parse(cls, mod: ModelOnDisk) -> dict[str, Any]: def test_minimal_working_example(datadir: Path): model_path = datadir / "minimal_config_model.json" overrides = {"base": BaseModelType.StableDiffusion1} - config = ModelConfigBase.classify(model_path, **overrides) + config = ModelConfigFactory.from_model_on_disk( + mod=model_path, + overrides=overrides, + ) assert isinstance(config, MinimalConfigExample) assert config.base == BaseModelType.StableDiffusion1 @@ -160,7 +163,10 @@ def test_regression_against_model_probe(datadir: Path, override_model_loading): try: stripped_mod = StrippedModelOnDisk(path) - new_config = ModelConfigBase.classify(stripped_mod, hash=fake_hash, key=fake_key) + new_config = ModelConfigFactory.from_model_on_disk( + mod=stripped_mod, + overrides={"hash": fake_hash, "key": fake_key}, + ) except InvalidModelConfigException: pass From 42206575c5f900f895c2d5346bf42df8420be038 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:07:29 +1000 Subject: [PATCH 17/62] fix(mm): abstractmethod bork --- invokeai/backend/model_manager/config.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index bb77cf6fdfd..c3e196b53c2 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -25,7 +25,7 @@ import logging import re import time -from abc import ABC, abstractmethod +from abc import ABC from enum import Enum from inspect import isabstract from pathlib import Path @@ -280,12 +280,11 @@ def get_tag(cls) -> Tag: return Tag(f"{type}.{format}") @classmethod - @abstractmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: """Given the model on disk and any overrides, return an instance of this config class. Implementations should raise NotAMatch if the model does not match this config class.""" - pass + raise NotImplementedError(f"from_model_on_disk not implemented for {cls.__name__}") class UnknownModelConfig(ModelConfigBase): From 6c60e6dff9f66510ff959cb4b677c245b739e1f6 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:07:51 +1000 Subject: [PATCH 18/62] tidy(mm): clarify that model id utils are private --- invokeai/backend/model_manager/config.py | 57 +++++++++++++----------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index c3e196b53c2..297812f1cce 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -96,8 +96,11 @@ def __init__( DEFAULTS_PRECISION = Literal["fp16", "fp32"] +# These utility functions are tightly coupled to the config classes below in order to make the process of raising +# NotAMatch exceptions as easy and consistent as possible. -def get_config_or_raise( + +def _get_config_or_raise( config_class: type, config_path: Path, ) -> dict[str, Any]: @@ -112,14 +115,14 @@ def get_config_or_raise( raise NotAMatch(config_class, f"unable to load config file: {config_path}") from e -def raise_for_class_names( +def _validate_class_names( config_class: type, config_path: Path, valid_class_names: set[str], ) -> None: """Raise NotAMatch if the config file is missing or does not contain a valid class name.""" - config = get_config_or_raise(config_class, config_path) + config = _get_config_or_raise(config_class, config_path) try: if "_class_name" in config: @@ -135,8 +138,8 @@ def raise_for_class_names( raise NotAMatch(config_class, f"model class is not one of {valid_class_names}, got {config_class_name}") -def matches_overrides( - config_class: "Type[AnyModelConfig]", +def _validate_overrides( + config_class: type, provided_overrides: dict[str, Any], valid_overrides: dict[str, Any], ) -> bool: @@ -389,7 +392,7 @@ class T5EncoderConfig(T5EncoderConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -399,7 +402,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: if mod.path.is_file(): raise NotAMatch(cls, "model path is a file, not a directory") - raise_for_class_names( + _validate_class_names( config_class=cls, config_path=mod.path / "text_encoder_2" / "config.json", valid_class_names=cls.VALID_CLASS_NAMES, @@ -428,7 +431,7 @@ class T5EncoderBnbQuantizedLlmInt8bConfig(T5EncoderConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -436,7 +439,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) # Heuristic: Look for the T5EncoderModel class name in the config - raise_for_class_names( + _validate_class_names( config_class=cls, config_path=mod.path / "text_encoder_2" / "config.json", valid_class_names=cls.VALID_CLASS_NAMES, @@ -464,7 +467,7 @@ class LoRAOmiConfig(LoRAConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -519,7 +522,7 @@ class LoRALyCORISConfig(LoRAConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -595,7 +598,7 @@ class LoRADiffusersConfig(LoRAConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -634,7 +637,7 @@ class VAECheckpointConfig(VAEConfigBase, CheckpointConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -681,7 +684,7 @@ class VAEDiffusersConfig(VAEConfigBase, DiffusersConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -691,7 +694,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: if mod.path.is_file(): raise NotAMatch(cls, "model path is a file, not a directory") - raise_for_class_names( + _validate_class_names( config_class=cls, config_path=mod.path / "config.json", valid_class_names=cls.VALID_CLASS_NAMES, @@ -721,7 +724,7 @@ def _guess_name(cls, mod: ModelOnDisk) -> str: @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: - config = get_config_or_raise(cls, mod.path / "config.json") + config = _get_config_or_raise(cls, mod.path / "config.json") if cls._config_looks_like_sdxl(config): return BaseModelType.StableDiffusionXL elif cls._name_looks_like_sdxl(mod): @@ -825,7 +828,7 @@ def get_tag(cls) -> Tag: @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -858,7 +861,7 @@ def get_tag(cls) -> Tag: @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -988,7 +991,7 @@ def get_tag(cls) -> Tag: @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -1000,13 +1003,13 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: config_path = mod.path / "config.json" - raise_for_class_names( + _validate_class_names( config_class=cls, config_path=config_path, valid_class_names=cls.VALID_CLASS_NAMES, ) - config = get_config_or_raise(cls, config_path) + config = _get_config_or_raise(cls, config_path) clip_variant = cls.get_clip_variant_type(config) @@ -1033,7 +1036,7 @@ def get_tag(cls) -> Tag: @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -1045,13 +1048,13 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: config_path = mod.path / "config.json" - raise_for_class_names( + _validate_class_names( config_class=cls, config_path=config_path, valid_class_names=cls.VALID_CLASS_NAMES, ) - config = get_config_or_raise(cls, config_path) + config = _get_config_or_raise(cls, config_path) clip_variant = cls.get_clip_variant_type(config) if clip_variant is not ClipVariantType.L: @@ -1089,7 +1092,7 @@ class SpandrelImageToImageConfig(ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -1147,7 +1150,7 @@ class LlavaOnevisionConfig(DiffusersConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if matches_overrides( + if _validate_overrides( config_class=cls, provided_overrides=fields, valid_overrides=cls.VALID_OVERRIDES, @@ -1159,7 +1162,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: config_path = mod.path / "config.json" - raise_for_class_names( + _validate_class_names( config_class=cls, config_path=config_path, valid_class_names=cls.VALID_CLASS_NAMES, From b1780f9c5e9ce4c0a8047c864fed3dc3da6b34ec Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:17:57 +1000 Subject: [PATCH 19/62] fix(mm): fall back to UnknownModelConfig correctly --- invokeai/backend/model_manager/config.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 297812f1cce..af083618d17 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -91,7 +91,7 @@ def __init__( config_class: type, reason: str, ): - super().__init__(f"{config_class.__name__} does not match: {reason}") + super().__init__(f"{config_class.__name__}: {reason}") DEFAULTS_PRECISION = Literal["fp16", "fp32"] @@ -1344,32 +1344,34 @@ def from_model_on_disk( # Store results as a mapping of config class to either an instance of that class or an exception # that was raised when trying to build it. - results: dict[type[AnyModelConfig], AnyModelConfig | Exception] = {} + results: dict[str, AnyModelConfig | Exception] = {} # Try to build an instance of each model config class that uses the classify API. # Each class will either return an instance of itself or raise NotAMatch if it doesn't match. # Other exceptions may be raised if something unexpected happens during matching or building. for config_class in ModelConfigBase.USING_CLASSIFY_API: + class_name = config_class.__name__ try: instance = config_class.from_model_on_disk(mod, fields) - results[config_class] = instance + results[class_name] = instance except NotAMatch as e: - results[config_class] = e + results[class_name] = e logger.debug(f"No match for {config_class.__name__} on model {mod.name}") except ValidationError as e: # This means the model matched, but we couldn't create the pydantic model instance for the config. # Maybe invalid overrides were provided? - results[config_class] = e + results[class_name] = e logger.warning(f"Schema validation error for {config_class.__name__} on model {mod.name}: {e}") except Exception as e: - results[config_class] = e + results[class_name] = e logger.warning(f"Unexpected exception while matching {mod.name} to {config_class.__name__}: {e}") matches = [r for r in results.values() if isinstance(r, ModelConfigBase)] if not matches and app_config.allow_unknown_models: logger.warning(f"Unable to identify model {mod.name}, classifying as UnknownModelConfig") - return UnknownModelConfig.from_model_on_disk(mod, fields) + logger.debug(f"Model matching results: {results}") + return UnknownModelConfig(**fields) instance = next(iter(matches)) if len(matches) > 1: From cfef47800f6dae6991394b0dd7d60e2b1fcf5940 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:30:49 +1000 Subject: [PATCH 20/62] feat(mm): port CLIPVisionDiffusersConfig to new api --- invokeai/backend/model_manager/config.py | 33 +++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index af083618d17..f9b74b067bb 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -1063,12 +1063,43 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -class CLIPVisionDiffusersConfig(DiffusersConfigBase, LegacyProbeMixin, ModelConfigBase): +class CLIPVisionDiffusersConfig(DiffusersConfigBase, ModelConfigBase): """Model config for CLIPVision.""" type: Literal[ModelType.CLIPVision] = ModelType.CLIPVision format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers + VALID_OVERRIDES: ClassVar = { + "type": ModelType.CLIPVision, + "format": ModelFormat.Diffusers, + } + + VALID_CLASS_NAMES: ClassVar = { + "CLIPVisionModelWithProjection", + } + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + if _validate_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, + ): + return cls(**fields) + + if mod.path.is_file(): + raise NotAMatch(cls, "model path is a file, not a directory") + + config_path = mod.path / "config.json" + + _validate_class_names( + config_class=cls, + config_path=config_path, + valid_class_names=cls.VALID_CLASS_NAMES, + ) + + return cls(**fields) + class T2IAdapterConfig(DiffusersConfigBase, ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for T2I.""" From 4f4268e570c64c1f1a6c070ed33daea57369f90d Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:34:14 +1000 Subject: [PATCH 21/62] feat(mm): port SigLIPDiffusersConfig to new api --- invokeai/backend/model_manager/config.py | 33 +++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index f9b74b067bb..29624eef956 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -1149,12 +1149,43 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: raise NotAMatch(cls, "model does not match SpandrelImageToImage heuristics") from e -class SigLIPConfig(DiffusersConfigBase, LegacyProbeMixin, ModelConfigBase): +class SigLIPConfig(DiffusersConfigBase, ModelConfigBase): """Model config for SigLIP.""" type: Literal[ModelType.SigLIP] = ModelType.SigLIP format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers + VALID_OVERRIDES: ClassVar = { + "type": ModelType.SigLIP, + "format": ModelFormat.Diffusers, + } + + VALID_CLASS_NAMES: ClassVar = { + "SiglipModel", + } + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + if _validate_overrides( + config_class=cls, + provided_overrides=fields, + valid_overrides=cls.VALID_OVERRIDES, + ): + return cls(**fields) + + if mod.path.is_file(): + raise NotAMatch(cls, "model path is a file, not a directory") + + config_path = mod.path / "config.json" + + _validate_class_names( + config_class=cls, + config_path=config_path, + valid_class_names=cls.VALID_CLASS_NAMES, + ) + + return cls(**fields) + class FluxReduxConfig(LegacyProbeMixin, ModelConfigBase): """Model config for FLUX Tools Redux model.""" From 01104f56ab09505906eb7914c149c000123bfaea Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:50:06 +1000 Subject: [PATCH 22/62] feat(mm): make match helpers more succint --- invokeai/backend/model_manager/config.py | 250 ++++++++--------------- 1 file changed, 89 insertions(+), 161 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 29624eef956..640ab563180 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -170,6 +170,24 @@ def _validate_overrides( return is_perfect_match +def _raise_if_not_file( + config_class: type, + mod: ModelOnDisk, +) -> None: + """Raise NotAMatch if the model path is not a file.""" + if not mod.path.is_file(): + raise NotAMatch(config_class, "model path is not a file") + + +def _raise_if_not_dir( + config_class: type, + mod: ModelOnDisk, +) -> None: + """Raise NotAMatch if the model path is not a directory.""" + if not mod.path.is_dir(): + raise NotAMatch(config_class, "model path is not a directory") + + class SubmodelDefinition(BaseModel): path_or_prefix: str model_type: ModelType @@ -392,21 +410,12 @@ class T5EncoderConfig(T5EncoderConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + _raise_if_not_dir(cls, mod) - if mod.path.is_file(): - raise NotAMatch(cls, "model path is a file, not a directory") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) - _validate_class_names( - config_class=cls, - config_path=mod.path / "text_encoder_2" / "config.json", - valid_class_names=cls.VALID_CLASS_NAMES, - ) + _validate_class_names(cls, mod.path / "text_encoder_2" / "config.json", cls.VALID_CLASS_NAMES) # Heuristic: Look for the presence of the unquantized config file (not present for bnb-quantized models) has_unquantized_config = (mod.path / "text_encoder_2" / "model.safetensors.index.json").exists() @@ -431,19 +440,13 @@ class T5EncoderBnbQuantizedLlmInt8bConfig(T5EncoderConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): + _raise_if_not_dir(cls, mod) + + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): return cls(**fields) # Heuristic: Look for the T5EncoderModel class name in the config - _validate_class_names( - config_class=cls, - config_path=mod.path / "text_encoder_2" / "config.json", - valid_class_names=cls.VALID_CLASS_NAMES, - ) + _validate_class_names(cls, mod.path / "text_encoder_2" / "config.json", cls.VALID_CLASS_NAMES) # Heuristic: look for the quantization in the filename name filename_looks_like_bnb = any(x for x in mod.weight_files() if "llm_int8" in x.as_posix()) @@ -467,16 +470,11 @@ class LoRAOmiConfig(LoRAConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + # OMI LoRAs are always files + _raise_if_not_file(cls, mod) - # Heuristic: OMI LoRAs are always files, never directories - if mod.path.is_dir(): - raise NotAMatch(cls, "model path is a directory, not a file") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) # Heuristic: differential diagnosis vs ControlLoRA and Diffusers if cls.flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: @@ -522,16 +520,11 @@ class LoRALyCORISConfig(LoRAConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + # LyCORIS LoRAs are always files, never directories + _raise_if_not_file(cls, mod) - # Heuristic: LyCORIS LoRAs are always files, never directories - if mod.path.is_dir(): - raise NotAMatch(cls, "model path is a directory, not a file") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) # Heuristic: differential diagnosis vs ControlLoRA and Diffusers if cls.flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: @@ -598,16 +591,11 @@ class LoRADiffusersConfig(LoRAConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + # Diffusers-style models always directories + _raise_if_not_dir(cls, mod) - # Heuristic: Diffusers LoRAs are always directories, never files - if mod.path.is_file(): - raise NotAMatch(cls, "model path is a file, not a directory") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) is_flux_lora_diffusers = cls.flux_lora_format(mod) == FluxLoRAFormat.Diffusers @@ -637,15 +625,10 @@ class VAECheckpointConfig(VAEConfigBase, CheckpointConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + _raise_if_not_file(cls, mod) - if mod.path.is_dir(): - raise NotAMatch(cls, "model path is a directory, not a file") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) if not mod.has_keys_starting_with({"encoder.conv_in", "decoder.conv_in"}): raise NotAMatch(cls, "model does not match Checkpoint VAE heuristics") @@ -684,21 +667,12 @@ class VAEDiffusersConfig(VAEConfigBase, DiffusersConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + _raise_if_not_dir(cls, mod) - if mod.path.is_file(): - raise NotAMatch(cls, "model path is a file, not a directory") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) - _validate_class_names( - config_class=cls, - config_path=mod.path / "config.json", - valid_class_names=cls.VALID_CLASS_NAMES, - ) + _validate_class_names(cls, mod.path / "config.json", cls.VALID_CLASS_NAMES) base = fields.get("base") or cls._get_base_or_raise(mod) return cls(**fields, base=base) @@ -828,15 +802,10 @@ def get_tag(cls) -> Tag: @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + _raise_if_not_file(cls, mod) - if mod.path.is_dir(): - raise NotAMatch(cls, "model path is a directory, not a file") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) if not cls._file_looks_like_embedding(mod): raise NotAMatch(cls, "model does not look like a textual inversion embedding file") @@ -861,15 +830,10 @@ def get_tag(cls) -> Tag: @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + _raise_if_not_dir(cls, mod) - if mod.path.is_file(): - raise NotAMatch(cls, "model path is a file, not a directory") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) for p in mod.weight_files(): if cls._file_looks_like_embedding(mod, p): @@ -991,23 +955,14 @@ def get_tag(cls) -> Tag: @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + _raise_if_not_dir(cls, mod) - if mod.path.is_file(): - raise NotAMatch(cls, "model path is a file, not a directory") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) config_path = mod.path / "config.json" - _validate_class_names( - config_class=cls, - config_path=config_path, - valid_class_names=cls.VALID_CLASS_NAMES, - ) + _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) config = _get_config_or_raise(cls, config_path) @@ -1036,23 +991,14 @@ def get_tag(cls) -> Tag: @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + _raise_if_not_dir(cls, mod) - if mod.path.is_file(): - raise NotAMatch(cls, "model path is a file, not a directory") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) config_path = mod.path / "config.json" - _validate_class_names( - config_class=cls, - config_path=config_path, - valid_class_names=cls.VALID_CLASS_NAMES, - ) + _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) config = _get_config_or_raise(cls, config_path) clip_variant = cls.get_clip_variant_type(config) @@ -1080,23 +1026,14 @@ class CLIPVisionDiffusersConfig(DiffusersConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + _raise_if_not_dir(cls, mod) - if mod.path.is_file(): - raise NotAMatch(cls, "model path is a file, not a directory") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) config_path = mod.path / "config.json" - _validate_class_names( - config_class=cls, - config_path=config_path, - valid_class_names=cls.VALID_CLASS_NAMES, - ) + _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) return cls(**fields) @@ -1123,15 +1060,10 @@ class SpandrelImageToImageConfig(ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + _raise_if_not_file(cls, mod) - if not mod.path.is_file(): - raise NotAMatch(cls, "model path is a directory, not a file") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) try: # It would be nice to avoid having to load the Spandrel model from disk here. A couple of options were @@ -1166,33 +1098,38 @@ class SigLIPConfig(DiffusersConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + _raise_if_not_dir(cls, mod) - if mod.path.is_file(): - raise NotAMatch(cls, "model path is a file, not a directory") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) config_path = mod.path / "config.json" - _validate_class_names( - config_class=cls, - config_path=config_path, - valid_class_names=cls.VALID_CLASS_NAMES, - ) + _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) return cls(**fields) -class FluxReduxConfig(LegacyProbeMixin, ModelConfigBase): +class FluxReduxConfig(ModelConfigBase): """Model config for FLUX Tools Redux model.""" type: Literal[ModelType.FluxRedux] = ModelType.FluxRedux format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint + VALID_OVERRIDES: ClassVar = { + "type": ModelType.SigLIP, + "format": ModelFormat.Diffusers, + } + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _raise_if_not_file(cls, mod) + + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) + + return cls(**fields) + class LlavaOnevisionConfig(DiffusersConfigBase, ModelConfigBase): """Model config for Llava Onevision models.""" @@ -1212,23 +1149,14 @@ class LlavaOnevisionConfig(DiffusersConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - if _validate_overrides( - config_class=cls, - provided_overrides=fields, - valid_overrides=cls.VALID_OVERRIDES, - ): - return cls(**fields) + _raise_if_not_dir(cls, mod) - if mod.path.is_file(): - raise NotAMatch(cls, "model path is a file, not a directory") + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) config_path = mod.path / "config.json" - _validate_class_names( - config_class=cls, - config_path=config_path, - valid_class_names=cls.VALID_CLASS_NAMES, - ) + _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) return cls(**fields) From 6c6601388fbff727a22404739e640a5ca6902ab4 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:53:30 +1000 Subject: [PATCH 23/62] feat(mm): port flux redux to new api --- .../backend/flux/redux/flux_redux_state_dict_utils.py | 4 ++-- invokeai/backend/model_manager/config.py | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/invokeai/backend/flux/redux/flux_redux_state_dict_utils.py b/invokeai/backend/flux/redux/flux_redux_state_dict_utils.py index a5a13b402d3..83e96d38451 100644 --- a/invokeai/backend/flux/redux/flux_redux_state_dict_utils.py +++ b/invokeai/backend/flux/redux/flux_redux_state_dict_utils.py @@ -1,7 +1,7 @@ -from typing import Any, Dict +from typing import Any -def is_state_dict_likely_flux_redux(state_dict: Dict[str, Any]) -> bool: +def is_state_dict_likely_flux_redux(state_dict: dict[str | int, Any]) -> bool: """Checks if the provided state dict is likely a FLUX Redux model.""" expected_keys = {"redux_down.bias", "redux_down.weight", "redux_up.bias", "redux_up.weight"} diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 640ab563180..a1e043dc35e 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -45,6 +45,7 @@ from invokeai.app.services.config.config_default import get_config from invokeai.app.util.misc import uuid_string +from invokeai.backend.flux.redux.flux_redux_state_dict_utils import is_state_dict_likely_flux_redux from invokeai.backend.model_hash.hash_validator import validate_hash from invokeai.backend.model_hash.model_hash import HASHING_ALGORITHMS from invokeai.backend.model_manager.model_on_disk import ModelOnDisk @@ -1117,8 +1118,8 @@ class FluxReduxConfig(ModelConfigBase): format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint VALID_OVERRIDES: ClassVar = { - "type": ModelType.SigLIP, - "format": ModelFormat.Diffusers, + "type": ModelType.FluxRedux, + "format": ModelFormat.Checkpoint, } @classmethod @@ -1128,6 +1129,9 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): return cls(**fields) + if not is_state_dict_likely_flux_redux(mod.load_state_dict()): + raise NotAMatch(cls, "model does not match FLUX Tools Redux heuristics") + return cls(**fields) From 20db2cb6edefcf5030c37d3372081067d5e53361 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 24 Sep 2025 20:08:08 +1000 Subject: [PATCH 24/62] feat(mm): port ip adapter to new api --- .../flux/ip_adapter/state_dict_utils.py | 6 +- invokeai/backend/model_manager/config.py | 141 +++++++++++++++++- 2 files changed, 136 insertions(+), 11 deletions(-) diff --git a/invokeai/backend/flux/ip_adapter/state_dict_utils.py b/invokeai/backend/flux/ip_adapter/state_dict_utils.py index 90f11ff642b..24ac53550f9 100644 --- a/invokeai/backend/flux/ip_adapter/state_dict_utils.py +++ b/invokeai/backend/flux/ip_adapter/state_dict_utils.py @@ -1,11 +1,11 @@ -from typing import Any, Dict +from typing import Any import torch from invokeai.backend.flux.ip_adapter.xlabs_ip_adapter_flux import XlabsIpAdapterParams -def is_state_dict_xlabs_ip_adapter(sd: Dict[str, Any]) -> bool: +def is_state_dict_xlabs_ip_adapter(sd: dict[str | int, Any]) -> bool: """Is the state dict for an XLabs FLUX IP-Adapter model? This is intended to be a reasonably high-precision detector, but it is not guaranteed to have perfect precision. @@ -27,7 +27,7 @@ def is_state_dict_xlabs_ip_adapter(sd: Dict[str, Any]) -> bool: return False -def infer_xlabs_ip_adapter_params_from_state_dict(state_dict: dict[str, torch.Tensor]) -> XlabsIpAdapterParams: +def infer_xlabs_ip_adapter_params_from_state_dict(state_dict: dict[str | int, torch.Tensor]) -> XlabsIpAdapterParams: num_double_blocks = 0 context_dim = 0 hidden_dim = 0 diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index a1e043dc35e..03fb8c66e1d 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -45,6 +45,7 @@ from invokeai.app.services.config.config_default import get_config from invokeai.app.util.misc import uuid_string +from invokeai.backend.flux.ip_adapter.state_dict_utils import is_state_dict_xlabs_ip_adapter from invokeai.backend.flux.redux.flux_redux_state_dict_utils import is_state_dict_likely_flux_redux from invokeai.backend.model_hash.hash_validator import validate_hash from invokeai.backend.model_hash.model_hash import HASHING_ALGORITHMS @@ -149,7 +150,8 @@ def _validate_overrides( Args: config_class: The config class that is being tested. provided_overrides: The overrides provided by the user. - valid_overrides: The overrides that are valid for this config class. + valid_overrides: The overrides that are valid for this config class. The value can be a specific value or a + callable that takes the provided value and returns True if it is valid. Returns: True if all provided overrides match the valid overrides, False if some valid overrides are missing. @@ -158,14 +160,21 @@ def _validate_overrides( NotAMatch if any override does not match the allowed value. """ is_perfect_match = True - for key, value in valid_overrides.items(): - if key not in provided_overrides: + for override_name, constraint in valid_overrides.items(): + if override_name not in provided_overrides: is_perfect_match = False continue - if provided_overrides[key] != value: + # Handle the typical case where the constraint is a specific value + if provided_overrides[override_name] != constraint: raise NotAMatch( config_class, - f"override {key}={provided_overrides[key]} does not match required value {key}={value}", + f"override {override_name}={provided_overrides[override_name]} does not match required value {override_name}={constraint}", + ) + # Handle the less common case where the constraint is a callable + elif callable(constraint) and not constraint(provided_overrides[override_name]): + raise NotAMatch( + config_class, + f"override {override_name}={provided_overrides[override_name]} does not match required value {override_name}=callable", ) return is_perfect_match @@ -521,7 +530,6 @@ class LoRALyCORISConfig(LoRAConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - # LyCORIS LoRAs are always files, never directories _raise_if_not_file(cls, mod) if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): @@ -895,20 +903,137 @@ class IPAdapterConfigBase(ABC, BaseModel): type: Literal[ModelType.IPAdapter] = ModelType.IPAdapter -class IPAdapterInvokeAIConfig(IPAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): +IPAdapterInvokeAIConfigBaseTypes: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, +] +"""Helper TypeAlias for valid base types for IP Adapter models in the InvokeAI format.""" + +ip_adapter_invoke_ai_base_type_adapter = TypeAdapter[IPAdapterInvokeAIConfigBaseTypes](IPAdapterInvokeAIConfigBaseTypes) +"""Helper TypeAdapter for IP Adapter InvokeAI base types.""" + + +class IPAdapterInvokeAIConfig(IPAdapterConfigBase, ModelConfigBase): """Model config for IP Adapter diffusers format models.""" # TODO(ryand): Should we deprecate this field? From what I can tell, it hasn't been probed correctly for a long # time. Need to go through the history to make sure I'm understanding this fully. image_encoder_model_id: str format: Literal[ModelFormat.InvokeAI] = ModelFormat.InvokeAI + base: IPAdapterInvokeAIConfigBaseTypes = Field(...) + + VALID_OVERRIDES: ClassVar = { + "type": ModelType.IPAdapter, + "format": ModelFormat.InvokeAI, + "base": ip_adapter_invoke_ai_base_type_adapter.validate_python, + } + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _raise_if_not_dir(cls, mod) + + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) + + weights_file = mod.path / "ip_adapter.bin" + if not weights_file.exists(): + raise NotAMatch(cls, "missing ip_adapter.bin weights file") + + image_encoder_metadata_file = mod.path / "image_encoder.txt" + if not image_encoder_metadata_file.exists(): + raise NotAMatch(cls, "missing image_encoder.txt metadata file") + + base = fields.get("base") or cls._get_base_or_raise(mod) + return cls(**fields, base=base) + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterInvokeAIConfigBaseTypes: + state_dict = mod.load_state_dict() + + try: + cross_attention_dim = state_dict["ip_adapter"]["1.to_k_ip.weight"].shape[-1] + except Exception as e: + raise NotAMatch(cls, f"unable to determine cross attention dimension: {e}") from e + + match cross_attention_dim: + case 1280: + return BaseModelType.StableDiffusionXL + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case _: + raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") + +IPAdapterCheckpointConfigBaseTypes: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, + BaseModelType.Flux, +] +"""Helper TypeAlias for valid base types for IP Adapter models in the Checkpoint format.""" + +ip_adapter_checkpoint_base_type_adapter = TypeAdapter[IPAdapterCheckpointConfigBaseTypes]( + IPAdapterCheckpointConfigBaseTypes +) +"""Helper TypeAdapter for IP Adapter Checkpoint base types.""" -class IPAdapterCheckpointConfig(IPAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): + +class IPAdapterCheckpointConfig(IPAdapterConfigBase, ModelConfigBase): """Model config for IP Adapter checkpoint format models.""" format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint + VALID_OVERRIDES: ClassVar = { + "type": ModelType.IPAdapter, + "format": ModelFormat.Checkpoint, + "base": ip_adapter_checkpoint_base_type_adapter.validate_python, + } + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _raise_if_not_file(cls, mod) + + if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): + return cls(**fields) + + if not mod.has_keys_starting_with( + { + "image_proj.", + "ip_adapter.", + # XLabs FLUX IP-Adapter models have keys startinh with "ip_adapter_proj_model.". + "ip_adapter_proj_model.", + } + ): + raise NotAMatch(cls, "model does not match Checkpoint IP Adapter heuristics") + + base = fields.get("base") or cls._get_base_or_raise(mod) + return cls(**fields, base=base) + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterCheckpointConfigBaseTypes: + state_dict = mod.load_state_dict() + + if is_state_dict_xlabs_ip_adapter(state_dict): + return BaseModelType.Flux + + try: + cross_attention_dim = state_dict["ip_adapter.1.to_k_ip.weight"].shape[-1] + except Exception as e: + raise NotAMatch(cls, f"unable to determine cross attention dimension: {e}") from e + + match cross_attention_dim: + case 1280: + return BaseModelType.StableDiffusionXL + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case _: + raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") + class CLIPEmbedDiffusersConfig(DiffusersConfigBase): """Model config for Clip Embeddings.""" From f0e931c35bc93c453a47695a2f8d39951acff1d1 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 24 Sep 2025 23:16:14 +1000 Subject: [PATCH 25/62] tidy(mm): skip optimistic override handling for now --- invokeai/backend/model_manager/config.py | 108 ++++++----------------- 1 file changed, 26 insertions(+), 82 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 03fb8c66e1d..9260656c1e8 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -144,40 +144,26 @@ def _validate_overrides( config_class: type, provided_overrides: dict[str, Any], valid_overrides: dict[str, Any], -) -> bool: +) -> None: """Check if the provided overrides match the valid overrides for this config class. Args: config_class: The config class that is being tested. provided_overrides: The overrides provided by the user. - valid_overrides: The overrides that are valid for this config class. The value can be a specific value or a - callable that takes the provided value and returns True if it is valid. - - Returns: - True if all provided overrides match the valid overrides, False if some valid overrides are missing. + valid_overrides: The overrides that are valid for this config class. Raises: NotAMatch if any override does not match the allowed value. """ - is_perfect_match = True - for override_name, constraint in valid_overrides.items(): - if override_name not in provided_overrides: - is_perfect_match = False + for key, value in valid_overrides.items(): + if key not in provided_overrides: continue - # Handle the typical case where the constraint is a specific value - if provided_overrides[override_name] != constraint: - raise NotAMatch( - config_class, - f"override {override_name}={provided_overrides[override_name]} does not match required value {override_name}={constraint}", - ) - # Handle the less common case where the constraint is a callable - elif callable(constraint) and not constraint(provided_overrides[override_name]): + if provided_overrides[key] != value: raise NotAMatch( config_class, - f"override {override_name}={provided_overrides[override_name]} does not match required value {override_name}=callable", + f"override {key}={provided_overrides[key]} does not match required value {key}={value}", ) - return is_perfect_match def _raise_if_not_file( @@ -422,8 +408,7 @@ class T5EncoderConfig(T5EncoderConfigBase, ModelConfigBase): def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_dir(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) _validate_class_names(cls, mod.path / "text_encoder_2" / "config.json", cls.VALID_CLASS_NAMES) @@ -452,8 +437,7 @@ class T5EncoderBnbQuantizedLlmInt8bConfig(T5EncoderConfigBase, ModelConfigBase): def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_dir(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) # Heuristic: Look for the T5EncoderModel class name in the config _validate_class_names(cls, mod.path / "text_encoder_2" / "config.json", cls.VALID_CLASS_NAMES) @@ -483,8 +467,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: # OMI LoRAs are always files _raise_if_not_file(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) # Heuristic: differential diagnosis vs ControlLoRA and Diffusers if cls.flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: @@ -532,8 +515,7 @@ class LoRALyCORISConfig(LoRAConfigBase, ModelConfigBase): def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_file(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) # Heuristic: differential diagnosis vs ControlLoRA and Diffusers if cls.flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: @@ -603,8 +585,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: # Diffusers-style models always directories _raise_if_not_dir(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) is_flux_lora_diffusers = cls.flux_lora_format(mod) == FluxLoRAFormat.Diffusers @@ -636,8 +617,7 @@ class VAECheckpointConfig(VAEConfigBase, CheckpointConfigBase, ModelConfigBase): def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_file(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) if not mod.has_keys_starting_with({"encoder.conv_in", "decoder.conv_in"}): raise NotAMatch(cls, "model does not match Checkpoint VAE heuristics") @@ -678,8 +658,7 @@ class VAEDiffusersConfig(VAEConfigBase, DiffusersConfigBase, ModelConfigBase): def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_dir(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) _validate_class_names(cls, mod.path / "config.json", cls.VALID_CLASS_NAMES) @@ -813,8 +792,7 @@ def get_tag(cls) -> Tag: def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_file(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) if not cls._file_looks_like_embedding(mod): raise NotAMatch(cls, "model does not look like a textual inversion embedding file") @@ -841,8 +819,7 @@ def get_tag(cls) -> Tag: def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_dir(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) for p in mod.weight_files(): if cls._file_looks_like_embedding(mod, p): @@ -903,15 +880,6 @@ class IPAdapterConfigBase(ABC, BaseModel): type: Literal[ModelType.IPAdapter] = ModelType.IPAdapter -IPAdapterInvokeAIConfigBaseTypes: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, -] -"""Helper TypeAlias for valid base types for IP Adapter models in the InvokeAI format.""" - -ip_adapter_invoke_ai_base_type_adapter = TypeAdapter[IPAdapterInvokeAIConfigBaseTypes](IPAdapterInvokeAIConfigBaseTypes) -"""Helper TypeAdapter for IP Adapter InvokeAI base types.""" class IPAdapterInvokeAIConfig(IPAdapterConfigBase, ModelConfigBase): @@ -921,20 +889,17 @@ class IPAdapterInvokeAIConfig(IPAdapterConfigBase, ModelConfigBase): # time. Need to go through the history to make sure I'm understanding this fully. image_encoder_model_id: str format: Literal[ModelFormat.InvokeAI] = ModelFormat.InvokeAI - base: IPAdapterInvokeAIConfigBaseTypes = Field(...) VALID_OVERRIDES: ClassVar = { "type": ModelType.IPAdapter, "format": ModelFormat.InvokeAI, - "base": ip_adapter_invoke_ai_base_type_adapter.validate_python, } @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_dir(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) weights_file = mod.path / "ip_adapter.bin" if not weights_file.exists(): @@ -948,7 +913,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields, base=base) @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterInvokeAIConfigBaseTypes: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: state_dict = mod.load_state_dict() try: @@ -967,18 +932,6 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterInvokeAIConfigBaseType raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") -IPAdapterCheckpointConfigBaseTypes: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - BaseModelType.Flux, -] -"""Helper TypeAlias for valid base types for IP Adapter models in the Checkpoint format.""" - -ip_adapter_checkpoint_base_type_adapter = TypeAdapter[IPAdapterCheckpointConfigBaseTypes]( - IPAdapterCheckpointConfigBaseTypes -) -"""Helper TypeAdapter for IP Adapter Checkpoint base types.""" class IPAdapterCheckpointConfig(IPAdapterConfigBase, ModelConfigBase): @@ -989,15 +942,13 @@ class IPAdapterCheckpointConfig(IPAdapterConfigBase, ModelConfigBase): VALID_OVERRIDES: ClassVar = { "type": ModelType.IPAdapter, "format": ModelFormat.Checkpoint, - "base": ip_adapter_checkpoint_base_type_adapter.validate_python, } @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_file(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) if not mod.has_keys_starting_with( { @@ -1013,7 +964,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields, base=base) @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterCheckpointConfigBaseTypes: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: state_dict = mod.load_state_dict() if is_state_dict_xlabs_ip_adapter(state_dict): @@ -1083,8 +1034,7 @@ def get_tag(cls) -> Tag: def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_dir(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) config_path = mod.path / "config.json" @@ -1119,8 +1069,7 @@ def get_tag(cls) -> Tag: def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_dir(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) config_path = mod.path / "config.json" @@ -1154,8 +1103,7 @@ class CLIPVisionDiffusersConfig(DiffusersConfigBase, ModelConfigBase): def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_dir(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) config_path = mod.path / "config.json" @@ -1188,8 +1136,7 @@ class SpandrelImageToImageConfig(ModelConfigBase): def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_file(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) try: # It would be nice to avoid having to load the Spandrel model from disk here. A couple of options were @@ -1226,8 +1173,7 @@ class SigLIPConfig(DiffusersConfigBase, ModelConfigBase): def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_dir(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) config_path = mod.path / "config.json" @@ -1251,8 +1197,7 @@ class FluxReduxConfig(ModelConfigBase): def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_file(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) if not is_state_dict_likely_flux_redux(mod.load_state_dict()): raise NotAMatch(cls, "model does not match FLUX Tools Redux heuristics") @@ -1280,8 +1225,7 @@ class LlavaOnevisionConfig(DiffusersConfigBase, ModelConfigBase): def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_dir(cls, mod) - if _validate_overrides(cls, fields, cls.VALID_OVERRIDES): - return cls(**fields) + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) config_path = mod.path / "config.json" From 2813ec49e13c38572a734ae13cb6673238bcf821 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:08:48 +1000 Subject: [PATCH 26/62] refactor(mm): continue iterating on config --- invokeai/app/invocations/model.py | 5 +- invokeai/app/util/custom_openapi.py | 8 + invokeai/backend/model_manager/config.py | 519 ++++-- invokeai/backend/model_manager/taxonomy.py | 41 +- .../backend/model_manager/util/model_util.py | 6 +- .../patches/lora_conversions/formats.py | 2 +- .../web/src/features/modelManagerV2/models.ts | 8 +- .../ModelManagerPanel/ModelFormatBadge.tsx | 12 +- .../src/features/nodes/types/common.test-d.ts | 2 + .../web/src/features/nodes/types/common.ts | 6 +- .../web/src/features/nodes/types/field.ts | 5 +- .../nodes/util/graph/generation/Graph.test.ts | 2 + .../MainModelPicker.tsx | 4 +- .../layouts/InitialStateMainModelPicker.tsx | 4 +- .../frontend/web/src/services/api/schema.ts | 1572 +++++++++-------- .../frontend/web/src/services/api/types.ts | 24 +- .../src/services/events/setEventListeners.tsx | 13 +- scripts/classify-model.py | 5 +- 18 files changed, 1245 insertions(+), 993 deletions(-) diff --git a/invokeai/app/invocations/model.py b/invokeai/app/invocations/model.py index 2d338c677d2..327de6ac700 100644 --- a/invokeai/app/invocations/model.py +++ b/invokeai/app/invocations/model.py @@ -24,8 +24,9 @@ class ModelIdentifierField(BaseModel): name: str = Field(description="The model's name") base: BaseModelType = Field(description="The model's base model type") type: ModelType = Field(description="The model's type") - submodel_type: Optional[SubModelType] = Field( - description="The submodel to load, if this is a main model", default=None + submodel_type: SubModelType | None = Field( + description="The submodel to load, if this is a main model", + default=None, ) @classmethod diff --git a/invokeai/app/util/custom_openapi.py b/invokeai/app/util/custom_openapi.py index d6b8f3786f1..2e07622530d 100644 --- a/invokeai/app/util/custom_openapi.py +++ b/invokeai/app/util/custom_openapi.py @@ -12,6 +12,7 @@ from invokeai.app.invocations.model import ModelIdentifierField from invokeai.app.services.events.events_common import EventBase from invokeai.app.services.session_processor.session_processor_common import ProgressImage +from invokeai.backend.model_manager.config import AnyModelConfigValidator from invokeai.backend.util.logging import InvokeAILogger logger = InvokeAILogger.get_logger() @@ -115,6 +116,13 @@ def openapi() -> dict[str, Any]: # additional_schemas[1] is a dict of $defs that we need to add to the top level of the schema move_defs_to_top_level(openapi_schema, additional_schemas[1]) + any_model_config_schema = AnyModelConfigValidator.json_schema( + mode="serialization", + ref_template="#/components/schemas/{model}", + ) + move_defs_to_top_level(openapi_schema, any_model_config_schema) + openapi_schema["components"]["schemas"]["AnyModelConfig"] = any_model_config_schema + if post_transform is not None: openapi_schema = post_transform(openapi_schema) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 9260656c1e8..7bc16099f0c 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -20,7 +20,6 @@ """ -# pyright: reportIncompatibleVariableOverride=false import json import logging import re @@ -111,7 +110,9 @@ def _get_config_or_raise( raise NotAMatch(config_class, f"missing config file: {config_path}") try: - config = load_json(config_path) + with open(config_path, "r") as file: + config = json.load(file) + return config except Exception as e: raise NotAMatch(config_class, f"unable to load config file: {config_path}") from e @@ -165,7 +166,6 @@ def _validate_overrides( ) - def _raise_if_not_file( config_class: type, mod: ModelOnDisk, @@ -245,45 +245,75 @@ class ModelConfigBase(ABC, BaseModel): See MinimalConfigExample in test_model_probe.py for an example implementation. """ - @staticmethod - def json_schema_extra(schema: dict[str, Any]) -> None: - schema["required"].extend(["key", "base", "type", "format"]) - - model_config = ConfigDict(validate_assignment=True, json_schema_extra=json_schema_extra) - - key: str = Field(description="A unique key for this model.", default_factory=uuid_string) - hash: str = Field(description="The hash of the model file(s).") + key: str = Field( + description="A unique key for this model.", + default_factory=uuid_string, + ) + hash: str = Field( + description="The hash of the model file(s).", + ) path: str = Field( - description="Path to the model on the filesystem. Relative paths are relative to the Invoke root directory." + description="Path to the model on the filesystem. Relative paths are relative to the Invoke root directory.", + ) + file_size: int = Field( + description="The size of the model in bytes.", ) - file_size: int = Field(description="The size of the model in bytes.") - name: str = Field(description="Name of the model.") - type: ModelType = Field(description="Model type") - format: ModelFormat = Field(description="Model format") - base: BaseModelType = Field(description="The base model.") - source: str = Field(description="The original source of the model (path, URL or repo_id).") - source_type: ModelSourceType = Field(description="The type of source") - - description: Optional[str] = Field(description="Model description", default=None) - source_api_response: Optional[str] = Field( - description="The original API response from the source, as stringified JSON.", default=None + name: str = Field( + description="Name of the model.", + ) + description: str | None = Field( + description="Model description", + default=None, ) - cover_image: Optional[str] = Field(description="Url for image to preview model", default=None) - submodels: Optional[Dict[SubModelType, SubmodelDefinition]] = Field( - description="Loadable submodels in this model", default=None + source: str = Field( + description="The original source of the model (path, URL or repo_id).", + ) + source_type: ModelSourceType = Field( + description="The type of source", + ) + source_api_response: str | None = Field( + description="The original API response from the source, as stringified JSON.", + default=None, + ) + cover_image: str | None = Field( + description="Url for image to preview model", + default=None, + ) + submodels: dict[SubModelType, SubmodelDefinition] | None = Field( + description="Loadable submodels in this model", + default=None, + ) + usage_info: str | None = Field( + default=None, + description="Usage information for this model", ) - usage_info: Optional[str] = Field(default=None, description="Usage information for this model") USING_LEGACY_PROBE: ClassVar[set[Type["AnyModelConfig"]]] = set() USING_CLASSIFY_API: ClassVar[set[Type["AnyModelConfig"]]] = set() + model_config = ConfigDict( + validate_assignment=True, + json_schema_serialization_defaults_required=True, + json_schema_mode_override="serialization", + ) + + @classmethod def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) + if issubclass(cls, LegacyProbeMixin): ModelConfigBase.USING_LEGACY_PROBE.add(cls) else: ModelConfigBase.USING_CLASSIFY_API.add(cls) + @classmethod + def __pydantic_init_subclass__(cls, **kwargs): + # Ensure that subclasses define 'base', 'type' and 'format' fields. These are not in this base class, because + # subclasses may redefine them as different types, causing type-checking issues. + assert "base" in cls.model_fields, f"{cls.__name__} must define a 'base' field" + assert "type" in cls.model_fields, f"{cls.__name__} must define a 'type' field" + assert "format" in cls.model_fields, f"{cls.__name__} must define a 'format' field" + @staticmethod def all_config_classes(): subclasses = ModelConfigBase.USING_LEGACY_PROBE | ModelConfigBase.USING_CLASSIFY_API @@ -305,9 +335,9 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class UnknownModelConfig(ModelConfigBase): - base: Literal[BaseModelType.Unknown] = BaseModelType.Unknown - type: Literal[ModelType.Unknown] = ModelType.Unknown - format: Literal[ModelFormat.Unknown] = ModelFormat.Unknown + base: Literal[BaseModelType.Unknown] = Field(default=BaseModelType.Unknown) + type: Literal[ModelType.Unknown] = Field(default=ModelType.Unknown) + format: Literal[ModelFormat.Unknown] = Field(default=ModelFormat.Unknown) @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @@ -317,14 +347,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class CheckpointConfigBase(ABC, BaseModel): """Base class for checkpoint-style models.""" - format: Literal[ModelFormat.Checkpoint, ModelFormat.BnbQuantizednf4b, ModelFormat.GGUFQuantized] = Field( - description="Format of the provided checkpoint model", - default=ModelFormat.Checkpoint, - ) - config_path: str | None = Field( - description="path to the checkpoint model config file", - default=None, - ) + config_path: str | None = Field(None, description="Path to the config for this model, if any.") converted_at: float | None = Field( description="When this model was last converted to diffusers", default_factory=time.time, @@ -334,66 +357,14 @@ class CheckpointConfigBase(ABC, BaseModel): class DiffusersConfigBase(ABC, BaseModel): """Base class for diffusers-style models.""" - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers - repo_variant: Optional[ModelRepoVariant] = ModelRepoVariant.Default - + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + repo_variant: Optional[ModelRepoVariant] = Field(ModelRepoVariant.Default) -class LoRAConfigBase(ABC, BaseModel): - """Base class for LoRA models.""" - - type: Literal[ModelType.LoRA] = ModelType.LoRA - trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None) - default_settings: Optional[LoraModelDefaultSettings] = Field( - description="Default settings for this model", default=None - ) - - @classmethod - def flux_lora_format(cls, mod: ModelOnDisk): - key = "FLUX_LORA_FORMAT" - if key in mod.cache: - return mod.cache[key] - from invokeai.backend.patches.lora_conversions.formats import flux_format_from_state_dict - - sd = mod.load_state_dict(mod.path) - value = flux_format_from_state_dict(sd, mod.metadata()) - mod.cache[key] = value - return value - - @classmethod - def base_model(cls, mod: ModelOnDisk) -> BaseModelType: - if cls.flux_lora_format(mod): - return BaseModelType.Flux - - state_dict = mod.load_state_dict() - # If we've gotten here, we assume that the model is a Stable Diffusion model - token_vector_length = lora_token_vector_length(state_dict) - if token_vector_length == 768: - return BaseModelType.StableDiffusion1 - elif token_vector_length == 1024: - return BaseModelType.StableDiffusion2 - elif token_vector_length == 1280: - return BaseModelType.StableDiffusionXL # recognizes format at https://civitai.com/models/224641 - elif token_vector_length == 2048: - return BaseModelType.StableDiffusionXL - else: - raise InvalidModelConfigException("Unknown LoRA type") - - -class T5EncoderConfigBase(ABC, BaseModel): - """Base class for diffusers-style models.""" - - base: Literal[BaseModelType.Any] = BaseModelType.Any - type: Literal[ModelType.T5Encoder] = ModelType.T5Encoder - - -def load_json(path: Path) -> dict[str, Any]: - with open(path, "r") as file: - return json.load(file) - - -class T5EncoderConfig(T5EncoderConfigBase, ModelConfigBase): - format: Literal[ModelFormat.T5Encoder] = ModelFormat.T5Encoder +class T5EncoderConfig(ModelConfigBase): + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + type: Literal[ModelType.T5Encoder] = Field(default=ModelType.T5Encoder) + format: Literal[ModelFormat.T5Encoder] = Field(default=ModelFormat.T5Encoder) VALID_OVERRIDES: ClassVar = { "type": ModelType.T5Encoder, @@ -421,8 +392,10 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -class T5EncoderBnbQuantizedLlmInt8bConfig(T5EncoderConfigBase, ModelConfigBase): - format: Literal[ModelFormat.BnbQuantizedLlmInt8b] = ModelFormat.BnbQuantizedLlmInt8b +class T5EncoderBnbQuantizedLlmInt8bConfig(ModelConfigBase): + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + type: Literal[ModelType.T5Encoder] = Field(default=ModelType.T5Encoder) + format: Literal[ModelFormat.BnbQuantizedLlmInt8b] = Field(default=ModelFormat.BnbQuantizedLlmInt8b) VALID_OVERRIDES: ClassVar = { "type": ModelType.T5Encoder, @@ -454,8 +427,32 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) +class LoRAConfigBase(ABC, BaseModel): + """Base class for LoRA models.""" + + type: Literal[ModelType.LoRA] = Field(default=ModelType.LoRA) + trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None) + default_settings: Optional[LoraModelDefaultSettings] = Field( + description="Default settings for this model", default=None + ) + + +def get_flux_lora_format(mod: ModelOnDisk) -> FluxLoRAFormat | None: + key = "FLUX_LORA_FORMAT" + if key in mod.cache: + return mod.cache[key] + + from invokeai.backend.patches.lora_conversions.formats import flux_format_from_state_dict + + sd = mod.load_state_dict(mod.path) + value = flux_format_from_state_dict(sd, mod.metadata()) + mod.cache[key] = value + return value + + class LoRAOmiConfig(LoRAConfigBase, ModelConfigBase): - format: Literal[ModelFormat.OMI] = ModelFormat.OMI + base: Literal[BaseModelType.Flux, BaseModelType.StableDiffusionXL] = Field() + format: Literal[ModelFormat.OMI] = Field(default=ModelFormat.OMI) VALID_OVERRIDES: ClassVar = { "type": ModelType.LoRA, @@ -470,7 +467,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_overrides(cls, fields, cls.VALID_OVERRIDES) # Heuristic: differential diagnosis vs ControlLoRA and Diffusers - if cls.flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: + if get_flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: raise NotAMatch(cls, "model is a ControlLoRA or Diffusers LoRA") # Heuristic: Look for OMI LoRA metadata @@ -489,7 +486,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields, base=base) @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> Literal[BaseModelType.Flux, BaseModelType.StableDiffusionXL]: metadata = mod.metadata() architecture = metadata["modelspec.architecture"] @@ -501,10 +498,20 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, f"unrecognised/unsupported architecture for OMI LoRA: {architecture}") +LoRALyCORIS_SupportedBases: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, + BaseModelType.Flux, +] + + class LoRALyCORISConfig(LoRAConfigBase, ModelConfigBase): """Model config for LoRA/Lycoris models.""" - format: Literal[ModelFormat.LyCORIS] = ModelFormat.LyCORIS + base: LoRALyCORIS_SupportedBases = Field() + type: Literal[ModelType.LoRA] = Field(default=ModelType.LoRA) + format: Literal[ModelFormat.LyCORIS] = Field(default=ModelFormat.LyCORIS) VALID_OVERRIDES: ClassVar = { "type": ModelType.LoRA, @@ -518,7 +525,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_overrides(cls, fields, cls.VALID_OVERRIDES) # Heuristic: differential diagnosis vs ControlLoRA and Diffusers - if cls.flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: + if get_flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: raise NotAMatch(cls, "model is a ControlLoRA or Diffusers LoRA") # Note: Existence of these key prefixes/suffixes does not guarantee that this is a LoRA. @@ -547,33 +554,69 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> LoRALyCORIS_SupportedBases: + if get_flux_lora_format(mod): + return BaseModelType.Flux + + state_dict = mod.load_state_dict() + # If we've gotten here, we assume that the model is a Stable Diffusion model + token_vector_length = lora_token_vector_length(state_dict) + if token_vector_length == 768: + return BaseModelType.StableDiffusion1 + elif token_vector_length == 1024: + return BaseModelType.StableDiffusion2 + elif token_vector_length == 1280: + return BaseModelType.StableDiffusionXL # recognizes format at https://civitai.com/models/224641 + elif token_vector_length == 2048: + return BaseModelType.StableDiffusionXL + else: + raise NotAMatch(cls, f"unrecognized token vector length {token_vector_length}") + class ControlAdapterConfigBase(ABC, BaseModel): - default_settings: Optional[ControlAdapterDefaultSettings] = Field( - description="Default settings for this model", default=None - ) + default_settings: ControlAdapterDefaultSettings | None = Field(None) + + +ControlLoRALyCORIS_SupportedBases: TypeAlias = Literal[BaseModelType.Flux] class ControlLoRALyCORISConfig(ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for Control LoRA models.""" - type: Literal[ModelType.ControlLoRa] = ModelType.ControlLoRa - trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None) - format: Literal[ModelFormat.LyCORIS] = ModelFormat.LyCORIS + base: ControlLoRALyCORIS_SupportedBases = Field() + type: Literal[ModelType.ControlLoRa] = Field(default=ModelType.ControlLoRa) + format: Literal[ModelFormat.LyCORIS] = Field(default=ModelFormat.LyCORIS) + + trigger_phrases: set[str] | None = Field(None) + + +ControlLoRADiffusers_SupportedBases: TypeAlias = Literal[BaseModelType.Flux] class ControlLoRADiffusersConfig(ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for Control LoRA models.""" - type: Literal[ModelType.ControlLoRa] = ModelType.ControlLoRa - trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None) - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers + base: ControlLoRADiffusers_SupportedBases = Field() + type: Literal[ModelType.ControlLoRa] = Field(default=ModelType.ControlLoRa) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + + trigger_phrases: set[str] | None = Field(None) + + +LoRADiffusers_SupportedBases: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, + BaseModelType.Flux, +] class LoRADiffusersConfig(LoRAConfigBase, ModelConfigBase): """Model config for LoRA/Diffusers models.""" - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers + base: LoRADiffusers_SupportedBases = Field() + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) VALID_OVERRIDES: ClassVar = { "type": ModelType.LoRA, @@ -587,7 +630,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_overrides(cls, fields, cls.VALID_OVERRIDES) - is_flux_lora_diffusers = cls.flux_lora_format(mod) == FluxLoRAFormat.Diffusers + is_flux_lora_diffusers = get_flux_lora_format(mod) is FluxLoRAFormat.Diffusers suffixes = ["bin", "safetensors"] weight_files = [mod.path / f"pytorch_lora_weights.{sfx}" for sfx in suffixes] @@ -599,20 +642,33 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -class VAEConfigBase(ABC, BaseModel): - type: Literal[ModelType.VAE] = ModelType.VAE +VAECheckpointConfig_SupportedBases: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, + BaseModelType.Flux, +] -class VAECheckpointConfig(VAEConfigBase, CheckpointConfigBase, ModelConfigBase): +class VAECheckpointConfig(CheckpointConfigBase, ModelConfigBase): """Model config for standalone VAE models.""" - format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint + base: VAECheckpointConfig_SupportedBases = Field() + type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) VALID_OVERRIDES: ClassVar = { "type": ModelType.VAE, "format": ModelFormat.Checkpoint, } + REGEX_TO_BASE: ClassVar[dict[str, VAECheckpointConfig_SupportedBases]] = { + r"xl": BaseModelType.StableDiffusionXL, + r"sd2": BaseModelType.StableDiffusion2, + r"vae": BaseModelType.StableDiffusion1, + r"FLUX.1-schnell_ae": BaseModelType.Flux, + } + @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_file(cls, mod) @@ -626,24 +682,27 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields, base=base) @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> VAECheckpointConfig_SupportedBases: # Heuristic: VAEs of all architectures have a similar structure; the best we can do is guess based on name - for regexp, basetype in [ - (r"xl", BaseModelType.StableDiffusionXL), - (r"sd2", BaseModelType.StableDiffusion2), - (r"vae", BaseModelType.StableDiffusion1), - (r"FLUX.1-schnell_ae", BaseModelType.Flux), - ]: + for regexp, base in cls.REGEX_TO_BASE.items(): if re.search(regexp, mod.path.name, re.IGNORECASE): - return basetype + return base raise NotAMatch(cls, "cannot determine base type") -class VAEDiffusersConfig(VAEConfigBase, DiffusersConfigBase, ModelConfigBase): +VAEDiffusersConfig_SupportedBases: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusionXL, +] + + +class VAEDiffusersConfig(DiffusersConfigBase, ModelConfigBase): """Model config for standalone VAE models (diffusers version).""" - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers + base: VAEDiffusersConfig_SupportedBases = Field() + type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) VALID_OVERRIDES: ClassVar = { "type": ModelType.VAE, @@ -685,7 +744,7 @@ def _guess_name(cls, mod: ModelOnDisk) -> str: return name @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> VAEDiffusersConfig_SupportedBases: config = _get_config_or_raise(cls, mod.path / "config.json") if cls._config_looks_like_sdxl(config): return BaseModelType.StableDiffusionXL @@ -696,21 +755,48 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: return BaseModelType.StableDiffusion1 +ControlNetDiffusers_SupportedBases: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, + BaseModelType.Flux, +] + + class ControlNetDiffusersConfig(DiffusersConfigBase, ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for ControlNet models (diffusers version).""" - type: Literal[ModelType.ControlNet] = ModelType.ControlNet - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers + base: ControlNetDiffusers_SupportedBases = Field() + type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + + +ControlNetCheckpoint_SupportedBases: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, + BaseModelType.Flux, +] class ControlNetCheckpointConfig(CheckpointConfigBase, ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for ControlNet models (diffusers version).""" - type: Literal[ModelType.ControlNet] = ModelType.ControlNet + base: ControlNetDiffusers_SupportedBases = Field() + type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + + +TextualInversion_SupportedBases: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, +] class TextualInversionConfigBase(ABC, BaseModel): - type: Literal[ModelType.TextualInversion] = ModelType.TextualInversion + base: TextualInversion_SupportedBases = Field() + type: Literal[ModelType.TextualInversion] = Field(default=ModelType.TextualInversion) KNOWN_KEYS: ClassVar = {"string_to_param", "emb_params", "clip_g"} @@ -743,7 +829,7 @@ def _file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) return False @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseModelType: + def _get_base_or_raise(cls, mod: ModelOnDisk, path: Path | None = None) -> TextualInversion_SupportedBases: p = path or mod.path try: @@ -777,7 +863,7 @@ def _get_base_or_raise(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseM class TextualInversionFileConfig(TextualInversionConfigBase, ModelConfigBase): """Model config for textual inversion embeddings.""" - format: Literal[ModelFormat.EmbeddingFile] = ModelFormat.EmbeddingFile + format: Literal[ModelFormat.EmbeddingFile] = Field(default=ModelFormat.EmbeddingFile) VALID_OVERRIDES: ClassVar = { "type": ModelType.TextualInversion, @@ -804,7 +890,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class TextualInversionFolderConfig(TextualInversionConfigBase, ModelConfigBase): """Model config for textual inversion embeddings.""" - format: Literal[ModelFormat.EmbeddingFolder] = ModelFormat.EmbeddingFolder + format: Literal[ModelFormat.EmbeddingFolder] = Field(default=ModelFormat.EmbeddingFolder) VALID_OVERRIDES: ClassVar = { "type": ModelType.TextualInversion, @@ -830,65 +916,89 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class MainConfigBase(ABC, BaseModel): - type: Literal[ModelType.Main] = ModelType.Main + type: Literal[ModelType.Main] = Field(default=ModelType.Main) trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None) default_settings: Optional[MainModelDefaultSettings] = Field( description="Default settings for this model", default=None ) - variant: ModelVariantType | FluxVariantType = ModelVariantType.Normal + variant: ModelVariantType | FluxVariantType = Field() -class VideoConfigBase(ABC, BaseModel): - type: Literal[ModelType.Video] = ModelType.Video - trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None) - default_settings: Optional[MainModelDefaultSettings] = Field( - description="Default settings for this model", default=None - ) - variant: ModelVariantType = ModelVariantType.Normal +MainCheckpointConfigBase_SupportedBases: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusion3, + BaseModelType.StableDiffusionXL, + BaseModelType.StableDiffusionXLRefiner, + BaseModelType.Flux, + BaseModelType.CogView4, +] class MainCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for main checkpoint models.""" - prediction_type: SchedulerPredictionType = SchedulerPredictionType.Epsilon - upcast_attention: bool = False + base: MainCheckpointConfigBase_SupportedBases = Field() + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + prediction_type: SchedulerPredictionType = Field(default=SchedulerPredictionType.Epsilon) + upcast_attention: bool = Field(False) class MainBnbQuantized4bCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for main checkpoint models.""" - format: Literal[ModelFormat.BnbQuantizednf4b] = ModelFormat.BnbQuantizednf4b - prediction_type: SchedulerPredictionType = SchedulerPredictionType.Epsilon + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + format: Literal[ModelFormat.BnbQuantizednf4b] = Field(default=ModelFormat.BnbQuantizednf4b) + prediction_type: SchedulerPredictionType = Field(default=SchedulerPredictionType.Epsilon) upcast_attention: bool = False class MainGGUFCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for main checkpoint models.""" - format: Literal[ModelFormat.GGUFQuantized] = ModelFormat.GGUFQuantized - prediction_type: SchedulerPredictionType = SchedulerPredictionType.Epsilon - upcast_attention: bool = False + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + format: Literal[ModelFormat.GGUFQuantized] = Field(default=ModelFormat.GGUFQuantized) + prediction_type: SchedulerPredictionType = Field(default=SchedulerPredictionType.Epsilon) + upcast_attention: bool = Field(False) + + +MainDiffusers_SupportedBases: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusion3, + BaseModelType.StableDiffusionXL, + BaseModelType.StableDiffusionXLRefiner, + BaseModelType.Flux, + BaseModelType.CogView4, +] class MainDiffusersConfig(DiffusersConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for main diffusers models.""" - pass + base: MainDiffusers_SupportedBases = Field() class IPAdapterConfigBase(ABC, BaseModel): - type: Literal[ModelType.IPAdapter] = ModelType.IPAdapter + type: Literal[ModelType.IPAdapter] = Field(default=ModelType.IPAdapter) +IPAdapterInvokeAI_SupportedBases: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, +] class IPAdapterInvokeAIConfig(IPAdapterConfigBase, ModelConfigBase): """Model config for IP Adapter diffusers format models.""" + base: IPAdapterInvokeAI_SupportedBases = Field() + format: Literal[ModelFormat.InvokeAI] = Field(default=ModelFormat.InvokeAI) + # TODO(ryand): Should we deprecate this field? From what I can tell, it hasn't been probed correctly for a long # time. Need to go through the history to make sure I'm understanding this fully. - image_encoder_model_id: str - format: Literal[ModelFormat.InvokeAI] = ModelFormat.InvokeAI + image_encoder_model_id: str = Field() VALID_OVERRIDES: ClassVar = { "type": ModelType.IPAdapter, @@ -913,7 +1023,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields, base=base) @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterInvokeAI_SupportedBases: state_dict = mod.load_state_dict() try: @@ -932,12 +1042,19 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") +IPAdapterCheckpoint_SupportedBases: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, + BaseModelType.Flux, +] class IPAdapterCheckpointConfig(IPAdapterConfigBase, ModelConfigBase): """Model config for IP Adapter checkpoint format models.""" - format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint + base: IPAdapterCheckpoint_SupportedBases = Field() + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) VALID_OVERRIDES: ClassVar = { "type": ModelType.IPAdapter, @@ -964,7 +1081,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields, base=base) @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterCheckpoint_SupportedBases: state_dict = mod.load_state_dict() if is_state_dict_xlabs_ip_adapter(state_dict): @@ -989,10 +1106,9 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: class CLIPEmbedDiffusersConfig(DiffusersConfigBase): """Model config for Clip Embeddings.""" - variant: ClipVariantType = Field(...) - type: Literal[ModelType.CLIPEmbed] = ModelType.CLIPEmbed - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers - base: Literal[BaseModelType.Any] = BaseModelType.Any + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + type: Literal[ModelType.CLIPEmbed] = Field(default=ModelType.CLIPEmbed) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) VALID_CLASS_NAMES: ClassVar = { "CLIPModel", @@ -1018,7 +1134,7 @@ def get_clip_variant_type(cls, config: dict[str, Any]) -> ClipVariantType | None class CLIPGEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): """Model config for CLIP-G Embeddings.""" - variant: Literal[ClipVariantType.G] = ClipVariantType.G + variant: Literal[ClipVariantType.G] = Field(default=ClipVariantType.G) VALID_OVERRIDES: ClassVar = { "type": ModelType.CLIPEmbed, @@ -1053,7 +1169,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class CLIPLEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): """Model config for CLIP-L Embeddings.""" - variant: Literal[ClipVariantType.L] = ClipVariantType.L + variant: Literal[ClipVariantType.L] = Field(default=ClipVariantType.L) VALID_OVERRIDES: ClassVar = { "type": ModelType.CLIPEmbed, @@ -1087,8 +1203,9 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class CLIPVisionDiffusersConfig(DiffusersConfigBase, ModelConfigBase): """Model config for CLIPVision.""" - type: Literal[ModelType.CLIPVision] = ModelType.CLIPVision - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + type: Literal[ModelType.CLIPVision] = Field(default=ModelType.CLIPVision) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) VALID_OVERRIDES: ClassVar = { "type": ModelType.CLIPVision, @@ -1112,24 +1229,31 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) +T2IAdapterCheckpoint_SupportedBases: TypeAlias = Literal[ + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, +] + + class T2IAdapterConfig(DiffusersConfigBase, ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): """Model config for T2I.""" - type: Literal[ModelType.T2IAdapter] = ModelType.T2IAdapter - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers + base: T2IAdapterCheckpoint_SupportedBases = Field() + type: Literal[ModelType.T2IAdapter] = Field(default=ModelType.T2IAdapter) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) class SpandrelImageToImageConfig(ModelConfigBase): """Model config for Spandrel Image to Image models.""" - base: Literal[BaseModelType.Any] = BaseModelType.Any - type: Literal[ModelType.SpandrelImageToImage] = ModelType.SpandrelImageToImage - format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + type: Literal[ModelType.SpandrelImageToImage] = Field(default=ModelType.SpandrelImageToImage) + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) VALID_OVERRIDES: ClassVar = { "type": ModelType.SpandrelImageToImage, "format": ModelFormat.Checkpoint, - "base": BaseModelType.Any, } @classmethod @@ -1148,8 +1272,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: # This logic is not exposed in spandrel's public API. We could copy the logic here, but then we have to # maintain it, and the risk of false positive detections is higher. SpandrelImageToImageModel.load_from_file(mod.path) - base = fields.get("base") or BaseModelType.Any - return cls(**fields, base=base) + return cls(**fields) except Exception as e: raise NotAMatch(cls, "model does not match SpandrelImageToImage heuristics") from e @@ -1157,8 +1280,9 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class SigLIPConfig(DiffusersConfigBase, ModelConfigBase): """Model config for SigLIP.""" - type: Literal[ModelType.SigLIP] = ModelType.SigLIP - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers + type: Literal[ModelType.SigLIP] = Field(default=ModelType.SigLIP) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) VALID_OVERRIDES: ClassVar = { "type": ModelType.SigLIP, @@ -1185,8 +1309,9 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class FluxReduxConfig(ModelConfigBase): """Model config for FLUX Tools Redux model.""" - type: Literal[ModelType.FluxRedux] = ModelType.FluxRedux - format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint + type: Literal[ModelType.FluxRedux] = Field(default=ModelType.FluxRedux) + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) VALID_OVERRIDES: ClassVar = { "type": ModelType.FluxRedux, @@ -1208,9 +1333,9 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class LlavaOnevisionConfig(DiffusersConfigBase, ModelConfigBase): """Model config for Llava Onevision models.""" - type: Literal[ModelType.LlavaOnevision] = ModelType.LlavaOnevision - base: Literal[BaseModelType.Any] = BaseModelType.Any - variant: Literal[ModelVariantType.Normal] = ModelVariantType.Normal + type: Literal[ModelType.LlavaOnevision] = Field(default=ModelType.LlavaOnevision) + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + variant: Literal[ModelVariantType.Normal] = Field(default=ModelVariantType.Normal) VALID_OVERRIDES: ClassVar = { "type": ModelType.LlavaOnevision, @@ -1234,20 +1359,44 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) +ApiModel_SupportedBases: TypeAlias = Literal[ + BaseModelType.ChatGPT4o, + BaseModelType.Gemini2_5, + BaseModelType.Imagen3, + BaseModelType.Imagen4, + BaseModelType.FluxKontext, +] + + class ApiModelConfig(MainConfigBase, ModelConfigBase): """Model config for API-based models.""" - format: Literal[ModelFormat.Api] = ModelFormat.Api + type: Literal[ModelType.Main] = Field(default=ModelType.Main) + format: Literal[ModelFormat.Api] = Field(default=ModelFormat.Api) + base: ApiModel_SupportedBases = Field() @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: raise NotAMatch(cls, "API models cannot be built from disk") -class VideoApiModelConfig(VideoConfigBase, ModelConfigBase): +VideoApiModel_SupportedBases: TypeAlias = Literal[ + BaseModelType.Veo3, + BaseModelType.Runway, +] + + +class VideoApiModelConfig(ModelConfigBase): """Model config for API-based video models.""" - format: Literal[ModelFormat.Api] = ModelFormat.Api + type: Literal[ModelType.Video] = Field(default=ModelType.Video) + base: VideoApiModel_SupportedBases = Field() + format: Literal[ModelFormat.Api] = Field(default=ModelFormat.Api) + + trigger_phrases: set[str] | None = Field(description="Set of trigger phrases for this model", default=None) + default_settings: MainModelDefaultSettings | None = Field( + description="Default settings for this model", default=None + ) @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: diff --git a/invokeai/backend/model_manager/taxonomy.py b/invokeai/backend/model_manager/taxonomy.py index 15a3d3ce724..99a31f438d1 100644 --- a/invokeai/backend/model_manager/taxonomy.py +++ b/invokeai/backend/model_manager/taxonomy.py @@ -1,41 +1,70 @@ from enum import Enum from typing import Dict, TypeAlias, Union -import diffusers import onnxruntime as ort import torch -from diffusers import ModelMixin +from diffusers.models.modeling_utils import ModelMixin +from diffusers.pipelines.pipeline_utils import DiffusionPipeline from pydantic import TypeAdapter from invokeai.backend.raw_model import RawModel # ModelMixin is the base class for all diffusers and transformers models # RawModel is the InvokeAI wrapper class for ip_adapters, loras, textual_inversion and onnx runtime -AnyModel = Union[ - ModelMixin, RawModel, torch.nn.Module, Dict[str, torch.Tensor], diffusers.DiffusionPipeline, ort.InferenceSession +AnyModel: TypeAlias = Union[ + ModelMixin, + RawModel, + torch.nn.Module, + Dict[str, torch.Tensor], + DiffusionPipeline, + ort.InferenceSession, ] +"""Type alias for any kind of runtime, in-memory model representation. For example, a torch module or diffusers pipeline.""" class BaseModelType(str, Enum): - """Base model type.""" + """An enumeration of base model architectures. For example, Stable Diffusion 1.x, Stable Diffusion 2.x, FLUX, etc. + + Every model config must have a base architecture type. + + Not all models are associated with a base architecture. For example, CLIP models are their own thing, not related + to any particular model architecture. To simplify internal APIs and make it easier to work with models, we use a + fallback/null value `BaseModelType.Any` for these models, instead of making the model base optional.""" Any = "any" + """`Any` is essentially a fallback/null value for models with no base architecture association. + For example, CLIP models are not related to Stable Diffusion, FLUX, or any other model arch.""" StableDiffusion1 = "sd-1" + """Indicates the model is associated with the Stable Diffusion 1.x model architecture, including 1.4 and 1.5.""" StableDiffusion2 = "sd-2" + """Indicates the model is associated with the Stable Diffusion 2.x model architecture, including 2.0 and 2.1.""" StableDiffusion3 = "sd-3" + """Indicates the model is associated with the Stable Diffusion 3.5 model architecture.""" StableDiffusionXL = "sdxl" + """Indicates the model is associated with the Stable Diffusion XL model architecture.""" StableDiffusionXLRefiner = "sdxl-refiner" + """Indicates the model is associated with the Stable Diffusion XL Refiner model architecture.""" Flux = "flux" + """Indicates the model is associated with FLUX.1 model architecture, including FLUX Dev, Schnell and Fill.""" CogView4 = "cogview4" + """Indicates the model is associated with CogView 4 model architecture.""" Imagen3 = "imagen3" + """Indicates the model is associated with Google Imagen 3 model architecture. This is an external API model.""" Imagen4 = "imagen4" + """Indicates the model is associated with Google Imagen 4 model architecture. This is an external API model.""" Gemini2_5 = "gemini-2.5" + """Indicates the model is associated with Google Gemini 2.5 Flash Image model architecture. This is an external API model.""" ChatGPT4o = "chatgpt-4o" - # This is actually the FLUX Kontext API model. Local FLUX Kontext is just BaseModelType.Flux. + """Indicates the model is associated with OpenAI ChatGPT 4o Image model architecture. This is an external API model.""" FluxKontext = "flux-kontext" + """Indicates the model is associated with FLUX Kontext model architecture. This is an external API model; local FLUX + Kontext models use the base `Flux`.""" Veo3 = "veo3" + """Indicates the model is associated with Google Veo 3 video model architecture. This is an external API model.""" Runway = "runway" + """Indicates the model is associated with Runway video model architecture. This is an external API model.""" Unknown = "unknown" + """Indicates the model's base architecture is unknown.""" class ModelType(str, Enum): diff --git a/invokeai/backend/model_manager/util/model_util.py b/invokeai/backend/model_manager/util/model_util.py index 4fa095b5999..c153129353b 100644 --- a/invokeai/backend/model_manager/util/model_util.py +++ b/invokeai/backend/model_manager/util/model_util.py @@ -83,14 +83,14 @@ def read_checkpoint_meta(path: Union[str, Path], scan: bool = True) -> Dict[str, return checkpoint -def lora_token_vector_length(checkpoint: Dict[str, torch.Tensor]) -> Optional[int]: +def lora_token_vector_length(checkpoint: dict[str | int, torch.Tensor]) -> Optional[int]: """ Given a checkpoint in memory, return the lora token vector length :param checkpoint: The checkpoint """ - def _get_shape_1(key: str, tensor: torch.Tensor, checkpoint: Dict[str, torch.Tensor]) -> Optional[int]: + def _get_shape_1(key: str, tensor: torch.Tensor, checkpoint: dict[str | int, torch.Tensor]) -> Optional[int]: lora_token_vector_length = None if "." not in key: @@ -136,6 +136,8 @@ def _get_shape_1(key: str, tensor: torch.Tensor, checkpoint: Dict[str, torch.Ten lora_te1_length = None lora_te2_length = None for key, tensor in checkpoint.items(): + if isinstance(key, int): + continue if key.startswith("lora_unet_") and ("_attn2_to_k." in key or "_attn2_to_v." in key): lora_token_vector_length = _get_shape_1(key, tensor, checkpoint) elif key.startswith("lora_unet_") and ( diff --git a/invokeai/backend/patches/lora_conversions/formats.py b/invokeai/backend/patches/lora_conversions/formats.py index 4fe6eb8772c..4cde7c98f67 100644 --- a/invokeai/backend/patches/lora_conversions/formats.py +++ b/invokeai/backend/patches/lora_conversions/formats.py @@ -17,7 +17,7 @@ def flux_format_from_state_dict( - state_dict: dict[str, Any], + state_dict: dict[str | int, Any], metadata: dict[str, Any] | None = None, ) -> FluxLoRAFormat | None: if is_state_dict_likely_in_flux_kohya_format(state_dict): diff --git a/invokeai/frontend/web/src/features/modelManagerV2/models.ts b/invokeai/frontend/web/src/features/modelManagerV2/models.ts index 85ab9b126ee..0b4096e010b 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/models.ts +++ b/invokeai/frontend/web/src/features/modelManagerV2/models.ts @@ -1,4 +1,4 @@ -import type { BaseModelType, ModelFormat, ModelType, ModelVariantType } from 'features/nodes/types/common'; +import type { AnyModelVariant, BaseModelType, ModelFormat, ModelType } from 'features/nodes/types/common'; import type { AnyModelConfig } from 'services/api/types'; import { isCLIPEmbedModelConfig, @@ -219,13 +219,15 @@ export const MODEL_BASE_TO_SHORT_NAME: Record = { unknown: 'Unknown', }; -export const MODEL_VARIANT_TO_LONG_NAME: Record = { +export const MODEL_VARIANT_TO_LONG_NAME: Record = { normal: 'Normal', inpaint: 'Inpaint', depth: 'Depth', dev: 'FLUX Dev', - dev_fill: 'FLUX Dev Fill', + dev_fill: 'FLUX Dev - Fill', schnell: 'FLUX Schnell', + large: 'CLIP L', + gigantic: 'CLIP G', }; export const MODEL_FORMAT_TO_LONG_NAME: Record = { 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 87923f9f00e..e139639f1f0 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx @@ -1,12 +1,12 @@ import { Badge } from '@invoke-ai/ui-library'; +import type { ModelFormat } from 'features/nodes/types/common'; import { memo } from 'react'; -import type { AnyModelConfig } from 'services/api/types'; type Props = { - format: AnyModelConfig['format']; + format: ModelFormat; }; -const FORMAT_NAME_MAP: Record = { +const FORMAT_NAME_MAP: Record = { diffusers: 'diffusers', lycoris: 'lycoris', checkpoint: 'checkpoint', @@ -20,9 +20,11 @@ const FORMAT_NAME_MAP: Record = { api: 'api', omi: 'omi', unknown: 'unknown', + olive: 'olive', + onnx: 'onnx', }; -const FORMAT_COLOR_MAP: Record = { +const FORMAT_COLOR_MAP: Record = { diffusers: 'base', omi: 'base', lycoris: 'base', @@ -36,6 +38,8 @@ const FORMAT_COLOR_MAP: Record = { gguf_quantized: 'base', api: 'base', unknown: 'red', + olive: 'base', + onnx: 'base', }; const ModelFormatBadge = ({ format }: Props) => { 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 e3fa3772bb8..c223747b931 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 @@ -12,6 +12,7 @@ import type { T2IAdapterField, zBaseModelType, zClipVariantType, + zFluxVariantType, zModelFormat, zModelVariantType, zSubModelType, @@ -45,6 +46,7 @@ describe('Common types', () => { test('ModelIdentifier', () => assert, S['SubModelType']>>()); test('ClipVariantType', () => assert, S['ClipVariantType']>>()); test('ModelVariantType', () => assert, S['ModelVariantType']>>()); + test('FluxVariantType', () => assert, S['FluxVariantType']>>()); test('ModelFormat', () => assert, S['ModelFormat']>>()); // Misc types diff --git a/invokeai/frontend/web/src/features/nodes/types/common.ts b/invokeai/frontend/web/src/features/nodes/types/common.ts index 96356f7cb4a..c51defd79c5 100644 --- a/invokeai/frontend/web/src/features/nodes/types/common.ts +++ b/invokeai/frontend/web/src/features/nodes/types/common.ts @@ -147,8 +147,10 @@ export const zSubModelType = z.enum([ ]); export const zClipVariantType = z.enum(['large', 'gigantic']); -export const zModelVariantType = z.enum(['normal', 'inpaint', 'depth', 'dev', 'dev_fill', 'schnell']); -export type ModelVariantType = z.infer; +export const zModelVariantType = z.enum(['normal', 'inpaint', 'depth']); +export const zFluxVariantType = z.enum(['dev', 'dev_fill', 'schnell']); +export const zAnyModelVariant = z.union([zModelVariantType, zClipVariantType, zFluxVariantType]); +export type AnyModelVariant = z.infer; export const zModelFormat = z.enum([ 'omi', 'diffusers', diff --git a/invokeai/frontend/web/src/features/nodes/types/field.ts b/invokeai/frontend/web/src/features/nodes/types/field.ts index 320a4ac521c..5b8634daa2b 100644 --- a/invokeai/frontend/web/src/features/nodes/types/field.ts +++ b/invokeai/frontend/web/src/features/nodes/types/field.ts @@ -10,15 +10,14 @@ import { z } from 'zod'; import type { ImageField } from './common'; import { + zAnyModelVariant, zBaseModelType, zBoardField, - zClipVariantType, zColorField, zImageField, zModelFormat, zModelIdentifierField, zModelType, - zModelVariantType, zSchedulerField, } from './common'; @@ -73,7 +72,7 @@ const zFieldInputTemplateBase = zFieldTemplateBase.extend({ ui_choice_labels: z.record(z.string(), z.string()).nullish(), ui_model_base: z.array(zBaseModelType).nullish(), ui_model_type: z.array(zModelType).nullish(), - ui_model_variant: z.array(zModelVariantType.or(zClipVariantType)).nullish(), + ui_model_variant: z.array(zAnyModelVariant).nullish(), ui_model_format: z.array(zModelFormat).nullish(), }); const zFieldOutputTemplateBase = zFieldTemplateBase.extend({ diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts index 24ef7123576..5a766e8d399 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts @@ -673,6 +673,8 @@ describe('Graph', () => { variant: 'inpaint', format: 'diffusers', repo_variant: 'fp16', + submodels: null, + usage_info: null, }); expect(field).toEqual({ key: 'b00ee8df-523d-40d2-9578-597283b07cb2', diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx index 27194efbd55..3f31d8ec769 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx @@ -25,9 +25,7 @@ export const MainModelPicker = memo(() => { const isFluxDevSelected = useMemo( () => - selectedModelConfig && - isCheckpointMainModelConfig(selectedModelConfig) && - selectedModelConfig.variant === 'flux_dev', + selectedModelConfig && isCheckpointMainModelConfig(selectedModelConfig) && selectedModelConfig.variant === 'dev', [selectedModelConfig] ); diff --git a/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx b/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx index b98cbd8067e..b0aca495183 100644 --- a/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx +++ b/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx @@ -24,9 +24,7 @@ export const InitialStateMainModelPicker = memo(() => { const isFluxDevSelected = useMemo( () => - selectedModelConfig && - isCheckpointMainModelConfig(selectedModelConfig) && - selectedModelConfig.variant === 'flux_dev', + selectedModelConfig && isCheckpointMainModelConfig(selectedModelConfig) && selectedModelConfig.variant === 'dev', [selectedModelConfig] ); diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts index eb27c4266dd..388263e6ece 100644 --- a/invokeai/frontend/web/src/services/api/schema.ts +++ b/invokeai/frontend/web/src/services/api/schema.ts @@ -2200,6 +2200,7 @@ export type components = { */ type: "alpha_mask_to_tensor"; }; + AnyModelConfig: components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"] | components["schemas"]["MainBnbQuantized4bCheckpointConfig"] | components["schemas"]["MainGGUFCheckpointConfig"] | components["schemas"]["VAEDiffusersConfig"] | components["schemas"]["VAECheckpointConfig"] | components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"] | components["schemas"]["LoRALyCORISConfig"] | components["schemas"]["LoRAOmiConfig"] | components["schemas"]["ControlLoRALyCORISConfig"] | components["schemas"]["ControlLoRADiffusersConfig"] | components["schemas"]["LoRADiffusersConfig"] | components["schemas"]["T5EncoderConfig"] | components["schemas"]["T5EncoderBnbQuantizedLlmInt8bConfig"] | components["schemas"]["TextualInversionFileConfig"] | components["schemas"]["TextualInversionFolderConfig"] | components["schemas"]["IPAdapterInvokeAIConfig"] | components["schemas"]["IPAdapterCheckpointConfig"] | components["schemas"]["T2IAdapterConfig"] | components["schemas"]["SpandrelImageToImageConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["CLIPLEmbedDiffusersConfig"] | components["schemas"]["CLIPGEmbedDiffusersConfig"] | components["schemas"]["SigLIPConfig"] | components["schemas"]["FluxReduxConfig"] | components["schemas"]["LlavaOnevisionConfig"] | components["schemas"]["ApiModelConfig"] | components["schemas"]["VideoApiModelConfig"] | components["schemas"]["UnknownModelConfig"]; /** * ApiModelConfig * @description Model config for API-based models. @@ -2231,19 +2232,10 @@ export type components = { */ name: string; /** - * Type - * @default main - * @constant - */ - type: "main"; - /** - * Format - * @default api - * @constant + * Description + * @description Model description */ - format: "api"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -2251,45 +2243,54 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Type + * @default main + * @constant + */ + type: "main"; /** * Trigger Phrases * @description Set of trigger phrases for this model */ - trigger_phrases?: string[] | null; + trigger_phrases: string[] | null; /** @description Default settings for this model */ - default_settings?: components["schemas"]["MainModelDefaultSettings"] | null; + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; + /** Variant */ + variant: components["schemas"]["ModelVariantType"] | components["schemas"]["FluxVariantType"]; /** - * Variant - * @default normal + * Format + * @default api + * @constant + */ + format: "api"; + /** + * Base + * @enum {string} */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | null; + base: "chatgpt-4o" | "gemini-2.5" | "imagen3" | "imagen4" | "flux-kontext"; }; /** * AppConfig @@ -2464,7 +2465,13 @@ export type components = { }; /** * BaseModelType - * @description Base model type. + * @description An enumeration of base model architectures. For example, Stable Diffusion 1.x, Stable Diffusion 2.x, FLUX, etc. + * + * Every model config must have a base architecture type. + * + * Not all models are associated with a base architecture. For example, CLIP models are their own thing, not related + * to any particular model architecture. To simplify internal APIs and make it easier to work with models, we use a + * 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" | "cogview4" | "imagen3" | "imagen4" | "gemini-2.5" | "chatgpt-4o" | "flux-kontext" | "veo3" | "runway" | "unknown"; @@ -3505,19 +3512,10 @@ export type components = { */ name: string; /** - * Type - * @default clip_embed - * @constant - */ - type: "clip_embed"; - /** - * Format - * @default diffusers - * @constant + * Description + * @description Model description */ - format: "diffusers"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -3525,41 +3523,54 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; /** @default */ - repo_variant?: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Base + * @default any + * @constant + */ + base: "any"; + /** + * Type + * @default clip_embed + * @constant + */ + type: "clip_embed"; /** * Variant * @default gigantic * @constant */ - variant?: "gigantic"; + variant: "gigantic"; }; /** * CLIPLEmbedDiffusersConfig @@ -3592,19 +3603,10 @@ export type components = { */ name: string; /** - * Type - * @default clip_embed - * @constant - */ - type: "clip_embed"; - /** - * Format - * @default diffusers - * @constant + * Description + * @description Model description */ - format: "diffusers"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -3612,41 +3614,54 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; /** @default */ - repo_variant?: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Base + * @default any + * @constant + */ + base: "any"; + /** + * Type + * @default clip_embed + * @constant + */ + type: "clip_embed"; /** * Variant * @default large * @constant */ - variant?: "large"; + variant: "large"; }; /** * CLIPOutput @@ -3755,19 +3770,10 @@ export type components = { */ name: string; /** - * Type - * @default clip_vision - * @constant - */ - type: "clip_vision"; - /** - * Format - * @default diffusers - * @constant + * Description + * @description Model description */ - format: "diffusers"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -3775,35 +3781,48 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; /** @default */ - repo_variant?: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Base + * @default any + * @constant + */ + base: "any"; + /** + * Type + * @default clip_vision + * @constant + */ + type: "clip_vision"; }; /** * CV2 Infill @@ -5251,19 +5270,10 @@ export type components = { */ name: string; /** - * Type - * @default control_lora - * @constant - */ - type: "control_lora"; - /** - * Format - * @default diffusers - * @constant + * Description + * @description Model description */ - format: "diffusers"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -5271,40 +5281,48 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; - /** @description Default settings for this model */ - default_settings?: components["schemas"]["ControlAdapterDefaultSettings"] | null; + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** - * Trigger Phrases - * @description Set of trigger phrases for this model + * Base + * @constant */ - trigger_phrases?: string[] | null; + base: "flux"; + /** + * Type + * @default control_lora + * @constant + */ + type: "control_lora"; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; + /** Trigger Phrases */ + trigger_phrases: string[] | null; }; /** ControlLoRAField */ ControlLoRAField: { @@ -5349,19 +5367,10 @@ export type components = { */ name: string; /** - * Type - * @default control_lora - * @constant - */ - type: "control_lora"; - /** - * Format - * @default lycoris - * @constant + * Description + * @description Model description */ - format: "lycoris"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -5369,40 +5378,48 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; - /** @description Default settings for this model */ - default_settings?: components["schemas"]["ControlAdapterDefaultSettings"] | null; + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** - * Trigger Phrases - * @description Set of trigger phrases for this model + * Base + * @constant */ - trigger_phrases?: string[] | null; + base: "flux"; + /** + * Type + * @default control_lora + * @constant + */ + type: "control_lora"; + /** + * Format + * @default lycoris + * @constant + */ + format: "lycoris"; + /** Trigger Phrases */ + trigger_phrases: string[] | null; }; /** * ControlNetCheckpointConfig @@ -5435,20 +5452,10 @@ export type components = { */ name: string; /** - * Type - * @default controlnet - * @constant - */ - type: "controlnet"; - /** - * Format - * @description Format of the provided checkpoint model - * @default checkpoint - * @enum {string} + * Description + * @description Model description */ - format: "checkpoint" | "bnb_quantized_nf4b" | "gguf_quantized"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -5456,56 +5463,67 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; - /** @description Default settings for this model */ - default_settings?: components["schemas"]["ControlAdapterDefaultSettings"] | null; + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** * Config Path - * @description path to the checkpoint model config file + * @description Path to the config for this model, if any. */ - config_path?: string | null; + config_path: string | null; /** * Converted At * @description When this model was last converted to diffusers */ - converted_at?: number | null; - }; - /** - * ControlNetDiffusersConfig - * @description Model config for ControlNet models (diffusers version). - */ - ControlNetDiffusersConfig: { + converted_at: number | null; /** - * Key - * @description A unique key for this model. + * Base + * @enum {string} */ - key: string; + base: "sd-1" | "sd-2" | "sdxl" | "flux"; + /** + * Type + * @default controlnet + * @constant + */ + type: "controlnet"; + /** + * Format + * @default checkpoint + * @constant + */ + format: "checkpoint"; + }; + /** + * ControlNetDiffusersConfig + * @description Model config for ControlNet models (diffusers version). + */ + ControlNetDiffusersConfig: { + /** + * Key + * @description A unique key for this model. + */ + key: string; /** * Hash * @description The hash of the model file(s). @@ -5527,19 +5545,10 @@ export type components = { */ name: string; /** - * Type - * @default controlnet - * @constant - */ - type: "controlnet"; - /** - * Format - * @default diffusers - * @constant + * Description + * @description Model description */ - format: "diffusers"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -5547,37 +5556,48 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; - /** @description Default settings for this model */ - default_settings?: components["schemas"]["ControlAdapterDefaultSettings"] | null; + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; /** @default */ - repo_variant?: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Base + * @enum {string} + */ + base: "sd-1" | "sd-2" | "sdxl" | "flux"; + /** + * Type + * @default controlnet + * @constant + */ + type: "controlnet"; }; /** * ControlNet - SD1.5, SD2, SDXL @@ -8887,19 +8907,10 @@ export type components = { */ name: string; /** - * Type - * @default flux_redux - * @constant - */ - type: "flux_redux"; - /** - * Format - * @default checkpoint - * @constant + * Description + * @description Model description */ - format: "checkpoint"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -8907,33 +8918,46 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Type + * @default flux_redux + * @constant + */ + type: "flux_redux"; + /** + * Format + * @default checkpoint + * @constant + */ + format: "checkpoint"; + /** + * Base + * @default flux + * @constant + */ + base: "flux"; }; /** * FLUX Redux @@ -9162,6 +9186,11 @@ export type components = { */ type: "flux_vae_encode"; }; + /** + * FluxVariantType + * @enum {string} + */ + FluxVariantType: "schnell" | "dev" | "dev_fill"; /** FoundModel */ FoundModel: { /** @@ -9677,19 +9706,10 @@ export type components = { */ name: string; /** - * Type - * @default ip_adapter - * @constant - */ - type: "ip_adapter"; - /** - * Format - * @default checkpoint - * @constant + * Description + * @description Model description */ - format: "checkpoint"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -9697,33 +9717,45 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Type + * @default ip_adapter + * @constant + */ + type: "ip_adapter"; + /** + * Base + * @enum {string} + */ + base: "sd-1" | "sd-2" | "sdxl" | "flux"; + /** + * Format + * @default checkpoint + * @constant + */ + format: "checkpoint"; }; /** IPAdapterField */ IPAdapterField: { @@ -9881,19 +9913,10 @@ export type components = { */ name: string; /** - * Type - * @default ip_adapter - * @constant - */ - type: "ip_adapter"; - /** - * Format - * @default invokeai - * @constant + * Description + * @description Model description */ - format: "invokeai"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -9901,33 +9924,45 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Type + * @default ip_adapter + * @constant + */ + type: "ip_adapter"; + /** + * Base + * @enum {string} + */ + base: "sd-1" | "sd-2" | "sdxl"; + /** + * Format + * @default invokeai + * @constant + */ + format: "invokeai"; /** Image Encoder Model Id */ image_encoder_model_id: string; }; @@ -14037,19 +14072,10 @@ export type components = { */ name: string; /** - * Type - * @default llava_onevision - * @constant - */ - type: "llava_onevision"; - /** - * Format - * @default diffusers - * @constant + * Description + * @description Model description */ - format: "diffusers"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -14057,35 +14083,54 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; /** @default */ - repo_variant?: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Type + * @default llava_onevision + * @constant + */ + type: "llava_onevision"; + /** + * Base + * @default any + * @constant + */ + base: "any"; + /** + * Variant + * @default normal + * @constant + */ + variant: "normal"; }; /** * LLaVA OneVision VLLM @@ -14212,19 +14257,10 @@ export type components = { */ name: string; /** - * Type - * @default lora - * @constant - */ - type: "lora"; - /** - * Format - * @default diffusers - * @constant + * Description + * @description Model description */ - format: "diffusers"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -14232,40 +14268,52 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Type + * @default lora + * @constant + */ + type: "lora"; /** * Trigger Phrases * @description Set of trigger phrases for this model */ - trigger_phrases?: string[] | null; + trigger_phrases: string[] | null; /** @description Default settings for this model */ - default_settings?: components["schemas"]["LoraModelDefaultSettings"] | null; + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; + /** + * Base + * @enum {string} + */ + base: "sd-1" | "sd-2" | "sdxl" | "flux"; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; }; /** LoRAField */ LoRAField: { @@ -14385,19 +14433,10 @@ export type components = { */ name: string; /** - * Type - * @default lora - * @constant + * Description + * @description Model description */ - type: "lora"; - /** - * Format - * @default lycoris - * @constant - */ - format: "lycoris"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -14405,40 +14444,52 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Type + * @default lora + * @constant + */ + type: "lora"; /** * Trigger Phrases * @description Set of trigger phrases for this model */ - trigger_phrases?: string[] | null; + trigger_phrases: string[] | null; /** @description Default settings for this model */ - default_settings?: components["schemas"]["LoraModelDefaultSettings"] | null; + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; + /** + * Base + * @enum {string} + */ + base: "sd-1" | "sd-2" | "sdxl" | "flux"; + /** + * Format + * @default lycoris + * @constant + */ + format: "lycoris"; }; /** * LoRAMetadataField @@ -14481,19 +14532,10 @@ export type components = { */ name: string; /** - * Type - * @default lora - * @constant - */ - type: "lora"; - /** - * Format - * @default omi - * @constant + * Description + * @description Model description */ - format: "omi"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -14501,40 +14543,52 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Type + * @default lora + * @constant + */ + type: "lora"; /** * Trigger Phrases * @description Set of trigger phrases for this model */ - trigger_phrases?: string[] | null; + trigger_phrases: string[] | null; /** @description Default settings for this model */ - default_settings?: components["schemas"]["LoraModelDefaultSettings"] | null; + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; + /** + * Base + * @enum {string} + */ + base: "flux" | "sdxl"; + /** + * Format + * @default omi + * @constant + */ + format: "omi"; }; /** * Select LoRA @@ -14754,19 +14808,10 @@ export type components = { */ name: string; /** - * Type - * @default main - * @constant - */ - type: "main"; - /** - * Format - * @default bnb_quantized_nf4b - * @constant + * Description + * @description Model description */ - format: "bnb_quantized_nf4b"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -14774,62 +14819,72 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Type + * @default main + * @constant + */ + type: "main"; /** * Trigger Phrases * @description Set of trigger phrases for this model */ - trigger_phrases?: string[] | null; + trigger_phrases: string[] | null; /** @description Default settings for this model */ - default_settings?: components["schemas"]["MainModelDefaultSettings"] | null; - /** - * Variant - * @default normal - */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | null; + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; + /** Variant */ + variant: components["schemas"]["ModelVariantType"] | components["schemas"]["FluxVariantType"]; /** * Config Path - * @description path to the checkpoint model config file + * @description Path to the config for this model, if any. */ - config_path?: string | null; + config_path: string | null; /** * Converted At * @description When this model was last converted to diffusers */ - converted_at?: number | null; + converted_at: number | null; + /** + * Base + * @default flux + * @constant + */ + base: "flux"; + /** + * Format + * @default bnb_quantized_nf4b + * @constant + */ + format: "bnb_quantized_nf4b"; /** @default epsilon */ - prediction_type?: components["schemas"]["SchedulerPredictionType"]; + prediction_type: components["schemas"]["SchedulerPredictionType"]; /** * Upcast Attention * @default false */ - upcast_attention?: boolean; + upcast_attention: boolean; }; /** * MainCheckpointConfig @@ -14862,20 +14917,10 @@ export type components = { */ name: string; /** - * Type - * @default main - * @constant - */ - type: "main"; - /** - * Format - * @description Format of the provided checkpoint model - * @default checkpoint - * @enum {string} + * Description + * @description Model description */ - format: "checkpoint" | "bnb_quantized_nf4b" | "gguf_quantized"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -14883,62 +14928,71 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Type + * @default main + * @constant + */ + type: "main"; /** * Trigger Phrases * @description Set of trigger phrases for this model */ - trigger_phrases?: string[] | null; + trigger_phrases: string[] | null; /** @description Default settings for this model */ - default_settings?: components["schemas"]["MainModelDefaultSettings"] | null; - /** - * Variant - * @default normal - */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | null; + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; + /** Variant */ + variant: components["schemas"]["ModelVariantType"] | components["schemas"]["FluxVariantType"]; /** * Config Path - * @description path to the checkpoint model config file + * @description Path to the config for this model, if any. */ - config_path?: string | null; + config_path: string | null; /** * Converted At * @description When this model was last converted to diffusers */ - converted_at?: number | null; + converted_at: number | null; + /** + * Base + * @enum {string} + */ + base: "sd-1" | "sd-2" | "sd-3" | "sdxl" | "sdxl-refiner" | "flux" | "cogview4"; + /** + * Format + * @default checkpoint + * @constant + */ + format: "checkpoint"; /** @default epsilon */ - prediction_type?: components["schemas"]["SchedulerPredictionType"]; + prediction_type: components["schemas"]["SchedulerPredictionType"]; /** * Upcast Attention * @default false */ - upcast_attention?: boolean; + upcast_attention: boolean; }; /** * MainDiffusersConfig @@ -14971,19 +15025,10 @@ export type components = { */ name: string; /** - * Type - * @default main - * @constant - */ - type: "main"; - /** - * Format - * @default diffusers - * @constant + * Description + * @description Model description */ - format: "diffusers"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -14991,47 +15036,56 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Type + * @default main + * @constant + */ + type: "main"; /** * Trigger Phrases * @description Set of trigger phrases for this model */ - trigger_phrases?: string[] | null; + trigger_phrases: string[] | null; /** @description Default settings for this model */ - default_settings?: components["schemas"]["MainModelDefaultSettings"] | null; + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; + /** Variant */ + variant: components["schemas"]["ModelVariantType"] | components["schemas"]["FluxVariantType"]; /** - * Variant - * @default normal + * Format + * @default diffusers + * @constant */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | null; + format: "diffusers"; /** @default */ - repo_variant?: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Base + * @enum {string} + */ + base: "sd-1" | "sd-2" | "sd-3" | "sdxl" | "sdxl-refiner" | "flux" | "cogview4"; }; /** * MainGGUFCheckpointConfig @@ -15064,19 +15118,10 @@ export type components = { */ name: string; /** - * Type - * @default main - * @constant - */ - type: "main"; - /** - * Format - * @default gguf_quantized - * @constant + * Description + * @description Model description */ - format: "gguf_quantized"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -15084,62 +15129,72 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Type + * @default main + * @constant + */ + type: "main"; /** * Trigger Phrases * @description Set of trigger phrases for this model */ - trigger_phrases?: string[] | null; + trigger_phrases: string[] | null; /** @description Default settings for this model */ - default_settings?: components["schemas"]["MainModelDefaultSettings"] | null; - /** - * Variant - * @default normal - */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | null; + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; + /** Variant */ + variant: components["schemas"]["ModelVariantType"] | components["schemas"]["FluxVariantType"]; /** * Config Path - * @description path to the checkpoint model config file + * @description Path to the config for this model, if any. */ - config_path?: string | null; + config_path: string | null; /** * Converted At * @description When this model was last converted to diffusers */ - converted_at?: number | null; + converted_at: number | null; + /** + * Base + * @default flux + * @constant + */ + base: "flux"; + /** + * Format + * @default gguf_quantized + * @constant + */ + format: "gguf_quantized"; /** @default epsilon */ - prediction_type?: components["schemas"]["SchedulerPredictionType"]; + prediction_type: components["schemas"]["SchedulerPredictionType"]; /** * Upcast Attention * @default false */ - upcast_attention?: boolean; + upcast_attention: boolean; }; /** MainModelDefaultSettings */ MainModelDefaultSettings: { @@ -17428,7 +17483,7 @@ export type components = { * Variant * @description The variant of the model. */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | null; + variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | null; /** @description The prediction type of the model. */ prediction_type?: components["schemas"]["SchedulerPredictionType"] | null; /** @@ -17486,7 +17541,7 @@ export type components = { * @description Variant type. * @enum {string} */ - ModelVariantType: "normal" | "inpaint" | "depth" | "flux_dev" | "flux_dev_fill" | "flux_schnell"; + ModelVariantType: "normal" | "inpaint" | "depth"; /** * ModelsList * @description Return list of configs. @@ -20105,19 +20160,10 @@ export type components = { */ name: string; /** - * Type - * @default siglip - * @constant - */ - type: "siglip"; - /** - * Format - * @default diffusers - * @constant + * Description + * @description Model description */ - format: "diffusers"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -20125,37 +20171,50 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; - /** @default */ - repo_variant?: components["schemas"]["ModelRepoVariant"] | null; - }; - /** + usage_info: string | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Type + * @default siglip + * @constant + */ + type: "siglip"; + /** + * Base + * @default any + * @constant + */ + base: "any"; + }; + /** * Image-to-Image (Autoscale) * @description Run any spandrel image-to-image model (https://github.com/chaiNNer-org/spandrel) until the target scale is reached. */ @@ -20254,19 +20313,10 @@ export type components = { */ name: string; /** - * Type - * @default spandrel_image_to_image - * @constant - */ - type: "spandrel_image_to_image"; - /** - * Format - * @default checkpoint - * @constant + * Description + * @description Model description */ - format: "checkpoint"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -20274,33 +20324,46 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Base + * @default any + * @constant + */ + base: "any"; + /** + * Type + * @default spandrel_image_to_image + * @constant + */ + type: "spandrel_image_to_image"; + /** + * Format + * @default checkpoint + * @constant + */ + format: "checkpoint"; }; /** * Image-to-Image @@ -20940,7 +21003,7 @@ export type components = { path_or_prefix: string; model_type: components["schemas"]["ModelType"]; /** Variant */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | null; + variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | null; }; /** * Subtract Integers @@ -21014,19 +21077,10 @@ export type components = { */ name: string; /** - * Type - * @default t2i_adapter - * @constant - */ - type: "t2i_adapter"; - /** - * Format - * @default diffusers - * @constant + * Description + * @description Model description */ - format: "diffusers"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -21034,37 +21088,48 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; - /** @description Default settings for this model */ - default_settings?: components["schemas"]["ControlAdapterDefaultSettings"] | null; + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; /** @default */ - repo_variant?: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Base + * @enum {string} + */ + base: "sd-1" | "sd-2" | "sdxl"; + /** + * Type + * @default t2i_adapter + * @constant + */ + type: "t2i_adapter"; }; /** T2IAdapterField */ T2IAdapterField: { @@ -21242,19 +21307,10 @@ export type components = { */ name: string; /** - * Type - * @default t5_encoder - * @constant - */ - type: "t5_encoder"; - /** - * Format - * @default bnb_quantized_int8b - * @constant + * Description + * @description Model description */ - format: "bnb_quantized_int8b"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -21262,33 +21318,46 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Base + * @default any + * @constant + */ + base: "any"; + /** + * Type + * @default t5_encoder + * @constant + */ + type: "t5_encoder"; + /** + * Format + * @default bnb_quantized_int8b + * @constant + */ + format: "bnb_quantized_int8b"; }; /** T5EncoderConfig */ T5EncoderConfig: { @@ -21318,19 +21387,10 @@ export type components = { */ name: string; /** - * Type - * @default t5_encoder - * @constant - */ - type: "t5_encoder"; - /** - * Format - * @default t5_encoder - * @constant + * Description + * @description Model description */ - format: "t5_encoder"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -21338,33 +21398,46 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Base + * @default any + * @constant + */ + base: "any"; + /** + * Type + * @default t5_encoder + * @constant + */ + type: "t5_encoder"; + /** + * Format + * @default t5_encoder + * @constant + */ + format: "t5_encoder"; }; /** T5EncoderField */ T5EncoderField: { @@ -21431,19 +21504,10 @@ export type components = { */ name: string; /** - * Type - * @default embedding - * @constant - */ - type: "embedding"; - /** - * Format - * @default embedding_file - * @constant + * Description + * @description Model description */ - format: "embedding_file"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -21451,33 +21515,45 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Base + * @enum {string} + */ + base: "sd-1" | "sd-2" | "sdxl"; + /** + * Type + * @default embedding + * @constant + */ + type: "embedding"; + /** + * Format + * @default embedding_file + * @constant + */ + format: "embedding_file"; }; /** * TextualInversionFolderConfig @@ -21510,19 +21586,10 @@ export type components = { */ name: string; /** - * Type - * @default embedding - * @constant - */ - type: "embedding"; - /** - * Format - * @default embedding_folder - * @constant + * Description + * @description Model description */ - format: "embedding_folder"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -21530,33 +21597,45 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Base + * @enum {string} + */ + base: "sd-1" | "sd-2" | "sdxl"; + /** + * Type + * @default embedding + * @constant + */ + type: "embedding"; + /** + * Format + * @default embedding_folder + * @constant + */ + format: "embedding_folder"; }; /** Tile */ Tile: { @@ -21968,23 +22047,10 @@ export type components = { */ name: string; /** - * Type - * @default unknown - * @constant - */ - type: "unknown"; - /** - * Format - * @default unknown - * @constant - */ - format: "unknown"; - /** - * Base - * @default unknown - * @constant + * Description + * @description Model description */ - base: "unknown"; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -21992,33 +22058,46 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Base + * @default unknown + * @constant + */ + base: "unknown"; + /** + * Type + * @default unknown + * @constant + */ + type: "unknown"; + /** + * Format + * @default unknown + * @constant + */ + format: "unknown"; }; /** * Unsharp Mask @@ -22146,20 +22225,10 @@ export type components = { */ name: string; /** - * Type - * @default vae - * @constant - */ - type: "vae"; - /** - * Format - * @description Format of the provided checkpoint model - * @default checkpoint - * @enum {string} + * Description + * @description Model description */ - format: "checkpoint" | "bnb_quantized_nf4b" | "gguf_quantized"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -22167,43 +22236,55 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; /** * Config Path - * @description path to the checkpoint model config file + * @description Path to the config for this model, if any. */ - config_path?: string | null; + config_path: string | null; /** * Converted At * @description When this model was last converted to diffusers */ - converted_at?: number | null; + converted_at: number | null; + /** + * Base + * @enum {string} + */ + base: "sd-1" | "sd-2" | "sdxl" | "flux"; + /** + * Type + * @default vae + * @constant + */ + type: "vae"; + /** + * Format + * @default checkpoint + * @constant + */ + format: "checkpoint"; }; /** * VAEDiffusersConfig @@ -22236,19 +22317,10 @@ export type components = { */ name: string; /** - * Type - * @default vae - * @constant - */ - type: "vae"; - /** - * Format - * @default diffusers - * @constant + * Description + * @description Model description */ - format: "diffusers"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -22256,33 +22328,47 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Base + * @enum {string} + */ + base: "sd-1" | "sdxl"; + /** + * Type + * @default vae + * @constant + */ + type: "vae"; }; /** VAEField */ VAEField: { @@ -22404,19 +22490,10 @@ export type components = { */ name: string; /** - * Type - * @default video - * @constant - */ - type: "video"; - /** - * Format - * @default api - * @constant + * Description + * @description Model description */ - format: "api"; - /** @description The base model. */ - base: components["schemas"]["BaseModelType"]; + description: string | null; /** * Source * @description The original source of the model (path, URL or repo_id). @@ -22424,45 +22501,52 @@ export type components = { source: string; /** @description The type of source */ source_type: components["schemas"]["ModelSourceType"]; - /** - * Description - * @description Model description - */ - description?: string | null; /** * Source Api Response * @description The original API response from the source, as stringified JSON. */ - source_api_response?: string | null; + source_api_response: string | null; /** * Cover Image * @description Url for image to preview model */ - cover_image?: string | null; + cover_image: string | null; /** * Submodels * @description Loadable submodels in this model */ - submodels?: { + submodels: { [key: string]: components["schemas"]["SubmodelDefinition"]; } | null; /** * Usage Info * @description Usage information for this model */ - usage_info?: string | null; + usage_info: string | null; + /** + * Type + * @default video + * @constant + */ + type: "video"; + /** + * Base + * @enum {string} + */ + base: "veo3" | "runway"; + /** + * Format + * @default api + * @constant + */ + format: "api"; /** * Trigger Phrases * @description Set of trigger phrases for this model */ - trigger_phrases?: string[] | null; + trigger_phrases: string[] | null; /** @description Default settings for this model */ - default_settings?: components["schemas"]["MainModelDefaultSettings"] | null; - /** - * Variant - * @default normal - */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | null; + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; }; /** * VideoDTO diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts index b9798c04d99..48313bd7ef0 100644 --- a/invokeai/frontend/web/src/services/api/types.ts +++ b/invokeai/frontend/web/src/services/api/types.ts @@ -119,8 +119,6 @@ type LlavaOnevisionConfig = S['LlavaOnevisionConfig']; export type T5EncoderModelConfig = S['T5EncoderConfig']; export type T5EncoderBnbQuantizedLlmInt8bModelConfig = S['T5EncoderBnbQuantizedLlmInt8bConfig']; export type SpandrelImageToImageModelConfig = S['SpandrelImageToImageConfig']; -type TextualInversionModelConfig = S['TextualInversionFileConfig'] | S['TextualInversionFolderConfig']; -type DiffusersModelConfig = S['MainDiffusersConfig']; export type CheckpointModelConfig = S['MainCheckpointConfig']; type CLIPVisionDiffusersConfig = S['CLIPVisionDiffusersConfig']; type SigLipModelConfig = S['SigLIPConfig']; @@ -128,29 +126,11 @@ export type FLUXReduxModelConfig = S['FluxReduxConfig']; type ApiModelConfig = S['ApiModelConfig']; export type VideoApiModelConfig = S['VideoApiModelConfig']; type UnknownModelConfig = S['UnknownModelConfig']; -export type MainModelConfig = DiffusersModelConfig | CheckpointModelConfig | ApiModelConfig; +export type MainModelConfig = Extract; export type FLUXKontextModelConfig = MainModelConfig; export type ChatGPT4oModelConfig = ApiModelConfig; export type Gemini2_5ModelConfig = ApiModelConfig; -export type AnyModelConfig = - | ControlLoRAModelConfig - | LoRAModelConfig - | VAEModelConfig - | ControlNetModelConfig - | IPAdapterModelConfig - | T5EncoderModelConfig - | T5EncoderBnbQuantizedLlmInt8bModelConfig - | CLIPEmbedModelConfig - | T2IAdapterModelConfig - | SpandrelImageToImageModelConfig - | TextualInversionModelConfig - | MainModelConfig - | VideoApiModelConfig - | CLIPVisionDiffusersConfig - | SigLipModelConfig - | FLUXReduxModelConfig - | LlavaOnevisionConfig - | UnknownModelConfig; +export type AnyModelConfig = S['AnyModelConfig']; /** * Checks if a list of submodels contains any that match a given variant or type diff --git a/invokeai/frontend/web/src/services/events/setEventListeners.tsx b/invokeai/frontend/web/src/services/events/setEventListeners.tsx index 0cfa4cef26f..5804399f1f7 100644 --- a/invokeai/frontend/web/src/services/events/setEventListeners.tsx +++ b/invokeai/frontend/web/src/services/events/setEventListeners.tsx @@ -295,18 +295,7 @@ export const setEventListeners = ({ socket, store, setIsConnected }: SetEventLis const { id, config } = data; - if ( - config.type === 'unknown' || - config.base === 'unknown' || - /** - * Checking if type/base are 'unknown' technically narrows the config such that it's not possible for a config - * that passes to the `config.[type|base] === 'unknown'` checks. In the future, if we have more model config - * classes, this may change, so we will continue to check all three. Any one being 'unknown' is concerning - * enough to warrant a toast. - */ - /* @ts-expect-error See note above */ - config.format === 'unknown' - ) { + if (config.type === 'unknown') { toast({ id: 'UNKNOWN_MODEL', title: t('modelManager.unidentifiedModelTitle'), diff --git a/scripts/classify-model.py b/scripts/classify-model.py index 78bbd5a2f68..2ae253b72fe 100755 --- a/scripts/classify-model.py +++ b/scripts/classify-model.py @@ -31,7 +31,10 @@ def classify_with_fallback(path: Path, hash_algo: HASHING_ALGORITHMS): try: return ModelProbe.probe(path, hash_algo=hash_algo) except InvalidModelConfigException: - return ModelConfigFactory.from_model_on_disk(mod=path, hash_algo=hash_algo,) + return ModelConfigFactory.from_model_on_disk( + mod=path, + hash_algo=hash_algo, + ) for path in args.model_path: From e0d91efebf5cc052f78c9242e230de96f3a0e32d Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:41:58 +1000 Subject: [PATCH 27/62] feat(mm): port flux "control lora" and t2i adapter to new api --- invokeai/backend/model_manager/config.py | 52 ++++++++++++------- .../flux_control_lora_utils.py | 2 +- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 7bc16099f0c..5fc22be845b 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -66,6 +66,7 @@ variant_type_adapter, ) from invokeai.backend.model_manager.util.model_util import lora_token_vector_length +from invokeai.backend.patches.lora_conversions.flux_control_lora_utils import is_state_dict_likely_flux_control from invokeai.backend.spandrel_image_to_image_model import SpandrelImageToImageModel from invokeai.backend.stable_diffusion.schedulers.schedulers import SCHEDULER_NAME_VALUES @@ -222,13 +223,7 @@ class ControlAdapterDefaultSettings(BaseModel): class LegacyProbeMixin: """Mixin for classes using the legacy probe for model classification.""" - @classmethod - def matches(cls, *args, **kwargs): - raise NotImplementedError(f"Method 'matches' not implemented for {cls.__name__}") - - @classmethod - def parse(cls, *args, **kwargs): - raise NotImplementedError(f"Method 'parse' not implemented for {cls.__name__}") + pass class ModelConfigBase(ABC, BaseModel): @@ -581,7 +576,7 @@ class ControlAdapterConfigBase(ABC, BaseModel): ControlLoRALyCORIS_SupportedBases: TypeAlias = Literal[BaseModelType.Flux] -class ControlLoRALyCORISConfig(ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): +class ControlLoRALyCORISConfig(ControlAdapterConfigBase, ModelConfigBase): """Model config for Control LoRA models.""" base: ControlLoRALyCORIS_SupportedBases = Field() @@ -590,18 +585,19 @@ class ControlLoRALyCORISConfig(ControlAdapterConfigBase, LegacyProbeMixin, Model trigger_phrases: set[str] | None = Field(None) + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _raise_if_not_file(cls, mod) -ControlLoRADiffusers_SupportedBases: TypeAlias = Literal[BaseModelType.Flux] + state_dict = mod.load_state_dict() + if not is_state_dict_likely_flux_control(state_dict): + raise NotAMatch(cls, "model state dict does not look like a Flux Control LoRA") -class ControlLoRADiffusersConfig(ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): - """Model config for Control LoRA models.""" + return cls(**fields) - base: ControlLoRADiffusers_SupportedBases = Field() - type: Literal[ModelType.ControlLoRa] = Field(default=ModelType.ControlLoRa) - format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - trigger_phrases: set[str] | None = Field(None) +ControlLoRADiffusers_SupportedBases: TypeAlias = Literal[BaseModelType.Flux] LoRADiffusers_SupportedBases: TypeAlias = Literal[ @@ -950,7 +946,7 @@ class MainBnbQuantized4bCheckpointConfig(CheckpointConfigBase, MainConfigBase, L base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) format: Literal[ModelFormat.BnbQuantizednf4b] = Field(default=ModelFormat.BnbQuantizednf4b) prediction_type: SchedulerPredictionType = Field(default=SchedulerPredictionType.Epsilon) - upcast_attention: bool = False + upcast_attention: bool = Field(False) class MainGGUFCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase): @@ -1236,13 +1232,34 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: ] -class T2IAdapterConfig(DiffusersConfigBase, ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): +class T2IAdapterConfig(DiffusersConfigBase, ControlAdapterConfigBase, ModelConfigBase): """Model config for T2I.""" base: T2IAdapterCheckpoint_SupportedBases = Field() type: Literal[ModelType.T2IAdapter] = Field(default=ModelType.T2IAdapter) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + VALID_OVERRIDES: ClassVar = { + "type": ModelType.T2IAdapter, + "format": ModelFormat.Diffusers, + } + + VALID_CLASS_NAMES: ClassVar = { + "T2IAdapter", + } + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _raise_if_not_dir(cls, mod) + + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + + config_path = mod.path / "config.json" + + _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) + + return cls(**fields) + class SpandrelImageToImageConfig(ModelConfigBase): """Model config for Spandrel Image to Image models.""" @@ -1454,7 +1471,6 @@ def get_model_discriminator_value(v: Any) -> str: Annotated[LoRALyCORISConfig, LoRALyCORISConfig.get_tag()], Annotated[LoRAOmiConfig, LoRAOmiConfig.get_tag()], Annotated[ControlLoRALyCORISConfig, ControlLoRALyCORISConfig.get_tag()], - Annotated[ControlLoRADiffusersConfig, ControlLoRADiffusersConfig.get_tag()], Annotated[LoRADiffusersConfig, LoRADiffusersConfig.get_tag()], Annotated[T5EncoderConfig, T5EncoderConfig.get_tag()], Annotated[T5EncoderBnbQuantizedLlmInt8bConfig, T5EncoderBnbQuantizedLlmInt8bConfig.get_tag()], diff --git a/invokeai/backend/patches/lora_conversions/flux_control_lora_utils.py b/invokeai/backend/patches/lora_conversions/flux_control_lora_utils.py index fa9cc764628..bd2b74e6089 100644 --- a/invokeai/backend/patches/lora_conversions/flux_control_lora_utils.py +++ b/invokeai/backend/patches/lora_conversions/flux_control_lora_utils.py @@ -18,7 +18,7 @@ FLUX_CONTROL_TRANSFORMER_KEY_REGEX = r"(\w+\.)+(lora_A\.weight|lora_B\.weight|lora_B\.bias|scale)" -def is_state_dict_likely_flux_control(state_dict: Dict[str, Any]) -> bool: +def is_state_dict_likely_flux_control(state_dict: dict[str | int, Any]) -> bool: """Checks if the provided state dict is likely in the FLUX Control LoRA format. This is intended to be a high-precision detector, but it is not guaranteed to have perfect precision. (A From 5deb9bb598c3e31c1c8b8fbf5807f99fe908241a Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:42:18 +1000 Subject: [PATCH 28/62] tidy(ui): use Extract to get model config types --- .../frontend/web/src/services/api/types.ts | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts index 48313bd7ef0..7506b29356f 100644 --- a/invokeai/frontend/web/src/services/api/types.ts +++ b/invokeai/frontend/web/src/services/api/types.ts @@ -106,31 +106,34 @@ export const isVideoDTO = (dto: ImageDTO | VideoDTO): dto is VideoDTO => { }; // Model Configs -export type ControlLoRAModelConfig = S['ControlLoRALyCORISConfig'] | S['ControlLoRADiffusersConfig']; -export type LoRAModelConfig = S['LoRADiffusersConfig'] | S['LoRALyCORISConfig'] | S['LoRAOmiConfig']; -export type VAEModelConfig = S['VAECheckpointConfig'] | S['VAEDiffusersConfig']; -export type ControlNetModelConfig = S['ControlNetDiffusersConfig'] | S['ControlNetCheckpointConfig']; -export type IPAdapterModelConfig = S['IPAdapterInvokeAIConfig'] | S['IPAdapterCheckpointConfig']; -export type T2IAdapterModelConfig = S['T2IAdapterConfig']; -export type CLIPLEmbedModelConfig = S['CLIPLEmbedDiffusersConfig']; -export type CLIPGEmbedModelConfig = S['CLIPGEmbedDiffusersConfig']; -export type CLIPEmbedModelConfig = CLIPLEmbedModelConfig | CLIPGEmbedModelConfig; -type LlavaOnevisionConfig = S['LlavaOnevisionConfig']; -export type T5EncoderModelConfig = S['T5EncoderConfig']; -export type T5EncoderBnbQuantizedLlmInt8bModelConfig = S['T5EncoderBnbQuantizedLlmInt8bConfig']; -export type SpandrelImageToImageModelConfig = S['SpandrelImageToImageConfig']; -export type CheckpointModelConfig = S['MainCheckpointConfig']; -type CLIPVisionDiffusersConfig = S['CLIPVisionDiffusersConfig']; -type SigLipModelConfig = S['SigLIPConfig']; -export type FLUXReduxModelConfig = S['FluxReduxConfig']; -type ApiModelConfig = S['ApiModelConfig']; -export type VideoApiModelConfig = S['VideoApiModelConfig']; -type UnknownModelConfig = S['UnknownModelConfig']; +export type AnyModelConfig = S['AnyModelConfig']; export type MainModelConfig = Extract; +export type ControlLoRAModelConfig = Extract; +export type LoRAModelConfig = Extract; +export type VAEModelConfig = Extract; +export type ControlNetModelConfig = Extract; +export type IPAdapterModelConfig = Extract; +export type T2IAdapterModelConfig = Extract; +export type CLIPLEmbedModelConfig = Extract; +export type CLIPGEmbedModelConfig = Extract; +export type CLIPEmbedModelConfig = Extract; +type LlavaOnevisionConfig = Extract; +export type T5EncoderModelConfig = Extract; +export type T5EncoderBnbQuantizedLlmInt8bModelConfig = Extract< + S['AnyModelConfig'], + { type: 't5_encoder'; format: 'bnb_quantized_int8b' } +>; +export type SpandrelImageToImageModelConfig = Extract; +export type CheckpointModelConfig = Extract; +type CLIPVisionDiffusersConfig = Extract; +type SigLipModelConfig = Extract; +export type FLUXReduxModelConfig = Extract; +type ApiModelConfig = Extract; +export type VideoApiModelConfig = Extract; +type UnknownModelConfig = Extract; export type FLUXKontextModelConfig = MainModelConfig; export type ChatGPT4oModelConfig = ApiModelConfig; export type Gemini2_5ModelConfig = ApiModelConfig; -export type AnyModelConfig = S['AnyModelConfig']; /** * Checks if a list of submodels contains any that match a given variant or type From 07e99c9dfb3d581a48f3d3d6d5bfa2ea6f8642dd Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:48:43 +1000 Subject: [PATCH 29/62] fix(mm): t2i base determination --- invokeai/backend/model_manager/config.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 5fc22be845b..7230c46c74e 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -1225,9 +1225,8 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -T2IAdapterCheckpoint_SupportedBases: TypeAlias = Literal[ +T2IAdapterDiffusers_SupportedBases: TypeAlias = Literal[ BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, BaseModelType.StableDiffusionXL, ] @@ -1235,7 +1234,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class T2IAdapterConfig(DiffusersConfigBase, ControlAdapterConfigBase, ModelConfigBase): """Model config for T2I.""" - base: T2IAdapterCheckpoint_SupportedBases = Field() + base: T2IAdapterDiffusers_SupportedBases = Field() type: Literal[ModelType.T2IAdapter] = Field(default=ModelType.T2IAdapter) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) @@ -1248,6 +1247,7 @@ class T2IAdapterConfig(DiffusersConfigBase, ControlAdapterConfigBase, ModelConfi "T2IAdapter", } + @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_dir(cls, mod) @@ -1258,8 +1258,23 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) - return cls(**fields) + base = fields.get("base") or cls._get_base_or_raise(mod) + return cls(**fields, base=base) + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> T2IAdapterDiffusers_SupportedBases: + config = _get_config_or_raise(cls, mod.path / "config.json") + + adapter_type = config.get("adapter_type") + + match adapter_type: + case "full_adapter_xl": + return BaseModelType.StableDiffusionXL + case "full_adapter" | "light_adapter": + return BaseModelType.StableDiffusion1 + case _: + raise NotAMatch(cls, f"unrecognized adapter_type '{adapter_type}'") class SpandrelImageToImageConfig(ModelConfigBase): """Model config for Spandrel Image to Image models.""" From d27bef10fd6a835c76006fbd728a4d8adae6e444 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 25 Sep 2025 21:06:34 +1000 Subject: [PATCH 30/62] feat(mm): port cnet to new api --- .../flux/controlnet/state_dict_utils.py | 4 +- invokeai/backend/model_manager/config.py | 115 +++++++++++++++++- 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/invokeai/backend/flux/controlnet/state_dict_utils.py b/invokeai/backend/flux/controlnet/state_dict_utils.py index aa44e6c10f0..87eae5a96bc 100644 --- a/invokeai/backend/flux/controlnet/state_dict_utils.py +++ b/invokeai/backend/flux/controlnet/state_dict_utils.py @@ -5,7 +5,7 @@ from invokeai.backend.flux.model import FluxParams -def is_state_dict_xlabs_controlnet(sd: Dict[str, Any]) -> bool: +def is_state_dict_xlabs_controlnet(sd: dict[str | int, Any]) -> bool: """Is the state dict for an XLabs ControlNet model? This is intended to be a reasonably high-precision detector, but it is not guaranteed to have perfect precision. @@ -25,7 +25,7 @@ def is_state_dict_xlabs_controlnet(sd: Dict[str, Any]) -> bool: return False -def is_state_dict_instantx_controlnet(sd: Dict[str, Any]) -> bool: +def is_state_dict_instantx_controlnet(sd: dict[str | int, Any]) -> bool: """Is the state dict for an InstantX ControlNet model? This is intended to be a reasonably high-precision detector, but it is not guaranteed to have perfect precision. diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 7230c46c74e..118bec28cb8 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -44,6 +44,10 @@ from invokeai.app.services.config.config_default import get_config from invokeai.app.util.misc import uuid_string +from invokeai.backend.flux.controlnet.state_dict_utils import ( + is_state_dict_instantx_controlnet, + is_state_dict_xlabs_controlnet, +) from invokeai.backend.flux.ip_adapter.state_dict_utils import is_state_dict_xlabs_ip_adapter from invokeai.backend.flux.redux.flux_redux_state_dict_utils import is_state_dict_likely_flux_redux from invokeai.backend.model_hash.hash_validator import validate_hash @@ -759,13 +763,56 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> VAEDiffusersConfig_SupportedBas ] -class ControlNetDiffusersConfig(DiffusersConfigBase, ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): +class ControlNetDiffusersConfig(DiffusersConfigBase, ControlAdapterConfigBase, ModelConfigBase): """Model config for ControlNet models (diffusers version).""" base: ControlNetDiffusers_SupportedBases = Field() type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + VALID_OVERRIDES: ClassVar = { + "type": ModelType.ControlNet, + "format": ModelFormat.Diffusers, + } + + VALID_CLASS_NAMES: ClassVar = { + "ControlNetModel", + "FluxControlNetModel", + } + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _raise_if_not_dir(cls, mod) + + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + + _validate_class_names(cls, mod.path / "config.json", cls.VALID_CLASS_NAMES) + + base = fields.get("base") or cls._get_base_or_raise(mod) + + return cls(**fields, base=base) + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> ControlNetDiffusers_SupportedBases: + config = _get_config_or_raise(cls, mod.path / "config.json") + + if config.get("_class_name") == "FluxControlNetModel": + return BaseModelType.Flux + + dimension = config.get("cross_attention_dim") + + match dimension: + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + # No obvious way to distinguish between sd2-base and sd2-768, but we don't really differentiate them + # anyway. + return BaseModelType.StableDiffusion2 + case 2048: + return BaseModelType.StableDiffusionXL + case _: + raise NotAMatch(cls, f"unrecognized cross_attention_dim {dimension}") + ControlNetCheckpoint_SupportedBases: TypeAlias = Literal[ BaseModelType.StableDiffusion1, @@ -775,13 +822,75 @@ class ControlNetDiffusersConfig(DiffusersConfigBase, ControlAdapterConfigBase, L ] -class ControlNetCheckpointConfig(CheckpointConfigBase, ControlAdapterConfigBase, LegacyProbeMixin, ModelConfigBase): +class ControlNetCheckpointConfig(CheckpointConfigBase, ControlAdapterConfigBase, ModelConfigBase): """Model config for ControlNet models (diffusers version).""" base: ControlNetDiffusers_SupportedBases = Field() type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + VALID_OVERRIDES: ClassVar = { + "type": ModelType.ControlNet, + "format": ModelFormat.Checkpoint, + } + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _raise_if_not_file(cls, mod) + + _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + + if not mod.has_keys_starting_with( + { + "controlnet", + "control_model", + "input_blocks", + # XLabs FLUX ControlNet models have keys starting with "controlnet_blocks." + # For example: https://huggingface.co/XLabs-AI/flux-controlnet-collections/blob/86ab1e915a389d5857135c00e0d350e9e38a9048/flux-canny-controlnet_v2.safetensors + # TODO(ryand): This is very fragile. XLabs FLUX ControlNet models also contain keys starting with + # "double_blocks.", which we check for above. But, I'm afraid to modify this logic because it is so + # delicate. + "controlnet_blocks", + } + ): + raise NotAMatch(cls, "state dict does not look like a ControlNet checkpoint") + + base = fields.get("base") or cls._get_base_or_raise(mod) + + return cls(**fields, base=base) + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> ControlNetCheckpoint_SupportedBases: + state_dict = mod.load_state_dict() + + if is_state_dict_xlabs_controlnet(state_dict) or is_state_dict_instantx_controlnet(state_dict): + # TODO(ryand): Should I distinguish between XLabs, InstantX and other ControlNet models by implementing + # get_format()? + return BaseModelType.Flux + + for key in ( + "control_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight", + "controlnet_mid_block.bias", + "input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight", + "down_blocks.1.attentions.0.transformer_blocks.0.attn2.to_k.weight", + ): + if key not in state_dict: + continue + width = state_dict[key].shape[-1] + match width: + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case 2048: + return BaseModelType.StableDiffusionXL + case 1280: + return BaseModelType.StableDiffusionXL + case _: + pass + + raise NotAMatch(cls, "unable to determine base type from state dict") + TextualInversion_SupportedBases: TypeAlias = Literal[ BaseModelType.StableDiffusion1, @@ -1247,7 +1356,6 @@ class T2IAdapterConfig(DiffusersConfigBase, ControlAdapterConfigBase, ModelConfi "T2IAdapter", } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _raise_if_not_dir(cls, mod) @@ -1276,6 +1384,7 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> T2IAdapterDiffusers_SupportedBa case _: raise NotAMatch(cls, f"unrecognized adapter_type '{adapter_type}'") + class SpandrelImageToImageConfig(ModelConfigBase): """Model config for Spandrel Image to Image models.""" From 1268b23d86fd59f6e5cb01dc179eaf8315824a0a Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 25 Sep 2025 21:53:05 +1000 Subject: [PATCH 31/62] refactor(mm): add config validation utils, make it all consistent and clean --- invokeai/backend/model_manager/config.py | 565 +++++++++++------------ 1 file changed, 264 insertions(+), 301 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 118bec28cb8..805202ef9b6 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -26,6 +26,7 @@ import time from abc import ABC from enum import Enum +from functools import cache from inspect import isabstract from pathlib import Path from typing import ( @@ -40,6 +41,7 @@ import torch from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag, TypeAdapter, ValidationError +from pydantic_core import CoreSchema, SchemaValidator from typing_extensions import Annotated, Any, Dict from invokeai.app.services.config.config_default import get_config @@ -102,6 +104,38 @@ def __init__( DEFAULTS_PRECISION = Literal["fp16", "fp32"] + +# Utility from https://github.com/pydantic/pydantic/discussions/7367#discussioncomment-14213144 +def find_field_schema(model: type[BaseModel], field_name: str) -> CoreSchema: + schema: CoreSchema = model.__pydantic_core_schema__.copy() + # we shallow copied, be careful not to mutate the original schema! + + assert schema["type"] in ["definitions", "model"] + + # find the field schema + field_schema = schema["schema"] # type: ignore + while "fields" not in field_schema: + field_schema = field_schema["schema"] # type: ignore + + field_schema = field_schema["fields"][field_name]["schema"] # type: ignore + + # if the original schema is a definition schema, replace the model schema with the field schema + if schema["type"] == "definitions": + schema["schema"] = field_schema + return schema + else: + return field_schema + + +@cache +def validator(model: type[BaseModel], field_name: str) -> SchemaValidator: + return SchemaValidator(find_field_schema(model, field_name)) + + +def validate_model_field(model: type[BaseModel], field_name: str, value: Any) -> Any: + return validator(model, field_name).validate_python(value) + + # These utility functions are tightly coupled to the config classes below in order to make the process of raising # NotAMatch exceptions as easy and consistent as possible. @@ -123,12 +157,15 @@ def _get_config_or_raise( raise NotAMatch(config_class, f"unable to load config file: {config_path}") from e -def _validate_class_names( +def _get_class_name_from_config( config_class: type, config_path: Path, - valid_class_names: set[str], -) -> None: - """Raise NotAMatch if the config file is missing or does not contain a valid class name.""" +) -> str: + """Load the config file and return the class name. + + Raises: + NotAMatch if the config file is missing or does not contain a valid class name. + """ config = _get_config_or_raise(config_class, config_path) @@ -142,36 +179,48 @@ def _validate_class_names( except Exception as e: raise NotAMatch(config_class, f"unable to determine class name from config file: {config_path}") from e - if config_class_name not in valid_class_names: - raise NotAMatch(config_class, f"model class is not one of {valid_class_names}, got {config_class_name}") + if not isinstance(config_class_name, str): + raise NotAMatch(config_class, f"_class_name or architectures field is not a string: {config_class_name}") + return config_class_name -def _validate_overrides( - config_class: type, - provided_overrides: dict[str, Any], - valid_overrides: dict[str, Any], + +def _validate_class_name(config_class: type[BaseModel], config_path: Path, expected: set[str]) -> None: + """Check if the class name in the config file matches the expected class names. + + Args: + config_class: The config class that is being tested. + config_path: The path to the config file. + expected: The expected class names.""" + + class_name = _get_class_name_from_config(config_class, config_path) + if class_name not in expected: + raise NotAMatch(config_class, f"invalid class name from config: {class_name}") + + +def _validate_override_fields( + config_class: type[BaseModel], + override_fields: dict[str, Any], ) -> None: - """Check if the provided overrides match the valid overrides for this config class. + """Check if the provided override fields are valid for the config class. Args: config_class: The config class that is being tested. - provided_overrides: The overrides provided by the user. - valid_overrides: The overrides that are valid for this config class. + override_fields: The override fields provided by the user. Raises: - NotAMatch if any override does not match the allowed value. + NotAMatch if any override field is invalid for the config. """ - for key, value in valid_overrides.items(): - if key not in provided_overrides: - continue - if provided_overrides[key] != value: - raise NotAMatch( - config_class, - f"override {key}={provided_overrides[key]} does not match required value {key}={value}", - ) + for field_name, override_value in override_fields.items(): + if field_name not in config_class.model_fields: + raise NotAMatch(config_class, f"unknown override field: {field_name}") + try: + validate_model_field(config_class, field_name, override_value) + except ValidationError as e: + raise NotAMatch(config_class, f"invalid override for field '{field_name}': {e}") from e -def _raise_if_not_file( +def _validate_is_file( config_class: type, mod: ModelOnDisk, ) -> None: @@ -180,7 +229,7 @@ def _raise_if_not_file( raise NotAMatch(config_class, "model path is not a file") -def _raise_if_not_dir( +def _validate_is_dir( config_class: type, mod: ModelOnDisk, ) -> None: @@ -346,7 +395,10 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: class CheckpointConfigBase(ABC, BaseModel): """Base class for checkpoint-style models.""" - config_path: str | None = Field(None, description="Path to the config for this model, if any.") + config_path: str | None = Field( + description="Path to the config for this model, if any.", + default=None, + ) converted_at: float | None = Field( description="When this model was last converted to diffusers", default_factory=time.time, @@ -365,66 +417,57 @@ class T5EncoderConfig(ModelConfigBase): type: Literal[ModelType.T5Encoder] = Field(default=ModelType.T5Encoder) format: Literal[ModelFormat.T5Encoder] = Field(default=ModelFormat.T5Encoder) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.T5Encoder, - "format": ModelFormat.T5Encoder, - } - - VALID_CLASS_NAMES: ClassVar = { - "T5EncoderModel", - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_dir(cls, mod) + _validate_is_dir(cls, mod) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_override_fields(cls, fields) - _validate_class_names(cls, mod.path / "text_encoder_2" / "config.json", cls.VALID_CLASS_NAMES) + _validate_class_name(cls, mod.path / "config.json", {"T5EncoderModel"}) - # Heuristic: Look for the presence of the unquantized config file (not present for bnb-quantized models) + cls._validate_has_unquantized_config_file(mod) + + return cls(**fields) + + @classmethod + def _validate_has_unquantized_config_file(cls, mod: ModelOnDisk) -> None: has_unquantized_config = (mod.path / "text_encoder_2" / "model.safetensors.index.json").exists() if not has_unquantized_config: raise NotAMatch(cls, "missing text_encoder_2/model.safetensors.index.json") - return cls(**fields) - class T5EncoderBnbQuantizedLlmInt8bConfig(ModelConfigBase): base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) type: Literal[ModelType.T5Encoder] = Field(default=ModelType.T5Encoder) format: Literal[ModelFormat.BnbQuantizedLlmInt8b] = Field(default=ModelFormat.BnbQuantizedLlmInt8b) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.T5Encoder, - "format": ModelFormat.BnbQuantizedLlmInt8b, - } - - VALID_CLASS_NAMES: ClassVar = { - "T5EncoderModel", - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_dir(cls, mod) - - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_is_dir(cls, mod) - # Heuristic: Look for the T5EncoderModel class name in the config - _validate_class_names(cls, mod.path / "text_encoder_2" / "config.json", cls.VALID_CLASS_NAMES) + _validate_override_fields(cls, fields) - # Heuristic: look for the quantization in the filename name - filename_looks_like_bnb = any(x for x in mod.weight_files() if "llm_int8" in x.as_posix()) + _validate_class_name(cls, mod.path / "config.json", {"T5EncoderModel"}) - # Heuristic: Look for the presence of "SCB" suffixes in state dict keys - has_scb_key_suffix = mod.has_keys_ending_with("SCB") + cls._validate_filename_looks_like_bnb_quantized(mod) - if not filename_looks_like_bnb and not has_scb_key_suffix: - raise NotAMatch(cls, "missing bnb quantization indicators") + cls._validate_model_looks_like_bnb_quantized(mod) return cls(**fields) + @classmethod + def _validate_filename_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: + filename_looks_like_bnb = any(x for x in mod.weight_files() if "llm_int8" in x.as_posix()) + if not filename_looks_like_bnb: + raise NotAMatch(cls, "filename does not look like bnb quantized llm_int8") + + @classmethod + def _validate_model_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: + has_scb_key_suffix = mod.has_keys_ending_with("SCB") + if not has_scb_key_suffix: + raise NotAMatch(cls, "state dict does not look like bnb quantized llm_int8") + class LoRAConfigBase(ABC, BaseModel): """Base class for LoRA models.""" @@ -453,36 +496,40 @@ class LoRAOmiConfig(LoRAConfigBase, ModelConfigBase): base: Literal[BaseModelType.Flux, BaseModelType.StableDiffusionXL] = Field() format: Literal[ModelFormat.OMI] = Field(default=ModelFormat.OMI) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.LoRA, - "format": ModelFormat.OMI, - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - # OMI LoRAs are always files - _raise_if_not_file(cls, mod) + _validate_is_file(cls, mod) + + _validate_override_fields(cls, fields) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + cls._validate_is_not_controllora_or_diffusers(mod) - # Heuristic: differential diagnosis vs ControlLoRA and Diffusers - if get_flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: - raise NotAMatch(cls, "model is a ControlLoRA or Diffusers LoRA") + cls._validate_metadata_looks_like_omi(mod) - # Heuristic: Look for OMI LoRA metadata + base = fields.get("base") or cls._get_base_or_raise(mod) + + return cls(**fields, base=base) + + @classmethod + def _validate_is_not_controllora_or_diffusers(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model is a ControlLoRA or Diffusers LoRA.""" + flux_format = get_flux_lora_format(mod) + if flux_format in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: + raise NotAMatch(cls, "model looks like ControlLoRA or Diffusers LoRA") + + @classmethod + def _validate_metadata_looks_like_omi(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model metadata does not look like an OMI LoRA.""" metadata = mod.metadata() - is_omi_lora_heuristic = ( + + metadata_looks_like_omi_lora = ( bool(metadata.get("modelspec.sai_model_spec")) and metadata.get("ot_branch") == "omi_format" and metadata.get("modelspec.architecture", "").split("/")[1].lower() == "lora" ) - if not is_omi_lora_heuristic: - raise NotAMatch(cls, "model does not match OMI LoRA heuristics") - - base = fields.get("base") or cls._get_base_or_raise(mod) - - return cls(**fields, base=base) + if not metadata_looks_like_omi_lora: + raise NotAMatch(cls, "metadata does not look like OMI LoRA") @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> Literal[BaseModelType.Flux, BaseModelType.StableDiffusionXL]: @@ -512,21 +559,20 @@ class LoRALyCORISConfig(LoRAConfigBase, ModelConfigBase): type: Literal[ModelType.LoRA] = Field(default=ModelType.LoRA) format: Literal[ModelFormat.LyCORIS] = Field(default=ModelFormat.LyCORIS) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.LoRA, - "format": ModelFormat.LyCORIS, - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_file(cls, mod) + _validate_is_file(cls, mod) + + _validate_override_fields(cls, fields) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + cls._validate_is_not_controllora_or_diffusers(mod) - # Heuristic: differential diagnosis vs ControlLoRA and Diffusers - if get_flux_lora_format(mod) in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: - raise NotAMatch(cls, "model is a ControlLoRA or Diffusers LoRA") + cls._validate_looks_like_lora(mod) + return cls(**fields) + + @classmethod + def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: # Note: Existence of these key prefixes/suffixes does not guarantee that this is a LoRA. # Some main models have these keys, likely due to the creator merging in a LoRA. has_key_with_lora_prefix = mod.has_keys_starting_with( @@ -551,7 +597,12 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: if not has_key_with_lora_prefix and not has_key_with_lora_suffix: raise NotAMatch(cls, "model does not match LyCORIS LoRA heuristics") - return cls(**fields) + @classmethod + def _validate_is_not_controllora_or_diffusers(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model is a ControlLoRA or Diffusers LoRA.""" + flux_format = get_flux_lora_format(mod) + if flux_format in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: + raise NotAMatch(cls, "model looks like ControlLoRA or Diffusers LoRA") @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> LoRALyCORIS_SupportedBases: @@ -591,15 +642,21 @@ class ControlLoRALyCORISConfig(ControlAdapterConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_file(cls, mod) + _validate_is_file(cls, mod) + + _validate_override_fields(cls, fields) + cls._validate_looks_like_control_lora(mod) + + return cls(**fields) + + @classmethod + def _validate_looks_like_control_lora(cls, mod: ModelOnDisk) -> None: state_dict = mod.load_state_dict() if not is_state_dict_likely_flux_control(state_dict): raise NotAMatch(cls, "model state dict does not look like a Flux Control LoRA") - return cls(**fields) - ControlLoRADiffusers_SupportedBases: TypeAlias = Literal[BaseModelType.Flux] @@ -618,28 +675,31 @@ class LoRADiffusersConfig(LoRAConfigBase, ModelConfigBase): base: LoRADiffusers_SupportedBases = Field() format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.LoRA, - "format": ModelFormat.Diffusers, - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - # Diffusers-style models always directories - _raise_if_not_dir(cls, mod) + _validate_is_dir(cls, mod) + + _validate_override_fields(cls, fields) + + cls._validate_looks_like_diffusers_lora(mod) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + cls._validate_has_lora_weight_file(mod) - is_flux_lora_diffusers = get_flux_lora_format(mod) is FluxLoRAFormat.Diffusers + return cls(**fields) + + @classmethod + def _validate_looks_like_diffusers_lora(cls, mod: ModelOnDisk) -> None: + flux_lora_format = get_flux_lora_format(mod) + if flux_lora_format is not FluxLoRAFormat.Diffusers: + raise NotAMatch(cls, "model does not look like a FLUX Diffusers LoRA") + @classmethod + def _validate_has_lora_weight_file(cls, mod: ModelOnDisk) -> None: suffixes = ["bin", "safetensors"] weight_files = [mod.path / f"pytorch_lora_weights.{sfx}" for sfx in suffixes] has_lora_weight_file = any(wf.exists() for wf in weight_files) - - if not is_flux_lora_diffusers and not has_lora_weight_file: - raise NotAMatch(cls, "model does not match Diffusers LoRA heuristics") - - return cls(**fields) + if not has_lora_weight_file: + raise NotAMatch(cls, "missing pytorch_lora_weights.bin or pytorch_lora_weights.safetensors") VAECheckpointConfig_SupportedBases: TypeAlias = Literal[ @@ -657,11 +717,6 @@ class VAECheckpointConfig(CheckpointConfigBase, ModelConfigBase): type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.VAE, - "format": ModelFormat.Checkpoint, - } - REGEX_TO_BASE: ClassVar[dict[str, VAECheckpointConfig_SupportedBases]] = { r"xl": BaseModelType.StableDiffusionXL, r"sd2": BaseModelType.StableDiffusion2, @@ -671,16 +726,20 @@ class VAECheckpointConfig(CheckpointConfigBase, ModelConfigBase): @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_file(cls, mod) + _validate_is_file(cls, mod) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_override_fields(cls, fields) - if not mod.has_keys_starting_with({"encoder.conv_in", "decoder.conv_in"}): - raise NotAMatch(cls, "model does not match Checkpoint VAE heuristics") + cls._validate_looks_like_vae(mod) base = fields.get("base") or cls._get_base_or_raise(mod) return cls(**fields, base=base) + @classmethod + def _validate_looks_like_vae(cls, mod: ModelOnDisk) -> None: + if not mod.has_keys_starting_with({"encoder.conv_in", "decoder.conv_in"}): + raise NotAMatch(cls, "model does not match Checkpoint VAE heuristics") + @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> VAECheckpointConfig_SupportedBases: # Heuristic: VAEs of all architectures have a similar structure; the best we can do is guess based on name @@ -704,22 +763,13 @@ class VAEDiffusersConfig(DiffusersConfigBase, ModelConfigBase): type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.VAE, - "format": ModelFormat.Diffusers, - } - VALID_CLASS_NAMES: ClassVar = { - "AutoencoderKL", - "AutoencoderTiny", - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_dir(cls, mod) + _validate_is_dir(cls, mod) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_override_fields(cls, fields) - _validate_class_names(cls, mod.path / "config.json", cls.VALID_CLASS_NAMES) + _validate_class_name(cls, mod.path / "config.json", {"AutoencoderKL", "AutoencoderTiny"}) base = fields.get("base") or cls._get_base_or_raise(mod) return cls(**fields, base=base) @@ -770,23 +820,13 @@ class ControlNetDiffusersConfig(DiffusersConfigBase, ControlAdapterConfigBase, M type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.ControlNet, - "format": ModelFormat.Diffusers, - } - - VALID_CLASS_NAMES: ClassVar = { - "ControlNetModel", - "FluxControlNetModel", - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_dir(cls, mod) + _validate_is_dir(cls, mod) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_override_fields(cls, fields) - _validate_class_names(cls, mod.path / "config.json", cls.VALID_CLASS_NAMES) + _validate_class_name(cls, mod.path / "config.json", {"ControlNetModel", "FluxControlNetModel"}) base = fields.get("base") or cls._get_base_or_raise(mod) @@ -829,17 +869,20 @@ class ControlNetCheckpointConfig(CheckpointConfigBase, ControlAdapterConfigBase, type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.ControlNet, - "format": ModelFormat.Checkpoint, - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_file(cls, mod) + _validate_is_file(cls, mod) + + _validate_override_fields(cls, fields) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + cls._validate_looks_like_controlnet(mod) + base = fields.get("base") or cls._get_base_or_raise(mod) + + return cls(**fields, base=base) + + @classmethod + def _validate_looks_like_controlnet(cls, mod: ModelOnDisk) -> None: if not mod.has_keys_starting_with( { "controlnet", @@ -855,10 +898,6 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: ): raise NotAMatch(cls, "state dict does not look like a ControlNet checkpoint") - base = fields.get("base") or cls._get_base_or_raise(mod) - - return cls(**fields, base=base) - @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> ControlNetCheckpoint_SupportedBases: state_dict = mod.load_state_dict() @@ -970,20 +1009,11 @@ class TextualInversionFileConfig(TextualInversionConfigBase, ModelConfigBase): format: Literal[ModelFormat.EmbeddingFile] = Field(default=ModelFormat.EmbeddingFile) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.TextualInversion, - "format": ModelFormat.EmbeddingFile, - } - - @classmethod - def get_tag(cls) -> Tag: - return Tag(f"{ModelType.TextualInversion.value}.{ModelFormat.EmbeddingFile.value}") - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_file(cls, mod) + _validate_is_file(cls, mod) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_override_fields(cls, fields) if not cls._file_looks_like_embedding(mod): raise NotAMatch(cls, "model does not look like a textual inversion embedding file") @@ -997,20 +1027,11 @@ class TextualInversionFolderConfig(TextualInversionConfigBase, ModelConfigBase): format: Literal[ModelFormat.EmbeddingFolder] = Field(default=ModelFormat.EmbeddingFolder) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.TextualInversion, - "format": ModelFormat.EmbeddingFolder, - } - - @classmethod - def get_tag(cls) -> Tag: - return Tag(f"{ModelType.TextualInversion.value}.{ModelFormat.EmbeddingFolder.value}") - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_dir(cls, mod) + _validate_is_dir(cls, mod) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_override_fields(cls, fields) for p in mod.weight_files(): if cls._file_looks_like_embedding(mod, p): @@ -1105,28 +1126,31 @@ class IPAdapterInvokeAIConfig(IPAdapterConfigBase, ModelConfigBase): # time. Need to go through the history to make sure I'm understanding this fully. image_encoder_model_id: str = Field() - VALID_OVERRIDES: ClassVar = { - "type": ModelType.IPAdapter, - "format": ModelFormat.InvokeAI, - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_dir(cls, mod) + _validate_is_dir(cls, mod) + + _validate_override_fields(cls, fields) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + cls._validate_has_weights_file(mod) + cls._validate_has_image_encoder_metadata_file(mod) + + base = fields.get("base") or cls._get_base_or_raise(mod) + return cls(**fields, base=base) + + @classmethod + def _validate_has_weights_file(cls, mod: ModelOnDisk) -> None: weights_file = mod.path / "ip_adapter.bin" if not weights_file.exists(): raise NotAMatch(cls, "missing ip_adapter.bin weights file") + @classmethod + def _validate_has_image_encoder_metadata_file(cls, mod: ModelOnDisk) -> None: image_encoder_metadata_file = mod.path / "image_encoder.txt" if not image_encoder_metadata_file.exists(): raise NotAMatch(cls, "missing image_encoder.txt metadata file") - base = fields.get("base") or cls._get_base_or_raise(mod) - return cls(**fields, base=base) - @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterInvokeAI_SupportedBases: state_dict = mod.load_state_dict() @@ -1161,17 +1185,19 @@ class IPAdapterCheckpointConfig(IPAdapterConfigBase, ModelConfigBase): base: IPAdapterCheckpoint_SupportedBases = Field() format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.IPAdapter, - "format": ModelFormat.Checkpoint, - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_file(cls, mod) + _validate_is_file(cls, mod) + + _validate_override_fields(cls, fields) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + cls._validate_looks_like_ip_adapter(mod) + base = fields.get("base") or cls._get_base_or_raise(mod) + return cls(**fields, base=base) + + @classmethod + def _validate_looks_like_ip_adapter(cls, mod: ModelOnDisk) -> None: if not mod.has_keys_starting_with( { "image_proj.", @@ -1182,9 +1208,6 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: ): raise NotAMatch(cls, "model does not match Checkpoint IP Adapter heuristics") - base = fields.get("base") or cls._get_base_or_raise(mod) - return cls(**fields, base=base) - @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterCheckpoint_SupportedBases: state_dict = mod.load_state_dict() @@ -1215,14 +1238,8 @@ class CLIPEmbedDiffusersConfig(DiffusersConfigBase): type: Literal[ModelType.CLIPEmbed] = Field(default=ModelType.CLIPEmbed) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - VALID_CLASS_NAMES: ClassVar = { - "CLIPModel", - "CLIPTextModel", - "CLIPTextModelWithProjection", - } - @classmethod - def get_clip_variant_type(cls, config: dict[str, Any]) -> ClipVariantType | None: + def _get_clip_variant_type(cls, config: dict[str, Any]) -> ClipVariantType | None: try: hidden_size = config.get("hidden_size") match hidden_size: @@ -1241,68 +1258,63 @@ class CLIPGEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): variant: Literal[ClipVariantType.G] = Field(default=ClipVariantType.G) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.CLIPEmbed, - "format": ModelFormat.Diffusers, - "variant": ClipVariantType.G, - } - @classmethod def get_tag(cls) -> Tag: return Tag(f"{ModelType.CLIPEmbed.value}.{ModelFormat.Diffusers.value}.{ClipVariantType.G.value}") @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_dir(cls, mod) + _validate_is_dir(cls, mod) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_override_fields(cls, fields) - config_path = mod.path / "config.json" + _validate_class_name( + cls, mod.path / "config.json", {"CLIPModel", "CLIPTextModel", "CLIPTextModelWithProjection"} + ) - _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) + cls._validate_clip_g_variant(mod) - config = _get_config_or_raise(cls, config_path) + return cls(**fields) - clip_variant = cls.get_clip_variant_type(config) + @classmethod + def _validate_clip_g_variant(cls, mod: ModelOnDisk) -> None: + config = _get_config_or_raise(cls, mod.path / "config.json") + clip_variant = cls._get_clip_variant_type(config) if clip_variant is not ClipVariantType.G: raise NotAMatch(cls, "model does not match CLIP-G heuristics") - return cls(**fields) - class CLIPLEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): """Model config for CLIP-L Embeddings.""" variant: Literal[ClipVariantType.L] = Field(default=ClipVariantType.L) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.CLIPEmbed, - "format": ModelFormat.Diffusers, - "variant": ClipVariantType.L, - } - @classmethod def get_tag(cls) -> Tag: return Tag(f"{ModelType.CLIPEmbed.value}.{ModelFormat.Diffusers.value}.{ClipVariantType.L.value}") @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_dir(cls, mod) + _validate_is_dir(cls, mod) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_override_fields(cls, fields) - config_path = mod.path / "config.json" + _validate_class_name( + cls, mod.path / "config.json", {"CLIPModel", "CLIPTextModel", "CLIPTextModelWithProjection"} + ) - _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) + cls._validate_clip_l_variant(mod) - config = _get_config_or_raise(cls, config_path) - clip_variant = cls.get_clip_variant_type(config) + return cls(**fields) - if clip_variant is not ClipVariantType.L: - raise NotAMatch(cls, "model does not match CLIP-L heuristics") + @classmethod + def _validate_clip_l_variant(cls, mod: ModelOnDisk) -> None: + config = _get_config_or_raise(cls, mod.path / "config.json") + clip_variant = cls._get_clip_variant_type(config) - return cls(**fields) + if clip_variant is not ClipVariantType.L: + raise NotAMatch(cls, "model does not match CLIP-G heuristics") class CLIPVisionDiffusersConfig(DiffusersConfigBase, ModelConfigBase): @@ -1312,24 +1324,13 @@ class CLIPVisionDiffusersConfig(DiffusersConfigBase, ModelConfigBase): type: Literal[ModelType.CLIPVision] = Field(default=ModelType.CLIPVision) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.CLIPVision, - "format": ModelFormat.Diffusers, - } - - VALID_CLASS_NAMES: ClassVar = { - "CLIPVisionModelWithProjection", - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_dir(cls, mod) + _validate_is_dir(cls, mod) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_override_fields(cls, fields) - config_path = mod.path / "config.json" - - _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) + _validate_class_name(cls, mod.path / "config.json", {"CLIPVisionModelWithProjection"}) return cls(**fields) @@ -1347,24 +1348,13 @@ class T2IAdapterConfig(DiffusersConfigBase, ControlAdapterConfigBase, ModelConfi type: Literal[ModelType.T2IAdapter] = Field(default=ModelType.T2IAdapter) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.T2IAdapter, - "format": ModelFormat.Diffusers, - } - - VALID_CLASS_NAMES: ClassVar = { - "T2IAdapter", - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_dir(cls, mod) - - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_is_dir(cls, mod) - config_path = mod.path / "config.json" + _validate_override_fields(cls, fields) - _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) + _validate_class_name(cls, mod.path / "config.json", {"T2IAdapter"}) base = fields.get("base") or cls._get_base_or_raise(mod) @@ -1392,17 +1382,18 @@ class SpandrelImageToImageConfig(ModelConfigBase): type: Literal[ModelType.SpandrelImageToImage] = Field(default=ModelType.SpandrelImageToImage) format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.SpandrelImageToImage, - "format": ModelFormat.Checkpoint, - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_file(cls, mod) + _validate_is_file(cls, mod) + + _validate_override_fields(cls, fields) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + cls._validate_spandrel_loads_model(mod) + return cls(**fields) + + @classmethod + def _validate_spandrel_loads_model(cls, mod: ModelOnDisk) -> None: try: # It would be nice to avoid having to load the Spandrel model from disk here. A couple of options were # explored to avoid this: @@ -1413,7 +1404,6 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: # This logic is not exposed in spandrel's public API. We could copy the logic here, but then we have to # maintain it, and the risk of false positive detections is higher. SpandrelImageToImageModel.load_from_file(mod.path) - return cls(**fields) except Exception as e: raise NotAMatch(cls, "model does not match SpandrelImageToImage heuristics") from e @@ -1425,24 +1415,13 @@ class SigLIPConfig(DiffusersConfigBase, ModelConfigBase): format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.SigLIP, - "format": ModelFormat.Diffusers, - } - - VALID_CLASS_NAMES: ClassVar = { - "SiglipModel", - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_dir(cls, mod) - - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_is_dir(cls, mod) - config_path = mod.path / "config.json" + _validate_override_fields(cls, fields) - _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) + _validate_class_name(cls, mod.path / "config.json", {"SiglipModel"}) return cls(**fields) @@ -1454,16 +1433,11 @@ class FluxReduxConfig(ModelConfigBase): format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.FluxRedux, - "format": ModelFormat.Checkpoint, - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_file(cls, mod) + _validate_is_file(cls, mod) - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_override_fields(cls, fields) if not is_state_dict_likely_flux_redux(mod.load_state_dict()): raise NotAMatch(cls, "model does not match FLUX Tools Redux heuristics") @@ -1478,24 +1452,13 @@ class LlavaOnevisionConfig(DiffusersConfigBase, ModelConfigBase): base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) variant: Literal[ModelVariantType.Normal] = Field(default=ModelVariantType.Normal) - VALID_OVERRIDES: ClassVar = { - "type": ModelType.LlavaOnevision, - "format": ModelFormat.Diffusers, - } - - VALID_CLASS_NAMES: ClassVar = { - "LlavaOnevisionForConditionalGeneration", - } - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _raise_if_not_dir(cls, mod) - - _validate_overrides(cls, fields, cls.VALID_OVERRIDES) + _validate_is_dir(cls, mod) - config_path = mod.path / "config.json" + _validate_override_fields(cls, fields) - _validate_class_names(cls, config_path, cls.VALID_CLASS_NAMES) + _validate_class_name(cls, mod.path / "config.json", {"LlavaOnevisionForConditionalGeneration"}) return cls(**fields) From 5f45a9cc023b85e7c6ad33c4a1f92c4d4f8a560a Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 25 Sep 2025 23:02:44 +1000 Subject: [PATCH 32/62] feat(mm): wip port of main models to new api --- invokeai/backend/model_manager/config.py | 232 ++++++++++++++++++++--- 1 file changed, 209 insertions(+), 23 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 805202ef9b6..8efb8857eef 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -1094,7 +1094,6 @@ class MainGGUFCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbe BaseModelType.StableDiffusion3, BaseModelType.StableDiffusionXL, BaseModelType.StableDiffusionXLRefiner, - BaseModelType.Flux, BaseModelType.CogView4, ] @@ -1104,6 +1103,157 @@ class MainDiffusersConfig(DiffusersConfigBase, MainConfigBase, LegacyProbeMixin, base: MainDiffusers_SupportedBases = Field() + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _validate_is_dir(cls, mod) + + _validate_override_fields(cls, fields) + + _validate_class_name( + cls, + mod.path / "config.json", + { + "StableDiffusionPipeline", + "StableDiffusionInpaintPipeline", + "StableDiffusionXLPipeline", + "StableDiffusionXLImg2ImgPipeline", + "StableDiffusionXLInpaintPipeline", + "StableDiffusion3Pipeline", + "LatentConsistencyModelPipeline", + "SD3Transformer2DModel", + "CogView4Pipeline", + }, + ) + + base = fields.get("base") or cls._get_base_or_raise(mod) + + return cls(**fields, base=base) + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> MainDiffusers_SupportedBases: + # Handle pipelines with a UNet (i.e SD 1.x, SD2, SDXL). + unet_config_path = mod.path / "unet" / "config.json" + if unet_config_path.exists(): + with open(unet_config_path) as file: + unet_conf = json.load(file) + cross_attention_dim = unet_conf.get("cross_attention_dim") + match cross_attention_dim: + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case 1280: + return BaseModelType.StableDiffusionXLRefiner + case 2048: + return BaseModelType.StableDiffusionXL + case _: + raise NotAMatch(cls, f"unrecognized cross_attention_dim {cross_attention_dim}") + + # Handle pipelines with a transformer (i.e. SD3). + transformer_config_path = mod.path / "transformer" / "config.json" + if transformer_config_path.exists(): + class_name = _get_class_name_from_config(cls, transformer_config_path) + match class_name: + case "SD3Transformer2DModel": + return BaseModelType.StableDiffusion3 + case "CogView4Transformer2DModel": + return BaseModelType.CogView4 + case _: + raise NotAMatch(cls, f"unrecognized transformer class name {class_name}") + + raise NotAMatch(cls, "unable to determine base type") + + @classmethod + def _get_scheduler_prediction_type_or_raise(cls, mod: ModelOnDisk, base: BaseModelType) -> SchedulerPredictionType: + if base not in { + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, + }: + raise ValueError(f"Attempted to get scheduler prediction type for non-UNet model base '{base}'") + + scheduler_conf = _get_config_or_raise(cls, mod.path / "scheduler" / "scheduler_config.json") + + # TODO(psyche): Is epsilon the right default or should we raise if it's not present? + prediction_type = scheduler_conf.get("prediction_type", "epsilon") + + match prediction_type: + case "v_prediction": + return SchedulerPredictionType.VPrediction + case "epsilon": + return SchedulerPredictionType.Epsilon + case _: + raise NotAMatch(cls, f"unrecognized scheduler prediction type {prediction_type}") + + @classmethod + def _get_variant_or_raise(cls, mod: ModelOnDisk, base: BaseModelType) -> ModelVariantType: + if base not in { + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, + }: + raise ValueError(f"Attempted to get variant for model base '{base}' but it does not have variants") + + unet_config = _get_config_or_raise(cls, mod.path / "unet" / "config.json") + in_channels = unet_config.get("in_channels") + + match in_channels: + case 4: + return ModelVariantType.Normal + case 5: + if base is not BaseModelType.StableDiffusion2: + raise NotAMatch(cls, "in_channels=5 is only valid for Stable Diffusion 2 models") + return ModelVariantType.Depth + case 9: + return ModelVariantType.Inpaint + case _: + raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels}") + + @classmethod + def _get_submodels_or_raise(cls, mod: ModelOnDisk, base: BaseModelType) -> dict[SubModelType, SubmodelDefinition]: + if base is not BaseModelType.StableDiffusion3: + raise ValueError(f"Attempted to get submodels for non-SD3 model base '{base}'") + + # Example: https://huggingface.co/stabilityai/stable-diffusion-3.5-medium/blob/main/model_index.json + config = _get_config_or_raise(cls, mod.path / "model_index.json") + + submodels: dict[SubModelType, SubmodelDefinition] = {} + + for key, value in config.items(): + # Anything that starts with an underscore is top-level metadata, not a submodel + if key.startswith("_") or not (isinstance(value, list) and len(value) == 2): + continue + # The key is something like "transformer" and is a submodel - it will be in a dir of the same name. + # The value value is something like ["diffusers", "SD3Transformer2DModel"] + _library_name, class_name = value + + match class_name: + case "CLIPTextModelWithProjection": + model_type = ModelType.CLIPEmbed + path_or_prefix = (mod.path / key).resolve().as_posix() + + # We need to read the config to determine the variant of the CLIP model. + clip_embed_config = _get_config_or_raise(cls, mod.path / key / "config.json") + variant = _get_clip_variant_type_from_config(clip_embed_config) + submodels[SubModelType(key)] = SubmodelDefinition( + path_or_prefix=path_or_prefix, + model_type=model_type, + variant=variant, + ) + case "SD3Transformer2DModel": + model_type = ModelType.Main + path_or_prefix = (mod.path / key).resolve().as_posix() + variant = None + submodels[SubModelType(key)] = SubmodelDefinition( + path_or_prefix=path_or_prefix, + model_type=model_type, + variant=variant, + ) + case _: + pass + + return submodels + class IPAdapterConfigBase(ABC, BaseModel): type: Literal[ModelType.IPAdapter] = Field(default=ModelType.IPAdapter) @@ -1231,6 +1381,20 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterCheckpoint_SupportedBa raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") +def _get_clip_variant_type_from_config(config: dict[str, Any]) -> ClipVariantType | None: + try: + hidden_size = config.get("hidden_size") + match hidden_size: + case 1280: + return ClipVariantType.G + case 768: + return ClipVariantType.L + case _: + return None + except Exception: + return None + + class CLIPEmbedDiffusersConfig(DiffusersConfigBase): """Model config for Clip Embeddings.""" @@ -1238,20 +1402,6 @@ class CLIPEmbedDiffusersConfig(DiffusersConfigBase): type: Literal[ModelType.CLIPEmbed] = Field(default=ModelType.CLIPEmbed) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - @classmethod - def _get_clip_variant_type(cls, config: dict[str, Any]) -> ClipVariantType | None: - try: - hidden_size = config.get("hidden_size") - match hidden_size: - case 1280: - return ClipVariantType.G - case 768: - return ClipVariantType.L - case _: - return None - except Exception: - return None - class CLIPGEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): """Model config for CLIP-G Embeddings.""" @@ -1269,7 +1419,13 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) _validate_class_name( - cls, mod.path / "config.json", {"CLIPModel", "CLIPTextModel", "CLIPTextModelWithProjection"} + cls, + mod.path / "config.json", + { + "CLIPModel", + "CLIPTextModel", + "CLIPTextModelWithProjection", + }, ) cls._validate_clip_g_variant(mod) @@ -1279,7 +1435,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_clip_g_variant(cls, mod: ModelOnDisk) -> None: config = _get_config_or_raise(cls, mod.path / "config.json") - clip_variant = cls._get_clip_variant_type(config) + clip_variant = _get_clip_variant_type_from_config(config) if clip_variant is not ClipVariantType.G: raise NotAMatch(cls, "model does not match CLIP-G heuristics") @@ -1301,7 +1457,13 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) _validate_class_name( - cls, mod.path / "config.json", {"CLIPModel", "CLIPTextModel", "CLIPTextModelWithProjection"} + cls, + mod.path / "config.json", + { + "CLIPModel", + "CLIPTextModel", + "CLIPTextModelWithProjection", + }, ) cls._validate_clip_l_variant(mod) @@ -1311,7 +1473,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_clip_l_variant(cls, mod: ModelOnDisk) -> None: config = _get_config_or_raise(cls, mod.path / "config.json") - clip_variant = cls._get_clip_variant_type(config) + clip_variant = _get_clip_variant_type_from_config(config) if clip_variant is not ClipVariantType.L: raise NotAMatch(cls, "model does not match CLIP-G heuristics") @@ -1330,7 +1492,13 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - _validate_class_name(cls, mod.path / "config.json", {"CLIPVisionModelWithProjection"}) + _validate_class_name( + cls, + mod.path / "config.json", + { + "CLIPVisionModelWithProjection", + }, + ) return cls(**fields) @@ -1354,7 +1522,13 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - _validate_class_name(cls, mod.path / "config.json", {"T2IAdapter"}) + _validate_class_name( + cls, + mod.path / "config.json", + { + "T2IAdapter", + }, + ) base = fields.get("base") or cls._get_base_or_raise(mod) @@ -1421,7 +1595,13 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - _validate_class_name(cls, mod.path / "config.json", {"SiglipModel"}) + _validate_class_name( + cls, + mod.path / "config.json", + { + "SiglipModel", + }, + ) return cls(**fields) @@ -1458,7 +1638,13 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - _validate_class_name(cls, mod.path / "config.json", {"LlavaOnevisionForConditionalGeneration"}) + _validate_class_name( + cls, + mod.path / "config.json", + { + "LlavaOnevisionForConditionalGeneration", + }, + ) return cls(**fields) From 7765c831d36978a73248d54d28ed73f913b59310 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 25 Sep 2025 23:08:40 +1000 Subject: [PATCH 33/62] feat(mm): wip port of main models to new api --- invokeai/backend/model_manager/config.py | 39 +++++++++++++++--------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 8efb8857eef..cb99b09eded 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -1164,7 +1164,9 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> MainDiffusers_SupportedBases: raise NotAMatch(cls, "unable to determine base type") @classmethod - def _get_scheduler_prediction_type_or_raise(cls, mod: ModelOnDisk, base: BaseModelType) -> SchedulerPredictionType: + def _get_scheduler_prediction_type_or_raise( + cls, mod: ModelOnDisk, base: MainDiffusers_SupportedBases + ) -> SchedulerPredictionType: if base not in { BaseModelType.StableDiffusion1, BaseModelType.StableDiffusion2, @@ -1186,7 +1188,7 @@ def _get_scheduler_prediction_type_or_raise(cls, mod: ModelOnDisk, base: BaseMod raise NotAMatch(cls, f"unrecognized scheduler prediction type {prediction_type}") @classmethod - def _get_variant_or_raise(cls, mod: ModelOnDisk, base: BaseModelType) -> ModelVariantType: + def _get_variant_or_raise(cls, mod: ModelOnDisk, base: MainDiffusers_SupportedBases) -> ModelVariantType: if base not in { BaseModelType.StableDiffusion1, BaseModelType.StableDiffusion2, @@ -1197,20 +1199,29 @@ def _get_variant_or_raise(cls, mod: ModelOnDisk, base: BaseModelType) -> ModelVa unet_config = _get_config_or_raise(cls, mod.path / "unet" / "config.json") in_channels = unet_config.get("in_channels") - match in_channels: - case 4: - return ModelVariantType.Normal - case 5: - if base is not BaseModelType.StableDiffusion2: - raise NotAMatch(cls, "in_channels=5 is only valid for Stable Diffusion 2 models") - return ModelVariantType.Depth - case 9: - return ModelVariantType.Inpaint - case _: - raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels}") + if base is BaseModelType.StableDiffusion2: + match in_channels: + case 4: + return ModelVariantType.Normal + case 9: + return ModelVariantType.Inpaint + case 5: + return ModelVariantType.Depth + case _: + raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels} for base '{base}'") + else: + match in_channels: + case 4: + return ModelVariantType.Normal + case 9: + return ModelVariantType.Inpaint + case _: + raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels} for base '{base}'") @classmethod - def _get_submodels_or_raise(cls, mod: ModelOnDisk, base: BaseModelType) -> dict[SubModelType, SubmodelDefinition]: + def _get_submodels_or_raise( + cls, mod: ModelOnDisk, base: MainDiffusers_SupportedBases + ) -> dict[SubModelType, SubmodelDefinition]: if base is not BaseModelType.StableDiffusion3: raise ValueError(f"Attempted to get submodels for non-SD3 model base '{base}'") From 3a44fde91e49fcd74c18ad6cecbf57f41689296a Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 Sep 2025 00:11:42 +1000 Subject: [PATCH 34/62] feat(mm): wip port of main models to new api --- invokeai/backend/model_manager/config.py | 40 +++++++++++++++++++++--- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index cb99b09eded..f1b287af643 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -1126,12 +1126,29 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: ) base = fields.get("base") or cls._get_base_or_raise(mod) + if base in { + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, + }: + variant = fields.get("variant") or cls._get_variant_or_raise(mod, base) + prediction_type = fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod, base) + upcast_attention = fields.get("upcast_attention") or cls._get_upcast_attention_or_raise(base, prediction_type) + else: + variant= None + prediction_type = None + upcast_attention = False - return cls(**fields, base=base) + if base is BaseModelType.StableDiffusion3: + submodels = fields.get("submodels") or cls._get_submodels_or_raise(mod, base) + else: + submodels = None + + return cls(**fields, base=base, variant=variant, prediction_type=prediction_type, upcast_attention=upcast_attention, submodels=submodels,) @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> MainDiffusers_SupportedBases: - # Handle pipelines with a UNet (i.e SD 1.x, SD2, SDXL). + # Handle pipelines with a UNet (i.e SD 1.x, SD2.x, SDXL). unet_config_path = mod.path / "unet" / "config.json" if unet_config_path.exists(): with open(unet_config_path) as file: @@ -1172,7 +1189,7 @@ def _get_scheduler_prediction_type_or_raise( BaseModelType.StableDiffusion2, BaseModelType.StableDiffusionXL, }: - raise ValueError(f"Attempted to get scheduler prediction type for non-UNet model base '{base}'") + raise ValueError(f"Attempted to get scheduler prediction_type for non-UNet model base '{base}'") scheduler_conf = _get_config_or_raise(cls, mod.path / "scheduler" / "scheduler_config.json") @@ -1185,7 +1202,7 @@ def _get_scheduler_prediction_type_or_raise( case "epsilon": return SchedulerPredictionType.Epsilon case _: - raise NotAMatch(cls, f"unrecognized scheduler prediction type {prediction_type}") + raise NotAMatch(cls, f"unrecognized scheduler prediction_type {prediction_type}") @classmethod def _get_variant_or_raise(cls, mod: ModelOnDisk, base: MainDiffusers_SupportedBases) -> ModelVariantType: @@ -1266,6 +1283,21 @@ def _get_submodels_or_raise( return submodels + @classmethod + def _get_upcast_attention_or_raise(cls, base: MainDiffusers_SupportedBases, prediction_type: SchedulerPredictionType) -> bool: + if base not in { + BaseModelType.StableDiffusion1, + BaseModelType.StableDiffusion2, + BaseModelType.StableDiffusionXL, + }: + raise ValueError(f"Attempted to get upcast_attention flag for non-UNet model base '{base}'") + + if base is BaseModelType.StableDiffusion2 and prediction_type is SchedulerPredictionType.VPrediction: + # SD2 v-prediction models need upcast_attention to be True + return True + + return False + class IPAdapterConfigBase(ABC, BaseModel): type: Literal[ModelType.IPAdapter] = Field(default=ModelType.IPAdapter) From 69efdc3b38ac68c0c01724850820bf7d640be354 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 Sep 2025 18:35:00 +1000 Subject: [PATCH 35/62] docs(mm): add todos --- invokeai/backend/model_manager/config.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index f1b287af643..9a66a472658 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -1133,9 +1133,11 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: }: variant = fields.get("variant") or cls._get_variant_or_raise(mod, base) prediction_type = fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod, base) - upcast_attention = fields.get("upcast_attention") or cls._get_upcast_attention_or_raise(base, prediction_type) + upcast_attention = fields.get("upcast_attention") or cls._get_upcast_attention_or_raise( + base, prediction_type + ) else: - variant= None + variant = None prediction_type = None upcast_attention = False @@ -1144,7 +1146,16 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: else: submodels = None - return cls(**fields, base=base, variant=variant, prediction_type=prediction_type, upcast_attention=upcast_attention, submodels=submodels,) + return cls( + **fields, + base=base, + # TODO(psyche): figure out variant/prediction_type/upcast_attention + variant=variant, + prediction_type=prediction_type, + upcast_attention=upcast_attention, + # TODO(psyche): This is only for SD3 models - split up the config classes + submodels=submodels, + ) @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> MainDiffusers_SupportedBases: @@ -1282,9 +1293,10 @@ def _get_submodels_or_raise( return submodels - @classmethod - def _get_upcast_attention_or_raise(cls, base: MainDiffusers_SupportedBases, prediction_type: SchedulerPredictionType) -> bool: + def _get_upcast_attention_or_raise( + cls, base: MainDiffusers_SupportedBases, prediction_type: SchedulerPredictionType + ) -> bool: if base not in { BaseModelType.StableDiffusion1, BaseModelType.StableDiffusion2, @@ -1298,6 +1310,7 @@ def _get_upcast_attention_or_raise(cls, base: MainDiffusers_SupportedBases, pred return False + class IPAdapterConfigBase(ABC, BaseModel): type: Literal[ModelType.IPAdapter] = Field(default=ModelType.IPAdapter) From 7765df41ea731888f45a1deca54d4adec6cdc952 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:44:42 +1000 Subject: [PATCH 36/62] tidy(mm): removed unused model merge class --- invokeai/backend/model_manager/merge.py | 163 ------------------------ 1 file changed, 163 deletions(-) delete mode 100644 invokeai/backend/model_manager/merge.py diff --git a/invokeai/backend/model_manager/merge.py b/invokeai/backend/model_manager/merge.py deleted file mode 100644 index 03056b10f59..00000000000 --- a/invokeai/backend/model_manager/merge.py +++ /dev/null @@ -1,163 +0,0 @@ -""" -invokeai.backend.model_manager.merge exports: -merge_diffusion_models() -- combine multiple models by location and return a pipeline object -merge_diffusion_models_and_commit() -- combine multiple models by ModelManager ID and write to the models tables - -Copyright (c) 2023 Lincoln Stein and the InvokeAI Development Team -""" - -import warnings -from enum import Enum -from pathlib import Path -from typing import Any, List, Optional, Set - -import torch -from diffusers import AutoPipelineForText2Image -from diffusers.utils import logging as dlogging - -from invokeai.app.services.model_install import ModelInstallServiceBase -from invokeai.app.services.model_records.model_records_base import ModelRecordChanges -from invokeai.backend.model_manager import AnyModelConfig, BaseModelType, ModelType, ModelVariantType -from invokeai.backend.model_manager.config import MainDiffusersConfig -from invokeai.backend.util.devices import TorchDevice - - -class MergeInterpolationMethod(str, Enum): - WeightedSum = "weighted_sum" - Sigmoid = "sigmoid" - InvSigmoid = "inv_sigmoid" - AddDifference = "add_difference" - - -class ModelMerger(object): - """Wrapper class for model merge function.""" - - def __init__(self, installer: ModelInstallServiceBase): - """ - Initialize a ModelMerger object with the model installer. - """ - self._installer = installer - self._dtype = TorchDevice.choose_torch_dtype() - - def merge_diffusion_models( - self, - model_paths: List[Path], - alpha: float = 0.5, - interp: Optional[MergeInterpolationMethod] = None, - force: bool = False, - variant: Optional[str] = None, - **kwargs: Any, - ) -> Any: # pipe.merge is an untyped function. - """ - :param model_paths: up to three models, designated by their local paths or HuggingFace repo_ids - :param alpha: The interpolation parameter. Ranges from 0 to 1. It affects the ratio in which the checkpoints are merged. A 0.8 alpha - would mean that the first model checkpoints would affect the final result far less than an alpha of 0.2 - :param interp: The interpolation method to use for the merging. Supports "sigmoid", "inv_sigmoid", "add_difference" and None. - Passing None uses the default interpolation which is weighted sum interpolation. For merging three checkpoints, only "add_difference" is supported. - :param force: Whether to ignore mismatch in model_config.json for the current models. Defaults to False. - - **kwargs - the default DiffusionPipeline.get_config_dict kwargs: - cache_dir, resume_download, force_download, proxies, local_files_only, use_auth_token, revision, torch_dtype, device_map - """ - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - verbosity = dlogging.get_verbosity() - dlogging.set_verbosity_error() - dtype = torch.float16 if variant == "fp16" else self._dtype - - # Note that checkpoint_merger will not work with downloaded HuggingFace fp16 models - # until upstream https://github.com/huggingface/diffusers/pull/6670 is merged and released. - pipe = AutoPipelineForText2Image.from_pretrained( - model_paths[0], - custom_pipeline="checkpoint_merger", - torch_dtype=dtype, - variant=variant, - ) # type: ignore - merged_pipe = pipe.merge( - pretrained_model_name_or_path_list=model_paths, - alpha=alpha, - interp=interp.value if interp else None, # diffusers API treats None as "weighted sum" - force=force, - torch_dtype=dtype, - variant=variant, - **kwargs, - ) - dlogging.set_verbosity(verbosity) - return merged_pipe - - def merge_diffusion_models_and_save( - self, - model_keys: List[str], - merged_model_name: str, - alpha: float = 0.5, - force: bool = False, - interp: Optional[MergeInterpolationMethod] = None, - merge_dest_directory: Optional[Path] = None, - variant: Optional[str] = None, - **kwargs: Any, - ) -> AnyModelConfig: - """ - :param models: up to three models, designated by their registered InvokeAI model name - :param merged_model_name: name for new model - :param alpha: The interpolation parameter. Ranges from 0 to 1. It affects the ratio in which the checkpoints are merged. A 0.8 alpha - would mean that the first model checkpoints would affect the final result far less than an alpha of 0.2 - :param interp: The interpolation method to use for the merging. Supports "weighted_average", "sigmoid", "inv_sigmoid", "add_difference" and None. - Passing None uses the default interpolation which is weighted sum interpolation. For merging three checkpoints, only "add_difference" is supported. Add_difference is A+(B-C). - :param force: Whether to ignore mismatch in model_config.json for the current models. Defaults to False. - :param merge_dest_directory: Save the merged model to the designated directory (with 'merged_model_name' appended) - **kwargs - the default DiffusionPipeline.get_config_dict kwargs: - cache_dir, resume_download, force_download, proxies, local_files_only, use_auth_token, revision, torch_dtype, device_map - """ - model_paths: List[Path] = [] - model_names: List[str] = [] - config = self._installer.app_config - store = self._installer.record_store - base_models: Set[BaseModelType] = set() - variant = None if self._installer.app_config.precision == "float32" else "fp16" - - assert len(model_keys) <= 2 or interp == MergeInterpolationMethod.AddDifference, ( - "When merging three models, only the 'add_difference' merge method is supported" - ) - - for key in model_keys: - info = store.get_model(key) - model_names.append(info.name) - assert isinstance(info, MainDiffusersConfig), ( - f"{info.name} ({info.key}) is not a diffusers model. It must be optimized before merging" - ) - assert info.variant == ModelVariantType("normal"), ( - f"{info.name} ({info.key}) is a {info.variant} model, which cannot currently be merged" - ) - - # tally base models used - base_models.add(info.base) - model_paths.extend([config.models_path / info.path]) - - assert len(base_models) == 1, f"All models to merge must have same base model, but found bases {base_models}" - base_model = base_models.pop() - - merge_method = None if interp == "weighted_sum" else MergeInterpolationMethod(interp) - merged_pipe = self.merge_diffusion_models(model_paths, alpha, merge_method, force, variant=variant, **kwargs) - dump_path = ( - Path(merge_dest_directory) - if merge_dest_directory - else config.models_path / base_model.value / ModelType.Main.value - ) - dump_path.mkdir(parents=True, exist_ok=True) - dump_path = dump_path / merged_model_name - - dtype = torch.float16 if variant == "fp16" else self._dtype - merged_pipe.save_pretrained(dump_path.as_posix(), safe_serialization=True, torch_dtype=dtype, variant=variant) - - # register model and get its unique key - key = self._installer.register_path(dump_path) - - # update model's config - model_config = self._installer.record_store.get_model(key) - model_config.name = merged_model_name - model_config.description = f"Merge of models {', '.join(model_names)}" - - self._installer.record_store.update_model( - key, ModelRecordChanges(name=model_config.name, description=model_config.description) - ) - return model_config From 9676cb88102e595379945762627daf08163ef47d Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 29 Sep 2025 21:14:55 +1000 Subject: [PATCH 37/62] feat(mm): wip port main models to new api --- invokeai/app/api/routers/model_manager.py | 12 +- .../model_install/model_install_default.py | 22 +- invokeai/backend/model_manager/config.py | 634 +++++++++++++----- .../model_manager/load/model_loaders/flux.py | 12 +- .../load/model_loaders/stable_diffusion.py | 13 +- .../backend/model_manager/model_on_disk.py | 4 + invokeai/backend/util/hotfixes.py | 4 +- 7 files changed, 500 insertions(+), 201 deletions(-) diff --git a/invokeai/app/api/routers/model_manager.py b/invokeai/app/api/routers/model_manager.py index 6142239cf65..c91d2ed7220 100644 --- a/invokeai/app/api/routers/model_manager.py +++ b/invokeai/app/api/routers/model_manager.py @@ -29,10 +29,7 @@ ) from invokeai.app.util.suppress_output import SuppressOutput from invokeai.backend.model_manager import BaseModelType, ModelFormat, ModelType -from invokeai.backend.model_manager.config import ( - AnyModelConfig, - MainCheckpointConfig, -) +from invokeai.backend.model_manager.config import AnyModelConfig, SD_1_2_XL_XLRefiner_CheckpointConfig from invokeai.backend.model_manager.load.model_cache.cache_stats import CacheStats from invokeai.backend.model_manager.metadata.fetch.huggingface import HuggingFaceMetadataFetch from invokeai.backend.model_manager.metadata.metadata_base import ModelMetadataWithFiles, UnknownMetadataException @@ -741,9 +738,10 @@ async def convert_model( logger.error(str(e)) raise HTTPException(status_code=424, detail=str(e)) - if not isinstance(model_config, MainCheckpointConfig): - logger.error(f"The model with key {key} is not a main checkpoint model.") - raise HTTPException(400, f"The model with key {key} is not a main checkpoint model.") + if isinstance(model_config, SD_1_2_XL_XLRefiner_CheckpointConfig): + msg = f"The model with key {key} is not a main SD 1/2/XL checkpoint model." + logger.error(msg) + raise HTTPException(400, msg) with TemporaryDirectory(dir=ApiDependencies.invoker.services.configuration.models_path) as tmpdir: convert_path = pathlib.Path(tmpdir) / pathlib.Path(model_config.path).stem diff --git a/invokeai/app/services/model_install/model_install_default.py b/invokeai/app/services/model_install/model_install_default.py index 4cae15b8e36..06608df8e80 100644 --- a/invokeai/app/services/model_install/model_install_default.py +++ b/invokeai/app/services/model_install/model_install_default.py @@ -41,7 +41,6 @@ InvalidModelConfigException, ModelConfigFactory, ) -from invokeai.backend.model_manager.legacy_probe import ModelProbe from invokeai.backend.model_manager.metadata import ( AnyModelRepoMetadata, HuggingFaceMetadataFetch, @@ -601,22 +600,11 @@ def _probe(self, model_path: Path, config: Optional[ModelRecordChanges] = None): hash_algo = self._app_config.hashing_algorithm fields = config.model_dump() - # WARNING! - # The legacy probe relies on the implicit order of tests to determine model classification. - # This can lead to regressions between the legacy and new probes. - # Do NOT change the order of `probe` and `classify` without implementing one of the following fixes: - # Short-term fix: `classify` tests `matches` in the same order as the legacy probe. - # Long-term fix: Improve `matches` to be more specific so that only one config matches - # any given model - eliminating ambiguity and removing reliance on order. - # After implementing either of these fixes, remove @pytest.mark.xfail from `test_regression_against_model_probe` - try: - return ModelProbe.probe(model_path=model_path, fields=deepcopy(fields), hash_algo=hash_algo) # type: ignore - except InvalidModelConfigException: - return ModelConfigFactory.from_model_on_disk( - mod=model_path, - overrides=deepcopy(fields), - hash_algo=hash_algo, - ) + return ModelConfigFactory.from_model_on_disk( + mod=model_path, + overrides=deepcopy(fields), + hash_algo=hash_algo, + ) def _register( self, model_path: Path, config: Optional[ModelRecordChanges] = None, info: Optional[AnyModelConfig] = None diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 9a66a472658..57f52b10451 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -73,6 +73,7 @@ ) from invokeai.backend.model_manager.util.model_util import lora_token_vector_length from invokeai.backend.patches.lora_conversions.flux_control_lora_utils import is_state_dict_likely_flux_control +from invokeai.backend.quantization.gguf.ggml_tensor import GGMLTensor from invokeai.backend.spandrel_image_to_image_model import SpandrelImageToImageModel from invokeai.backend.stable_diffusion.schedulers.schedulers import SCHEDULER_NAME_VALUES @@ -142,24 +143,33 @@ def validate_model_field(model: type[BaseModel], field_name: str, value: Any) -> def _get_config_or_raise( config_class: type, - config_path: Path, + config_path: Path | set[Path], ) -> dict[str, Any]: """Load the config file at the given path, or raise NotAMatch if it cannot be loaded.""" - if not config_path.exists(): - raise NotAMatch(config_class, f"missing config file: {config_path}") + paths_to_check = config_path if isinstance(config_path, set) else {config_path} - try: - with open(config_path, "r") as file: - config = json.load(file) + problems: dict[Path, str] = {} - return config - except Exception as e: - raise NotAMatch(config_class, f"unable to load config file: {config_path}") from e + for p in paths_to_check: + if not p.exists(): + problems[p] = "file does not exist" + continue + + try: + with open(p, "r") as file: + config = json.load(file) + + return config + except Exception as e: + problems[p] = str(e) + continue + + raise NotAMatch(config_class, f"unable to load config file(s): {problems}") def _get_class_name_from_config( config_class: type, - config_path: Path, + config_path: Path | set[Path], ) -> str: """Load the config file and return the class name. @@ -185,7 +195,7 @@ def _get_class_name_from_config( return config_class_name -def _validate_class_name(config_class: type[BaseModel], config_path: Path, expected: set[str]) -> None: +def _validate_class_name(config_class: type[BaseModel], config_path: Path | set[Path], expected: set[str]) -> None: """Check if the class name in the config file matches the expected class names. Args: @@ -336,8 +346,7 @@ class ModelConfigBase(ABC, BaseModel): description="Usage information for this model", ) - USING_LEGACY_PROBE: ClassVar[set[Type["AnyModelConfig"]]] = set() - USING_CLASSIFY_API: ClassVar[set[Type["AnyModelConfig"]]] = set() + CONFIG_CLASSES: ClassVar[set[Type["AnyModelConfig"]]] = set() model_config = ConfigDict( validate_assignment=True, @@ -348,11 +357,9 @@ class ModelConfigBase(ABC, BaseModel): @classmethod def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) - - if issubclass(cls, LegacyProbeMixin): - ModelConfigBase.USING_LEGACY_PROBE.add(cls) - else: - ModelConfigBase.USING_CLASSIFY_API.add(cls) + # Register non-abstract subclasses so we can iterate over them later during model probing. + if not isabstract(cls): + cls.CONFIG_CLASSES.add(cls) @classmethod def __pydantic_init_subclass__(cls, **kwargs): @@ -362,12 +369,6 @@ def __pydantic_init_subclass__(cls, **kwargs): assert "type" in cls.model_fields, f"{cls.__name__} must define a 'type' field" assert "format" in cls.model_fields, f"{cls.__name__} must define a 'format' field" - @staticmethod - def all_config_classes(): - subclasses = ModelConfigBase.USING_LEGACY_PROBE | ModelConfigBase.USING_CLASSIFY_API - concrete = {cls for cls in subclasses if not isabstract(cls)} - return concrete - @classmethod def get_tag(cls) -> Tag: type = cls.model_fields["type"].default.value @@ -411,6 +412,22 @@ class DiffusersConfigBase(ABC, BaseModel): format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) repo_variant: Optional[ModelRepoVariant] = Field(ModelRepoVariant.Default) + @classmethod + def _get_repo_variant_or_raise(cls, mod: ModelOnDisk) -> ModelRepoVariant: + # get all files ending in .bin or .safetensors + weight_files = list(mod.path.glob("**/*.safetensors")) + weight_files.extend(list(mod.path.glob("**/*.bin"))) + for x in weight_files: + if ".fp16" in x.suffixes: + return ModelRepoVariant.FP16 + if "openvino_model" in x.name: + return ModelRepoVariant.OpenVINO + if "flax_model" in x.name: + return ModelRepoVariant.Flax + if x.suffix == ".onnx": + return ModelRepoVariant.ONNX + return ModelRepoVariant.Default + class T5EncoderConfig(ModelConfigBase): base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) @@ -423,7 +440,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - _validate_class_name(cls, mod.path / "config.json", {"T5EncoderModel"}) + _validate_class_name(cls, mod.common_config_paths(), {"T5EncoderModel"}) cls._validate_has_unquantized_config_file(mod) @@ -448,7 +465,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - _validate_class_name(cls, mod.path / "config.json", {"T5EncoderModel"}) + _validate_class_name(cls, mod.common_config_paths(), {"T5EncoderModel"}) cls._validate_filename_looks_like_bnb_quantized(mod) @@ -769,7 +786,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - _validate_class_name(cls, mod.path / "config.json", {"AutoencoderKL", "AutoencoderTiny"}) + _validate_class_name(cls, mod.common_config_paths(), {"AutoencoderKL", "AutoencoderTiny"}) base = fields.get("base") or cls._get_base_or_raise(mod) return cls(**fields, base=base) @@ -795,7 +812,7 @@ def _guess_name(cls, mod: ModelOnDisk) -> str: @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> VAEDiffusersConfig_SupportedBases: - config = _get_config_or_raise(cls, mod.path / "config.json") + config = _get_config_or_raise(cls, mod.common_config_paths()) if cls._config_looks_like_sdxl(config): return BaseModelType.StableDiffusionXL elif cls._name_looks_like_sdxl(mod): @@ -826,7 +843,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - _validate_class_name(cls, mod.path / "config.json", {"ControlNetModel", "FluxControlNetModel"}) + _validate_class_name(cls, mod.common_config_paths(), {"ControlNetModel", "FluxControlNetModel"}) base = fields.get("base") or cls._get_base_or_raise(mod) @@ -834,7 +851,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> ControlNetDiffusers_SupportedBases: - config = _get_config_or_raise(cls, mod.path / "config.json") + config = _get_config_or_raise(cls, mod.common_config_paths()) if config.get("_class_name") == "FluxControlNetModel": return BaseModelType.Flux @@ -942,8 +959,6 @@ class TextualInversionConfigBase(ABC, BaseModel): base: TextualInversion_SupportedBases = Field() type: Literal[ModelType.TextualInversion] = Field(default=ModelType.TextualInversion) - KNOWN_KEYS: ClassVar = {"string_to_param", "emb_params", "clip_g"} - @classmethod def _file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) -> bool: try: @@ -961,7 +976,7 @@ def _file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) state_dict = mod.load_state_dict(p) # Heuristic: textual inversion embeddings have these keys - if any(key in cls.KNOWN_KEYS for key in state_dict.keys()): + if any(key in {"string_to_param", "emb_params", "clip_g"} for key in state_dict.keys()): return True # Heuristic: small state dict with all tensor values @@ -1047,61 +1062,361 @@ class MainConfigBase(ABC, BaseModel): default_settings: Optional[MainModelDefaultSettings] = Field( description="Default settings for this model", default=None ) - variant: ModelVariantType | FluxVariantType = Field() -MainCheckpointConfigBase_SupportedBases: TypeAlias = Literal[ +def _has_bnb_nf4_keys(state_dict: dict[str | int, Any]) -> bool: + bnb_nf4_keys = { + "double_blocks.0.img_attn.proj.weight.quant_state.bitsandbytes__nf4", + "model.diffusion_model.double_blocks.0.img_attn.proj.weight.quant_state.bitsandbytes__nf4", + } + return any(key in state_dict for key in bnb_nf4_keys) + + +def _has_ggml_tensors(state_dict: dict[str | int, Any]) -> bool: + return any(isinstance(v, GGMLTensor) for v in state_dict.values()) + + +def _has_main_keys(state_dict: dict[str | int, Any]) -> bool: + for key in state_dict.keys(): + if isinstance(key, int): + continue + elif key.startswith( + ( + "cond_stage_model.", + "first_stage_model.", + "model.diffusion_model.", + # Some FLUX checkpoint files contain transformer keys prefixed with "model.diffusion_model". + # This prefix is typically used to distinguish between multiple models bundled in a single file. + "model.diffusion_model.double_blocks.", + ) + ): + return True + elif key.startswith("double_blocks.") and "ip_adapter" not in key: + # FLUX models in the official BFL format contain keys with the "double_blocks." prefix, but we must be + # careful to avoid false positives on XLabs FLUX IP-Adapter models. + return True + return False + + +SD_1_2_XL_XLRefiner_CheckpointConfig_SupportedBases: TypeAlias = Literal[ BaseModelType.StableDiffusion1, BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusion3, BaseModelType.StableDiffusionXL, BaseModelType.StableDiffusionXLRefiner, - BaseModelType.Flux, - BaseModelType.CogView4, ] -class MainCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase): +class SD_1_2_XL_XLRefiner_CheckpointConfig(CheckpointConfigBase, MainConfigBase, ModelConfigBase): + """Model config for main checkpoint models.""" + + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + + base: SD_1_2_XL_XLRefiner_CheckpointConfig_SupportedBases = Field() + prediction_type: SchedulerPredictionType = Field() + variant: ModelVariantType = Field() + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _validate_is_file(cls, mod) + + _validate_override_fields(cls, fields) + + cls._validate_looks_like_main_model(mod) + + base = fields.get("base") or cls._get_base_or_raise(mod) + prediction_type = fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod, base) + variant = fields.get("variant") or cls._get_variant_or_raise(mod, base) + + return cls(**fields, base=base, prediction_type=prediction_type, variant=variant) + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> SD_1_2_XL_XLRefiner_CheckpointConfig_SupportedBases: + state_dict = mod.load_state_dict() + + key_name = "model.diffusion_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight" + if key_name in state_dict and state_dict[key_name].shape[-1] == 768: + return BaseModelType.StableDiffusion1 + if key_name in state_dict and state_dict[key_name].shape[-1] == 1024: + return BaseModelType.StableDiffusion2 + + key_name = "model.diffusion_model.input_blocks.4.1.transformer_blocks.0.attn2.to_k.weight" + if key_name in state_dict and state_dict[key_name].shape[-1] == 2048: + return BaseModelType.StableDiffusionXL + elif key_name in state_dict and state_dict[key_name].shape[-1] == 1280: + return BaseModelType.StableDiffusionXLRefiner + + raise NotAMatch(cls, "unable to determine base type from state dict") + + @classmethod + def _get_scheduler_prediction_type_or_raise( + cls, mod: ModelOnDisk, base: SD_1_2_XL_XLRefiner_CheckpointConfig_SupportedBases + ) -> SchedulerPredictionType: + if base is BaseModelType.StableDiffusion2: + state_dict = mod.load_state_dict() + key_name = "model.diffusion_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight" + if key_name in state_dict and state_dict[key_name].shape[-1] == 1024: + if "global_step" in state_dict: + if state_dict["global_step"] == 220000: + return SchedulerPredictionType.Epsilon + elif state_dict["global_step"] == 110000: + return SchedulerPredictionType.VPrediction + return SchedulerPredictionType.VPrediction + else: + return SchedulerPredictionType.Epsilon + + @classmethod + def _get_variant_or_raise( + cls, mod: ModelOnDisk, base: SD_1_2_XL_XLRefiner_CheckpointConfig_SupportedBases + ) -> ModelVariantType: + state_dict = mod.load_state_dict() + key_name = "model.diffusion_model.input_blocks.0.0.weight" + + if key_name not in state_dict: + raise NotAMatch(cls, "unable to determine model variant from state dict") + + in_channels = state_dict["model.diffusion_model.input_blocks.0.0.weight"].shape[1] + + match in_channels: + case 4: + return ModelVariantType.Normal + case 5: + # Only SD2 has a depth variant + assert base is BaseModelType.StableDiffusion2, f"unexpected unet in_channels 5 for base '{base}'" + return ModelVariantType.Depth + case 9: + return ModelVariantType.Inpaint + case _: + raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels} for base '{base}'") + + @classmethod + def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: + has_main_model_keys = _has_main_keys(mod.load_state_dict()) + if not has_main_model_keys: + raise NotAMatch(cls, "state dict does not look like a main model") + + +def _get_flux_variant(state_dict: dict[str | int, Any]) -> FluxVariantType | None: + # FLUX Model variant types are distinguished by input channels and the presence of certain keys. + + # Input channels are derived from the shape of either "img_in.weight" or "model.diffusion_model.img_in.weight". + # + # Known models that use the latter key: + # - https://civitai.com/models/885098?modelVersionId=990775 + # - https://civitai.com/models/1018060?modelVersionId=1596255 + # - https://civitai.com/models/978314/ultrareal-fine-tune?modelVersionId=1413133 + # + # Input channels for known FLUX models: + # - Unquantized Dev and Schnell have in_channels=64 + # - BNB-NF4 Dev and Schnell have in_channels=1 + # - FLUX Fill has in_channels=384 + # - Unsure of quantized FLUX Fill models + # - Unsure of GGUF-quantized models + + in_channels = None + for key in {"img_in.weight", "model.diffusion_model.img_in.weight"}: + if key in state_dict: + in_channels = state_dict[key].shape[1] + break + + if in_channels is None: + # TODO(psyche): Should we have a graceful fallback here? Previously we fell back to the "normal" variant, + # but this variant is no longer used for FLUX models. If we get here, but the model is definitely a FLUX + # model, we should figure out a good fallback value. + return None + + # Because FLUX Dev and Schnell models have the same in_channels, we need to check for the presence of + # certain keys to distinguish between them. + is_flux_dev = ( + "guidance_in.out_layer.weight" in state_dict + or "model.diffusion_model.guidance_in.out_layer.weight" in state_dict + ) + + if is_flux_dev and in_channels == 384: + return FluxVariantType.DevFill + elif is_flux_dev: + return FluxVariantType.Dev + else: + # Must be a Schnell model...? + return FluxVariantType.Schnell + + +class FLUX_Unquantized_CheckpointConfig(CheckpointConfigBase, MainConfigBase, ModelConfigBase): """Model config for main checkpoint models.""" - base: MainCheckpointConfigBase_SupportedBases = Field() format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - prediction_type: SchedulerPredictionType = Field(default=SchedulerPredictionType.Epsilon) - upcast_attention: bool = Field(False) + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + variant: FluxVariantType = Field() + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _validate_is_file(cls, mod) + + _validate_override_fields(cls, fields) -class MainBnbQuantized4bCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase): + cls._validate_looks_like_main_model(mod) + + cls._validate_is_flux(mod) + + cls._validate_does_not_look_like_bnb_quantized(mod) + + cls._validate_does_not_look_like_gguf_quantized(mod) + + variant = fields.get("variant") or cls._get_variant_or_raise(mod) + + return cls(**fields, variant=variant) + + @classmethod + def _validate_is_flux(cls, mod: ModelOnDisk) -> None: + if not mod.has_keys_exact( + { + "double_blocks.0.img_attn.norm.key_norm.scale", + "model.diffusion_model.double_blocks.0.img_attn.norm.key_norm.scale", + }, + ): + raise NotAMatch(cls, "state dict does not look like a FLUX checkpoint") + + @classmethod + def _get_variant_or_raise(cls, mod: ModelOnDisk) -> FluxVariantType: + # FLUX Model variant types are distinguished by input channels and the presence of certain keys. + state_dict = mod.load_state_dict() + variant = _get_flux_variant(state_dict) + + if variant is None: + # TODO(psyche): Should we have a graceful fallback here? Previously we fell back to the "normal" variant, + # but this variant is no longer used for FLUX models. If we get here, but the model is definitely a FLUX + # model, we should figure out a good fallback value. + raise NotAMatch(cls, "unable to determine model variant from state dict") + + return variant + + @classmethod + def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: + has_main_model_keys = _has_main_keys(mod.load_state_dict()) + if not has_main_model_keys: + raise NotAMatch(cls, "state dict does not look like a main model") + + @classmethod + def _validate_does_not_look_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: + has_bnb_nf4_keys = _has_bnb_nf4_keys(mod.load_state_dict()) + if has_bnb_nf4_keys: + raise NotAMatch(cls, "state dict looks like bnb quantized nf4") + + @classmethod + def _validate_does_not_look_like_gguf_quantized(cls, mod: ModelOnDisk): + has_ggml_tensors = _has_ggml_tensors(mod.load_state_dict()) + if has_ggml_tensors: + raise NotAMatch(cls, "state dict looks like GGUF quantized") + + +class FLUX_Quantized_BnB_NF4_CheckpointConfig(CheckpointConfigBase, MainConfigBase, ModelConfigBase): """Model config for main checkpoint models.""" base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) format: Literal[ModelFormat.BnbQuantizednf4b] = Field(default=ModelFormat.BnbQuantizednf4b) - prediction_type: SchedulerPredictionType = Field(default=SchedulerPredictionType.Epsilon) - upcast_attention: bool = Field(False) + variant: FluxVariantType = Field() + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _validate_is_file(cls, mod) + + _validate_override_fields(cls, fields) + + cls._validate_looks_like_main_model(mod) + + cls._validate_model_looks_like_bnb_quantized(mod) + + variant = fields.get("variant") or cls._get_variant_or_raise(mod) + + return cls(**fields, variant=variant) + + @classmethod + def _get_variant_or_raise(cls, mod: ModelOnDisk) -> FluxVariantType: + # FLUX Model variant types are distinguished by input channels and the presence of certain keys. + state_dict = mod.load_state_dict() + variant = _get_flux_variant(state_dict) + if variant is None: + # TODO(psyche): Should we have a graceful fallback here? Previously we fell back to the "normal" variant, + # but this variant is no longer used for FLUX models. If we get here, but the model is definitely a FLUX + # model, we should figure out a good fallback value. + raise NotAMatch(cls, "unable to determine model variant from state dict") -class MainGGUFCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase): + return variant + + @classmethod + def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: + has_main_model_keys = _has_main_keys(mod.load_state_dict()) + if not has_main_model_keys: + raise NotAMatch(cls, "state dict does not look like a main model") + + @classmethod + def _validate_model_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: + has_bnb_nf4_keys = _has_bnb_nf4_keys(mod.load_state_dict()) + if not has_bnb_nf4_keys: + raise NotAMatch(cls, "state dict does not look like bnb quantized nf4") + + +class FLUX_Quantized_GGUF_CheckpointConfig(CheckpointConfigBase, MainConfigBase, ModelConfigBase): """Model config for main checkpoint models.""" base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) format: Literal[ModelFormat.GGUFQuantized] = Field(default=ModelFormat.GGUFQuantized) - prediction_type: SchedulerPredictionType = Field(default=SchedulerPredictionType.Epsilon) - upcast_attention: bool = Field(False) + variant: FluxVariantType = Field() + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _validate_is_file(cls, mod) + _validate_override_fields(cls, fields) -MainDiffusers_SupportedBases: TypeAlias = Literal[ + cls._validate_looks_like_main_model(mod) + + cls._validate_looks_like_gguf_quantized(mod) + + variant = fields.get("variant") or cls._get_variant_or_raise(mod) + + return cls(**fields, variant=variant) + + @classmethod + def _get_variant_or_raise(cls, mod: ModelOnDisk) -> FluxVariantType: + # FLUX Model variant types are distinguished by input channels and the presence of certain keys. + state_dict = mod.load_state_dict() + variant = _get_flux_variant(state_dict) + + if variant is None: + # TODO(psyche): Should we have a graceful fallback here? Previously we fell back to the "normal" variant, + # but this variant is no longer used for FLUX models. If we get here, but the model is definitely a FLUX + # model, we should figure out a good fallback value. + raise NotAMatch(cls, "unable to determine model variant from state dict") + + return variant + + @classmethod + def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: + has_main_model_keys = _has_main_keys(mod.load_state_dict()) + if not has_main_model_keys: + raise NotAMatch(cls, "state dict does not look like a main model") + + @classmethod + def _validate_looks_like_gguf_quantized(cls, mod: ModelOnDisk) -> None: + has_ggml_tensors = _has_ggml_tensors(mod.load_state_dict()) + if not has_ggml_tensors: + raise NotAMatch(cls, "state dict does not look like GGUF quantized") + + +SD_1_2_XL_XLRefiner_DiffusersConfig_SupportedBases: TypeAlias = Literal[ BaseModelType.StableDiffusion1, BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusion3, BaseModelType.StableDiffusionXL, BaseModelType.StableDiffusionXLRefiner, - BaseModelType.CogView4, ] -class MainDiffusersConfig(DiffusersConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase): - """Model config for main diffusers models.""" - - base: MainDiffusers_SupportedBases = Field() +class SD_1_2_XL_XLRefiner_DiffusersConfig(DiffusersConfigBase, MainConfigBase, ModelConfigBase): + base: SD_1_2_XL_XLRefiner_DiffusersConfig_SupportedBases = Field() + prediction_type: SchedulerPredictionType = Field() + variant: ModelVariantType = Field() @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @@ -1111,54 +1426,39 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.path / "config.json", + mod.common_config_paths(), { + # SD 1.x and 2.x "StableDiffusionPipeline", "StableDiffusionInpaintPipeline", + # SDXL "StableDiffusionXLPipeline", - "StableDiffusionXLImg2ImgPipeline", "StableDiffusionXLInpaintPipeline", - "StableDiffusion3Pipeline", + # SDXL Refiner + "StableDiffusionXLImg2ImgPipeline", + # TODO(psyche): Do we actually support LCM models? I don't see using this class anywhere in the codebase. "LatentConsistencyModelPipeline", - "SD3Transformer2DModel", - "CogView4Pipeline", }, ) base = fields.get("base") or cls._get_base_or_raise(mod) - if base in { - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - }: - variant = fields.get("variant") or cls._get_variant_or_raise(mod, base) - prediction_type = fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod, base) - upcast_attention = fields.get("upcast_attention") or cls._get_upcast_attention_or_raise( - base, prediction_type - ) - else: - variant = None - prediction_type = None - upcast_attention = False - if base is BaseModelType.StableDiffusion3: - submodels = fields.get("submodels") or cls._get_submodels_or_raise(mod, base) - else: - submodels = None + variant = fields.get("variant") or cls._get_variant_or_raise(mod, base) + + prediction_type = fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod, base) + + repo_variant = fields.get("repo_variant") or cls._get_repo_variant_or_raise(mod) return cls( **fields, base=base, - # TODO(psyche): figure out variant/prediction_type/upcast_attention variant=variant, prediction_type=prediction_type, - upcast_attention=upcast_attention, - # TODO(psyche): This is only for SD3 models - split up the config classes - submodels=submodels, + repo_variant=repo_variant, ) @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> MainDiffusers_SupportedBases: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> SD_1_2_XL_XLRefiner_DiffusersConfig_SupportedBases: # Handle pipelines with a UNet (i.e SD 1.x, SD2.x, SDXL). unet_config_path = mod.path / "unet" / "config.json" if unet_config_path.exists(): @@ -1177,31 +1477,12 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> MainDiffusers_SupportedBases: case _: raise NotAMatch(cls, f"unrecognized cross_attention_dim {cross_attention_dim}") - # Handle pipelines with a transformer (i.e. SD3). - transformer_config_path = mod.path / "transformer" / "config.json" - if transformer_config_path.exists(): - class_name = _get_class_name_from_config(cls, transformer_config_path) - match class_name: - case "SD3Transformer2DModel": - return BaseModelType.StableDiffusion3 - case "CogView4Transformer2DModel": - return BaseModelType.CogView4 - case _: - raise NotAMatch(cls, f"unrecognized transformer class name {class_name}") - raise NotAMatch(cls, "unable to determine base type") @classmethod def _get_scheduler_prediction_type_or_raise( - cls, mod: ModelOnDisk, base: MainDiffusers_SupportedBases + cls, mod: ModelOnDisk, base: SD_1_2_XL_XLRefiner_DiffusersConfig_SupportedBases ) -> SchedulerPredictionType: - if base not in { - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - }: - raise ValueError(f"Attempted to get scheduler prediction_type for non-UNet model base '{base}'") - scheduler_conf = _get_config_or_raise(cls, mod.path / "scheduler" / "scheduler_config.json") # TODO(psyche): Is epsilon the right default or should we raise if it's not present? @@ -1216,45 +1497,58 @@ def _get_scheduler_prediction_type_or_raise( raise NotAMatch(cls, f"unrecognized scheduler prediction_type {prediction_type}") @classmethod - def _get_variant_or_raise(cls, mod: ModelOnDisk, base: MainDiffusers_SupportedBases) -> ModelVariantType: - if base not in { - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - }: - raise ValueError(f"Attempted to get variant for model base '{base}' but it does not have variants") - + def _get_variant_or_raise( + cls, mod: ModelOnDisk, base: SD_1_2_XL_XLRefiner_DiffusersConfig_SupportedBases + ) -> ModelVariantType: unet_config = _get_config_or_raise(cls, mod.path / "unet" / "config.json") in_channels = unet_config.get("in_channels") - if base is BaseModelType.StableDiffusion2: - match in_channels: - case 4: - return ModelVariantType.Normal - case 9: - return ModelVariantType.Inpaint - case 5: - return ModelVariantType.Depth - case _: - raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels} for base '{base}'") - else: - match in_channels: - case 4: - return ModelVariantType.Normal - case 9: - return ModelVariantType.Inpaint - case _: - raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels} for base '{base}'") + match in_channels: + case 4: + return ModelVariantType.Normal + case 5: + # Only SD2 has a depth variant + assert base is BaseModelType.StableDiffusion2, f"unexpected unet in_channels 5 for base '{base}'" + return ModelVariantType.Depth + case 9: + return ModelVariantType.Inpaint + case _: + raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels} for base '{base}'") + + +class SD_3_DiffusersConfig(DiffusersConfigBase, MainConfigBase, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion3] = Field(BaseModelType.StableDiffusion3) @classmethod - def _get_submodels_or_raise( - cls, mod: ModelOnDisk, base: MainDiffusers_SupportedBases - ) -> dict[SubModelType, SubmodelDefinition]: - if base is not BaseModelType.StableDiffusion3: - raise ValueError(f"Attempted to get submodels for non-SD3 model base '{base}'") + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _validate_is_dir(cls, mod) + _validate_override_fields(cls, fields) + + _validate_class_name( + cls, + mod.common_config_paths(), + { + "StableDiffusion3Pipeline", + "SD3Transformer2DModel", + }, + ) + + submodels = fields.get("submodels") or cls._get_submodels_or_raise(mod) + + repo_variant = fields.get("repo_variant") or cls._get_repo_variant_or_raise(mod) + + return cls( + **fields, + base=BaseModelType.StableDiffusion3, + submodels=submodels, + repo_variant=repo_variant, + ) + + @classmethod + def _get_submodels_or_raise(cls, mod: ModelOnDisk) -> dict[SubModelType, SubmodelDefinition]: # Example: https://huggingface.co/stabilityai/stable-diffusion-3.5-medium/blob/main/model_index.json - config = _get_config_or_raise(cls, mod.path / "model_index.json") + config = _get_config_or_raise(cls, mod.common_config_paths()) submodels: dict[SubModelType, SubmodelDefinition] = {} @@ -1272,7 +1566,9 @@ def _get_submodels_or_raise( path_or_prefix = (mod.path / key).resolve().as_posix() # We need to read the config to determine the variant of the CLIP model. - clip_embed_config = _get_config_or_raise(cls, mod.path / key / "config.json") + clip_embed_config = _get_config_or_raise( + cls, {mod.path / key / "config.json", mod.path / key / "model_index.json"} + ) variant = _get_clip_variant_type_from_config(clip_embed_config) submodels[SubModelType(key)] = SubmodelDefinition( path_or_prefix=path_or_prefix, @@ -1293,22 +1589,28 @@ def _get_submodels_or_raise( return submodels + +class CogView4_DiffusersConfig(DiffusersConfigBase, MainConfigBase, ModelConfigBase): + base: Literal[BaseModelType.CogView4] = Field(BaseModelType.CogView4) + @classmethod - def _get_upcast_attention_or_raise( - cls, base: MainDiffusers_SupportedBases, prediction_type: SchedulerPredictionType - ) -> bool: - if base not in { - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - }: - raise ValueError(f"Attempted to get upcast_attention flag for non-UNet model base '{base}'") + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _validate_is_dir(cls, mod) - if base is BaseModelType.StableDiffusion2 and prediction_type is SchedulerPredictionType.VPrediction: - # SD2 v-prediction models need upcast_attention to be True - return True + _validate_override_fields(cls, fields) + + _validate_class_name( + cls, + mod.common_config_paths(), + {"CogView4Pipeline"}, + ) + + repo_variant = fields.get("repo_variant") or cls._get_repo_variant_or_raise(mod) - return False + return cls( + **fields, + repo_variant=repo_variant, + ) class IPAdapterConfigBase(ABC, BaseModel): @@ -1476,7 +1778,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.path / "config.json", + mod.common_config_paths(), { "CLIPModel", "CLIPTextModel", @@ -1490,7 +1792,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_clip_g_variant(cls, mod: ModelOnDisk) -> None: - config = _get_config_or_raise(cls, mod.path / "config.json") + config = _get_config_or_raise(cls, mod.common_config_paths()) clip_variant = _get_clip_variant_type_from_config(config) if clip_variant is not ClipVariantType.G: @@ -1514,7 +1816,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.path / "config.json", + mod.common_config_paths(), { "CLIPModel", "CLIPTextModel", @@ -1528,7 +1830,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_clip_l_variant(cls, mod: ModelOnDisk) -> None: - config = _get_config_or_raise(cls, mod.path / "config.json") + config = _get_config_or_raise(cls, mod.common_config_paths()) clip_variant = _get_clip_variant_type_from_config(config) if clip_variant is not ClipVariantType.L: @@ -1550,7 +1852,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.path / "config.json", + mod.common_config_paths(), { "CLIPVisionModelWithProjection", }, @@ -1580,7 +1882,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.path / "config.json", + mod.common_config_paths(), { "T2IAdapter", }, @@ -1592,7 +1894,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> T2IAdapterDiffusers_SupportedBases: - config = _get_config_or_raise(cls, mod.path / "config.json") + config = _get_config_or_raise(cls, mod.common_config_paths()) adapter_type = config.get("adapter_type") @@ -1653,7 +1955,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.path / "config.json", + mod.common_config_paths(), { "SiglipModel", }, @@ -1696,7 +1998,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.path / "config.json", + mod.common_config_paths(), { "LlavaOnevisionForConditionalGeneration", }, @@ -1789,10 +2091,15 @@ def get_model_discriminator_value(v: Any) -> str: # when AnyModelConfig is constructed dynamically using ModelConfigBase.all_config_classes AnyModelConfig = Annotated[ Union[ - Annotated[MainDiffusersConfig, MainDiffusersConfig.get_tag()], - Annotated[MainCheckpointConfig, MainCheckpointConfig.get_tag()], - Annotated[MainBnbQuantized4bCheckpointConfig, MainBnbQuantized4bCheckpointConfig.get_tag()], - Annotated[MainGGUFCheckpointConfig, MainGGUFCheckpointConfig.get_tag()], + # Annotated[MainDiffusersConfig, MainDiffusersConfig.get_tag()], + # Annotated[MainCheckpointConfig, MainCheckpointConfig.get_tag()], + # SD_1_2_XL_XLRefiner_CheckpointConfig + Annotated[FLUX_Unquantized_CheckpointConfig, FLUX_Unquantized_CheckpointConfig.get_tag()], + Annotated[FLUX_Quantized_BnB_NF4_CheckpointConfig, FLUX_Quantized_BnB_NF4_CheckpointConfig.get_tag()], + Annotated[FLUX_Quantized_GGUF_CheckpointConfig, FLUX_Quantized_GGUF_CheckpointConfig.get_tag()], + Annotated[SD_1_2_XL_XLRefiner_DiffusersConfig, SD_1_2_XL_XLRefiner_DiffusersConfig.get_tag()], + Annotated[SD_3_DiffusersConfig, SD_3_DiffusersConfig.get_tag()], + Annotated[CogView4_DiffusersConfig, CogView4_DiffusersConfig.get_tag()], Annotated[VAEDiffusersConfig, VAEDiffusersConfig.get_tag()], Annotated[VAECheckpointConfig, VAECheckpointConfig.get_tag()], Annotated[ControlNetDiffusersConfig, ControlNetDiffusersConfig.get_tag()], @@ -1877,7 +2184,6 @@ def build_common_fields( fields["hash"] = _overrides.get("hash") or mod.hash() fields["key"] = _overrides.get("key") or uuid_string() fields["description"] = _overrides.get("description") - fields["repo_variant"] = _overrides.get("repo_variant") or mod.repo_variant() fields["file_size"] = _overrides.get("file_size") or mod.size() return fields @@ -1906,7 +2212,7 @@ def from_model_on_disk( # Try to build an instance of each model config class that uses the classify API. # Each class will either return an instance of itself or raise NotAMatch if it doesn't match. # Other exceptions may be raised if something unexpected happens during matching or building. - for config_class in ModelConfigBase.USING_CLASSIFY_API: + for config_class in ModelConfigBase.CONFIG_CLASSES: class_name = config_class.__name__ try: instance = config_class.from_model_on_disk(mod, fields) diff --git a/invokeai/backend/model_manager/load/model_loaders/flux.py b/invokeai/backend/model_manager/load/model_loaders/flux.py index ca38f1bdca2..570069632a7 100644 --- a/invokeai/backend/model_manager/load/model_loaders/flux.py +++ b/invokeai/backend/model_manager/load/model_loaders/flux.py @@ -40,11 +40,11 @@ CLIPEmbedDiffusersConfig, ControlNetCheckpointConfig, ControlNetDiffusersConfig, + FLUX_Quantized_BnB_NF4_CheckpointConfig, + FLUX_Quantized_GGUF_CheckpointConfig, + FLUX_Unquantized_CheckpointConfig, FluxReduxConfig, IPAdapterCheckpointConfig, - MainBnbQuantized4bCheckpointConfig, - MainCheckpointConfig, - MainGGUFCheckpointConfig, T5EncoderBnbQuantizedLlmInt8bConfig, T5EncoderConfig, VAECheckpointConfig, @@ -226,7 +226,7 @@ def _load_from_singlefile( self, config: AnyModelConfig, ) -> AnyModel: - assert isinstance(config, MainCheckpointConfig) + assert isinstance(config, FLUX_Unquantized_CheckpointConfig) model_path = Path(config.path) with accelerate.init_empty_weights(): @@ -268,7 +268,7 @@ def _load_from_singlefile( self, config: AnyModelConfig, ) -> AnyModel: - assert isinstance(config, MainGGUFCheckpointConfig) + assert isinstance(config, FLUX_Quantized_GGUF_CheckpointConfig) model_path = Path(config.path) with accelerate.init_empty_weights(): @@ -314,7 +314,7 @@ def _load_from_singlefile( self, config: AnyModelConfig, ) -> AnyModel: - assert isinstance(config, MainBnbQuantized4bCheckpointConfig) + assert isinstance(config, FLUX_Quantized_BnB_NF4_CheckpointConfig) if not bnb_available: raise ImportError( "The bnb modules are not available. Please install bitsandbytes if available on your platform." diff --git a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py index aa692478cad..9d771feae72 100644 --- a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py +++ b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py @@ -4,18 +4,19 @@ from pathlib import Path from typing import Optional -from diffusers import ( - StableDiffusionInpaintPipeline, - StableDiffusionPipeline, +from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion import StableDiffusionPipeline +from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_inpaint import StableDiffusionInpaintPipeline +from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl import StableDiffusionXLPipeline +from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl_inpaint import ( StableDiffusionXLInpaintPipeline, - StableDiffusionXLPipeline, ) from invokeai.backend.model_manager.config import ( AnyModelConfig, CheckpointConfigBase, DiffusersConfigBase, - MainCheckpointConfig, + SD_1_2_XL_XLRefiner_CheckpointConfig, + SD_1_2_XL_XLRefiner_DiffusersConfig, ) from invokeai.backend.model_manager.load.model_cache.model_cache import get_model_cache_key from invokeai.backend.model_manager.load.model_loader_registry import ModelLoaderRegistry @@ -107,7 +108,7 @@ def _load_from_singlefile( ModelVariantType.Normal: StableDiffusionXLPipeline, }, } - assert isinstance(config, MainCheckpointConfig) + assert isinstance(config, (SD_1_2_XL_XLRefiner_DiffusersConfig, SD_1_2_XL_XLRefiner_CheckpointConfig)) try: load_class = load_classes[config.base][config.variant] except KeyError as e: diff --git a/invokeai/backend/model_manager/model_on_disk.py b/invokeai/backend/model_manager/model_on_disk.py index 1f7625e09c0..69272009224 100644 --- a/invokeai/backend/model_manager/model_on_disk.py +++ b/invokeai/backend/model_manager/model_on_disk.py @@ -147,3 +147,7 @@ def has_keys_ending_with(self, suffixes: str | set[str], path: Optional[Path] = return any( any(key.endswith(suffix) for suffix in _suffixes) for key in state_dict.keys() if isinstance(key, str) ) + + def common_config_paths(self) -> set[Path]: + """Returns common config file paths for models stored in directories.""" + return {self.path / "config.json", self.path / "model_index.json"} diff --git a/invokeai/backend/util/hotfixes.py b/invokeai/backend/util/hotfixes.py index 95f2c904ad8..7e258b87795 100644 --- a/invokeai/backend/util/hotfixes.py +++ b/invokeai/backend/util/hotfixes.py @@ -23,6 +23,7 @@ from diffusers.models.unets.unet_2d_condition import UNet2DConditionModel from torch import nn +from invokeai.backend.model_manager.taxonomy import BaseModelType, SchedulerPredictionType from invokeai.backend.util.logging import InvokeAILogger # TODO: create PR to diffusers @@ -407,7 +408,8 @@ def from_unet( use_linear_projection=unet.config.use_linear_projection, class_embed_type=unet.config.class_embed_type, num_class_embeds=unet.config.num_class_embeds, - upcast_attention=unet.config.upcast_attention, + upcast_attention=unet.config.base is BaseModelType.StableDiffusion2 + and unet.config.prediction_type is SchedulerPredictionType.VPrediction, resnet_time_scale_shift=unet.config.resnet_time_scale_shift, projection_class_embeddings_input_dim=unet.config.projection_class_embeddings_input_dim, controlnet_conditioning_channel_order=controlnet_conditioning_channel_order, From 09449cfd053d399c2ee71d454537556b11b10902 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 1 Oct 2025 12:21:19 +1000 Subject: [PATCH 38/62] tidy(mm): clean up model heuristic utils --- invokeai/backend/model_manager/config.py | 177 ++++++++++++------ .../backend/model_manager/model_on_disk.py | 23 --- 2 files changed, 124 insertions(+), 76 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 57f52b10451..2c9ad226a26 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -106,35 +106,67 @@ def __init__( DEFAULTS_PRECISION = Literal["fp16", "fp32"] -# Utility from https://github.com/pydantic/pydantic/discussions/7367#discussioncomment-14213144 -def find_field_schema(model: type[BaseModel], field_name: str) -> CoreSchema: - schema: CoreSchema = model.__pydantic_core_schema__.copy() - # we shallow copied, be careful not to mutate the original schema! +class FieldValidator: + """Utility class for validating individual fields of a Pydantic model without instantiating the whole model. - assert schema["type"] in ["definitions", "model"] + See: https://github.com/pydantic/pydantic/discussions/7367#discussioncomment-14213144 + """ - # find the field schema - field_schema = schema["schema"] # type: ignore - while "fields" not in field_schema: - field_schema = field_schema["schema"] # type: ignore + @staticmethod + def find_field_schema(model: type[BaseModel], field_name: str) -> CoreSchema: + """Find the Pydantic core schema for a specific field in a model.""" + schema: CoreSchema = model.__pydantic_core_schema__.copy() + # we shallow copied, be careful not to mutate the original schema! - field_schema = field_schema["fields"][field_name]["schema"] # type: ignore + assert schema["type"] in ["definitions", "model"] + + # find the field schema + field_schema = schema["schema"] # type: ignore + while "fields" not in field_schema: + field_schema = field_schema["schema"] # type: ignore + + field_schema = field_schema["fields"][field_name]["schema"] # type: ignore + + # if the original schema is a definition schema, replace the model schema with the field schema + if schema["type"] == "definitions": + schema["schema"] = field_schema + return schema + else: + return field_schema + + @cache + @staticmethod + def get_validator(model: type[BaseModel], field_name: str) -> SchemaValidator: + """Get a SchemaValidator for a specific field in a model.""" + return SchemaValidator(FieldValidator.find_field_schema(model, field_name)) + + @staticmethod + def validate_field(model: type[BaseModel], field_name: str, value: Any) -> Any: + """Validate a value for a specific field in a model.""" + return FieldValidator.get_validator(model, field_name).validate_python(value) - # if the original schema is a definition schema, replace the model schema with the field schema - if schema["type"] == "definitions": - schema["schema"] = field_schema - return schema - else: - return field_schema +def has_keys_exact(state_dict: dict[str | int, Any], keys: str | set[str]) -> bool: + """Returns true if the state dict has all of the specified keys.""" + _keys = {keys} if isinstance(keys, str) else keys + return _keys.issubset({key for key in state_dict.keys() if isinstance(key, str)}) -@cache -def validator(model: type[BaseModel], field_name: str) -> SchemaValidator: - return SchemaValidator(find_field_schema(model, field_name)) +def has_keys_starting_with(state_dict: dict[str | int, Any], prefixes: str | set[str]) -> bool: + """Returns true if the state dict has any keys starting with any of the specified prefixes.""" + _prefixes = {prefixes} if isinstance(prefixes, str) else prefixes + return any(any(key.startswith(prefix) for prefix in _prefixes) for key in state_dict.keys() if isinstance(key, str)) -def validate_model_field(model: type[BaseModel], field_name: str, value: Any) -> Any: - return validator(model, field_name).validate_python(value) + +def has_keys_ending_with(state_dict: dict[str | int, Any], suffixes: str | set[str]) -> bool: + """Returns true if the state dict has any keys ending with any of the specified suffixes.""" + _suffixes = {suffixes} if isinstance(suffixes, str) else suffixes + return any(any(key.endswith(suffix) for suffix in _suffixes) for key in state_dict.keys() if isinstance(key, str)) + + +def common_config_paths(path: Path) -> set[Path]: + """Returns common config file paths for models stored in directories.""" + return {path / "config.json", path / "model_index.json"} # These utility functions are tightly coupled to the config classes below in order to make the process of raising @@ -225,7 +257,7 @@ def _validate_override_fields( if field_name not in config_class.model_fields: raise NotAMatch(config_class, f"unknown override field: {field_name}") try: - validate_model_field(config_class, field_name, override_value) + FieldValidator.validate_field(config_class, field_name, override_value) except ValidationError as e: raise NotAMatch(config_class, f"invalid override for field '{field_name}': {e}") from e @@ -440,7 +472,13 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - _validate_class_name(cls, mod.common_config_paths(), {"T5EncoderModel"}) + _validate_class_name( + cls, + common_config_paths(mod.path), + { + "T5EncoderModel", + }, + ) cls._validate_has_unquantized_config_file(mod) @@ -465,7 +503,13 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - _validate_class_name(cls, mod.common_config_paths(), {"T5EncoderModel"}) + _validate_class_name( + cls, + common_config_paths(mod.path), + { + "T5EncoderModel", + }, + ) cls._validate_filename_looks_like_bnb_quantized(mod) @@ -481,7 +525,7 @@ def _validate_filename_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: @classmethod def _validate_model_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: - has_scb_key_suffix = mod.has_keys_ending_with("SCB") + has_scb_key_suffix = has_keys_ending_with(mod.load_state_dict(), "SCB") if not has_scb_key_suffix: raise NotAMatch(cls, "state dict does not look like bnb quantized llm_int8") @@ -592,23 +636,25 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: # Note: Existence of these key prefixes/suffixes does not guarantee that this is a LoRA. # Some main models have these keys, likely due to the creator merging in a LoRA. - has_key_with_lora_prefix = mod.has_keys_starting_with( + has_key_with_lora_prefix = has_keys_starting_with( + mod.load_state_dict(), { "lora_te_", "lora_unet_", "lora_te1_", "lora_te2_", "lora_transformer_", - } + }, ) - has_key_with_lora_suffix = mod.has_keys_ending_with( + has_key_with_lora_suffix = has_keys_ending_with( + mod.load_state_dict(), { "to_k_lora.up.weight", "to_q_lora.down.weight", "lora_A.weight", "lora_B.weight", - } + }, ) if not has_key_with_lora_prefix and not has_key_with_lora_suffix: @@ -754,7 +800,13 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_looks_like_vae(cls, mod: ModelOnDisk) -> None: - if not mod.has_keys_starting_with({"encoder.conv_in", "decoder.conv_in"}): + if not has_keys_starting_with( + mod.load_state_dict(), + { + "encoder.conv_in", + "decoder.conv_in", + }, + ): raise NotAMatch(cls, "model does not match Checkpoint VAE heuristics") @classmethod @@ -786,7 +838,14 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - _validate_class_name(cls, mod.common_config_paths(), {"AutoencoderKL", "AutoencoderTiny"}) + _validate_class_name( + cls, + common_config_paths(mod.path), + { + "AutoencoderKL", + "AutoencoderTiny", + }, + ) base = fields.get("base") or cls._get_base_or_raise(mod) return cls(**fields, base=base) @@ -812,7 +871,7 @@ def _guess_name(cls, mod: ModelOnDisk) -> str: @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> VAEDiffusersConfig_SupportedBases: - config = _get_config_or_raise(cls, mod.common_config_paths()) + config = _get_config_or_raise(cls, common_config_paths(mod.path)) if cls._config_looks_like_sdxl(config): return BaseModelType.StableDiffusionXL elif cls._name_looks_like_sdxl(mod): @@ -843,7 +902,14 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - _validate_class_name(cls, mod.common_config_paths(), {"ControlNetModel", "FluxControlNetModel"}) + _validate_class_name( + cls, + common_config_paths(mod.path), + { + "ControlNetModel", + "FluxControlNetModel", + }, + ) base = fields.get("base") or cls._get_base_or_raise(mod) @@ -851,7 +917,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> ControlNetDiffusers_SupportedBases: - config = _get_config_or_raise(cls, mod.common_config_paths()) + config = _get_config_or_raise(cls, common_config_paths(mod.path)) if config.get("_class_name") == "FluxControlNetModel": return BaseModelType.Flux @@ -900,7 +966,8 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_looks_like_controlnet(cls, mod: ModelOnDisk) -> None: - if not mod.has_keys_starting_with( + if has_keys_starting_with( + mod.load_state_dict(), { "controlnet", "control_model", @@ -911,7 +978,7 @@ def _validate_looks_like_controlnet(cls, mod: ModelOnDisk) -> None: # "double_blocks.", which we check for above. But, I'm afraid to modify this logic because it is so # delicate. "controlnet_blocks", - } + }, ): raise NotAMatch(cls, "state dict does not look like a ControlNet checkpoint") @@ -1268,7 +1335,8 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_is_flux(cls, mod: ModelOnDisk) -> None: - if not mod.has_keys_exact( + if not has_keys_exact( + mod.load_state_dict(), { "double_blocks.0.img_attn.norm.key_norm.scale", "model.diffusion_model.double_blocks.0.img_attn.norm.key_norm.scale", @@ -1426,7 +1494,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.common_config_paths(), + common_config_paths(mod.path), { # SD 1.x and 2.x "StableDiffusionPipeline", @@ -1527,7 +1595,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.common_config_paths(), + common_config_paths(mod.path), { "StableDiffusion3Pipeline", "SD3Transformer2DModel", @@ -1548,7 +1616,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _get_submodels_or_raise(cls, mod: ModelOnDisk) -> dict[SubModelType, SubmodelDefinition]: # Example: https://huggingface.co/stabilityai/stable-diffusion-3.5-medium/blob/main/model_index.json - config = _get_config_or_raise(cls, mod.common_config_paths()) + config = _get_config_or_raise(cls, common_config_paths(mod.path)) submodels: dict[SubModelType, SubmodelDefinition] = {} @@ -1601,8 +1669,10 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.common_config_paths(), - {"CogView4Pipeline"}, + common_config_paths(mod.path), + { + "CogView4Pipeline", + }, ) repo_variant = fields.get("repo_variant") or cls._get_repo_variant_or_raise(mod) @@ -1706,13 +1776,14 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_looks_like_ip_adapter(cls, mod: ModelOnDisk) -> None: - if not mod.has_keys_starting_with( + if not has_keys_starting_with( + mod.load_state_dict(), { "image_proj.", "ip_adapter.", # XLabs FLUX IP-Adapter models have keys startinh with "ip_adapter_proj_model.". "ip_adapter_proj_model.", - } + }, ): raise NotAMatch(cls, "model does not match Checkpoint IP Adapter heuristics") @@ -1778,7 +1849,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.common_config_paths(), + common_config_paths(mod.path), { "CLIPModel", "CLIPTextModel", @@ -1792,7 +1863,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_clip_g_variant(cls, mod: ModelOnDisk) -> None: - config = _get_config_or_raise(cls, mod.common_config_paths()) + config = _get_config_or_raise(cls, common_config_paths(mod.path)) clip_variant = _get_clip_variant_type_from_config(config) if clip_variant is not ClipVariantType.G: @@ -1816,7 +1887,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.common_config_paths(), + common_config_paths(mod.path), { "CLIPModel", "CLIPTextModel", @@ -1830,7 +1901,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_clip_l_variant(cls, mod: ModelOnDisk) -> None: - config = _get_config_or_raise(cls, mod.common_config_paths()) + config = _get_config_or_raise(cls, common_config_paths(mod.path)) clip_variant = _get_clip_variant_type_from_config(config) if clip_variant is not ClipVariantType.L: @@ -1852,7 +1923,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.common_config_paths(), + common_config_paths(mod.path), { "CLIPVisionModelWithProjection", }, @@ -1882,7 +1953,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.common_config_paths(), + common_config_paths(mod.path), { "T2IAdapter", }, @@ -1894,7 +1965,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> T2IAdapterDiffusers_SupportedBases: - config = _get_config_or_raise(cls, mod.common_config_paths()) + config = _get_config_or_raise(cls, common_config_paths(mod.path)) adapter_type = config.get("adapter_type") @@ -1955,7 +2026,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.common_config_paths(), + common_config_paths(mod.path), { "SiglipModel", }, @@ -1998,7 +2069,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - mod.common_config_paths(), + common_config_paths(mod.path), { "LlavaOnevisionForConditionalGeneration", }, diff --git a/invokeai/backend/model_manager/model_on_disk.py b/invokeai/backend/model_manager/model_on_disk.py index 69272009224..502ca596a62 100644 --- a/invokeai/backend/model_manager/model_on_disk.py +++ b/invokeai/backend/model_manager/model_on_disk.py @@ -128,26 +128,3 @@ def resolve_weight_file(self, path: Optional[Path] = None) -> Path: f"Please specify the intended file using the 'path' argument" ) return path - - def has_keys_exact(self, keys: str | set[str], path: Optional[Path] = None) -> bool: - _keys = {keys} if isinstance(keys, str) else keys - state_dict = self.load_state_dict(path) - return _keys.issubset({key for key in state_dict.keys() if isinstance(key, str)}) - - def has_keys_starting_with(self, prefixes: str | set[str], path: Optional[Path] = None) -> bool: - _prefixes = {prefixes} if isinstance(prefixes, str) else prefixes - state_dict = self.load_state_dict(path) - return any( - any(key.startswith(prefix) for prefix in _prefixes) for key in state_dict.keys() if isinstance(key, str) - ) - - def has_keys_ending_with(self, suffixes: str | set[str], path: Optional[Path] = None) -> bool: - _suffixes = {suffixes} if isinstance(suffixes, str) else suffixes - state_dict = self.load_state_dict(path) - return any( - any(key.endswith(suffix) for suffix in _suffixes) for key in state_dict.keys() if isinstance(key, str) - ) - - def common_config_paths(self) -> set[Path]: - """Returns common config file paths for models stored in directories.""" - return {self.path / "config.json", self.path / "model_index.json"} From d63348b9e374b9e6ea90e0625fb1610b0ead7d02 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 1 Oct 2025 13:02:18 +1000 Subject: [PATCH 39/62] tidy(mm): clean up ModelOnDisk caching --- invokeai/backend/model_manager/config.py | 3 --- .../backend/model_manager/model_on_disk.py | 20 +++++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 2c9ad226a26..c94ff281176 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -2162,9 +2162,6 @@ def get_model_discriminator_value(v: Any) -> str: # when AnyModelConfig is constructed dynamically using ModelConfigBase.all_config_classes AnyModelConfig = Annotated[ Union[ - # Annotated[MainDiffusersConfig, MainDiffusersConfig.get_tag()], - # Annotated[MainCheckpointConfig, MainCheckpointConfig.get_tag()], - # SD_1_2_XL_XLRefiner_CheckpointConfig Annotated[FLUX_Unquantized_CheckpointConfig, FLUX_Unquantized_CheckpointConfig.get_tag()], Annotated[FLUX_Quantized_BnB_NF4_CheckpointConfig, FLUX_Quantized_BnB_NF4_CheckpointConfig.get_tag()], Annotated[FLUX_Quantized_GGUF_CheckpointConfig, FLUX_Quantized_GGUF_CheckpointConfig.get_tag()], diff --git a/invokeai/backend/model_manager/model_on_disk.py b/invokeai/backend/model_manager/model_on_disk.py index 502ca596a62..a86e94d3a4c 100644 --- a/invokeai/backend/model_manager/model_on_disk.py +++ b/invokeai/backend/model_manager/model_on_disk.py @@ -30,7 +30,8 @@ def __init__(self, path: Path, hash_algo: HASHING_ALGORITHMS = "blake3_single"): self.hash_algo = hash_algo # Having a cache helps users of ModelOnDisk (i.e. configs) to save state # This prevents redundant computations during matching and parsing - self.cache = {"_CACHED_STATE_DICTS": {}} + self._state_dict_cache: dict[Path, Any] = {} + self._metadata_cache: dict[Path, Any] = {} def hash(self) -> str: return ModelHash(algorithm=self.hash_algo).hash(self.path) @@ -47,13 +48,18 @@ def weight_files(self) -> set[Path]: return {f for f in self.path.rglob("*") if f.suffix in extensions} def metadata(self, path: Optional[Path] = None) -> dict[str, str]: + path = path or self.path + if path in self._metadata_cache: + return self._metadata_cache[path] try: with safe_open(self.path, framework="pt", device="cpu") as f: metadata = f.metadata() assert isinstance(metadata, dict) - return metadata except Exception: - return {} + metadata = {} + + self._metadata_cache[path] = metadata + return metadata def repo_variant(self) -> Optional[ModelRepoVariant]: if self.path.is_file(): @@ -73,10 +79,8 @@ def repo_variant(self) -> Optional[ModelRepoVariant]: return ModelRepoVariant.Default def load_state_dict(self, path: Optional[Path] = None) -> StateDict: - sd_cache = self.cache["_CACHED_STATE_DICTS"] - - if path in sd_cache: - return sd_cache[path] + if path in self._state_dict_cache: + return self._state_dict_cache[path] path = self.resolve_weight_file(path) @@ -111,7 +115,7 @@ def load_state_dict(self, path: Optional[Path] = None) -> StateDict: raise ValueError(f"Unrecognized model extension: {path.suffix}") state_dict = checkpoint.get("state_dict", checkpoint) - sd_cache[path] = state_dict + self._state_dict_cache[path] = state_dict return state_dict def resolve_weight_file(self, path: Optional[Path] = None) -> Path: From bab7f62b33e272d12b418da61bfd063d0d212d1b Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 1 Oct 2025 14:46:38 +1000 Subject: [PATCH 40/62] tidy(mm): flux lora format util --- invokeai/backend/model_manager/config.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index c94ff281176..c36aca067fb 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -540,16 +540,12 @@ class LoRAConfigBase(ABC, BaseModel): ) -def get_flux_lora_format(mod: ModelOnDisk) -> FluxLoRAFormat | None: - key = "FLUX_LORA_FORMAT" - if key in mod.cache: - return mod.cache[key] - +def _get_flux_lora_format(mod: ModelOnDisk) -> FluxLoRAFormat | None: + # TODO(psyche): Moving this import to the function to avoid circular imports. Refactor later. from invokeai.backend.patches.lora_conversions.formats import flux_format_from_state_dict - sd = mod.load_state_dict(mod.path) - value = flux_format_from_state_dict(sd, mod.metadata()) - mod.cache[key] = value + state_dict = mod.load_state_dict(mod.path) + value = flux_format_from_state_dict(state_dict, mod.metadata()) return value @@ -574,7 +570,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_is_not_controllora_or_diffusers(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model is a ControlLoRA or Diffusers LoRA.""" - flux_format = get_flux_lora_format(mod) + flux_format = _get_flux_lora_format(mod) if flux_format in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: raise NotAMatch(cls, "model looks like ControlLoRA or Diffusers LoRA") @@ -663,13 +659,13 @@ def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: @classmethod def _validate_is_not_controllora_or_diffusers(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model is a ControlLoRA or Diffusers LoRA.""" - flux_format = get_flux_lora_format(mod) + flux_format = _get_flux_lora_format(mod) if flux_format in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: raise NotAMatch(cls, "model looks like ControlLoRA or Diffusers LoRA") @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> LoRALyCORIS_SupportedBases: - if get_flux_lora_format(mod): + if _get_flux_lora_format(mod): return BaseModelType.Flux state_dict = mod.load_state_dict() @@ -752,7 +748,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_looks_like_diffusers_lora(cls, mod: ModelOnDisk) -> None: - flux_lora_format = get_flux_lora_format(mod) + flux_lora_format = _get_flux_lora_format(mod) if flux_lora_format is not FluxLoRAFormat.Diffusers: raise NotAMatch(cls, "model does not look like a FLUX Diffusers LoRA") From 935fafe80e59b9cec89a521e43d87f5a9f8c25e4 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 1 Oct 2025 16:52:28 +1000 Subject: [PATCH 41/62] refactor(mm): make config classes narrow Simpler logic to identify, less complexity to add new model, fewer useless attrs that do not relate to the model arch, etc --- invokeai/app/invocations/flux_ip_adapter.py | 4 +- invokeai/app/invocations/ip_adapter.py | 6 +- invokeai/backend/model_manager/config.py | 903 +++++++++++------- .../backend/model_manager/load/load_base.py | 4 +- .../load/model_loaders/controlnet.py | 4 +- .../model_manager/load/model_loaders/flux.py | 48 +- .../load/model_loaders/stable_diffusion.py | 24 +- .../model_manager/load/model_loaders/vae.py | 6 +- .../model_records/test_model_records_sql.py | 8 +- 9 files changed, 605 insertions(+), 402 deletions(-) diff --git a/invokeai/app/invocations/flux_ip_adapter.py b/invokeai/app/invocations/flux_ip_adapter.py index db5754ee2b0..cfd166815d9 100644 --- a/invokeai/app/invocations/flux_ip_adapter.py +++ b/invokeai/app/invocations/flux_ip_adapter.py @@ -17,8 +17,8 @@ from invokeai.app.invocations.util import validate_begin_end_step, validate_weights from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.backend.model_manager.config import ( + IPAdapter_InvokeAI_Config_Base, IPAdapterCheckpointConfig, - IPAdapterInvokeAIConfig, ) from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType @@ -68,7 +68,7 @@ def validate_begin_end_step_percent(self) -> Self: def invoke(self, context: InvocationContext) -> IPAdapterOutput: # Lookup the CLIP Vision encoder that is intended to be used with the IP-Adapter model. ip_adapter_info = context.models.get_config(self.ip_adapter_model.key) - assert isinstance(ip_adapter_info, (IPAdapterInvokeAIConfig, IPAdapterCheckpointConfig)) + assert isinstance(ip_adapter_info, (IPAdapter_InvokeAI_Config_Base, IPAdapterCheckpointConfig)) # Note: There is a IPAdapterInvokeAIConfig.image_encoder_model_id field, but it isn't trustworthy. image_encoder_starter_model = CLIP_VISION_MODEL_MAP[self.clip_vision_model] diff --git a/invokeai/app/invocations/ip_adapter.py b/invokeai/app/invocations/ip_adapter.py index 35a98ff6ba0..5b99f72369f 100644 --- a/invokeai/app/invocations/ip_adapter.py +++ b/invokeai/app/invocations/ip_adapter.py @@ -13,8 +13,8 @@ from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.backend.model_manager.config import ( AnyModelConfig, + IPAdapter_InvokeAI_Config_Base, IPAdapterCheckpointConfig, - IPAdapterInvokeAIConfig, ) from invokeai.backend.model_manager.starter_models import ( StarterModel, @@ -123,9 +123,9 @@ def validate_begin_end_step_percent(self) -> Self: def invoke(self, context: InvocationContext) -> IPAdapterOutput: # Lookup the CLIP Vision encoder that is intended to be used with the IP-Adapter model. ip_adapter_info = context.models.get_config(self.ip_adapter_model.key) - assert isinstance(ip_adapter_info, (IPAdapterInvokeAIConfig, IPAdapterCheckpointConfig)) + assert isinstance(ip_adapter_info, (IPAdapter_InvokeAI_Config_Base, IPAdapterCheckpointConfig)) - if isinstance(ip_adapter_info, IPAdapterInvokeAIConfig): + if isinstance(ip_adapter_info, IPAdapter_InvokeAI_Config_Base): image_encoder_model_id = ip_adapter_info.image_encoder_model_id image_encoder_model_name = image_encoder_model_id.split("/")[-1].strip() else: diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index c36aca067fb..0b6e5fd83cb 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -41,7 +41,7 @@ import torch from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag, TypeAdapter, ValidationError -from pydantic_core import CoreSchema, SchemaValidator +from pydantic_core import CoreSchema, PydanticUndefined, SchemaValidator from typing_extensions import Annotated, Any, Dict from invokeai.app.services.config.config_default import get_config @@ -323,14 +323,17 @@ class LegacyProbeMixin: class ModelConfigBase(ABC, BaseModel): """ - Abstract Base class for model configurations. + Abstract base class for model configurations. A model config describes a specific combination of model base, type and + format, along with other metadata about the model. For example, a Stable Diffusion 1.x main model in checkpoint format + would have base=sd-1, type=main, format=checkpoint. To create a new config type, inherit from this class and implement its interface: - - (mandatory) override methods 'matches' and 'parse' - - (mandatory) define fields 'type' and 'format' as class attributes + - Define method 'from_model_on_disk' that returns an instance of the class or raises NotAMatch. This method will be + called during model installation to determine the correct config class for a model. + - Define fields 'type', 'base' and 'format' as pydantic fields. These should be Literals with a single value. A + default must be provided for each of these fields. - - (optional) override method 'get_tag' - - (optional) override field _MATCH_SPEED + If multiple combinations of base, type and format need to be supported, create a separate subclass for each. See MinimalConfigExample in test_model_probe.py for an example implementation. """ @@ -395,17 +398,23 @@ def __init_subclass__(cls, **kwargs): @classmethod def __pydantic_init_subclass__(cls, **kwargs): - # Ensure that subclasses define 'base', 'type' and 'format' fields. These are not in this base class, because - # subclasses may redefine them as different types, causing type-checking issues. - assert "base" in cls.model_fields, f"{cls.__name__} must define a 'base' field" - assert "type" in cls.model_fields, f"{cls.__name__} must define a 'type' field" - assert "format" in cls.model_fields, f"{cls.__name__} must define a 'format' field" + # Ensure that subclasses define 'base', 'type' and 'format' fields and provide defaults for them. Each subclass + # is expected to represent a single combination of base, type and format. + for name in ("type", "base", "format"): + assert name in cls.model_fields, f"{cls.__name__} must define a '{name}' field" + assert cls.model_fields[name].default is not PydanticUndefined, ( + f"{cls.__name__} must define a default for the '{name}' field" + ) @classmethod def get_tag(cls) -> Tag: - type = cls.model_fields["type"].default.value - format = cls.model_fields["format"].default.value - return Tag(f"{type}.{format}") + tag_strings: list[str] = [] + for name in ("type", "base", "format", "variant"): + if field := cls.model_fields.get(name): + if field.default is not PydanticUndefined: + # We assume each of these fields has an Enum for its default + tag_strings.append(str(field.default.value)) + return Tag(".".join(tag_strings)) @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @@ -415,7 +424,9 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: raise NotImplementedError(f"from_model_on_disk not implemented for {cls.__name__}") -class UnknownModelConfig(ModelConfigBase): +class Unknown_Config(ModelConfigBase): + """Model config for unknown models, used as a fallback when we cannot identify a model.""" + base: Literal[BaseModelType.Unknown] = Field(default=BaseModelType.Unknown) type: Literal[ModelType.Unknown] = Field(default=ModelType.Unknown) format: Literal[ModelFormat.Unknown] = Field(default=ModelFormat.Unknown) @@ -461,7 +472,7 @@ def _get_repo_variant_or_raise(cls, mod: ModelOnDisk) -> ModelRepoVariant: return ModelRepoVariant.Default -class T5EncoderConfig(ModelConfigBase): +class T5Encoder_T5Encoder_Config(ModelConfigBase): base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) type: Literal[ModelType.T5Encoder] = Field(default=ModelType.T5Encoder) format: Literal[ModelFormat.T5Encoder] = Field(default=ModelFormat.T5Encoder) @@ -492,7 +503,7 @@ def _validate_has_unquantized_config_file(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "missing text_encoder_2/model.safetensors.index.json") -class T5EncoderBnbQuantizedLlmInt8bConfig(ModelConfigBase): +class T5Encoder_BnBLLMint8_Config(ModelConfigBase): base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) type: Literal[ModelType.T5Encoder] = Field(default=ModelType.T5Encoder) format: Literal[ModelFormat.BnbQuantizedLlmInt8b] = Field(default=ModelFormat.BnbQuantizedLlmInt8b) @@ -549,8 +560,7 @@ def _get_flux_lora_format(mod: ModelOnDisk) -> FluxLoRAFormat | None: return value -class LoRAOmiConfig(LoRAConfigBase, ModelConfigBase): - base: Literal[BaseModelType.Flux, BaseModelType.StableDiffusionXL] = Field() +class LoRA_OMI_Config_Base(LoRAConfigBase): format: Literal[ModelFormat.OMI] = Field(default=ModelFormat.OMI) @classmethod @@ -559,24 +569,27 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - cls._validate_is_not_controllora_or_diffusers(mod) + cls._validate_looks_like_omi_lora(mod) - cls._validate_metadata_looks_like_omi(mod) + cls._validate_base(mod) - base = fields.get("base") or cls._get_base_or_raise(mod) + return cls(**fields) - return cls(**fields, base=base) + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @classmethod - def _validate_is_not_controllora_or_diffusers(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model is a ControlLoRA or Diffusers LoRA.""" + def _validate_looks_like_omi_lora(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model metadata does not look like an OMI LoRA.""" flux_format = _get_flux_lora_format(mod) if flux_format in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: raise NotAMatch(cls, "model looks like ControlLoRA or Diffusers LoRA") - @classmethod - def _validate_metadata_looks_like_omi(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model metadata does not look like an OMI LoRA.""" metadata = mod.metadata() metadata_looks_like_omi_lora = ( @@ -601,18 +614,17 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> Literal[BaseModelType.Flux, Bas raise NotAMatch(cls, f"unrecognised/unsupported architecture for OMI LoRA: {architecture}") -LoRALyCORIS_SupportedBases: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - BaseModelType.Flux, -] +class LoRA_OMI_SDXL_Config(LoRA_OMI_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class LoRA_OMI_FLUX_Config(LoRA_OMI_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) -class LoRALyCORISConfig(LoRAConfigBase, ModelConfigBase): +class LoRA_LyCORIS_Config_Base(LoRAConfigBase): """Model config for LoRA/Lycoris models.""" - base: LoRALyCORIS_SupportedBases = Field() type: Literal[ModelType.LoRA] = Field(default=ModelType.LoRA) format: Literal[ModelFormat.LyCORIS] = Field(default=ModelFormat.LyCORIS) @@ -622,14 +634,27 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) - cls._validate_is_not_controllora_or_diffusers(mod) - cls._validate_looks_like_lora(mod) + cls._validate_base(mod) + return cls(**fields) + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") + @classmethod def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: + # First rule out ControlLoRA and Diffusers LoRA + flux_format = _get_flux_lora_format(mod) + if flux_format in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: + raise NotAMatch(cls, "model looks like ControlLoRA or Diffusers LoRA") + # Note: Existence of these key prefixes/suffixes does not guarantee that this is a LoRA. # Some main models have these keys, likely due to the creator merging in a LoRA. has_key_with_lora_prefix = has_keys_starting_with( @@ -657,14 +682,7 @@ def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "model does not match LyCORIS LoRA heuristics") @classmethod - def _validate_is_not_controllora_or_diffusers(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model is a ControlLoRA or Diffusers LoRA.""" - flux_format = _get_flux_lora_format(mod) - if flux_format in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: - raise NotAMatch(cls, "model looks like ControlLoRA or Diffusers LoRA") - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> LoRALyCORIS_SupportedBases: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: if _get_flux_lora_format(mod): return BaseModelType.Flux @@ -683,17 +701,30 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> LoRALyCORIS_SupportedBases: raise NotAMatch(cls, f"unrecognized token vector length {token_vector_length}") -class ControlAdapterConfigBase(ABC, BaseModel): - default_settings: ControlAdapterDefaultSettings | None = Field(None) +class LoRA_LyCORIS_SD1_Config(LoRA_LyCORIS_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class LoRA_LyCORIS_SD2_Config(LoRA_LyCORIS_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class LoRA_LyCORIS_SDXL_Config(LoRA_LyCORIS_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -ControlLoRALyCORIS_SupportedBases: TypeAlias = Literal[BaseModelType.Flux] +class LoRA_LyCORIS_FLUX_Config(LoRA_LyCORIS_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + + +class ControlAdapterConfigBase(ABC, BaseModel): + default_settings: ControlAdapterDefaultSettings | None = Field(None) -class ControlLoRALyCORISConfig(ControlAdapterConfigBase, ModelConfigBase): +class ControlLoRA_LyCORIS_FLUX_Config(ControlAdapterConfigBase, ModelConfigBase): """Model config for Control LoRA models.""" - base: ControlLoRALyCORIS_SupportedBases = Field() + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) type: Literal[ModelType.ControlLoRa] = Field(default=ModelType.ControlLoRa) format: Literal[ModelFormat.LyCORIS] = Field(default=ModelFormat.LyCORIS) @@ -717,66 +748,53 @@ def _validate_looks_like_control_lora(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "model state dict does not look like a Flux Control LoRA") -ControlLoRADiffusers_SupportedBases: TypeAlias = Literal[BaseModelType.Flux] +# LoRADiffusers_SupportedBases: TypeAlias = Literal[ +# BaseModelType.StableDiffusion1, +# BaseModelType.StableDiffusion2, +# BaseModelType.StableDiffusionXL, +# BaseModelType.Flux, +# ] -LoRADiffusers_SupportedBases: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - BaseModelType.Flux, -] - - -class LoRADiffusersConfig(LoRAConfigBase, ModelConfigBase): - """Model config for LoRA/Diffusers models.""" - - base: LoRADiffusers_SupportedBases = Field() - format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) +# class LoRADiffusersConfig(LoRAConfigBase, ModelConfigBase): +# """Model config for LoRA/Diffusers models.""" - _validate_override_fields(cls, fields) +# # TODO(psyche): Needs base handling. For FLUX, the Diffusers format does not indicate a folder model; it indicates +# # the weights format. FLUX Diffusers LoRAs are single files. - cls._validate_looks_like_diffusers_lora(mod) +# base: LoRADiffusers_SupportedBases = Field() +# format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - cls._validate_has_lora_weight_file(mod) +# @classmethod +# def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: +# _validate_is_dir(cls, mod) - return cls(**fields) +# _validate_override_fields(cls, fields) - @classmethod - def _validate_looks_like_diffusers_lora(cls, mod: ModelOnDisk) -> None: - flux_lora_format = _get_flux_lora_format(mod) - if flux_lora_format is not FluxLoRAFormat.Diffusers: - raise NotAMatch(cls, "model does not look like a FLUX Diffusers LoRA") +# cls._validate_looks_like_diffusers_lora(mod) - @classmethod - def _validate_has_lora_weight_file(cls, mod: ModelOnDisk) -> None: - suffixes = ["bin", "safetensors"] - weight_files = [mod.path / f"pytorch_lora_weights.{sfx}" for sfx in suffixes] - has_lora_weight_file = any(wf.exists() for wf in weight_files) - if not has_lora_weight_file: - raise NotAMatch(cls, "missing pytorch_lora_weights.bin or pytorch_lora_weights.safetensors") +# return cls(**fields) +# @classmethod +# def _validate_looks_like_diffusers_lora(cls, mod: ModelOnDisk) -> None: +# suffixes = ["bin", "safetensors"] +# weight_files = [mod.path / f"pytorch_lora_weights.{sfx}" for sfx in suffixes] +# has_lora_weight_file = any(wf.exists() for wf in weight_files) +# if not has_lora_weight_file: +# raise NotAMatch(cls, "missing pytorch_lora_weights.bin or pytorch_lora_weights.safetensors") -VAECheckpointConfig_SupportedBases: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - BaseModelType.Flux, -] +# flux_lora_format = _get_flux_lora_format(mod) +# if flux_lora_format is not FluxLoRAFormat.Diffusers: +# raise NotAMatch(cls, "model does not look like a FLUX Diffusers LoRA") -class VAECheckpointConfig(CheckpointConfigBase, ModelConfigBase): +class VAE_Checkpoint_Config_Base(CheckpointConfigBase): """Model config for standalone VAE models.""" - base: VAECheckpointConfig_SupportedBases = Field() type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - REGEX_TO_BASE: ClassVar[dict[str, VAECheckpointConfig_SupportedBases]] = { + REGEX_TO_BASE: ClassVar[dict[str, BaseModelType]] = { r"xl": BaseModelType.StableDiffusionXL, r"sd2": BaseModelType.StableDiffusion2, r"vae": BaseModelType.StableDiffusion1, @@ -791,8 +809,17 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: cls._validate_looks_like_vae(mod) - base = fields.get("base") or cls._get_base_or_raise(mod) - return cls(**fields, base=base) + cls._validate_base(mod) + + return cls(**fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @classmethod def _validate_looks_like_vae(cls, mod: ModelOnDisk) -> None: @@ -806,7 +833,7 @@ def _validate_looks_like_vae(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "model does not match Checkpoint VAE heuristics") @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> VAECheckpointConfig_SupportedBases: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: # Heuristic: VAEs of all architectures have a similar structure; the best we can do is guess based on name for regexp, base in cls.REGEX_TO_BASE.items(): if re.search(regexp, mod.path.name, re.IGNORECASE): @@ -815,16 +842,25 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> VAECheckpointConfig_SupportedBa raise NotAMatch(cls, "cannot determine base type") -VAEDiffusersConfig_SupportedBases: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusionXL, -] +class VAE_SD1_Checkpoint_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class VAE_SD2_Checkpoint_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class VAE_SDXL_Checkpoint_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class VAEDiffusersConfig(DiffusersConfigBase, ModelConfigBase): +class VAE_FLUX_Checkpoint_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + + +class VAE_Diffusers_Config_Base(DiffusersConfigBase): """Model config for standalone VAE models (diffusers version).""" - base: VAEDiffusersConfig_SupportedBases = Field() type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) @@ -843,8 +879,17 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: }, ) - base = fields.get("base") or cls._get_base_or_raise(mod) - return cls(**fields, base=base) + cls._validate_base(mod) + + return cls(**fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @classmethod def _config_looks_like_sdxl(cls, config: dict[str, Any]) -> bool: @@ -866,7 +911,7 @@ def _guess_name(cls, mod: ModelOnDisk) -> str: return name @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> VAEDiffusersConfig_SupportedBases: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: config = _get_config_or_raise(cls, common_config_paths(mod.path)) if cls._config_looks_like_sdxl(config): return BaseModelType.StableDiffusionXL @@ -877,18 +922,17 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> VAEDiffusersConfig_SupportedBas return BaseModelType.StableDiffusion1 -ControlNetDiffusers_SupportedBases: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - BaseModelType.Flux, -] +class VAE_SD1_Diffusers_Config(VAE_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class ControlNetDiffusersConfig(DiffusersConfigBase, ControlAdapterConfigBase, ModelConfigBase): +class VAE_SDXL_Diffusers_Config(VAE_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class ControlNet_Diffusers_Config_Base(DiffusersConfigBase, ControlAdapterConfigBase): """Model config for ControlNet models (diffusers version).""" - base: ControlNetDiffusers_SupportedBases = Field() type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) @@ -907,12 +951,20 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: }, ) - base = fields.get("base") or cls._get_base_or_raise(mod) + cls._validate_base(mod) - return cls(**fields, base=base) + return cls(**fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> ControlNetDiffusers_SupportedBases: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: config = _get_config_or_raise(cls, common_config_paths(mod.path)) if config.get("_class_name") == "FluxControlNetModel": @@ -933,6 +985,22 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> ControlNetDiffusers_SupportedBa raise NotAMatch(cls, f"unrecognized cross_attention_dim {dimension}") +class ControlNet_SD1_Diffusers_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class ControlNet_SD2_Diffusers_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class ControlNet_SDXL_Diffusers_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class ControlNet_FLUX_Diffusers_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + + ControlNetCheckpoint_SupportedBases: TypeAlias = Literal[ BaseModelType.StableDiffusion1, BaseModelType.StableDiffusion2, @@ -941,10 +1009,9 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> ControlNetDiffusers_SupportedBa ] -class ControlNetCheckpointConfig(CheckpointConfigBase, ControlAdapterConfigBase, ModelConfigBase): +class ControlNet_Checkpoint_Config_Base(CheckpointConfigBase, ControlAdapterConfigBase): """Model config for ControlNet models (diffusers version).""" - base: ControlNetDiffusers_SupportedBases = Field() type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) @@ -956,9 +1023,17 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: cls._validate_looks_like_controlnet(mod) - base = fields.get("base") or cls._get_base_or_raise(mod) + cls._validate_base(mod) + + return cls(**fields) - return cls(**fields, base=base) + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @classmethod def _validate_looks_like_controlnet(cls, mod: ModelOnDisk) -> None: @@ -1011,17 +1086,33 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> ControlNetCheckpoint_SupportedB raise NotAMatch(cls, "unable to determine base type from state dict") -TextualInversion_SupportedBases: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, -] +class ControlNet_SD1_Checkpoint_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + +class ControlNet_SD2_Checkpoint_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class TextualInversionConfigBase(ABC, BaseModel): - base: TextualInversion_SupportedBases = Field() + +class ControlNet_SDXL_Checkpoint_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class ControlNet_FLUX_Checkpoint_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + + +class TI_Config_Base(ABC, BaseModel): type: Literal[ModelType.TextualInversion] = Field(default=ModelType.TextualInversion) + @classmethod + def _validate_base(cls, mod: ModelOnDisk, path: Path | None = None) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod, path) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") + @classmethod def _file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) -> bool: try: @@ -1051,7 +1142,7 @@ def _file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) return False @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk, path: Path | None = None) -> TextualInversion_SupportedBases: + def _get_base_or_raise(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseModelType: p = path or mod.path try: @@ -1082,7 +1173,7 @@ def _get_base_or_raise(cls, mod: ModelOnDisk, path: Path | None = None) -> Textu raise NotAMatch(cls, f"unrecognized token dimension {token_dim}") -class TextualInversionFileConfig(TextualInversionConfigBase, ModelConfigBase): +class TI_File_Config_Base(TI_Config_Base): """Model config for textual inversion embeddings.""" format: Literal[ModelFormat.EmbeddingFile] = Field(default=ModelFormat.EmbeddingFile) @@ -1096,11 +1187,24 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: if not cls._file_looks_like_embedding(mod): raise NotAMatch(cls, "model does not look like a textual inversion embedding file") - base = fields.get("base") or cls._get_base_or_raise(mod) - return cls(**fields, base=base) + cls._validate_base(mod) + + return cls(**fields) + + +class TI_SD1_File_Config(TI_File_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class TextualInversionFolderConfig(TextualInversionConfigBase, ModelConfigBase): +class TI_SD2_File_Config(TI_File_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class TI_SDXL_File_Config(TI_File_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class TI_Folder_Config_Base(TI_Config_Base): """Model config for textual inversion embeddings.""" format: Literal[ModelFormat.EmbeddingFolder] = Field(default=ModelFormat.EmbeddingFolder) @@ -1113,12 +1217,24 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: for p in mod.weight_files(): if cls._file_looks_like_embedding(mod, p): - base = fields.get("base") or cls._get_base_or_raise(mod, p) - return cls(**fields, base=base) + cls._validate_base(mod, p) + return cls(**fields) raise NotAMatch(cls, "model does not look like a textual inversion embedding folder") +class TI_SD1_Folder_Config(TI_Folder_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class TI_SD2_Folder_Config(TI_Folder_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class TI_SDXL_Folder_Config(TI_Folder_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + class MainConfigBase(ABC, BaseModel): type: Literal[ModelType.Main] = Field(default=ModelType.Main) trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None) @@ -1161,20 +1277,11 @@ def _has_main_keys(state_dict: dict[str | int, Any]) -> bool: return False -SD_1_2_XL_XLRefiner_CheckpointConfig_SupportedBases: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - BaseModelType.StableDiffusionXLRefiner, -] - - -class SD_1_2_XL_XLRefiner_CheckpointConfig(CheckpointConfigBase, MainConfigBase, ModelConfigBase): +class Main_Checkpoint_Config_Base(CheckpointConfigBase, MainConfigBase): """Model config for main checkpoint models.""" format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - base: SD_1_2_XL_XLRefiner_CheckpointConfig_SupportedBases = Field() prediction_type: SchedulerPredictionType = Field() variant: ModelVariantType = Field() @@ -1186,14 +1293,24 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: cls._validate_looks_like_main_model(mod) - base = fields.get("base") or cls._get_base_or_raise(mod) - prediction_type = fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod, base) - variant = fields.get("variant") or cls._get_variant_or_raise(mod, base) + cls._validate_base(mod) + + prediction_type = fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod) - return cls(**fields, base=base, prediction_type=prediction_type, variant=variant) + variant = fields.get("variant") or cls._get_variant_or_raise(mod) + + return cls(**fields, prediction_type=prediction_type, variant=variant) @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> SD_1_2_XL_XLRefiner_CheckpointConfig_SupportedBases: + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: state_dict = mod.load_state_dict() key_name = "model.diffusion_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight" @@ -1211,9 +1328,9 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> SD_1_2_XL_XLRefiner_CheckpointC raise NotAMatch(cls, "unable to determine base type from state dict") @classmethod - def _get_scheduler_prediction_type_or_raise( - cls, mod: ModelOnDisk, base: SD_1_2_XL_XLRefiner_CheckpointConfig_SupportedBases - ) -> SchedulerPredictionType: + def _get_scheduler_prediction_type_or_raise(cls, mod: ModelOnDisk) -> SchedulerPredictionType: + base = cls.model_fields["base"].default.value + if base is BaseModelType.StableDiffusion2: state_dict = mod.load_state_dict() key_name = "model.diffusion_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight" @@ -1228,9 +1345,9 @@ def _get_scheduler_prediction_type_or_raise( return SchedulerPredictionType.Epsilon @classmethod - def _get_variant_or_raise( - cls, mod: ModelOnDisk, base: SD_1_2_XL_XLRefiner_CheckpointConfig_SupportedBases - ) -> ModelVariantType: + def _get_variant_or_raise(cls, mod: ModelOnDisk) -> ModelVariantType: + base = cls.model_fields["base"].default.value + state_dict = mod.load_state_dict() key_name = "model.diffusion_model.input_blocks.0.0.weight" @@ -1258,6 +1375,22 @@ def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "state dict does not look like a main model") +class Main_SD1_Checkpoint_Config(Main_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class Main_SD2_Checkpoint_Config(Main_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class Main_SDXL_Checkpoint_Config(Main_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class Main_SDXLRefiner_Checkpoint_Config(Main_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXLRefiner] = Field(default=BaseModelType.StableDiffusionXLRefiner) + + def _get_flux_variant(state_dict: dict[str | int, Any]) -> FluxVariantType | None: # FLUX Model variant types are distinguished by input channels and the presence of certain keys. @@ -1303,7 +1436,7 @@ def _get_flux_variant(state_dict: dict[str | int, Any]) -> FluxVariantType | Non return FluxVariantType.Schnell -class FLUX_Unquantized_CheckpointConfig(CheckpointConfigBase, MainConfigBase, ModelConfigBase): +class Main_FLUX_Checkpoint_Config(CheckpointConfigBase, MainConfigBase, ModelConfigBase): """Model config for main checkpoint models.""" format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) @@ -1373,11 +1506,12 @@ def _validate_does_not_look_like_gguf_quantized(cls, mod: ModelOnDisk): raise NotAMatch(cls, "state dict looks like GGUF quantized") -class FLUX_Quantized_BnB_NF4_CheckpointConfig(CheckpointConfigBase, MainConfigBase, ModelConfigBase): +class Main_FLUX_BnBNF4_Config(CheckpointConfigBase, MainConfigBase, ModelConfigBase): """Model config for main checkpoint models.""" base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) format: Literal[ModelFormat.BnbQuantizednf4b] = Field(default=ModelFormat.BnbQuantizednf4b) + variant: FluxVariantType = Field() @classmethod @@ -1421,11 +1555,12 @@ def _validate_model_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "state dict does not look like bnb quantized nf4") -class FLUX_Quantized_GGUF_CheckpointConfig(CheckpointConfigBase, MainConfigBase, ModelConfigBase): +class Main_FLUX_GGUF_Config(CheckpointConfigBase, MainConfigBase, ModelConfigBase): """Model config for main checkpoint models.""" base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) format: Literal[ModelFormat.GGUFQuantized] = Field(default=ModelFormat.GGUFQuantized) + variant: FluxVariantType = Field() @classmethod @@ -1469,16 +1604,7 @@ def _validate_looks_like_gguf_quantized(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "state dict does not look like GGUF quantized") -SD_1_2_XL_XLRefiner_DiffusersConfig_SupportedBases: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - BaseModelType.StableDiffusionXLRefiner, -] - - -class SD_1_2_XL_XLRefiner_DiffusersConfig(DiffusersConfigBase, MainConfigBase, ModelConfigBase): - base: SD_1_2_XL_XLRefiner_DiffusersConfig_SupportedBases = Field() +class Main_Diffusers_Config_Base(DiffusersConfigBase, MainConfigBase): prediction_type: SchedulerPredictionType = Field() variant: ModelVariantType = Field() @@ -1505,24 +1631,31 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: }, ) - base = fields.get("base") or cls._get_base_or_raise(mod) + cls._validate_base(mod) - variant = fields.get("variant") or cls._get_variant_or_raise(mod, base) + variant = fields.get("variant") or cls._get_variant_or_raise(mod) - prediction_type = fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod, base) + prediction_type = fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod) repo_variant = fields.get("repo_variant") or cls._get_repo_variant_or_raise(mod) return cls( **fields, - base=base, variant=variant, prediction_type=prediction_type, repo_variant=repo_variant, ) @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> SD_1_2_XL_XLRefiner_DiffusersConfig_SupportedBases: + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: # Handle pipelines with a UNet (i.e SD 1.x, SD2.x, SDXL). unet_config_path = mod.path / "unet" / "config.json" if unet_config_path.exists(): @@ -1544,9 +1677,7 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> SD_1_2_XL_XLRefiner_DiffusersCo raise NotAMatch(cls, "unable to determine base type") @classmethod - def _get_scheduler_prediction_type_or_raise( - cls, mod: ModelOnDisk, base: SD_1_2_XL_XLRefiner_DiffusersConfig_SupportedBases - ) -> SchedulerPredictionType: + def _get_scheduler_prediction_type_or_raise(cls, mod: ModelOnDisk) -> SchedulerPredictionType: scheduler_conf = _get_config_or_raise(cls, mod.path / "scheduler" / "scheduler_config.json") # TODO(psyche): Is epsilon the right default or should we raise if it's not present? @@ -1561,9 +1692,8 @@ def _get_scheduler_prediction_type_or_raise( raise NotAMatch(cls, f"unrecognized scheduler prediction_type {prediction_type}") @classmethod - def _get_variant_or_raise( - cls, mod: ModelOnDisk, base: SD_1_2_XL_XLRefiner_DiffusersConfig_SupportedBases - ) -> ModelVariantType: + def _get_variant_or_raise(cls, mod: ModelOnDisk) -> ModelVariantType: + base = cls.model_fields["base"].default.value unet_config = _get_config_or_raise(cls, mod.path / "unet" / "config.json") in_channels = unet_config.get("in_channels") @@ -1580,7 +1710,23 @@ def _get_variant_or_raise( raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels} for base '{base}'") -class SD_3_DiffusersConfig(DiffusersConfigBase, MainConfigBase, ModelConfigBase): +class Main_SD1_Diffusers_Config(Main_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(BaseModelType.StableDiffusion1) + + +class Main_SD2_Diffusers_Config(Main_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion2] = Field(BaseModelType.StableDiffusion2) + + +class Main_SDXL_Diffusers_Config(Main_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(BaseModelType.StableDiffusionXL) + + +class Main_SDXLRefiner_Diffusers_Config(Main_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXLRefiner] = Field(BaseModelType.StableDiffusionXLRefiner) + + +class Main_SD3_Diffusers_Config(DiffusersConfigBase, MainConfigBase, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion3] = Field(BaseModelType.StableDiffusion3) @classmethod @@ -1589,6 +1735,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) + # This check implies the base type - no further validation needed. _validate_class_name( cls, common_config_paths(mod.path), @@ -1604,7 +1751,6 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls( **fields, - base=BaseModelType.StableDiffusion3, submodels=submodels, repo_variant=repo_variant, ) @@ -1654,7 +1800,7 @@ def _get_submodels_or_raise(cls, mod: ModelOnDisk) -> dict[SubModelType, Submode return submodels -class CogView4_DiffusersConfig(DiffusersConfigBase, MainConfigBase, ModelConfigBase): +class Main_CogView4_Diffusers_Config(DiffusersConfigBase, MainConfigBase, ModelConfigBase): base: Literal[BaseModelType.CogView4] = Field(BaseModelType.CogView4) @classmethod @@ -1663,6 +1809,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_override_fields(cls, fields) + # This check implies the base type - no further validation needed. _validate_class_name( cls, common_config_paths(mod.path), @@ -1683,17 +1830,9 @@ class IPAdapterConfigBase(ABC, BaseModel): type: Literal[ModelType.IPAdapter] = Field(default=ModelType.IPAdapter) -IPAdapterInvokeAI_SupportedBases: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, -] - - -class IPAdapterInvokeAIConfig(IPAdapterConfigBase, ModelConfigBase): +class IPAdapter_InvokeAI_Config_Base(IPAdapterConfigBase): """Model config for IP Adapter diffusers format models.""" - base: IPAdapterInvokeAI_SupportedBases = Field() format: Literal[ModelFormat.InvokeAI] = Field(default=ModelFormat.InvokeAI) # TODO(ryand): Should we deprecate this field? From what I can tell, it hasn't been probed correctly for a long @@ -1710,8 +1849,17 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: cls._validate_has_image_encoder_metadata_file(mod) - base = fields.get("base") or cls._get_base_or_raise(mod) - return cls(**fields, base=base) + cls._validate_base(mod) + + return cls(**fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @classmethod def _validate_has_weights_file(cls, mod: ModelOnDisk) -> None: @@ -1726,7 +1874,7 @@ def _validate_has_image_encoder_metadata_file(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "missing image_encoder.txt metadata file") @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterInvokeAI_SupportedBases: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: state_dict = mod.load_state_dict() try: @@ -1745,18 +1893,21 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterInvokeAI_SupportedBase raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") -IPAdapterCheckpoint_SupportedBases: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - BaseModelType.Flux, -] +class IPAdapter_SD1_InvokeAI_Config(IPAdapter_InvokeAI_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class IPAdapter_SD2_InvokeAI_Config(IPAdapter_InvokeAI_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + +class IPAdapter_SDXL_InvokeAI_Config(IPAdapter_InvokeAI_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class IPAdapterCheckpointConfig(IPAdapterConfigBase, ModelConfigBase): + +class IPAdapter_Checkpoint_Config_Base(IPAdapterConfigBase): """Model config for IP Adapter checkpoint format models.""" - base: IPAdapterCheckpoint_SupportedBases = Field() format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) @classmethod @@ -1767,8 +1918,17 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: cls._validate_looks_like_ip_adapter(mod) - base = fields.get("base") or cls._get_base_or_raise(mod) - return cls(**fields, base=base) + cls._validate_base(mod) + + return cls(**fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @classmethod def _validate_looks_like_ip_adapter(cls, mod: ModelOnDisk) -> None: @@ -1784,7 +1944,7 @@ def _validate_looks_like_ip_adapter(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "model does not match Checkpoint IP Adapter heuristics") @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterCheckpoint_SupportedBases: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: state_dict = mod.load_state_dict() if is_state_dict_xlabs_ip_adapter(state_dict): @@ -1806,6 +1966,22 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> IPAdapterCheckpoint_SupportedBa raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") +class IPAdapter_SD1_Checkpoint_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class IPAdapter_SD2_Checkpoint_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class IPAdapter_SDXL_Checkpoint_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class IPAdapter_FLUX_Checkpoint_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + + def _get_clip_variant_type_from_config(config: dict[str, Any]) -> ClipVariantType | None: try: hidden_size = config.get("hidden_size") @@ -1820,23 +1996,11 @@ def _get_clip_variant_type_from_config(config: dict[str, Any]) -> ClipVariantTyp return None -class CLIPEmbedDiffusersConfig(DiffusersConfigBase): - """Model config for Clip Embeddings.""" - +class CLIPEmbed_Diffusers_Config_Base(DiffusersConfigBase): base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) type: Literal[ModelType.CLIPEmbed] = Field(default=ModelType.CLIPEmbed) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - -class CLIPGEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): - """Model config for CLIP-G Embeddings.""" - - variant: Literal[ClipVariantType.G] = Field(default=ClipVariantType.G) - - @classmethod - def get_tag(cls) -> Tag: - return Tag(f"{ModelType.CLIPEmbed.value}.{ModelFormat.Diffusers.value}.{ClipVariantType.G.value}") - @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_is_dir(cls, mod) @@ -1853,58 +2017,33 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: }, ) - cls._validate_clip_g_variant(mod) + cls._validate_variant(mod) return cls(**fields) @classmethod - def _validate_clip_g_variant(cls, mod: ModelOnDisk) -> None: + def _validate_variant(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model variant does not match this config class.""" + expected_variant = cls.model_fields["variant"].default.value config = _get_config_or_raise(cls, common_config_paths(mod.path)) - clip_variant = _get_clip_variant_type_from_config(config) - - if clip_variant is not ClipVariantType.G: - raise NotAMatch(cls, "model does not match CLIP-G heuristics") - - -class CLIPLEmbedDiffusersConfig(CLIPEmbedDiffusersConfig, ModelConfigBase): - """Model config for CLIP-L Embeddings.""" - - variant: Literal[ClipVariantType.L] = Field(default=ClipVariantType.L) - - @classmethod - def get_tag(cls) -> Tag: - return Tag(f"{ModelType.CLIPEmbed.value}.{ModelFormat.Diffusers.value}.{ClipVariantType.L.value}") - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) + recognized_variant = _get_clip_variant_type_from_config(config) - _validate_override_fields(cls, fields) + if recognized_variant is None: + raise NotAMatch(cls, "unable to determine CLIP variant from config") - _validate_class_name( - cls, - common_config_paths(mod.path), - { - "CLIPModel", - "CLIPTextModel", - "CLIPTextModelWithProjection", - }, - ) + if expected_variant is not recognized_variant: + raise NotAMatch(cls, f"variant is {recognized_variant}, not {expected_variant}") - cls._validate_clip_l_variant(mod) - return cls(**fields) +class CLIPEmbed_G_Diffusers_Config(CLIPEmbed_Diffusers_Config_Base, ModelConfigBase): + variant: Literal[ClipVariantType.G] = Field(default=ClipVariantType.G) - @classmethod - def _validate_clip_l_variant(cls, mod: ModelOnDisk) -> None: - config = _get_config_or_raise(cls, common_config_paths(mod.path)) - clip_variant = _get_clip_variant_type_from_config(config) - if clip_variant is not ClipVariantType.L: - raise NotAMatch(cls, "model does not match CLIP-G heuristics") +class CLIPEmbed_L_Diffusers_Config(CLIPEmbed_Diffusers_Config_Base, ModelConfigBase): + variant: Literal[ClipVariantType.L] = Field(default=ClipVariantType.L) -class CLIPVisionDiffusersConfig(DiffusersConfigBase, ModelConfigBase): +class CLIPVision_Diffusers_Config(DiffusersConfigBase, ModelConfigBase): """Model config for CLIPVision.""" base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) @@ -1928,16 +2067,9 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -T2IAdapterDiffusers_SupportedBases: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusionXL, -] - - -class T2IAdapterConfig(DiffusersConfigBase, ControlAdapterConfigBase, ModelConfigBase): +class T2IAdapter_Diffusers_Config_Base(DiffusersConfigBase, ControlAdapterConfigBase): """Model config for T2I.""" - base: T2IAdapterDiffusers_SupportedBases = Field() type: Literal[ModelType.T2IAdapter] = Field(default=ModelType.T2IAdapter) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) @@ -1955,12 +2087,20 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: }, ) - base = fields.get("base") or cls._get_base_or_raise(mod) + cls._validate_base(mod) - return cls(**fields, base=base) + return cls(**fields) @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> T2IAdapterDiffusers_SupportedBases: + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: config = _get_config_or_raise(cls, common_config_paths(mod.path)) adapter_type = config.get("adapter_type") @@ -1974,7 +2114,15 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> T2IAdapterDiffusers_SupportedBa raise NotAMatch(cls, f"unrecognized adapter_type '{adapter_type}'") -class SpandrelImageToImageConfig(ModelConfigBase): +class T2IAdapter_SD1_Diffusers_Config(T2IAdapter_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class T2IAdapter_SDXL_Diffusers_Config(T2IAdapter_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class Spandrel_Checkpoint_Config(ModelConfigBase): """Model config for Spandrel Image to Image models.""" base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) @@ -2007,7 +2155,7 @@ def _validate_spandrel_loads_model(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "model does not match SpandrelImageToImage heuristics") from e -class SigLIPConfig(DiffusersConfigBase, ModelConfigBase): +class SigLIP_Diffusers_Config(DiffusersConfigBase, ModelConfigBase): """Model config for SigLIP.""" type: Literal[ModelType.SigLIP] = Field(default=ModelType.SigLIP) @@ -2031,7 +2179,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -class FluxReduxConfig(ModelConfigBase): +class FLUXRedux_Checkpoint_Config(ModelConfigBase): """Model config for FLUX Tools Redux model.""" type: Literal[ModelType.FluxRedux] = Field(default=ModelType.FluxRedux) @@ -2050,7 +2198,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -class LlavaOnevisionConfig(DiffusersConfigBase, ModelConfigBase): +class LlavaOnevision_Diffusers_Config(DiffusersConfigBase, ModelConfigBase): """Model config for Llava Onevision models.""" type: Literal[ModelType.LlavaOnevision] = Field(default=ModelType.LlavaOnevision) @@ -2074,48 +2222,50 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -ApiModel_SupportedBases: TypeAlias = Literal[ - BaseModelType.ChatGPT4o, - BaseModelType.Gemini2_5, - BaseModelType.Imagen3, - BaseModelType.Imagen4, - BaseModelType.FluxKontext, -] - - -class ApiModelConfig(MainConfigBase, ModelConfigBase): +class ExternalAPI_Config_Base(ABC, BaseModel): """Model config for API-based models.""" - type: Literal[ModelType.Main] = Field(default=ModelType.Main) format: Literal[ModelFormat.Api] = Field(default=ModelFormat.Api) - base: ApiModel_SupportedBases = Field() @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - raise NotAMatch(cls, "API models cannot be built from disk") + raise NotAMatch(cls, "External API models cannot be built from disk") -VideoApiModel_SupportedBases: TypeAlias = Literal[ - BaseModelType.Veo3, - BaseModelType.Runway, -] +class ExternalAPI_ChatGPT4o_Config(ExternalAPI_Config_Base, MainConfigBase, ModelConfigBase): + base: Literal[BaseModelType.ChatGPT4o] = Field(default=BaseModelType.ChatGPT4o) -class VideoApiModelConfig(ModelConfigBase): - """Model config for API-based video models.""" +class ExternalAPI_Gemini2_5_Config(ExternalAPI_Config_Base, MainConfigBase, ModelConfigBase): + base: Literal[BaseModelType.Gemini2_5] = Field(default=BaseModelType.Gemini2_5) - type: Literal[ModelType.Video] = Field(default=ModelType.Video) - base: VideoApiModel_SupportedBases = Field() - format: Literal[ModelFormat.Api] = Field(default=ModelFormat.Api) +class ExternalAPI_Imagen3_Config(ExternalAPI_Config_Base, MainConfigBase, ModelConfigBase): + base: Literal[BaseModelType.Imagen3] = Field(default=BaseModelType.Imagen3) + + +class ExternalAPI_Imagen4_Config(ExternalAPI_Config_Base, MainConfigBase, ModelConfigBase): + base: Literal[BaseModelType.Imagen4] = Field(default=BaseModelType.Imagen4) + + +class ExternalAPI_FluxKontext_Config(ExternalAPI_Config_Base, MainConfigBase, ModelConfigBase): + base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) + + +class VideoConfigBase(ABC, BaseModel): + type: Literal[ModelType.Video] = Field(default=ModelType.Video) trigger_phrases: set[str] | None = Field(description="Set of trigger phrases for this model", default=None) default_settings: MainModelDefaultSettings | None = Field( description="Default settings for this model", default=None ) - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - raise NotAMatch(cls, "API models cannot be built from disk") + +class ExternalAPI_Veo3_Config(ExternalAPI_Config_Base, VideoConfigBase, ModelConfigBase): + base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) + + +class ExternalAPI_Runway_Config(ExternalAPI_Config_Base, VideoConfigBase, ModelConfigBase): + base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) def get_model_discriminator_value(v: Any) -> str: @@ -2123,72 +2273,109 @@ def get_model_discriminator_value(v: Any) -> str: Computes the discriminator value for a model config. https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions-with-callable-discriminator """ - format_ = type_ = variant_ = None - - if isinstance(v, dict): - format_ = v.get("format") - if isinstance(format_, Enum): - format_ = format_.value - - type_ = v.get("type") - if isinstance(type_, Enum): - type_ = type_.value - - variant_ = v.get("variant") - if isinstance(variant_, Enum): - variant_ = variant_.value - else: - format_ = v.format.value - type_ = v.type.value - variant_ = getattr(v, "variant", None) - if variant_: - variant_ = variant_.value - - # Ideally, each config would be uniquely identified with a combination of fields - # i.e. (type, format, variant) without any special cases. Alas... + if isinstance(v, ModelConfigBase): + return v.get_tag().tag - # Previously, CLIPEmbed did not have any variants, meaning older database entries lack a variant field. - # To maintain compatibility, we default to ClipVariantType.L in this case. - if type_ == ModelType.CLIPEmbed.value: - return f"{type_}.{format_}.{variant_}" - return f"{type_}.{format_}" + tag_strings: list[str] = [] + for name in ("type", "base", "format", "variant"): + field_value = v.get(name) + if isinstance(field_value, Enum): + field_value = field_value.value + if field_value is not None: + tag_strings.append(field_value) + return ".".join(tag_strings) # The types are listed explicitly because IDEs/LSPs can't identify the correct types # when AnyModelConfig is constructed dynamically using ModelConfigBase.all_config_classes AnyModelConfig = Annotated[ Union[ - Annotated[FLUX_Unquantized_CheckpointConfig, FLUX_Unquantized_CheckpointConfig.get_tag()], - Annotated[FLUX_Quantized_BnB_NF4_CheckpointConfig, FLUX_Quantized_BnB_NF4_CheckpointConfig.get_tag()], - Annotated[FLUX_Quantized_GGUF_CheckpointConfig, FLUX_Quantized_GGUF_CheckpointConfig.get_tag()], - Annotated[SD_1_2_XL_XLRefiner_DiffusersConfig, SD_1_2_XL_XLRefiner_DiffusersConfig.get_tag()], - Annotated[SD_3_DiffusersConfig, SD_3_DiffusersConfig.get_tag()], - Annotated[CogView4_DiffusersConfig, CogView4_DiffusersConfig.get_tag()], - Annotated[VAEDiffusersConfig, VAEDiffusersConfig.get_tag()], - Annotated[VAECheckpointConfig, VAECheckpointConfig.get_tag()], - Annotated[ControlNetDiffusersConfig, ControlNetDiffusersConfig.get_tag()], - Annotated[ControlNetCheckpointConfig, ControlNetCheckpointConfig.get_tag()], - Annotated[LoRALyCORISConfig, LoRALyCORISConfig.get_tag()], - Annotated[LoRAOmiConfig, LoRAOmiConfig.get_tag()], - Annotated[ControlLoRALyCORISConfig, ControlLoRALyCORISConfig.get_tag()], - Annotated[LoRADiffusersConfig, LoRADiffusersConfig.get_tag()], - Annotated[T5EncoderConfig, T5EncoderConfig.get_tag()], - Annotated[T5EncoderBnbQuantizedLlmInt8bConfig, T5EncoderBnbQuantizedLlmInt8bConfig.get_tag()], - Annotated[TextualInversionFileConfig, TextualInversionFileConfig.get_tag()], - Annotated[TextualInversionFolderConfig, TextualInversionFolderConfig.get_tag()], - Annotated[IPAdapterInvokeAIConfig, IPAdapterInvokeAIConfig.get_tag()], - Annotated[IPAdapterCheckpointConfig, IPAdapterCheckpointConfig.get_tag()], - Annotated[T2IAdapterConfig, T2IAdapterConfig.get_tag()], - Annotated[SpandrelImageToImageConfig, SpandrelImageToImageConfig.get_tag()], - Annotated[CLIPVisionDiffusersConfig, CLIPVisionDiffusersConfig.get_tag()], - Annotated[CLIPLEmbedDiffusersConfig, CLIPLEmbedDiffusersConfig.get_tag()], - Annotated[CLIPGEmbedDiffusersConfig, CLIPGEmbedDiffusersConfig.get_tag()], - Annotated[SigLIPConfig, SigLIPConfig.get_tag()], - Annotated[FluxReduxConfig, FluxReduxConfig.get_tag()], - Annotated[LlavaOnevisionConfig, LlavaOnevisionConfig.get_tag()], - Annotated[ApiModelConfig, ApiModelConfig.get_tag()], - Annotated[VideoApiModelConfig, VideoApiModelConfig.get_tag()], - Annotated[UnknownModelConfig, UnknownModelConfig.get_tag()], + # Main (Pipeline) - diffusers format + Annotated[Main_SD1_Diffusers_Config, Main_SD1_Diffusers_Config.get_tag()], + Annotated[Main_SD2_Diffusers_Config, Main_SD2_Diffusers_Config.get_tag()], + Annotated[Main_SDXL_Diffusers_Config, Main_SDXL_Diffusers_Config.get_tag()], + Annotated[Main_SDXLRefiner_Diffusers_Config, Main_SDXLRefiner_Diffusers_Config.get_tag()], + Annotated[Main_SD3_Diffusers_Config, Main_SD3_Diffusers_Config.get_tag()], + Annotated[Main_CogView4_Diffusers_Config, Main_CogView4_Diffusers_Config.get_tag()], + # Main (Pipeline) - checkpoint format + Annotated[Main_SD1_Checkpoint_Config, Main_SD1_Checkpoint_Config.get_tag()], + Annotated[Main_SD2_Checkpoint_Config, Main_SD2_Checkpoint_Config.get_tag()], + Annotated[Main_SDXL_Checkpoint_Config, Main_SDXL_Checkpoint_Config.get_tag()], + Annotated[Main_SDXLRefiner_Checkpoint_Config, Main_SDXLRefiner_Checkpoint_Config.get_tag()], + Annotated[Main_FLUX_Checkpoint_Config, Main_FLUX_Checkpoint_Config.get_tag()], + # Main (Pipeline) - quantized formats + Annotated[Main_FLUX_BnBNF4_Config, Main_FLUX_BnBNF4_Config.get_tag()], + Annotated[Main_FLUX_GGUF_Config, Main_FLUX_GGUF_Config.get_tag()], + # VAE - checkpoint format + Annotated[VAE_SD1_Checkpoint_Config, VAE_SD1_Checkpoint_Config.get_tag()], + Annotated[VAE_SD2_Checkpoint_Config, VAE_SD2_Checkpoint_Config.get_tag()], + Annotated[VAE_SDXL_Checkpoint_Config, VAE_SDXL_Checkpoint_Config.get_tag()], + Annotated[VAE_FLUX_Checkpoint_Config, VAE_FLUX_Checkpoint_Config.get_tag()], + # VAE - diffusers format + Annotated[VAE_SD1_Diffusers_Config, VAE_SD1_Diffusers_Config.get_tag()], + Annotated[VAE_SDXL_Diffusers_Config, VAE_SDXL_Diffusers_Config.get_tag()], + # ControlNet - checkpoint format + Annotated[ControlNet_SD1_Checkpoint_Config, ControlNet_SD1_Checkpoint_Config.get_tag()], + Annotated[ControlNet_SD2_Checkpoint_Config, ControlNet_SD2_Checkpoint_Config.get_tag()], + Annotated[ControlNet_SDXL_Checkpoint_Config, ControlNet_SDXL_Checkpoint_Config.get_tag()], + Annotated[ControlNet_FLUX_Checkpoint_Config, ControlNet_FLUX_Checkpoint_Config.get_tag()], + # ControlNet - diffusers format + Annotated[ControlNet_SD1_Diffusers_Config, ControlNet_SD1_Diffusers_Config.get_tag()], + Annotated[ControlNet_SD2_Diffusers_Config, ControlNet_SD2_Diffusers_Config.get_tag()], + Annotated[ControlNet_SDXL_Diffusers_Config, ControlNet_SDXL_Diffusers_Config.get_tag()], + Annotated[ControlNet_FLUX_Diffusers_Config, ControlNet_FLUX_Diffusers_Config.get_tag()], + # LoRA - LyCORIS format + Annotated[LoRA_LyCORIS_SD1_Config, LoRA_LyCORIS_SD1_Config.get_tag()], + Annotated[LoRA_LyCORIS_SD2_Config, LoRA_LyCORIS_SD2_Config.get_tag()], + Annotated[LoRA_LyCORIS_SDXL_Config, LoRA_LyCORIS_SDXL_Config.get_tag()], + Annotated[LoRA_LyCORIS_FLUX_Config, LoRA_LyCORIS_FLUX_Config.get_tag()], + # LoRA - OMI format + Annotated[LoRA_OMI_SDXL_Config, LoRA_OMI_SDXL_Config.get_tag()], + Annotated[LoRA_OMI_FLUX_Config, LoRA_OMI_FLUX_Config.get_tag()], + # LoRA - diffusers format (TODO) + # Annotated[LoRADiffusersConfig, LoRADiffusersConfig.get_tag()], + # ControlLoRA - diffusers format + Annotated[ControlLoRA_LyCORIS_FLUX_Config, ControlLoRA_LyCORIS_FLUX_Config.get_tag()], + Annotated[T5Encoder_T5Encoder_Config, T5Encoder_T5Encoder_Config.get_tag()], + Annotated[T5Encoder_BnBLLMint8_Config, T5Encoder_BnBLLMint8_Config.get_tag()], + # TI - file format + Annotated[TI_SD1_File_Config, TI_SD1_File_Config.get_tag()], + Annotated[TI_SD2_File_Config, TI_SD2_File_Config.get_tag()], + Annotated[TI_SDXL_File_Config, TI_SDXL_File_Config.get_tag()], + # TI - folder format + Annotated[TI_SD1_Folder_Config, TI_SD1_Folder_Config.get_tag()], + Annotated[TI_SD2_Folder_Config, TI_SD2_Folder_Config.get_tag()], + Annotated[TI_SDXL_Folder_Config, TI_SDXL_Folder_Config.get_tag()], + # IP Adapter - InvokeAI format + Annotated[IPAdapter_SD1_InvokeAI_Config, IPAdapter_SD1_InvokeAI_Config.get_tag()], + Annotated[IPAdapter_SD2_InvokeAI_Config, IPAdapter_SD2_InvokeAI_Config.get_tag()], + Annotated[IPAdapter_SDXL_InvokeAI_Config, IPAdapter_SDXL_InvokeAI_Config.get_tag()], + # IP Adapter - checkpoint format + Annotated[IPAdapter_SD1_Checkpoint_Config, IPAdapter_SD1_Checkpoint_Config.get_tag()], + Annotated[IPAdapter_SD2_Checkpoint_Config, IPAdapter_SD2_Checkpoint_Config.get_tag()], + Annotated[IPAdapter_SDXL_Checkpoint_Config, IPAdapter_SDXL_Checkpoint_Config.get_tag()], + Annotated[IPAdapter_FLUX_Checkpoint_Config, IPAdapter_FLUX_Checkpoint_Config.get_tag()], + # T2I Adapter - diffusers format + Annotated[T2IAdapter_SD1_Diffusers_Config, T2IAdapter_SD1_Diffusers_Config.get_tag()], + Annotated[T2IAdapter_SDXL_Diffusers_Config, T2IAdapter_SDXL_Diffusers_Config.get_tag()], + # Misc models + Annotated[Spandrel_Checkpoint_Config, Spandrel_Checkpoint_Config.get_tag()], + Annotated[CLIPEmbed_G_Diffusers_Config, CLIPEmbed_G_Diffusers_Config.get_tag()], + Annotated[CLIPEmbed_L_Diffusers_Config, CLIPEmbed_L_Diffusers_Config.get_tag()], + Annotated[CLIPVision_Diffusers_Config, CLIPVision_Diffusers_Config.get_tag()], + Annotated[SigLIP_Diffusers_Config, SigLIP_Diffusers_Config.get_tag()], + Annotated[FLUXRedux_Checkpoint_Config, FLUXRedux_Checkpoint_Config.get_tag()], + Annotated[LlavaOnevision_Diffusers_Config, LlavaOnevision_Diffusers_Config.get_tag()], + # API models + Annotated[ExternalAPI_ChatGPT4o_Config, ExternalAPI_ChatGPT4o_Config.get_tag()], + Annotated[ExternalAPI_Gemini2_5_Config, ExternalAPI_Gemini2_5_Config.get_tag()], + Annotated[ExternalAPI_Imagen3_Config, ExternalAPI_Imagen3_Config.get_tag()], + Annotated[ExternalAPI_Imagen4_Config, ExternalAPI_Imagen4_Config.get_tag()], + Annotated[ExternalAPI_FluxKontext_Config, ExternalAPI_FluxKontext_Config.get_tag()], + Annotated[ExternalAPI_Veo3_Config, ExternalAPI_Veo3_Config.get_tag()], + Annotated[ExternalAPI_Runway_Config, ExternalAPI_Runway_Config.get_tag()], + # Unknown model (fallback) + Annotated[Unknown_Config, Unknown_Config.get_tag()], ], Discriminator(get_model_discriminator_value), ] @@ -2298,7 +2485,7 @@ def from_model_on_disk( if not matches and app_config.allow_unknown_models: logger.warning(f"Unable to identify model {mod.name}, classifying as UnknownModelConfig") logger.debug(f"Model matching results: {results}") - return UnknownModelConfig(**fields) + return Unknown_Config(**fields) instance = next(iter(matches)) if len(matches) > 1: diff --git a/invokeai/backend/model_manager/load/load_base.py b/invokeai/backend/model_manager/load/load_base.py index 458fc0cfc0c..75191517c76 100644 --- a/invokeai/backend/model_manager/load/load_base.py +++ b/invokeai/backend/model_manager/load/load_base.py @@ -12,9 +12,7 @@ import torch from invokeai.app.services.config import InvokeAIAppConfig -from invokeai.backend.model_manager.config import ( - AnyModelConfig, -) +from invokeai.backend.model_manager.config import AnyModelConfig from invokeai.backend.model_manager.load.model_cache.cache_record import CacheRecord from invokeai.backend.model_manager.load.model_cache.model_cache import ModelCache from invokeai.backend.model_manager.taxonomy import AnyModel, SubModelType diff --git a/invokeai/backend/model_manager/load/model_loaders/controlnet.py b/invokeai/backend/model_manager/load/model_loaders/controlnet.py index 5bf93db3816..62a8ed4f65e 100644 --- a/invokeai/backend/model_manager/load/model_loaders/controlnet.py +++ b/invokeai/backend/model_manager/load/model_loaders/controlnet.py @@ -7,7 +7,7 @@ from invokeai.backend.model_manager.config import ( AnyModelConfig, - ControlNetCheckpointConfig, + ControlNet_Checkpoint_Config_Base, ) from invokeai.backend.model_manager.load.model_loader_registry import ModelLoaderRegistry from invokeai.backend.model_manager.load.model_loaders.generic_diffusers import GenericDiffusersLoader @@ -46,7 +46,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if isinstance(config, ControlNetCheckpointConfig): + if isinstance(config, ControlNet_Checkpoint_Config_Base): return ControlNetModel.from_single_file( config.path, torch_dtype=self._torch_dtype, diff --git a/invokeai/backend/model_manager/load/model_loaders/flux.py b/invokeai/backend/model_manager/load/model_loaders/flux.py index 570069632a7..4bb24b74665 100644 --- a/invokeai/backend/model_manager/load/model_loaders/flux.py +++ b/invokeai/backend/model_manager/load/model_loaders/flux.py @@ -37,26 +37,26 @@ from invokeai.backend.model_manager.config import ( AnyModelConfig, CheckpointConfigBase, - CLIPEmbedDiffusersConfig, - ControlNetCheckpointConfig, - ControlNetDiffusersConfig, - FLUX_Quantized_BnB_NF4_CheckpointConfig, - FLUX_Quantized_GGUF_CheckpointConfig, - FLUX_Unquantized_CheckpointConfig, - FluxReduxConfig, - IPAdapterCheckpointConfig, - T5EncoderBnbQuantizedLlmInt8bConfig, - T5EncoderConfig, - VAECheckpointConfig, + CLIPEmbed_Diffusers_Config_Base, + ControlNet_Checkpoint_Config_Base, + ControlNet_Diffusers_Config_Base, + FLUXRedux_Checkpoint_Config, + IPAdapter_Checkpoint_Config_Base, + Main_FLUX_BnBNF4_Config, + Main_FLUX_Checkpoint_Config, + Main_FLUX_GGUF_Config, + T5Encoder_BnBLLMint8_Config, + T5Encoder_T5Encoder_Config, + VAE_Checkpoint_Config_Base, ) 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.taxonomy import ( AnyModel, BaseModelType, + FluxVariantType, ModelFormat, ModelType, - ModelVariantType, SubModelType, ) from invokeai.backend.model_manager.util.model_util import ( @@ -86,7 +86,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if not isinstance(config, VAECheckpointConfig): + if not isinstance(config, VAE_Checkpoint_Config_Base): raise ValueError("Only VAECheckpointConfig models are currently supported here.") model_path = Path(config.path) @@ -116,7 +116,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if not isinstance(config, CLIPEmbedDiffusersConfig): + if not isinstance(config, CLIPEmbed_Diffusers_Config_Base): raise ValueError("Only CLIPEmbedDiffusersConfig models are currently supported here.") match submodel_type: @@ -139,7 +139,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if not isinstance(config, T5EncoderBnbQuantizedLlmInt8bConfig): + if not isinstance(config, T5Encoder_BnBLLMint8_Config): raise ValueError("Only T5EncoderBnbQuantizedLlmInt8bConfig models are currently supported here.") if not bnb_available: raise ImportError( @@ -186,7 +186,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if not isinstance(config, T5EncoderConfig): + if not isinstance(config, T5Encoder_T5Encoder_Config): raise ValueError("Only T5EncoderConfig models are currently supported here.") match submodel_type: @@ -226,7 +226,7 @@ def _load_from_singlefile( self, config: AnyModelConfig, ) -> AnyModel: - assert isinstance(config, FLUX_Unquantized_CheckpointConfig) + assert isinstance(config, Main_FLUX_Checkpoint_Config) model_path = Path(config.path) with accelerate.init_empty_weights(): @@ -268,7 +268,7 @@ def _load_from_singlefile( self, config: AnyModelConfig, ) -> AnyModel: - assert isinstance(config, FLUX_Quantized_GGUF_CheckpointConfig) + assert isinstance(config, Main_FLUX_GGUF_Config) model_path = Path(config.path) with accelerate.init_empty_weights(): @@ -314,7 +314,7 @@ def _load_from_singlefile( self, config: AnyModelConfig, ) -> AnyModel: - assert isinstance(config, FLUX_Quantized_BnB_NF4_CheckpointConfig) + assert isinstance(config, Main_FLUX_BnBNF4_Config) if not bnb_available: raise ImportError( "The bnb modules are not available. Please install bitsandbytes if available on your platform." @@ -342,9 +342,9 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if isinstance(config, ControlNetCheckpointConfig): + if isinstance(config, ControlNet_Checkpoint_Config_Base): model_path = Path(config.path) - elif isinstance(config, ControlNetDiffusersConfig): + elif isinstance(config, ControlNet_Diffusers_Config_Base): # If this is a diffusers directory, we simply ignore the config file and load from the weight file. model_path = Path(config.path) / "diffusion_pytorch_model.safetensors" else: @@ -363,7 +363,7 @@ def _load_model( def _load_xlabs_controlnet(self, sd: dict[str, torch.Tensor]) -> AnyModel: with accelerate.init_empty_weights(): # HACK(ryand): Is it safe to assume dev here? - model = XLabsControlNetFlux(get_flux_transformers_params(ModelVariantType.FluxDev)) + model = XLabsControlNetFlux(get_flux_transformers_params(FluxVariantType.Dev)) model.load_state_dict(sd, assign=True) return model @@ -389,7 +389,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if not isinstance(config, IPAdapterCheckpointConfig): + if not isinstance(config, IPAdapter_Checkpoint_Config_Base): raise ValueError(f"Unexpected model config type: {type(config)}.") sd = load_file(Path(config.path)) @@ -412,7 +412,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if not isinstance(config, FluxReduxConfig): + if not isinstance(config, FLUXRedux_Checkpoint_Config): raise ValueError(f"Unexpected model config type: {type(config)}.") sd = load_file(Path(config.path)) diff --git a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py index 9d771feae72..ab7982394b9 100644 --- a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py +++ b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py @@ -15,8 +15,14 @@ AnyModelConfig, CheckpointConfigBase, DiffusersConfigBase, - SD_1_2_XL_XLRefiner_CheckpointConfig, - SD_1_2_XL_XLRefiner_DiffusersConfig, + Main_SD1_Checkpoint_Config, + Main_SD1_Diffusers_Config, + Main_SD2_Checkpoint_Config, + Main_SD2_Diffusers_Config, + Main_SDXL_Checkpoint_Config, + Main_SDXL_Diffusers_Config, + Main_SDXLRefiner_Checkpoint_Config, + Main_SDXLRefiner_Diffusers_Config, ) from invokeai.backend.model_manager.load.model_cache.model_cache import get_model_cache_key from invokeai.backend.model_manager.load.model_loader_registry import ModelLoaderRegistry @@ -108,7 +114,19 @@ def _load_from_singlefile( ModelVariantType.Normal: StableDiffusionXLPipeline, }, } - assert isinstance(config, (SD_1_2_XL_XLRefiner_DiffusersConfig, SD_1_2_XL_XLRefiner_CheckpointConfig)) + assert isinstance( + config, + ( + Main_SD1_Diffusers_Config, + Main_SD2_Diffusers_Config, + Main_SDXL_Diffusers_Config, + Main_SDXLRefiner_Diffusers_Config, + Main_SD1_Checkpoint_Config, + Main_SD2_Checkpoint_Config, + Main_SDXL_Checkpoint_Config, + Main_SDXLRefiner_Checkpoint_Config, + ), + ) try: load_class = load_classes[config.base][config.variant] except KeyError as e: diff --git a/invokeai/backend/model_manager/load/model_loaders/vae.py b/invokeai/backend/model_manager/load/model_loaders/vae.py index 365fa0a547c..12789e58c2f 100644 --- a/invokeai/backend/model_manager/load/model_loaders/vae.py +++ b/invokeai/backend/model_manager/load/model_loaders/vae.py @@ -3,9 +3,9 @@ from typing import Optional -from diffusers import AutoencoderKL +from diffusers.models.autoencoders.autoencoder_kl import AutoencoderKL -from invokeai.backend.model_manager.config import AnyModelConfig, VAECheckpointConfig +from invokeai.backend.model_manager.config import AnyModelConfig, VAE_Checkpoint_Config_Base 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 ( @@ -27,7 +27,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if isinstance(config, VAECheckpointConfig): + if isinstance(config, VAE_Checkpoint_Config_Base): return AutoencoderKL.from_single_file( config.path, torch_dtype=self._torch_dtype, diff --git a/tests/app/services/model_records/test_model_records_sql.py b/tests/app/services/model_records/test_model_records_sql.py index c8b5698dd8b..41bbc2c024d 100644 --- a/tests/app/services/model_records/test_model_records_sql.py +++ b/tests/app/services/model_records/test_model_records_sql.py @@ -21,7 +21,7 @@ ControlAdapterDefaultSettings, MainDiffusersConfig, MainModelDefaultSettings, - TextualInversionFileConfig, + TI_File_Config, VAEDiffusersConfig, ) from invokeai.backend.model_manager.taxonomy import ModelSourceType @@ -40,8 +40,8 @@ def store( return ModelRecordServiceSQL(db, logger) -def example_ti_config(key: Optional[str] = None) -> TextualInversionFileConfig: - config = TextualInversionFileConfig( +def example_ti_config(key: Optional[str] = None) -> TI_File_Config: + config = TI_File_Config( source="test/source/", source_type=ModelSourceType.Path, path="/tmp/pokemon.bin", @@ -61,7 +61,7 @@ def test_type(store: ModelRecordServiceBase): config = example_ti_config("key1") store.add_model(config) config1 = store.get_model("key1") - assert isinstance(config1, TextualInversionFileConfig) + assert isinstance(config1, TI_File_Config) def test_raises_on_violating_uniqueness(store: ModelRecordServiceBase): From 17c5ad2bd4bae77029aa3127c9244b54cb39f96e Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 1 Oct 2025 18:00:50 +1000 Subject: [PATCH 42/62] refactor(mm): diffusers loras w --- invokeai/app/api/routers/model_manager.py | 18 +++- invokeai/app/invocations/flux_ip_adapter.py | 5 +- invokeai/app/invocations/ip_adapter.py | 4 +- invokeai/backend/model_manager/config.py | 97 +++++++++++++------ .../flux_aitoolkit_lora_conversion_utils.py | 4 +- .../flux_control_lora_utils.py | 4 +- .../flux_diffusers_lora_conversion_utils.py | 6 +- .../flux_kohya_lora_conversion_utils.py | 3 +- .../flux_onetrainer_lora_conversion_utils.py | 3 +- 9 files changed, 100 insertions(+), 44 deletions(-) diff --git a/invokeai/app/api/routers/model_manager.py b/invokeai/app/api/routers/model_manager.py index c91d2ed7220..6dfc58df49d 100644 --- a/invokeai/app/api/routers/model_manager.py +++ b/invokeai/app/api/routers/model_manager.py @@ -29,7 +29,13 @@ ) from invokeai.app.util.suppress_output import SuppressOutput from invokeai.backend.model_manager import BaseModelType, ModelFormat, ModelType -from invokeai.backend.model_manager.config import AnyModelConfig, SD_1_2_XL_XLRefiner_CheckpointConfig +from invokeai.backend.model_manager.config import ( + AnyModelConfig, + Main_SD1_Checkpoint_Config, + Main_SD2_Checkpoint_Config, + Main_SDXL_Checkpoint_Config, + Main_SDXLRefiner_Checkpoint_Config, +) from invokeai.backend.model_manager.load.model_cache.cache_stats import CacheStats from invokeai.backend.model_manager.metadata.fetch.huggingface import HuggingFaceMetadataFetch from invokeai.backend.model_manager.metadata.metadata_base import ModelMetadataWithFiles, UnknownMetadataException @@ -738,7 +744,15 @@ async def convert_model( logger.error(str(e)) raise HTTPException(status_code=424, detail=str(e)) - if isinstance(model_config, SD_1_2_XL_XLRefiner_CheckpointConfig): + if isinstance( + model_config, + ( + Main_SD1_Checkpoint_Config, + Main_SD2_Checkpoint_Config, + Main_SDXL_Checkpoint_Config, + Main_SDXLRefiner_Checkpoint_Config, + ), + ): msg = f"The model with key {key} is not a main SD 1/2/XL checkpoint model." logger.error(msg) raise HTTPException(400, msg) diff --git a/invokeai/app/invocations/flux_ip_adapter.py b/invokeai/app/invocations/flux_ip_adapter.py index cfd166815d9..970f330a581 100644 --- a/invokeai/app/invocations/flux_ip_adapter.py +++ b/invokeai/app/invocations/flux_ip_adapter.py @@ -17,8 +17,7 @@ from invokeai.app.invocations.util import validate_begin_end_step, validate_weights from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.backend.model_manager.config import ( - IPAdapter_InvokeAI_Config_Base, - IPAdapterCheckpointConfig, + IPAdapter_FLUX_Checkpoint_Config, ) from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType @@ -68,7 +67,7 @@ def validate_begin_end_step_percent(self) -> Self: def invoke(self, context: InvocationContext) -> IPAdapterOutput: # Lookup the CLIP Vision encoder that is intended to be used with the IP-Adapter model. ip_adapter_info = context.models.get_config(self.ip_adapter_model.key) - assert isinstance(ip_adapter_info, (IPAdapter_InvokeAI_Config_Base, IPAdapterCheckpointConfig)) + assert isinstance(ip_adapter_info, IPAdapter_FLUX_Checkpoint_Config) # Note: There is a IPAdapterInvokeAIConfig.image_encoder_model_id field, but it isn't trustworthy. image_encoder_starter_model = CLIP_VISION_MODEL_MAP[self.clip_vision_model] diff --git a/invokeai/app/invocations/ip_adapter.py b/invokeai/app/invocations/ip_adapter.py index 5b99f72369f..7c3234bdc71 100644 --- a/invokeai/app/invocations/ip_adapter.py +++ b/invokeai/app/invocations/ip_adapter.py @@ -13,8 +13,8 @@ from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.backend.model_manager.config import ( AnyModelConfig, + IPAdapter_Checkpoint_Config_Base, IPAdapter_InvokeAI_Config_Base, - IPAdapterCheckpointConfig, ) from invokeai.backend.model_manager.starter_models import ( StarterModel, @@ -123,7 +123,7 @@ def validate_begin_end_step_percent(self) -> Self: def invoke(self, context: InvocationContext) -> IPAdapterOutput: # Lookup the CLIP Vision encoder that is intended to be used with the IP-Adapter model. ip_adapter_info = context.models.get_config(self.ip_adapter_model.key) - assert isinstance(ip_adapter_info, (IPAdapter_InvokeAI_Config_Base, IPAdapterCheckpointConfig)) + assert isinstance(ip_adapter_info, (IPAdapter_InvokeAI_Config_Base, IPAdapter_Checkpoint_Config_Base)) if isinstance(ip_adapter_info, IPAdapter_InvokeAI_Config_Base): image_encoder_model_id = ip_adapter_info.image_encoder_model_id diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 0b6e5fd83cb..8d432b316a1 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -748,44 +748,78 @@ def _validate_looks_like_control_lora(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "model state dict does not look like a Flux Control LoRA") -# LoRADiffusers_SupportedBases: TypeAlias = Literal[ -# BaseModelType.StableDiffusion1, -# BaseModelType.StableDiffusion2, -# BaseModelType.StableDiffusionXL, -# BaseModelType.Flux, -# ] +class LoRA_Diffusers_Config_Base(LoRAConfigBase): + """Model config for LoRA/Diffusers models.""" + # TODO(psyche): Needs base handling. For FLUX, the Diffusers format does not indicate a folder model; it indicates + # the weights format. FLUX Diffusers LoRAs are single files. -# class LoRADiffusersConfig(LoRAConfigBase, ModelConfigBase): -# """Model config for LoRA/Diffusers models.""" + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) -# # TODO(psyche): Needs base handling. For FLUX, the Diffusers format does not indicate a folder model; it indicates -# # the weights format. FLUX Diffusers LoRAs are single files. + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + _validate_is_dir(cls, mod) -# base: LoRADiffusers_SupportedBases = Field() -# format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + _validate_override_fields(cls, fields) -# @classmethod -# def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: -# _validate_is_dir(cls, mod) + cls._validate_base(mod) -# _validate_override_fields(cls, fields) + return cls(**fields) -# cls._validate_looks_like_diffusers_lora(mod) + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default.value + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") -# return cls(**fields) + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + if _get_flux_lora_format(mod): + return BaseModelType.Flux -# @classmethod -# def _validate_looks_like_diffusers_lora(cls, mod: ModelOnDisk) -> None: -# suffixes = ["bin", "safetensors"] -# weight_files = [mod.path / f"pytorch_lora_weights.{sfx}" for sfx in suffixes] -# has_lora_weight_file = any(wf.exists() for wf in weight_files) -# if not has_lora_weight_file: -# raise NotAMatch(cls, "missing pytorch_lora_weights.bin or pytorch_lora_weights.safetensors") + # If we've gotten here, we assume that the LoRA is a Stable Diffusion LoRA + path_to_weight_file = cls._get_weight_file_or_raise(mod) + state_dict = mod.load_state_dict(path_to_weight_file) + token_vector_length = lora_token_vector_length(state_dict) -# flux_lora_format = _get_flux_lora_format(mod) -# if flux_lora_format is not FluxLoRAFormat.Diffusers: -# raise NotAMatch(cls, "model does not look like a FLUX Diffusers LoRA") + match token_vector_length: + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case 1280: + return BaseModelType.StableDiffusionXL # recognizes format at https://civitai.com/models/224641 + case 2048: + return BaseModelType.StableDiffusionXL + case _: + raise NotAMatch(cls, f"unrecognized token vector length {token_vector_length}") + + @classmethod + def _get_weight_file_or_raise(cls, mod: ModelOnDisk) -> Path: + suffixes = ["bin", "safetensors"] + weight_files = [mod.path / f"pytorch_lora_weights.{sfx}" for sfx in suffixes] + for wf in weight_files: + if wf.exists(): + return wf + raise NotAMatch(cls, "missing pytorch_lora_weights.bin or pytorch_lora_weights.safetensors") + + +class LoRA_SD1_Diffusers_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class LoRA_SD2_Diffusers_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class LoRA_SDXL_Diffusers_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class LoRA_FLUX_Diffusers_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) class VAE_Checkpoint_Config_Base(CheckpointConfigBase): @@ -2332,8 +2366,11 @@ def get_model_discriminator_value(v: Any) -> str: # LoRA - OMI format Annotated[LoRA_OMI_SDXL_Config, LoRA_OMI_SDXL_Config.get_tag()], Annotated[LoRA_OMI_FLUX_Config, LoRA_OMI_FLUX_Config.get_tag()], - # LoRA - diffusers format (TODO) - # Annotated[LoRADiffusersConfig, LoRADiffusersConfig.get_tag()], + # LoRA - diffusers format + Annotated[LoRA_SD1_Diffusers_Config, LoRA_SD1_Diffusers_Config.get_tag()], + Annotated[LoRA_SD2_Diffusers_Config, LoRA_SD2_Diffusers_Config.get_tag()], + Annotated[LoRA_SDXL_Diffusers_Config, LoRA_SDXL_Diffusers_Config.get_tag()], + Annotated[LoRA_FLUX_Diffusers_Config, LoRA_FLUX_Diffusers_Config.get_tag()], # ControlLoRA - diffusers format Annotated[ControlLoRA_LyCORIS_FLUX_Config, ControlLoRA_LyCORIS_FLUX_Config.get_tag()], Annotated[T5Encoder_T5Encoder_Config, T5Encoder_T5Encoder_Config.get_tag()], diff --git a/invokeai/backend/patches/lora_conversions/flux_aitoolkit_lora_conversion_utils.py b/invokeai/backend/patches/lora_conversions/flux_aitoolkit_lora_conversion_utils.py index db218d14bbe..f3c202268a7 100644 --- a/invokeai/backend/patches/lora_conversions/flux_aitoolkit_lora_conversion_utils.py +++ b/invokeai/backend/patches/lora_conversions/flux_aitoolkit_lora_conversion_utils.py @@ -13,7 +13,7 @@ def is_state_dict_likely_in_flux_aitoolkit_format( - state_dict: dict[str, Any], + state_dict: dict[str | int, Any], metadata: dict[str, Any] | None = None, ) -> bool: if metadata: @@ -23,7 +23,7 @@ def is_state_dict_likely_in_flux_aitoolkit_format( return False return software.get("name") == "ai-toolkit" # metadata got lost somewhere - return any("diffusion_model" == k.split(".", 1)[0] for k in state_dict.keys()) + return any("diffusion_model" == k.split(".", 1)[0] for k in state_dict.keys() if isinstance(k, str)) @dataclass diff --git a/invokeai/backend/patches/lora_conversions/flux_control_lora_utils.py b/invokeai/backend/patches/lora_conversions/flux_control_lora_utils.py index bd2b74e6089..1762a4d5f4c 100644 --- a/invokeai/backend/patches/lora_conversions/flux_control_lora_utils.py +++ b/invokeai/backend/patches/lora_conversions/flux_control_lora_utils.py @@ -25,7 +25,9 @@ def is_state_dict_likely_flux_control(state_dict: dict[str | int, Any]) -> bool: perfect-precision detector would require checking all keys against a whitelist and verifying tensor shapes.) """ - all_keys_match = all(re.match(FLUX_CONTROL_TRANSFORMER_KEY_REGEX, str(k)) for k in state_dict.keys()) + all_keys_match = all( + re.match(FLUX_CONTROL_TRANSFORMER_KEY_REGEX, k) for k in state_dict.keys() if isinstance(k, str) + ) # Check the shape of the img_in weight, because this layer shape is modified by FLUX control LoRAs. lora_a_weight = state_dict.get("img_in.lora_A.weight", None) diff --git a/invokeai/backend/patches/lora_conversions/flux_diffusers_lora_conversion_utils.py b/invokeai/backend/patches/lora_conversions/flux_diffusers_lora_conversion_utils.py index 188d118cc4d..f5b4bc66847 100644 --- a/invokeai/backend/patches/lora_conversions/flux_diffusers_lora_conversion_utils.py +++ b/invokeai/backend/patches/lora_conversions/flux_diffusers_lora_conversion_utils.py @@ -9,14 +9,16 @@ from invokeai.backend.patches.model_patch_raw import ModelPatchRaw -def is_state_dict_likely_in_flux_diffusers_format(state_dict: Dict[str, torch.Tensor]) -> bool: +def is_state_dict_likely_in_flux_diffusers_format(state_dict: dict[str | int, torch.Tensor]) -> bool: """Checks if the provided state dict is likely in the Diffusers FLUX LoRA format. This is intended to be a reasonably high-precision detector, but it is not guaranteed to have perfect precision. (A perfect-precision detector would require checking all keys against a whitelist and verifying tensor shapes.) """ # First, check that all keys end in "lora_A.weight" or "lora_B.weight" (i.e. are in PEFT format). - all_keys_in_peft_format = all(k.endswith(("lora_A.weight", "lora_B.weight")) for k in state_dict.keys()) + all_keys_in_peft_format = all( + k.endswith(("lora_A.weight", "lora_B.weight")) for k in state_dict.keys() if isinstance(k, str) + ) # Check if keys use transformer prefix transformer_prefix_keys = [ diff --git a/invokeai/backend/patches/lora_conversions/flux_kohya_lora_conversion_utils.py b/invokeai/backend/patches/lora_conversions/flux_kohya_lora_conversion_utils.py index 7b5f3468963..f5a6830c4f1 100644 --- a/invokeai/backend/patches/lora_conversions/flux_kohya_lora_conversion_utils.py +++ b/invokeai/backend/patches/lora_conversions/flux_kohya_lora_conversion_utils.py @@ -44,7 +44,7 @@ FLUX_KOHYA_T5_KEY_REGEX = r"lora_te2_encoder_block_(\d+)_layer_(\d+)_(DenseReluDense|SelfAttention)_(\w+)_?(\w+)?\.?.*" -def is_state_dict_likely_in_flux_kohya_format(state_dict: Dict[str, Any]) -> bool: +def is_state_dict_likely_in_flux_kohya_format(state_dict: dict[str | int, Any]) -> bool: """Checks if the provided state dict is likely in the Kohya FLUX LoRA format. This is intended to be a high-precision detector, but it is not guaranteed to have perfect precision. (A @@ -56,6 +56,7 @@ def is_state_dict_likely_in_flux_kohya_format(state_dict: Dict[str, Any]) -> boo or re.match(FLUX_KOHYA_CLIP_KEY_REGEX, k) or re.match(FLUX_KOHYA_T5_KEY_REGEX, k) for k in state_dict.keys() + if isinstance(k, str) ) diff --git a/invokeai/backend/patches/lora_conversions/flux_onetrainer_lora_conversion_utils.py b/invokeai/backend/patches/lora_conversions/flux_onetrainer_lora_conversion_utils.py index 0413f0ef49f..88aeee95e49 100644 --- a/invokeai/backend/patches/lora_conversions/flux_onetrainer_lora_conversion_utils.py +++ b/invokeai/backend/patches/lora_conversions/flux_onetrainer_lora_conversion_utils.py @@ -40,7 +40,7 @@ ) -def is_state_dict_likely_in_flux_onetrainer_format(state_dict: Dict[str, Any]) -> bool: +def is_state_dict_likely_in_flux_onetrainer_format(state_dict: dict[str | int, Any]) -> bool: """Checks if the provided state dict is likely in the OneTrainer FLUX LoRA format. This is intended to be a high-precision detector, but it is not guaranteed to have perfect precision. (A @@ -53,6 +53,7 @@ def is_state_dict_likely_in_flux_onetrainer_format(state_dict: Dict[str, Any]) - or re.match(FLUX_KOHYA_CLIP_KEY_REGEX, k) or re.match(FLUX_KOHYA_T5_KEY_REGEX, k) for k in state_dict.keys() + if isinstance(k, str) ) From 29087af557821b35c1ce3a7babf93c9403a18629 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 1 Oct 2025 18:09:49 +1000 Subject: [PATCH 43/62] feat(mm): consistent naming for all model config classes --- invokeai/app/api/routers/model_manager.py | 16 +- invokeai/app/invocations/flux_ip_adapter.py | 4 +- invokeai/backend/model_manager/config.py | 203 +++++++++--------- .../model_manager/load/model_loaders/flux.py | 12 +- .../load/model_loaders/stable_diffusion.py | 32 +-- 5 files changed, 130 insertions(+), 137 deletions(-) diff --git a/invokeai/app/api/routers/model_manager.py b/invokeai/app/api/routers/model_manager.py index 6dfc58df49d..84db65252e1 100644 --- a/invokeai/app/api/routers/model_manager.py +++ b/invokeai/app/api/routers/model_manager.py @@ -31,10 +31,10 @@ from invokeai.backend.model_manager import BaseModelType, ModelFormat, ModelType from invokeai.backend.model_manager.config import ( AnyModelConfig, - Main_SD1_Checkpoint_Config, - Main_SD2_Checkpoint_Config, - Main_SDXL_Checkpoint_Config, - Main_SDXLRefiner_Checkpoint_Config, + Main_Checkpoint_SD1_Config, + Main_Checkpoint_SD2_Config, + Main_Checkpoint_SDXL_Config, + Main_Checkpoint_SDXLRefiner_Config, ) from invokeai.backend.model_manager.load.model_cache.cache_stats import CacheStats from invokeai.backend.model_manager.metadata.fetch.huggingface import HuggingFaceMetadataFetch @@ -747,10 +747,10 @@ async def convert_model( if isinstance( model_config, ( - Main_SD1_Checkpoint_Config, - Main_SD2_Checkpoint_Config, - Main_SDXL_Checkpoint_Config, - Main_SDXLRefiner_Checkpoint_Config, + Main_Checkpoint_SD1_Config, + Main_Checkpoint_SD2_Config, + Main_Checkpoint_SDXL_Config, + Main_Checkpoint_SDXLRefiner_Config, ), ): msg = f"The model with key {key} is not a main SD 1/2/XL checkpoint model." diff --git a/invokeai/app/invocations/flux_ip_adapter.py b/invokeai/app/invocations/flux_ip_adapter.py index 970f330a581..c564023a3a0 100644 --- a/invokeai/app/invocations/flux_ip_adapter.py +++ b/invokeai/app/invocations/flux_ip_adapter.py @@ -17,7 +17,7 @@ from invokeai.app.invocations.util import validate_begin_end_step, validate_weights from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.backend.model_manager.config import ( - IPAdapter_FLUX_Checkpoint_Config, + IPAdapter_Checkpoint_FLUX_Config, ) from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType @@ -67,7 +67,7 @@ def validate_begin_end_step_percent(self) -> Self: def invoke(self, context: InvocationContext) -> IPAdapterOutput: # Lookup the CLIP Vision encoder that is intended to be used with the IP-Adapter model. ip_adapter_info = context.models.get_config(self.ip_adapter_model.key) - assert isinstance(ip_adapter_info, IPAdapter_FLUX_Checkpoint_Config) + assert isinstance(ip_adapter_info, IPAdapter_Checkpoint_FLUX_Config) # Note: There is a IPAdapterInvokeAIConfig.image_encoder_model_id field, but it isn't trustworthy. image_encoder_starter_model = CLIP_VISION_MODEL_MAP[self.clip_vision_model] diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 8d432b316a1..442f9731a1b 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -806,19 +806,19 @@ def _get_weight_file_or_raise(cls, mod: ModelOnDisk) -> Path: raise NotAMatch(cls, "missing pytorch_lora_weights.bin or pytorch_lora_weights.safetensors") -class LoRA_SD1_Diffusers_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): +class LoRA_Diffusers_SD1_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class LoRA_SD2_Diffusers_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): +class LoRA_Diffusers_SD2_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class LoRA_SDXL_Diffusers_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): +class LoRA_Diffusers_SDXL_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class LoRA_FLUX_Diffusers_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): +class LoRA_Diffusers_FLUX_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) @@ -876,19 +876,19 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, "cannot determine base type") -class VAE_SD1_Checkpoint_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): +class VAE_Checkpoint_SD1_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class VAE_SD2_Checkpoint_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): +class VAE_Checkpoint_SD2_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class VAE_SDXL_Checkpoint_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): +class VAE_Checkpoint_SDXL_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class VAE_FLUX_Checkpoint_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): +class VAE_Checkpoint_FLUX_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) @@ -956,11 +956,11 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: return BaseModelType.StableDiffusion1 -class VAE_SD1_Diffusers_Config(VAE_Diffusers_Config_Base, ModelConfigBase): +class VAE_Diffusers_SD1_Config(VAE_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class VAE_SDXL_Diffusers_Config(VAE_Diffusers_Config_Base, ModelConfigBase): +class VAE_Diffusers_SDXL_Config(VAE_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) @@ -1019,30 +1019,22 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, f"unrecognized cross_attention_dim {dimension}") -class ControlNet_SD1_Diffusers_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): +class ControlNet_Diffusers_SD1_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class ControlNet_SD2_Diffusers_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): +class ControlNet_Diffusers_SD2_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class ControlNet_SDXL_Diffusers_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): +class ControlNet_Diffusers_SDXL_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class ControlNet_FLUX_Diffusers_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): +class ControlNet_Diffusers_FLUX_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) -ControlNetCheckpoint_SupportedBases: TypeAlias = Literal[ - BaseModelType.StableDiffusion1, - BaseModelType.StableDiffusion2, - BaseModelType.StableDiffusionXL, - BaseModelType.Flux, -] - - class ControlNet_Checkpoint_Config_Base(CheckpointConfigBase, ControlAdapterConfigBase): """Model config for ControlNet models (diffusers version).""" @@ -1088,7 +1080,7 @@ def _validate_looks_like_controlnet(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "state dict does not look like a ControlNet checkpoint") @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> ControlNetCheckpoint_SupportedBases: + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: state_dict = mod.load_state_dict() if is_state_dict_xlabs_controlnet(state_dict) or is_state_dict_instantx_controlnet(state_dict): @@ -1120,19 +1112,19 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> ControlNetCheckpoint_SupportedB raise NotAMatch(cls, "unable to determine base type from state dict") -class ControlNet_SD1_Checkpoint_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): +class ControlNet_Checkpoint_SD1_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class ControlNet_SD2_Checkpoint_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): +class ControlNet_Checkpoint_SD2_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class ControlNet_SDXL_Checkpoint_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): +class ControlNet_Checkpoint_SDXL_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class ControlNet_FLUX_Checkpoint_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): +class ControlNet_Checkpoint_FLUX_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) @@ -1226,15 +1218,15 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -class TI_SD1_File_Config(TI_File_Config_Base, ModelConfigBase): +class TI_File_SD1_Config(TI_File_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class TI_SD2_File_Config(TI_File_Config_Base, ModelConfigBase): +class TI_File_SD2_Config(TI_File_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class TI_SDXL_File_Config(TI_File_Config_Base, ModelConfigBase): +class TI_File_SDXL_Config(TI_File_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) @@ -1257,15 +1249,15 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: raise NotAMatch(cls, "model does not look like a textual inversion embedding folder") -class TI_SD1_Folder_Config(TI_Folder_Config_Base, ModelConfigBase): +class TI_Folder_SD1_Config(TI_Folder_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class TI_SD2_Folder_Config(TI_Folder_Config_Base, ModelConfigBase): +class TI_Folder_SD2_Config(TI_Folder_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class TI_SDXL_Folder_Config(TI_Folder_Config_Base, ModelConfigBase): +class TI_Folder_SDXL_Config(TI_Folder_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) @@ -1409,19 +1401,19 @@ def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "state dict does not look like a main model") -class Main_SD1_Checkpoint_Config(Main_Checkpoint_Config_Base, ModelConfigBase): +class Main_Checkpoint_SD1_Config(Main_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class Main_SD2_Checkpoint_Config(Main_Checkpoint_Config_Base, ModelConfigBase): +class Main_Checkpoint_SD2_Config(Main_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class Main_SDXL_Checkpoint_Config(Main_Checkpoint_Config_Base, ModelConfigBase): +class Main_Checkpoint_SDXL_Config(Main_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class Main_SDXLRefiner_Checkpoint_Config(Main_Checkpoint_Config_Base, ModelConfigBase): +class Main_Checkpoint_SDXLRefiner_Config(Main_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXLRefiner] = Field(default=BaseModelType.StableDiffusionXLRefiner) @@ -1470,7 +1462,7 @@ def _get_flux_variant(state_dict: dict[str | int, Any]) -> FluxVariantType | Non return FluxVariantType.Schnell -class Main_FLUX_Checkpoint_Config(CheckpointConfigBase, MainConfigBase, ModelConfigBase): +class Main_Checkpoint_FLUX_Config(CheckpointConfigBase, MainConfigBase, ModelConfigBase): """Model config for main checkpoint models.""" format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) @@ -1540,7 +1532,7 @@ def _validate_does_not_look_like_gguf_quantized(cls, mod: ModelOnDisk): raise NotAMatch(cls, "state dict looks like GGUF quantized") -class Main_FLUX_BnBNF4_Config(CheckpointConfigBase, MainConfigBase, ModelConfigBase): +class Main_BnBNF4_FLUX_Config(CheckpointConfigBase, MainConfigBase, ModelConfigBase): """Model config for main checkpoint models.""" base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) @@ -1589,7 +1581,7 @@ def _validate_model_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "state dict does not look like bnb quantized nf4") -class Main_FLUX_GGUF_Config(CheckpointConfigBase, MainConfigBase, ModelConfigBase): +class Main_GGUF_FLUX_Config(CheckpointConfigBase, MainConfigBase, ModelConfigBase): """Model config for main checkpoint models.""" base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) @@ -1744,23 +1736,23 @@ def _get_variant_or_raise(cls, mod: ModelOnDisk) -> ModelVariantType: raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels} for base '{base}'") -class Main_SD1_Diffusers_Config(Main_Diffusers_Config_Base, ModelConfigBase): +class Main_Diffusers_SD1_Config(Main_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion1] = Field(BaseModelType.StableDiffusion1) -class Main_SD2_Diffusers_Config(Main_Diffusers_Config_Base, ModelConfigBase): +class Main_Diffusers_SD2_Config(Main_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion2] = Field(BaseModelType.StableDiffusion2) -class Main_SDXL_Diffusers_Config(Main_Diffusers_Config_Base, ModelConfigBase): +class Main_Diffusers_SDXL_Config(Main_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXL] = Field(BaseModelType.StableDiffusionXL) -class Main_SDXLRefiner_Diffusers_Config(Main_Diffusers_Config_Base, ModelConfigBase): +class Main_Diffusers_SDXLRefiner_Config(Main_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXLRefiner] = Field(BaseModelType.StableDiffusionXLRefiner) -class Main_SD3_Diffusers_Config(DiffusersConfigBase, MainConfigBase, ModelConfigBase): +class Main_Diffusers_SD3_Config(DiffusersConfigBase, MainConfigBase, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion3] = Field(BaseModelType.StableDiffusion3) @classmethod @@ -1834,7 +1826,7 @@ def _get_submodels_or_raise(cls, mod: ModelOnDisk) -> dict[SubModelType, Submode return submodels -class Main_CogView4_Diffusers_Config(DiffusersConfigBase, MainConfigBase, ModelConfigBase): +class Main_Diffusers_CogView4_Config(DiffusersConfigBase, MainConfigBase, ModelConfigBase): base: Literal[BaseModelType.CogView4] = Field(BaseModelType.CogView4) @classmethod @@ -1927,15 +1919,15 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") -class IPAdapter_SD1_InvokeAI_Config(IPAdapter_InvokeAI_Config_Base, ModelConfigBase): +class IPAdapter_InvokeAI_SD1_Config(IPAdapter_InvokeAI_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class IPAdapter_SD2_InvokeAI_Config(IPAdapter_InvokeAI_Config_Base, ModelConfigBase): +class IPAdapter_InvokeAI_SD2_Config(IPAdapter_InvokeAI_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class IPAdapter_SDXL_InvokeAI_Config(IPAdapter_InvokeAI_Config_Base, ModelConfigBase): +class IPAdapter_InvokeAI_SDXL_Config(IPAdapter_InvokeAI_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) @@ -2000,19 +1992,19 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") -class IPAdapter_SD1_Checkpoint_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): +class IPAdapter_Checkpoint_SD1_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class IPAdapter_SD2_Checkpoint_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): +class IPAdapter_Checkpoint_SD2_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class IPAdapter_SDXL_Checkpoint_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): +class IPAdapter_Checkpoint_SDXL_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class IPAdapter_FLUX_Checkpoint_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): +class IPAdapter_Checkpoint_FLUX_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) @@ -2069,11 +2061,11 @@ def _validate_variant(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, f"variant is {recognized_variant}, not {expected_variant}") -class CLIPEmbed_G_Diffusers_Config(CLIPEmbed_Diffusers_Config_Base, ModelConfigBase): +class CLIPEmbed_Diffusers_G_Config(CLIPEmbed_Diffusers_Config_Base, ModelConfigBase): variant: Literal[ClipVariantType.G] = Field(default=ClipVariantType.G) -class CLIPEmbed_L_Diffusers_Config(CLIPEmbed_Diffusers_Config_Base, ModelConfigBase): +class CLIPEmbed_Diffusers_L_Config(CLIPEmbed_Diffusers_Config_Base, ModelConfigBase): variant: Literal[ClipVariantType.L] = Field(default=ClipVariantType.L) @@ -2148,11 +2140,11 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, f"unrecognized adapter_type '{adapter_type}'") -class T2IAdapter_SD1_Diffusers_Config(T2IAdapter_Diffusers_Config_Base, ModelConfigBase): +class T2IAdapter_Diffusers_SD1_Config(T2IAdapter_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class T2IAdapter_SDXL_Diffusers_Config(T2IAdapter_Diffusers_Config_Base, ModelConfigBase): +class T2IAdapter_Diffusers_SDXL_Config(T2IAdapter_Diffusers_Config_Base, ModelConfigBase): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) @@ -2325,39 +2317,39 @@ def get_model_discriminator_value(v: Any) -> str: AnyModelConfig = Annotated[ Union[ # Main (Pipeline) - diffusers format - Annotated[Main_SD1_Diffusers_Config, Main_SD1_Diffusers_Config.get_tag()], - Annotated[Main_SD2_Diffusers_Config, Main_SD2_Diffusers_Config.get_tag()], - Annotated[Main_SDXL_Diffusers_Config, Main_SDXL_Diffusers_Config.get_tag()], - Annotated[Main_SDXLRefiner_Diffusers_Config, Main_SDXLRefiner_Diffusers_Config.get_tag()], - Annotated[Main_SD3_Diffusers_Config, Main_SD3_Diffusers_Config.get_tag()], - Annotated[Main_CogView4_Diffusers_Config, Main_CogView4_Diffusers_Config.get_tag()], + Annotated[Main_Diffusers_SD1_Config, Main_Diffusers_SD1_Config.get_tag()], + Annotated[Main_Diffusers_SD2_Config, Main_Diffusers_SD2_Config.get_tag()], + Annotated[Main_Diffusers_SDXL_Config, Main_Diffusers_SDXL_Config.get_tag()], + Annotated[Main_Diffusers_SDXLRefiner_Config, Main_Diffusers_SDXLRefiner_Config.get_tag()], + Annotated[Main_Diffusers_SD3_Config, Main_Diffusers_SD3_Config.get_tag()], + Annotated[Main_Diffusers_CogView4_Config, Main_Diffusers_CogView4_Config.get_tag()], # Main (Pipeline) - checkpoint format - Annotated[Main_SD1_Checkpoint_Config, Main_SD1_Checkpoint_Config.get_tag()], - Annotated[Main_SD2_Checkpoint_Config, Main_SD2_Checkpoint_Config.get_tag()], - Annotated[Main_SDXL_Checkpoint_Config, Main_SDXL_Checkpoint_Config.get_tag()], - Annotated[Main_SDXLRefiner_Checkpoint_Config, Main_SDXLRefiner_Checkpoint_Config.get_tag()], - Annotated[Main_FLUX_Checkpoint_Config, Main_FLUX_Checkpoint_Config.get_tag()], + Annotated[Main_Checkpoint_SD1_Config, Main_Checkpoint_SD1_Config.get_tag()], + Annotated[Main_Checkpoint_SD2_Config, Main_Checkpoint_SD2_Config.get_tag()], + Annotated[Main_Checkpoint_SDXL_Config, Main_Checkpoint_SDXL_Config.get_tag()], + Annotated[Main_Checkpoint_SDXLRefiner_Config, Main_Checkpoint_SDXLRefiner_Config.get_tag()], + Annotated[Main_Checkpoint_FLUX_Config, Main_Checkpoint_FLUX_Config.get_tag()], # Main (Pipeline) - quantized formats - Annotated[Main_FLUX_BnBNF4_Config, Main_FLUX_BnBNF4_Config.get_tag()], - Annotated[Main_FLUX_GGUF_Config, Main_FLUX_GGUF_Config.get_tag()], + Annotated[Main_BnBNF4_FLUX_Config, Main_BnBNF4_FLUX_Config.get_tag()], + Annotated[Main_GGUF_FLUX_Config, Main_GGUF_FLUX_Config.get_tag()], # VAE - checkpoint format - Annotated[VAE_SD1_Checkpoint_Config, VAE_SD1_Checkpoint_Config.get_tag()], - Annotated[VAE_SD2_Checkpoint_Config, VAE_SD2_Checkpoint_Config.get_tag()], - Annotated[VAE_SDXL_Checkpoint_Config, VAE_SDXL_Checkpoint_Config.get_tag()], - Annotated[VAE_FLUX_Checkpoint_Config, VAE_FLUX_Checkpoint_Config.get_tag()], + Annotated[VAE_Checkpoint_SD1_Config, VAE_Checkpoint_SD1_Config.get_tag()], + Annotated[VAE_Checkpoint_SD2_Config, VAE_Checkpoint_SD2_Config.get_tag()], + Annotated[VAE_Checkpoint_SDXL_Config, VAE_Checkpoint_SDXL_Config.get_tag()], + Annotated[VAE_Checkpoint_FLUX_Config, VAE_Checkpoint_FLUX_Config.get_tag()], # VAE - diffusers format - Annotated[VAE_SD1_Diffusers_Config, VAE_SD1_Diffusers_Config.get_tag()], - Annotated[VAE_SDXL_Diffusers_Config, VAE_SDXL_Diffusers_Config.get_tag()], + Annotated[VAE_Diffusers_SD1_Config, VAE_Diffusers_SD1_Config.get_tag()], + Annotated[VAE_Diffusers_SDXL_Config, VAE_Diffusers_SDXL_Config.get_tag()], # ControlNet - checkpoint format - Annotated[ControlNet_SD1_Checkpoint_Config, ControlNet_SD1_Checkpoint_Config.get_tag()], - Annotated[ControlNet_SD2_Checkpoint_Config, ControlNet_SD2_Checkpoint_Config.get_tag()], - Annotated[ControlNet_SDXL_Checkpoint_Config, ControlNet_SDXL_Checkpoint_Config.get_tag()], - Annotated[ControlNet_FLUX_Checkpoint_Config, ControlNet_FLUX_Checkpoint_Config.get_tag()], + Annotated[ControlNet_Checkpoint_SD1_Config, ControlNet_Checkpoint_SD1_Config.get_tag()], + Annotated[ControlNet_Checkpoint_SD2_Config, ControlNet_Checkpoint_SD2_Config.get_tag()], + Annotated[ControlNet_Checkpoint_SDXL_Config, ControlNet_Checkpoint_SDXL_Config.get_tag()], + Annotated[ControlNet_Checkpoint_FLUX_Config, ControlNet_Checkpoint_FLUX_Config.get_tag()], # ControlNet - diffusers format - Annotated[ControlNet_SD1_Diffusers_Config, ControlNet_SD1_Diffusers_Config.get_tag()], - Annotated[ControlNet_SD2_Diffusers_Config, ControlNet_SD2_Diffusers_Config.get_tag()], - Annotated[ControlNet_SDXL_Diffusers_Config, ControlNet_SDXL_Diffusers_Config.get_tag()], - Annotated[ControlNet_FLUX_Diffusers_Config, ControlNet_FLUX_Diffusers_Config.get_tag()], + Annotated[ControlNet_Diffusers_SD1_Config, ControlNet_Diffusers_SD1_Config.get_tag()], + Annotated[ControlNet_Diffusers_SD2_Config, ControlNet_Diffusers_SD2_Config.get_tag()], + Annotated[ControlNet_Diffusers_SDXL_Config, ControlNet_Diffusers_SDXL_Config.get_tag()], + Annotated[ControlNet_Diffusers_FLUX_Config, ControlNet_Diffusers_FLUX_Config.get_tag()], # LoRA - LyCORIS format Annotated[LoRA_LyCORIS_SD1_Config, LoRA_LyCORIS_SD1_Config.get_tag()], Annotated[LoRA_LyCORIS_SD2_Config, LoRA_LyCORIS_SD2_Config.get_tag()], @@ -2367,38 +2359,39 @@ def get_model_discriminator_value(v: Any) -> str: Annotated[LoRA_OMI_SDXL_Config, LoRA_OMI_SDXL_Config.get_tag()], Annotated[LoRA_OMI_FLUX_Config, LoRA_OMI_FLUX_Config.get_tag()], # LoRA - diffusers format - Annotated[LoRA_SD1_Diffusers_Config, LoRA_SD1_Diffusers_Config.get_tag()], - Annotated[LoRA_SD2_Diffusers_Config, LoRA_SD2_Diffusers_Config.get_tag()], - Annotated[LoRA_SDXL_Diffusers_Config, LoRA_SDXL_Diffusers_Config.get_tag()], - Annotated[LoRA_FLUX_Diffusers_Config, LoRA_FLUX_Diffusers_Config.get_tag()], + Annotated[LoRA_Diffusers_SD1_Config, LoRA_Diffusers_SD1_Config.get_tag()], + Annotated[LoRA_Diffusers_SD2_Config, LoRA_Diffusers_SD2_Config.get_tag()], + Annotated[LoRA_Diffusers_SDXL_Config, LoRA_Diffusers_SDXL_Config.get_tag()], + Annotated[LoRA_Diffusers_FLUX_Config, LoRA_Diffusers_FLUX_Config.get_tag()], # ControlLoRA - diffusers format Annotated[ControlLoRA_LyCORIS_FLUX_Config, ControlLoRA_LyCORIS_FLUX_Config.get_tag()], + # T5 Encoder - all formats Annotated[T5Encoder_T5Encoder_Config, T5Encoder_T5Encoder_Config.get_tag()], Annotated[T5Encoder_BnBLLMint8_Config, T5Encoder_BnBLLMint8_Config.get_tag()], # TI - file format - Annotated[TI_SD1_File_Config, TI_SD1_File_Config.get_tag()], - Annotated[TI_SD2_File_Config, TI_SD2_File_Config.get_tag()], - Annotated[TI_SDXL_File_Config, TI_SDXL_File_Config.get_tag()], + Annotated[TI_File_SD1_Config, TI_File_SD1_Config.get_tag()], + Annotated[TI_File_SD2_Config, TI_File_SD2_Config.get_tag()], + Annotated[TI_File_SDXL_Config, TI_File_SDXL_Config.get_tag()], # TI - folder format - Annotated[TI_SD1_Folder_Config, TI_SD1_Folder_Config.get_tag()], - Annotated[TI_SD2_Folder_Config, TI_SD2_Folder_Config.get_tag()], - Annotated[TI_SDXL_Folder_Config, TI_SDXL_Folder_Config.get_tag()], + Annotated[TI_Folder_SD1_Config, TI_Folder_SD1_Config.get_tag()], + Annotated[TI_Folder_SD2_Config, TI_Folder_SD2_Config.get_tag()], + Annotated[TI_Folder_SDXL_Config, TI_Folder_SDXL_Config.get_tag()], # IP Adapter - InvokeAI format - Annotated[IPAdapter_SD1_InvokeAI_Config, IPAdapter_SD1_InvokeAI_Config.get_tag()], - Annotated[IPAdapter_SD2_InvokeAI_Config, IPAdapter_SD2_InvokeAI_Config.get_tag()], - Annotated[IPAdapter_SDXL_InvokeAI_Config, IPAdapter_SDXL_InvokeAI_Config.get_tag()], + Annotated[IPAdapter_InvokeAI_SD1_Config, IPAdapter_InvokeAI_SD1_Config.get_tag()], + Annotated[IPAdapter_InvokeAI_SD2_Config, IPAdapter_InvokeAI_SD2_Config.get_tag()], + Annotated[IPAdapter_InvokeAI_SDXL_Config, IPAdapter_InvokeAI_SDXL_Config.get_tag()], # IP Adapter - checkpoint format - Annotated[IPAdapter_SD1_Checkpoint_Config, IPAdapter_SD1_Checkpoint_Config.get_tag()], - Annotated[IPAdapter_SD2_Checkpoint_Config, IPAdapter_SD2_Checkpoint_Config.get_tag()], - Annotated[IPAdapter_SDXL_Checkpoint_Config, IPAdapter_SDXL_Checkpoint_Config.get_tag()], - Annotated[IPAdapter_FLUX_Checkpoint_Config, IPAdapter_FLUX_Checkpoint_Config.get_tag()], + Annotated[IPAdapter_Checkpoint_SD1_Config, IPAdapter_Checkpoint_SD1_Config.get_tag()], + Annotated[IPAdapter_Checkpoint_SD2_Config, IPAdapter_Checkpoint_SD2_Config.get_tag()], + Annotated[IPAdapter_Checkpoint_SDXL_Config, IPAdapter_Checkpoint_SDXL_Config.get_tag()], + Annotated[IPAdapter_Checkpoint_FLUX_Config, IPAdapter_Checkpoint_FLUX_Config.get_tag()], # T2I Adapter - diffusers format - Annotated[T2IAdapter_SD1_Diffusers_Config, T2IAdapter_SD1_Diffusers_Config.get_tag()], - Annotated[T2IAdapter_SDXL_Diffusers_Config, T2IAdapter_SDXL_Diffusers_Config.get_tag()], + Annotated[T2IAdapter_Diffusers_SD1_Config, T2IAdapter_Diffusers_SD1_Config.get_tag()], + Annotated[T2IAdapter_Diffusers_SDXL_Config, T2IAdapter_Diffusers_SDXL_Config.get_tag()], # Misc models Annotated[Spandrel_Checkpoint_Config, Spandrel_Checkpoint_Config.get_tag()], - Annotated[CLIPEmbed_G_Diffusers_Config, CLIPEmbed_G_Diffusers_Config.get_tag()], - Annotated[CLIPEmbed_L_Diffusers_Config, CLIPEmbed_L_Diffusers_Config.get_tag()], + Annotated[CLIPEmbed_Diffusers_G_Config, CLIPEmbed_Diffusers_G_Config.get_tag()], + Annotated[CLIPEmbed_Diffusers_L_Config, CLIPEmbed_Diffusers_L_Config.get_tag()], Annotated[CLIPVision_Diffusers_Config, CLIPVision_Diffusers_Config.get_tag()], Annotated[SigLIP_Diffusers_Config, SigLIP_Diffusers_Config.get_tag()], Annotated[FLUXRedux_Checkpoint_Config, FLUXRedux_Checkpoint_Config.get_tag()], diff --git a/invokeai/backend/model_manager/load/model_loaders/flux.py b/invokeai/backend/model_manager/load/model_loaders/flux.py index 4bb24b74665..9340cdd21a0 100644 --- a/invokeai/backend/model_manager/load/model_loaders/flux.py +++ b/invokeai/backend/model_manager/load/model_loaders/flux.py @@ -42,9 +42,9 @@ ControlNet_Diffusers_Config_Base, FLUXRedux_Checkpoint_Config, IPAdapter_Checkpoint_Config_Base, - Main_FLUX_BnBNF4_Config, - Main_FLUX_Checkpoint_Config, - Main_FLUX_GGUF_Config, + Main_BnBNF4_FLUX_Config, + Main_Checkpoint_FLUX_Config, + Main_GGUF_FLUX_Config, T5Encoder_BnBLLMint8_Config, T5Encoder_T5Encoder_Config, VAE_Checkpoint_Config_Base, @@ -226,7 +226,7 @@ def _load_from_singlefile( self, config: AnyModelConfig, ) -> AnyModel: - assert isinstance(config, Main_FLUX_Checkpoint_Config) + assert isinstance(config, Main_Checkpoint_FLUX_Config) model_path = Path(config.path) with accelerate.init_empty_weights(): @@ -268,7 +268,7 @@ def _load_from_singlefile( self, config: AnyModelConfig, ) -> AnyModel: - assert isinstance(config, Main_FLUX_GGUF_Config) + assert isinstance(config, Main_GGUF_FLUX_Config) model_path = Path(config.path) with accelerate.init_empty_weights(): @@ -314,7 +314,7 @@ def _load_from_singlefile( self, config: AnyModelConfig, ) -> AnyModel: - assert isinstance(config, Main_FLUX_BnBNF4_Config) + assert isinstance(config, Main_BnBNF4_FLUX_Config) if not bnb_available: raise ImportError( "The bnb modules are not available. Please install bitsandbytes if available on your platform." diff --git a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py index ab7982394b9..5b7791a4bae 100644 --- a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py +++ b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py @@ -15,14 +15,14 @@ AnyModelConfig, CheckpointConfigBase, DiffusersConfigBase, - Main_SD1_Checkpoint_Config, - Main_SD1_Diffusers_Config, - Main_SD2_Checkpoint_Config, - Main_SD2_Diffusers_Config, - Main_SDXL_Checkpoint_Config, - Main_SDXL_Diffusers_Config, - Main_SDXLRefiner_Checkpoint_Config, - Main_SDXLRefiner_Diffusers_Config, + Main_Checkpoint_SD1_Config, + Main_Diffusers_SD1_Config, + Main_Checkpoint_SD2_Config, + Main_Diffusers_SD2_Config, + Main_Checkpoint_SDXL_Config, + Main_Diffusers_SDXL_Config, + Main_Checkpoint_SDXLRefiner_Config, + Main_Diffusers_SDXLRefiner_Config, ) from invokeai.backend.model_manager.load.model_cache.model_cache import get_model_cache_key from invokeai.backend.model_manager.load.model_loader_registry import ModelLoaderRegistry @@ -117,14 +117,14 @@ def _load_from_singlefile( assert isinstance( config, ( - Main_SD1_Diffusers_Config, - Main_SD2_Diffusers_Config, - Main_SDXL_Diffusers_Config, - Main_SDXLRefiner_Diffusers_Config, - Main_SD1_Checkpoint_Config, - Main_SD2_Checkpoint_Config, - Main_SDXL_Checkpoint_Config, - Main_SDXLRefiner_Checkpoint_Config, + Main_Diffusers_SD1_Config, + Main_Diffusers_SD2_Config, + Main_Diffusers_SDXL_Config, + Main_Diffusers_SDXLRefiner_Config, + Main_Checkpoint_SD1_Config, + Main_Checkpoint_SD2_Config, + Main_Checkpoint_SDXL_Config, + Main_Checkpoint_SDXLRefiner_Config, ), ) try: From 32a9ad14db90406e4288fbc57ed677c078dfb125 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:47:57 +1000 Subject: [PATCH 44/62] fix(mm): tag generation & scattered probe fixes --- invokeai/backend/model_manager/config.py | 177 ++++++++++++------ .../load/model_loaders/stable_diffusion.py | 6 +- 2 files changed, 119 insertions(+), 64 deletions(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index 442f9731a1b..c82b0673bdc 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -146,19 +146,19 @@ def validate_field(model: type[BaseModel], field_name: str, value: Any) -> Any: return FieldValidator.get_validator(model, field_name).validate_python(value) -def has_keys_exact(state_dict: dict[str | int, Any], keys: str | set[str]) -> bool: - """Returns true if the state dict has all of the specified keys.""" +def has_any_keys(state_dict: dict[str | int, Any], keys: str | set[str]) -> bool: + """Returns true if the state dict has any of the specified keys.""" _keys = {keys} if isinstance(keys, str) else keys - return _keys.issubset({key for key in state_dict.keys() if isinstance(key, str)}) + return any(key in state_dict for key in _keys) -def has_keys_starting_with(state_dict: dict[str | int, Any], prefixes: str | set[str]) -> bool: +def has_any_keys_starting_with(state_dict: dict[str | int, Any], prefixes: str | set[str]) -> bool: """Returns true if the state dict has any keys starting with any of the specified prefixes.""" _prefixes = {prefixes} if isinstance(prefixes, str) else prefixes return any(any(key.startswith(prefix) for prefix in _prefixes) for key in state_dict.keys() if isinstance(key, str)) -def has_keys_ending_with(state_dict: dict[str | int, Any], suffixes: str | set[str]) -> bool: +def has_any_keys_ending_with(state_dict: dict[str | int, Any], suffixes: str | set[str]) -> bool: """Returns true if the state dict has any keys ending with any of the specified suffixes.""" _suffixes = {suffixes} if isinstance(suffixes, str) else suffixes return any(any(key.endswith(suffix) for suffix in _suffixes) for key in state_dict.keys() if isinstance(key, str)) @@ -408,14 +408,63 @@ def __pydantic_init_subclass__(cls, **kwargs): @classmethod def get_tag(cls) -> Tag: + """Constructs a pydantic discriminated union tag for this model config class. When a config is deserialized, + pydantic uses the tag to determine which subclass to instantiate. + + The tag is a dot-separated string of the type, format, base and variant (if applicable). + """ tag_strings: list[str] = [] - for name in ("type", "base", "format", "variant"): + for name in ("type", "format", "base", "variant"): if field := cls.model_fields.get(name): if field.default is not PydanticUndefined: - # We assume each of these fields has an Enum for its default - tag_strings.append(str(field.default.value)) + # We expect each of these fields has an Enum for its default; we want the value of the enum. + tag_strings.append(field.default.value) return Tag(".".join(tag_strings)) + @staticmethod + def get_model_discriminator_value(v: Any) -> str: + """ + Computes the discriminator value for a model config. + https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions-with-callable-discriminator + """ + if isinstance(v, ModelConfigBase): + # We have an instance of a ModelConfigBase subclass - use its tag directly. + return v.get_tag().tag + if isinstance(v, dict): + # We have a dict - compute the tag from its fields. + tag_strings: list[str] = [] + if type_ := v.get("type"): + if isinstance(type_, Enum): + type_ = type_.value + tag_strings.append(type_) + + if format_ := v.get("format"): + if isinstance(format_, Enum): + format_ = format_.value + tag_strings.append(format_) + + if base_ := v.get("base"): + if isinstance(base_, Enum): + base_ = base_.value + tag_strings.append(base_) + + # Special case: CLIP Embed models also need the variant to distinguish them. + if ( + type_ == ModelType.CLIPEmbed.value + and format_ == ModelFormat.Diffusers.value + and base_ == BaseModelType.Any.value + ): + if variant_value := v.get("variant"): + if isinstance(variant_value, Enum): + variant_value = variant_value.value + tag_strings.append(variant_value) + else: + raise ValueError("CLIP Embed model config dict must include a 'variant' field") + + return ".".join(tag_strings) + else: + raise TypeError("Model config discriminator value must be computed from a dict or ModelConfigBase instance") + @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: """Given the model on disk and any overrides, return an instance of this config class. @@ -536,7 +585,7 @@ def _validate_filename_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: @classmethod def _validate_model_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: - has_scb_key_suffix = has_keys_ending_with(mod.load_state_dict(), "SCB") + has_scb_key_suffix = has_any_keys_ending_with(mod.load_state_dict(), "SCB") if not has_scb_key_suffix: raise NotAMatch(cls, "state dict does not look like bnb quantized llm_int8") @@ -578,7 +627,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_base(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @@ -643,7 +692,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_base(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @@ -657,7 +706,7 @@ def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: # Note: Existence of these key prefixes/suffixes does not guarantee that this is a LoRA. # Some main models have these keys, likely due to the creator merging in a LoRA. - has_key_with_lora_prefix = has_keys_starting_with( + has_key_with_lora_prefix = has_any_keys_starting_with( mod.load_state_dict(), { "lora_te_", @@ -668,7 +717,7 @@ def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: }, ) - has_key_with_lora_suffix = has_keys_ending_with( + has_key_with_lora_suffix = has_any_keys_ending_with( mod.load_state_dict(), { "to_k_lora.up.weight", @@ -769,7 +818,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_base(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @@ -850,14 +899,14 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_base(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @classmethod def _validate_looks_like_vae(cls, mod: ModelOnDisk) -> None: - if not has_keys_starting_with( + if not has_any_keys_starting_with( mod.load_state_dict(), { "encoder.conv_in", @@ -920,7 +969,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_base(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @@ -992,7 +1041,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_base(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @@ -1056,14 +1105,14 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_base(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @classmethod def _validate_looks_like_controlnet(cls, mod: ModelOnDisk) -> None: - if has_keys_starting_with( + if has_any_keys_starting_with( mod.load_state_dict(), { "controlnet", @@ -1134,7 +1183,7 @@ class TI_Config_Base(ABC, BaseModel): @classmethod def _validate_base(cls, mod: ModelOnDisk, path: Path | None = None) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod, path) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @@ -1330,7 +1379,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_base(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @@ -1355,7 +1404,7 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: @classmethod def _get_scheduler_prediction_type_or_raise(cls, mod: ModelOnDisk) -> SchedulerPredictionType: - base = cls.model_fields["base"].default.value + base = cls.model_fields["base"].default if base is BaseModelType.StableDiffusion2: state_dict = mod.load_state_dict() @@ -1372,7 +1421,7 @@ def _get_scheduler_prediction_type_or_raise(cls, mod: ModelOnDisk) -> SchedulerP @classmethod def _get_variant_or_raise(cls, mod: ModelOnDisk) -> ModelVariantType: - base = cls.model_fields["base"].default.value + base = cls.model_fields["base"].default state_dict = mod.load_state_dict() key_name = "model.diffusion_model.input_blocks.0.0.weight" @@ -1490,7 +1539,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_is_flux(cls, mod: ModelOnDisk) -> None: - if not has_keys_exact( + if not has_any_keys( mod.load_state_dict(), { "double_blocks.0.img_attn.norm.key_norm.scale", @@ -1675,7 +1724,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_base(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @@ -1719,7 +1768,7 @@ def _get_scheduler_prediction_type_or_raise(cls, mod: ModelOnDisk) -> SchedulerP @classmethod def _get_variant_or_raise(cls, mod: ModelOnDisk) -> ModelVariantType: - base = cls.model_fields["base"].default.value + base = cls.model_fields["base"].default unet_config = _get_config_or_raise(cls, mod.path / "unet" / "config.json") in_channels = unet_config.get("in_channels") @@ -1882,7 +1931,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_base(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @@ -1951,14 +2000,14 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_base(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @classmethod def _validate_looks_like_ip_adapter(cls, mod: ModelOnDisk) -> None: - if not has_keys_starting_with( + if not has_any_keys_starting_with( mod.load_state_dict(), { "image_proj.", @@ -2035,7 +2084,10 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: _validate_class_name( cls, - common_config_paths(mod.path), + { + mod.path / "config.json", + mod.path / "text_encoder" / "config.json", + }, { "CLIPModel", "CLIPTextModel", @@ -2050,8 +2102,14 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_variant(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model variant does not match this config class.""" - expected_variant = cls.model_fields["variant"].default.value - config = _get_config_or_raise(cls, common_config_paths(mod.path)) + expected_variant = cls.model_fields["variant"].default + config = _get_config_or_raise( + cls, + { + mod.path / "config.json", + mod.path / "text_encoder" / "config.json", + }, + ) recognized_variant = _get_clip_variant_type_from_config(config) if recognized_variant is None: @@ -2120,7 +2178,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: @classmethod def _validate_base(cls, mod: ModelOnDisk) -> None: """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default.value + expected_base = cls.model_fields["base"].default recognized_base = cls._get_base_or_raise(mod) if expected_base is not recognized_base: raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") @@ -2294,24 +2352,6 @@ class ExternalAPI_Runway_Config(ExternalAPI_Config_Base, VideoConfigBase, ModelC base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) -def get_model_discriminator_value(v: Any) -> str: - """ - Computes the discriminator value for a model config. - https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions-with-callable-discriminator - """ - if isinstance(v, ModelConfigBase): - return v.get_tag().tag - - tag_strings: list[str] = [] - for name in ("type", "base", "format", "variant"): - field_value = v.get(name) - if isinstance(field_value, Enum): - field_value = field_value.value - if field_value is not None: - tag_strings.append(field_value) - return ".".join(tag_strings) - - # The types are listed explicitly because IDEs/LSPs can't identify the correct types # when AnyModelConfig is constructed dynamically using ModelConfigBase.all_config_classes AnyModelConfig = Annotated[ @@ -2407,7 +2447,7 @@ def get_model_discriminator_value(v: Any) -> str: # Unknown model (fallback) Annotated[Unknown_Config, Unknown_Config.get_tag()], ], - Discriminator(get_model_discriminator_value), + Discriminator(ModelConfigBase.get_model_discriminator_value), ] AnyModelConfigValidator = TypeAdapter[AnyModelConfig](AnyModelConfig) @@ -2513,19 +2553,34 @@ def from_model_on_disk( matches = [r for r in results.values() if isinstance(r, ModelConfigBase)] if not matches and app_config.allow_unknown_models: - logger.warning(f"Unable to identify model {mod.name}, classifying as UnknownModelConfig") - logger.debug(f"Model matching results: {results}") + logger.warning(f"Unable to identify model {mod.name}, falling back to Unknown_Config") return Unknown_Config(**fields) - instance = next(iter(matches)) if len(matches) > 1: - # TODO(psyche): When we get multiple matches, at most only 1 will be correct. We should disambiguate the - # matches, probably on a case-by-case basis. + # We have multiple matches, in which case at most 1 is correct. We need to pick one. # - # One known case is certain SD main (pipeline) models can look like a LoRA. This could happen if the model - # contains merged in LoRA weights. + # Known cases: + # - SD main models can look like a LoRA when they have merged in LoRA weights. Prefer the main model. + # - SD main models in diffusers format can look like a CLIP Embed; they have a text_encoder folder with + # a config.json file. Prefer the main model. + + # Sort the matching according to known special cases. + def sort_key(m: AnyModelConfig) -> int: + match m.type: + case ModelType.Main: + return 0 + case ModelType.LoRA: + return 1 + case ModelType.CLIPEmbed: + return 2 + case _: + return 3 + + matches.sort(key=sort_key) logger.warning( - f"Multiple model config classes matched for model {mod.name}: {[type(m).__name__ for m in matches]}. Using {type(instance).__name__}." + f"Multiple model config classes matched for model {mod.name}: {[type(m).__name__ for m in matches]}. Using {type(matches[0]).__name__}." ) + + instance = matches[0] logger.info(f"Model {mod.name} classified as {type(instance).__name__}") return instance diff --git a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py index 5b7791a4bae..c8c751134c8 100644 --- a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py +++ b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py @@ -16,12 +16,12 @@ CheckpointConfigBase, DiffusersConfigBase, Main_Checkpoint_SD1_Config, - Main_Diffusers_SD1_Config, Main_Checkpoint_SD2_Config, - Main_Diffusers_SD2_Config, Main_Checkpoint_SDXL_Config, - Main_Diffusers_SDXL_Config, Main_Checkpoint_SDXLRefiner_Config, + Main_Diffusers_SD1_Config, + Main_Diffusers_SD2_Config, + Main_Diffusers_SDXL_Config, Main_Diffusers_SDXLRefiner_Config, ) from invokeai.backend.model_manager.load.model_cache.model_cache import get_model_cache_key From 508c48832fb234b361524291dda70f9ab04dfbd4 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 2 Oct 2025 13:38:12 +1000 Subject: [PATCH 45/62] tidy(mm): consistent class names --- .../app/invocations/create_gradient_mask.py | 4 +- invokeai/app/invocations/flux_model_loader.py | 4 +- .../model_install/model_install_default.py | 4 +- .../app/services/shared/invocation_context.py | 4 +- invokeai/backend/model_manager/__init__.py | 4 +- invokeai/backend/model_manager/config.py | 192 +++++++++--------- .../model_manager/load/load_default.py | 4 +- .../load/model_loader_registry.py | 6 +- .../load/model_loaders/clip_vision.py | 4 +- .../load/model_loaders/cogview4.py | 8 +- .../model_manager/load/model_loaders/flux.py | 8 +- .../load/model_loaders/generic_diffusers.py | 4 +- .../load/model_loaders/stable_diffusion.py | 8 +- tests/test_model_probe.py | 22 +- 14 files changed, 137 insertions(+), 139 deletions(-) diff --git a/invokeai/app/invocations/create_gradient_mask.py b/invokeai/app/invocations/create_gradient_mask.py index b232fbbc932..f6e046d096e 100644 --- a/invokeai/app/invocations/create_gradient_mask.py +++ b/invokeai/app/invocations/create_gradient_mask.py @@ -21,7 +21,7 @@ from invokeai.app.invocations.model import UNetField, VAEField from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.backend.model_manager import LoadedModel -from invokeai.backend.model_manager.config import MainConfigBase +from invokeai.backend.model_manager.config import Main_Config_Base from invokeai.backend.model_manager.taxonomy import ModelVariantType from invokeai.backend.stable_diffusion.diffusers_pipeline import image_resized_to_grid_as_tensor @@ -182,7 +182,7 @@ def invoke(self, context: InvocationContext) -> GradientMaskOutput: if self.unet is not None and self.vae is not None and self.image is not None: # all three fields must be present at the same time main_model_config = context.models.get_config(self.unet.unet.key) - assert isinstance(main_model_config, MainConfigBase) + assert isinstance(main_model_config, Main_Config_Base) if main_model_config.variant is ModelVariantType.Inpaint: mask = dilated_mask_tensor vae_info: LoadedModel = context.models.load(self.vae.vae) diff --git a/invokeai/app/invocations/flux_model_loader.py b/invokeai/app/invocations/flux_model_loader.py index 4ed3b91bc61..2803db48e02 100644 --- a/invokeai/app/invocations/flux_model_loader.py +++ b/invokeai/app/invocations/flux_model_loader.py @@ -15,7 +15,7 @@ ) from invokeai.backend.flux.util import get_flux_max_seq_length from invokeai.backend.model_manager.config import ( - CheckpointConfigBase, + Checkpoint_Config_Base, ) from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType, SubModelType @@ -87,7 +87,7 @@ def invoke(self, context: InvocationContext) -> FluxModelLoaderOutput: t5_encoder = preprocess_t5_encoder_model_identifier(self.t5_encoder_model) transformer_config = context.models.get_config(transformer) - assert isinstance(transformer_config, CheckpointConfigBase) + assert isinstance(transformer_config, Checkpoint_Config_Base) return FluxModelLoaderOutput( transformer=TransformerField(transformer=transformer, loras=[]), diff --git a/invokeai/app/services/model_install/model_install_default.py b/invokeai/app/services/model_install/model_install_default.py index 06608df8e80..10a954a5636 100644 --- a/invokeai/app/services/model_install/model_install_default.py +++ b/invokeai/app/services/model_install/model_install_default.py @@ -37,7 +37,7 @@ from invokeai.app.services.model_records.model_records_base import ModelRecordChanges from invokeai.backend.model_manager.config import ( AnyModelConfig, - CheckpointConfigBase, + Checkpoint_Config_Base, InvalidModelConfigException, ModelConfigFactory, ) @@ -625,7 +625,7 @@ def _register( info.path = model_path.as_posix() - if isinstance(info, CheckpointConfigBase) and info.config_path is not None: + if isinstance(info, Checkpoint_Config_Base) and info.config_path is not None: # Checkpoints have a config file needed for conversion. Same handling as the model weights - if it's in the # invoke-managed legacy config dir, we use a relative path. legacy_config_path = self.app_config.legacy_conf_path / info.config_path diff --git a/invokeai/app/services/shared/invocation_context.py b/invokeai/app/services/shared/invocation_context.py index 743b6208ead..16aacbb9855 100644 --- a/invokeai/app/services/shared/invocation_context.py +++ b/invokeai/app/services/shared/invocation_context.py @@ -21,7 +21,7 @@ from invokeai.app.util.step_callback import diffusion_step_callback from invokeai.backend.model_manager.config import ( AnyModelConfig, - ModelConfigBase, + Config_Base, ) from invokeai.backend.model_manager.load.load_base import LoadedModel, LoadedModelWithoutConfig from invokeai.backend.model_manager.taxonomy import AnyModel, BaseModelType, ModelFormat, ModelType, SubModelType @@ -558,7 +558,7 @@ def get_absolute_path(self, config_or_path: AnyModelConfig | Path | str) -> Path The absolute path to the model. """ - model_path = Path(config_or_path.path) if isinstance(config_or_path, ModelConfigBase) else Path(config_or_path) + model_path = Path(config_or_path.path) if isinstance(config_or_path, Config_Base) else Path(config_or_path) if model_path.is_absolute(): return model_path.resolve() diff --git a/invokeai/backend/model_manager/__init__.py b/invokeai/backend/model_manager/__init__.py index dca72f170e0..a167687d2e6 100644 --- a/invokeai/backend/model_manager/__init__.py +++ b/invokeai/backend/model_manager/__init__.py @@ -3,7 +3,7 @@ from invokeai.backend.model_manager.config import ( AnyModelConfig, InvalidModelConfigException, - ModelConfigBase, + Config_Base, ModelConfigFactory, ) from invokeai.backend.model_manager.legacy_probe import ModelProbe @@ -30,7 +30,7 @@ "ModelConfigFactory", "ModelProbe", "ModelSearch", - "ModelConfigBase", + "Config_Base", "AnyModel", "AnyVariant", "BaseModelType", diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index c82b0673bdc..188ac9ad11f 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -35,7 +35,6 @@ Optional, Self, Type, - TypeAlias, Union, ) @@ -321,7 +320,7 @@ class LegacyProbeMixin: pass -class ModelConfigBase(ABC, BaseModel): +class Config_Base(ABC, BaseModel): """ Abstract base class for model configurations. A model config describes a specific combination of model base, type and format, along with other metadata about the model. For example, a Stable Diffusion 1.x main model in checkpoint format @@ -427,7 +426,7 @@ def get_model_discriminator_value(v: Any) -> str: Computes the discriminator value for a model config. https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions-with-callable-discriminator """ - if isinstance(v, ModelConfigBase): + if isinstance(v, Config_Base): # We have an instance of a ModelConfigBase subclass - use its tag directly. return v.get_tag().tag if isinstance(v, dict): @@ -473,7 +472,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: raise NotImplementedError(f"from_model_on_disk not implemented for {cls.__name__}") -class Unknown_Config(ModelConfigBase): +class Unknown_Config(Config_Base): """Model config for unknown models, used as a fallback when we cannot identify a model.""" base: Literal[BaseModelType.Unknown] = Field(default=BaseModelType.Unknown) @@ -485,7 +484,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: raise NotAMatch(cls, "unknown model config cannot match any model") -class CheckpointConfigBase(ABC, BaseModel): +class Checkpoint_Config_Base(ABC, BaseModel): """Base class for checkpoint-style models.""" config_path: str | None = Field( @@ -498,7 +497,7 @@ class CheckpointConfigBase(ABC, BaseModel): ) -class DiffusersConfigBase(ABC, BaseModel): +class Diffusers_Config_Base(ABC, BaseModel): """Base class for diffusers-style models.""" format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) @@ -521,7 +520,7 @@ def _get_repo_variant_or_raise(cls, mod: ModelOnDisk) -> ModelRepoVariant: return ModelRepoVariant.Default -class T5Encoder_T5Encoder_Config(ModelConfigBase): +class T5Encoder_T5Encoder_Config(Config_Base): base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) type: Literal[ModelType.T5Encoder] = Field(default=ModelType.T5Encoder) format: Literal[ModelFormat.T5Encoder] = Field(default=ModelFormat.T5Encoder) @@ -552,7 +551,7 @@ def _validate_has_unquantized_config_file(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "missing text_encoder_2/model.safetensors.index.json") -class T5Encoder_BnBLLMint8_Config(ModelConfigBase): +class T5Encoder_BnBLLMint8_Config(Config_Base): base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) type: Literal[ModelType.T5Encoder] = Field(default=ModelType.T5Encoder) format: Literal[ModelFormat.BnbQuantizedLlmInt8b] = Field(default=ModelFormat.BnbQuantizedLlmInt8b) @@ -590,7 +589,7 @@ def _validate_model_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "state dict does not look like bnb quantized llm_int8") -class LoRAConfigBase(ABC, BaseModel): +class LoRA_Config_Base(ABC, BaseModel): """Base class for LoRA models.""" type: Literal[ModelType.LoRA] = Field(default=ModelType.LoRA) @@ -609,7 +608,7 @@ def _get_flux_lora_format(mod: ModelOnDisk) -> FluxLoRAFormat | None: return value -class LoRA_OMI_Config_Base(LoRAConfigBase): +class LoRA_OMI_Config_Base(LoRA_Config_Base): format: Literal[ModelFormat.OMI] = Field(default=ModelFormat.OMI) @classmethod @@ -663,15 +662,15 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> Literal[BaseModelType.Flux, Bas raise NotAMatch(cls, f"unrecognised/unsupported architecture for OMI LoRA: {architecture}") -class LoRA_OMI_SDXL_Config(LoRA_OMI_Config_Base, ModelConfigBase): +class LoRA_OMI_SDXL_Config(LoRA_OMI_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class LoRA_OMI_FLUX_Config(LoRA_OMI_Config_Base, ModelConfigBase): +class LoRA_OMI_FLUX_Config(LoRA_OMI_Config_Base, Config_Base): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) -class LoRA_LyCORIS_Config_Base(LoRAConfigBase): +class LoRA_LyCORIS_Config_Base(LoRA_Config_Base): """Model config for LoRA/Lycoris models.""" type: Literal[ModelType.LoRA] = Field(default=ModelType.LoRA) @@ -750,27 +749,27 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, f"unrecognized token vector length {token_vector_length}") -class LoRA_LyCORIS_SD1_Config(LoRA_LyCORIS_Config_Base, ModelConfigBase): +class LoRA_LyCORIS_SD1_Config(LoRA_LyCORIS_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class LoRA_LyCORIS_SD2_Config(LoRA_LyCORIS_Config_Base, ModelConfigBase): +class LoRA_LyCORIS_SD2_Config(LoRA_LyCORIS_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class LoRA_LyCORIS_SDXL_Config(LoRA_LyCORIS_Config_Base, ModelConfigBase): +class LoRA_LyCORIS_SDXL_Config(LoRA_LyCORIS_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class LoRA_LyCORIS_FLUX_Config(LoRA_LyCORIS_Config_Base, ModelConfigBase): +class LoRA_LyCORIS_FLUX_Config(LoRA_LyCORIS_Config_Base, Config_Base): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) -class ControlAdapterConfigBase(ABC, BaseModel): +class ControlAdapter_Config_Base(ABC, BaseModel): default_settings: ControlAdapterDefaultSettings | None = Field(None) -class ControlLoRA_LyCORIS_FLUX_Config(ControlAdapterConfigBase, ModelConfigBase): +class ControlLoRA_LyCORIS_FLUX_Config(ControlAdapter_Config_Base, Config_Base): """Model config for Control LoRA models.""" base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) @@ -797,7 +796,7 @@ def _validate_looks_like_control_lora(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "model state dict does not look like a Flux Control LoRA") -class LoRA_Diffusers_Config_Base(LoRAConfigBase): +class LoRA_Diffusers_Config_Base(LoRA_Config_Base): """Model config for LoRA/Diffusers models.""" # TODO(psyche): Needs base handling. For FLUX, the Diffusers format does not indicate a folder model; it indicates @@ -855,23 +854,23 @@ def _get_weight_file_or_raise(cls, mod: ModelOnDisk) -> Path: raise NotAMatch(cls, "missing pytorch_lora_weights.bin or pytorch_lora_weights.safetensors") -class LoRA_Diffusers_SD1_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): +class LoRA_Diffusers_SD1_Config(LoRA_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class LoRA_Diffusers_SD2_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): +class LoRA_Diffusers_SD2_Config(LoRA_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class LoRA_Diffusers_SDXL_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): +class LoRA_Diffusers_SDXL_Config(LoRA_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class LoRA_Diffusers_FLUX_Config(LoRA_Diffusers_Config_Base, ModelConfigBase): +class LoRA_Diffusers_FLUX_Config(LoRA_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) -class VAE_Checkpoint_Config_Base(CheckpointConfigBase): +class VAE_Checkpoint_Config_Base(Checkpoint_Config_Base): """Model config for standalone VAE models.""" type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) @@ -925,23 +924,23 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, "cannot determine base type") -class VAE_Checkpoint_SD1_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): +class VAE_Checkpoint_SD1_Config(VAE_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class VAE_Checkpoint_SD2_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): +class VAE_Checkpoint_SD2_Config(VAE_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class VAE_Checkpoint_SDXL_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): +class VAE_Checkpoint_SDXL_Config(VAE_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class VAE_Checkpoint_FLUX_Config(VAE_Checkpoint_Config_Base, ModelConfigBase): +class VAE_Checkpoint_FLUX_Config(VAE_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) -class VAE_Diffusers_Config_Base(DiffusersConfigBase): +class VAE_Diffusers_Config_Base(Diffusers_Config_Base): """Model config for standalone VAE models (diffusers version).""" type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) @@ -1005,15 +1004,15 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: return BaseModelType.StableDiffusion1 -class VAE_Diffusers_SD1_Config(VAE_Diffusers_Config_Base, ModelConfigBase): +class VAE_Diffusers_SD1_Config(VAE_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class VAE_Diffusers_SDXL_Config(VAE_Diffusers_Config_Base, ModelConfigBase): +class VAE_Diffusers_SDXL_Config(VAE_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class ControlNet_Diffusers_Config_Base(DiffusersConfigBase, ControlAdapterConfigBase): +class ControlNet_Diffusers_Config_Base(Diffusers_Config_Base, ControlAdapter_Config_Base): """Model config for ControlNet models (diffusers version).""" type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) @@ -1068,23 +1067,23 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, f"unrecognized cross_attention_dim {dimension}") -class ControlNet_Diffusers_SD1_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): +class ControlNet_Diffusers_SD1_Config(ControlNet_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class ControlNet_Diffusers_SD2_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): +class ControlNet_Diffusers_SD2_Config(ControlNet_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class ControlNet_Diffusers_SDXL_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): +class ControlNet_Diffusers_SDXL_Config(ControlNet_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class ControlNet_Diffusers_FLUX_Config(ControlNet_Diffusers_Config_Base, ModelConfigBase): +class ControlNet_Diffusers_FLUX_Config(ControlNet_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) -class ControlNet_Checkpoint_Config_Base(CheckpointConfigBase, ControlAdapterConfigBase): +class ControlNet_Checkpoint_Config_Base(Checkpoint_Config_Base, ControlAdapter_Config_Base): """Model config for ControlNet models (diffusers version).""" type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) @@ -1161,19 +1160,19 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, "unable to determine base type from state dict") -class ControlNet_Checkpoint_SD1_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): +class ControlNet_Checkpoint_SD1_Config(ControlNet_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class ControlNet_Checkpoint_SD2_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): +class ControlNet_Checkpoint_SD2_Config(ControlNet_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class ControlNet_Checkpoint_SDXL_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): +class ControlNet_Checkpoint_SDXL_Config(ControlNet_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class ControlNet_Checkpoint_FLUX_Config(ControlNet_Checkpoint_Config_Base, ModelConfigBase): +class ControlNet_Checkpoint_FLUX_Config(ControlNet_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) @@ -1267,15 +1266,15 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -class TI_File_SD1_Config(TI_File_Config_Base, ModelConfigBase): +class TI_File_SD1_Config(TI_File_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class TI_File_SD2_Config(TI_File_Config_Base, ModelConfigBase): +class TI_File_SD2_Config(TI_File_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class TI_File_SDXL_Config(TI_File_Config_Base, ModelConfigBase): +class TI_File_SDXL_Config(TI_File_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) @@ -1298,19 +1297,19 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: raise NotAMatch(cls, "model does not look like a textual inversion embedding folder") -class TI_Folder_SD1_Config(TI_Folder_Config_Base, ModelConfigBase): +class TI_Folder_SD1_Config(TI_Folder_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class TI_Folder_SD2_Config(TI_Folder_Config_Base, ModelConfigBase): +class TI_Folder_SD2_Config(TI_Folder_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class TI_Folder_SDXL_Config(TI_Folder_Config_Base, ModelConfigBase): +class TI_Folder_SDXL_Config(TI_Folder_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class MainConfigBase(ABC, BaseModel): +class Main_Config_Base(ABC, BaseModel): type: Literal[ModelType.Main] = Field(default=ModelType.Main) trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None) default_settings: Optional[MainModelDefaultSettings] = Field( @@ -1352,7 +1351,7 @@ def _has_main_keys(state_dict: dict[str | int, Any]) -> bool: return False -class Main_Checkpoint_Config_Base(CheckpointConfigBase, MainConfigBase): +class Main_Checkpoint_Config_Base(Checkpoint_Config_Base, Main_Config_Base): """Model config for main checkpoint models.""" format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) @@ -1450,19 +1449,19 @@ def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "state dict does not look like a main model") -class Main_Checkpoint_SD1_Config(Main_Checkpoint_Config_Base, ModelConfigBase): +class Main_Checkpoint_SD1_Config(Main_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class Main_Checkpoint_SD2_Config(Main_Checkpoint_Config_Base, ModelConfigBase): +class Main_Checkpoint_SD2_Config(Main_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class Main_Checkpoint_SDXL_Config(Main_Checkpoint_Config_Base, ModelConfigBase): +class Main_Checkpoint_SDXL_Config(Main_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class Main_Checkpoint_SDXLRefiner_Config(Main_Checkpoint_Config_Base, ModelConfigBase): +class Main_Checkpoint_SDXLRefiner_Config(Main_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXLRefiner] = Field(default=BaseModelType.StableDiffusionXLRefiner) @@ -1511,7 +1510,7 @@ def _get_flux_variant(state_dict: dict[str | int, Any]) -> FluxVariantType | Non return FluxVariantType.Schnell -class Main_Checkpoint_FLUX_Config(CheckpointConfigBase, MainConfigBase, ModelConfigBase): +class Main_Checkpoint_FLUX_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base): """Model config for main checkpoint models.""" format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) @@ -1581,7 +1580,7 @@ def _validate_does_not_look_like_gguf_quantized(cls, mod: ModelOnDisk): raise NotAMatch(cls, "state dict looks like GGUF quantized") -class Main_BnBNF4_FLUX_Config(CheckpointConfigBase, MainConfigBase, ModelConfigBase): +class Main_BnBNF4_FLUX_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base): """Model config for main checkpoint models.""" base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) @@ -1630,7 +1629,7 @@ def _validate_model_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "state dict does not look like bnb quantized nf4") -class Main_GGUF_FLUX_Config(CheckpointConfigBase, MainConfigBase, ModelConfigBase): +class Main_GGUF_FLUX_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base): """Model config for main checkpoint models.""" base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) @@ -1679,7 +1678,7 @@ def _validate_looks_like_gguf_quantized(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "state dict does not look like GGUF quantized") -class Main_Diffusers_Config_Base(DiffusersConfigBase, MainConfigBase): +class Main_Diffusers_Config_Base(Diffusers_Config_Base, Main_Config_Base): prediction_type: SchedulerPredictionType = Field() variant: ModelVariantType = Field() @@ -1785,23 +1784,23 @@ def _get_variant_or_raise(cls, mod: ModelOnDisk) -> ModelVariantType: raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels} for base '{base}'") -class Main_Diffusers_SD1_Config(Main_Diffusers_Config_Base, ModelConfigBase): +class Main_Diffusers_SD1_Config(Main_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(BaseModelType.StableDiffusion1) -class Main_Diffusers_SD2_Config(Main_Diffusers_Config_Base, ModelConfigBase): +class Main_Diffusers_SD2_Config(Main_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion2] = Field(BaseModelType.StableDiffusion2) -class Main_Diffusers_SDXL_Config(Main_Diffusers_Config_Base, ModelConfigBase): +class Main_Diffusers_SDXL_Config(Main_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(BaseModelType.StableDiffusionXL) -class Main_Diffusers_SDXLRefiner_Config(Main_Diffusers_Config_Base, ModelConfigBase): +class Main_Diffusers_SDXLRefiner_Config(Main_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXLRefiner] = Field(BaseModelType.StableDiffusionXLRefiner) -class Main_Diffusers_SD3_Config(DiffusersConfigBase, MainConfigBase, ModelConfigBase): +class Main_Diffusers_SD3_Config(Diffusers_Config_Base, Main_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion3] = Field(BaseModelType.StableDiffusion3) @classmethod @@ -1875,7 +1874,7 @@ def _get_submodels_or_raise(cls, mod: ModelOnDisk) -> dict[SubModelType, Submode return submodels -class Main_Diffusers_CogView4_Config(DiffusersConfigBase, MainConfigBase, ModelConfigBase): +class Main_Diffusers_CogView4_Config(Diffusers_Config_Base, Main_Config_Base, Config_Base): base: Literal[BaseModelType.CogView4] = Field(BaseModelType.CogView4) @classmethod @@ -1901,11 +1900,11 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: ) -class IPAdapterConfigBase(ABC, BaseModel): +class IPAdapter_Config_Base(ABC, BaseModel): type: Literal[ModelType.IPAdapter] = Field(default=ModelType.IPAdapter) -class IPAdapter_InvokeAI_Config_Base(IPAdapterConfigBase): +class IPAdapter_InvokeAI_Config_Base(IPAdapter_Config_Base): """Model config for IP Adapter diffusers format models.""" format: Literal[ModelFormat.InvokeAI] = Field(default=ModelFormat.InvokeAI) @@ -1968,19 +1967,19 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") -class IPAdapter_InvokeAI_SD1_Config(IPAdapter_InvokeAI_Config_Base, ModelConfigBase): +class IPAdapter_InvokeAI_SD1_Config(IPAdapter_InvokeAI_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class IPAdapter_InvokeAI_SD2_Config(IPAdapter_InvokeAI_Config_Base, ModelConfigBase): +class IPAdapter_InvokeAI_SD2_Config(IPAdapter_InvokeAI_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class IPAdapter_InvokeAI_SDXL_Config(IPAdapter_InvokeAI_Config_Base, ModelConfigBase): +class IPAdapter_InvokeAI_SDXL_Config(IPAdapter_InvokeAI_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class IPAdapter_Checkpoint_Config_Base(IPAdapterConfigBase): +class IPAdapter_Checkpoint_Config_Base(IPAdapter_Config_Base): """Model config for IP Adapter checkpoint format models.""" format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) @@ -2041,19 +2040,19 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") -class IPAdapter_Checkpoint_SD1_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): +class IPAdapter_Checkpoint_SD1_Config(IPAdapter_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class IPAdapter_Checkpoint_SD2_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): +class IPAdapter_Checkpoint_SD2_Config(IPAdapter_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) -class IPAdapter_Checkpoint_SDXL_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): +class IPAdapter_Checkpoint_SDXL_Config(IPAdapter_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class IPAdapter_Checkpoint_FLUX_Config(IPAdapter_Checkpoint_Config_Base, ModelConfigBase): +class IPAdapter_Checkpoint_FLUX_Config(IPAdapter_Checkpoint_Config_Base, Config_Base): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) @@ -2071,7 +2070,7 @@ def _get_clip_variant_type_from_config(config: dict[str, Any]) -> ClipVariantTyp return None -class CLIPEmbed_Diffusers_Config_Base(DiffusersConfigBase): +class CLIPEmbed_Diffusers_Config_Base(Diffusers_Config_Base): base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) type: Literal[ModelType.CLIPEmbed] = Field(default=ModelType.CLIPEmbed) format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) @@ -2119,15 +2118,15 @@ def _validate_variant(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, f"variant is {recognized_variant}, not {expected_variant}") -class CLIPEmbed_Diffusers_G_Config(CLIPEmbed_Diffusers_Config_Base, ModelConfigBase): +class CLIPEmbed_Diffusers_G_Config(CLIPEmbed_Diffusers_Config_Base, Config_Base): variant: Literal[ClipVariantType.G] = Field(default=ClipVariantType.G) -class CLIPEmbed_Diffusers_L_Config(CLIPEmbed_Diffusers_Config_Base, ModelConfigBase): +class CLIPEmbed_Diffusers_L_Config(CLIPEmbed_Diffusers_Config_Base, Config_Base): variant: Literal[ClipVariantType.L] = Field(default=ClipVariantType.L) -class CLIPVision_Diffusers_Config(DiffusersConfigBase, ModelConfigBase): +class CLIPVision_Diffusers_Config(Diffusers_Config_Base, Config_Base): """Model config for CLIPVision.""" base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) @@ -2151,7 +2150,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -class T2IAdapter_Diffusers_Config_Base(DiffusersConfigBase, ControlAdapterConfigBase): +class T2IAdapter_Diffusers_Config_Base(Diffusers_Config_Base, ControlAdapter_Config_Base): """Model config for T2I.""" type: Literal[ModelType.T2IAdapter] = Field(default=ModelType.T2IAdapter) @@ -2198,15 +2197,15 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatch(cls, f"unrecognized adapter_type '{adapter_type}'") -class T2IAdapter_Diffusers_SD1_Config(T2IAdapter_Diffusers_Config_Base, ModelConfigBase): +class T2IAdapter_Diffusers_SD1_Config(T2IAdapter_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) -class T2IAdapter_Diffusers_SDXL_Config(T2IAdapter_Diffusers_Config_Base, ModelConfigBase): +class T2IAdapter_Diffusers_SDXL_Config(T2IAdapter_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) -class Spandrel_Checkpoint_Config(ModelConfigBase): +class Spandrel_Checkpoint_Config(Config_Base): """Model config for Spandrel Image to Image models.""" base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) @@ -2239,7 +2238,7 @@ def _validate_spandrel_loads_model(cls, mod: ModelOnDisk) -> None: raise NotAMatch(cls, "model does not match SpandrelImageToImage heuristics") from e -class SigLIP_Diffusers_Config(DiffusersConfigBase, ModelConfigBase): +class SigLIP_Diffusers_Config(Diffusers_Config_Base, Config_Base): """Model config for SigLIP.""" type: Literal[ModelType.SigLIP] = Field(default=ModelType.SigLIP) @@ -2263,7 +2262,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -class FLUXRedux_Checkpoint_Config(ModelConfigBase): +class FLUXRedux_Checkpoint_Config(Config_Base): """Model config for FLUX Tools Redux model.""" type: Literal[ModelType.FluxRedux] = Field(default=ModelType.FluxRedux) @@ -2282,7 +2281,7 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: return cls(**fields) -class LlavaOnevision_Diffusers_Config(DiffusersConfigBase, ModelConfigBase): +class LlavaOnevision_Diffusers_Config(Diffusers_Config_Base, Config_Base): """Model config for Llava Onevision models.""" type: Literal[ModelType.LlavaOnevision] = Field(default=ModelType.LlavaOnevision) @@ -2316,23 +2315,23 @@ def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: raise NotAMatch(cls, "External API models cannot be built from disk") -class ExternalAPI_ChatGPT4o_Config(ExternalAPI_Config_Base, MainConfigBase, ModelConfigBase): +class ExternalAPI_ChatGPT4o_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): base: Literal[BaseModelType.ChatGPT4o] = Field(default=BaseModelType.ChatGPT4o) -class ExternalAPI_Gemini2_5_Config(ExternalAPI_Config_Base, MainConfigBase, ModelConfigBase): +class ExternalAPI_Gemini2_5_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): base: Literal[BaseModelType.Gemini2_5] = Field(default=BaseModelType.Gemini2_5) -class ExternalAPI_Imagen3_Config(ExternalAPI_Config_Base, MainConfigBase, ModelConfigBase): +class ExternalAPI_Imagen3_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): base: Literal[BaseModelType.Imagen3] = Field(default=BaseModelType.Imagen3) -class ExternalAPI_Imagen4_Config(ExternalAPI_Config_Base, MainConfigBase, ModelConfigBase): +class ExternalAPI_Imagen4_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): base: Literal[BaseModelType.Imagen4] = Field(default=BaseModelType.Imagen4) -class ExternalAPI_FluxKontext_Config(ExternalAPI_Config_Base, MainConfigBase, ModelConfigBase): +class ExternalAPI_FluxKontext_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) @@ -2344,11 +2343,11 @@ class VideoConfigBase(ABC, BaseModel): ) -class ExternalAPI_Veo3_Config(ExternalAPI_Config_Base, VideoConfigBase, ModelConfigBase): +class ExternalAPI_Veo3_Config(ExternalAPI_Config_Base, VideoConfigBase, Config_Base): base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) -class ExternalAPI_Runway_Config(ExternalAPI_Config_Base, VideoConfigBase, ModelConfigBase): +class ExternalAPI_Runway_Config(ExternalAPI_Config_Base, VideoConfigBase, Config_Base): base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) @@ -2447,11 +2446,10 @@ class ExternalAPI_Runway_Config(ExternalAPI_Config_Base, VideoConfigBase, ModelC # Unknown model (fallback) Annotated[Unknown_Config, Unknown_Config.get_tag()], ], - Discriminator(ModelConfigBase.get_model_discriminator_value), + Discriminator(Config_Base.get_model_discriminator_value), ] AnyModelConfigValidator = TypeAdapter[AnyModelConfig](AnyModelConfig) -AnyDefaultSettings: TypeAlias = Union[MainModelDefaultSettings, LoraModelDefaultSettings, ControlAdapterDefaultSettings] class ModelConfigFactory: @@ -2459,7 +2457,7 @@ class ModelConfigFactory: def make_config(model_data: Dict[str, Any], timestamp: Optional[float] = None) -> AnyModelConfig: """Return the appropriate config object from raw dict values.""" model = AnyModelConfigValidator.validate_python(model_data) - if isinstance(model, CheckpointConfigBase) and timestamp: + if isinstance(model, Checkpoint_Config_Base) and timestamp: model.converted_at = timestamp validate_hash(model.hash) return model @@ -2533,7 +2531,7 @@ def from_model_on_disk( # Try to build an instance of each model config class that uses the classify API. # Each class will either return an instance of itself or raise NotAMatch if it doesn't match. # Other exceptions may be raised if something unexpected happens during matching or building. - for config_class in ModelConfigBase.CONFIG_CLASSES: + for config_class in Config_Base.CONFIG_CLASSES: class_name = config_class.__name__ try: instance = config_class.from_model_on_disk(mod, fields) @@ -2550,7 +2548,7 @@ def from_model_on_disk( results[class_name] = e logger.warning(f"Unexpected exception while matching {mod.name} to {config_class.__name__}: {e}") - matches = [r for r in results.values() if isinstance(r, ModelConfigBase)] + matches = [r for r in results.values() if isinstance(r, Config_Base)] if not matches and app_config.allow_unknown_models: logger.warning(f"Unable to identify model {mod.name}, falling back to Unknown_Config") diff --git a/invokeai/backend/model_manager/load/load_default.py b/invokeai/backend/model_manager/load/load_default.py index 3c26a956b76..139a7d2940b 100644 --- a/invokeai/backend/model_manager/load/load_default.py +++ b/invokeai/backend/model_manager/load/load_default.py @@ -6,7 +6,7 @@ from typing import Optional from invokeai.app.services.config import InvokeAIAppConfig -from invokeai.backend.model_manager.config import AnyModelConfig, DiffusersConfigBase, InvalidModelConfigException +from invokeai.backend.model_manager.config import AnyModelConfig, Diffusers_Config_Base, InvalidModelConfigException from invokeai.backend.model_manager.load.load_base import LoadedModel, ModelLoaderBase from invokeai.backend.model_manager.load.model_cache.cache_record import CacheRecord from invokeai.backend.model_manager.load.model_cache.model_cache import ModelCache, get_model_cache_key @@ -90,7 +90,7 @@ def get_size_fs( return calc_model_size_by_fs( model_path=model_path, subfolder=submodel_type.value if submodel_type else None, - variant=config.repo_variant if isinstance(config, DiffusersConfigBase) else None, + variant=config.repo_variant if isinstance(config, Diffusers_Config_Base) else None, ) # This needs to be implemented in the subclass diff --git a/invokeai/backend/model_manager/load/model_loader_registry.py b/invokeai/backend/model_manager/load/model_loader_registry.py index ecc4d1fe93b..9b242fe1673 100644 --- a/invokeai/backend/model_manager/load/model_loader_registry.py +++ b/invokeai/backend/model_manager/load/model_loader_registry.py @@ -20,7 +20,7 @@ from invokeai.backend.model_manager.config import ( AnyModelConfig, - ModelConfigBase, + Config_Base, ) from invokeai.backend.model_manager.load import ModelLoaderBase from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType, SubModelType @@ -40,7 +40,7 @@ def register( @abstractmethod def get_implementation( cls, config: AnyModelConfig, submodel_type: Optional[SubModelType] - ) -> Tuple[Type[ModelLoaderBase], ModelConfigBase, Optional[SubModelType]]: + ) -> Tuple[Type[ModelLoaderBase], Config_Base, Optional[SubModelType]]: """ Get subclass of ModelLoaderBase registered to handle base and type. @@ -84,7 +84,7 @@ def decorator(subclass: Type[TModelLoader]) -> Type[TModelLoader]: @classmethod def get_implementation( cls, config: AnyModelConfig, submodel_type: Optional[SubModelType] - ) -> Tuple[Type[ModelLoaderBase], ModelConfigBase, Optional[SubModelType]]: + ) -> Tuple[Type[ModelLoaderBase], Config_Base, Optional[SubModelType]]: """Get subclass of ModelLoaderBase registered to handle base and type.""" key1 = cls._to_registry_key(config.base, config.type, config.format) # for a specific base type diff --git a/invokeai/backend/model_manager/load/model_loaders/clip_vision.py b/invokeai/backend/model_manager/load/model_loaders/clip_vision.py index 29d7bc691cf..9065e51fbfb 100644 --- a/invokeai/backend/model_manager/load/model_loaders/clip_vision.py +++ b/invokeai/backend/model_manager/load/model_loaders/clip_vision.py @@ -5,7 +5,7 @@ from invokeai.backend.model_manager.config import ( AnyModelConfig, - DiffusersConfigBase, + Diffusers_Config_Base, ) from invokeai.backend.model_manager.load.load_default import ModelLoader from invokeai.backend.model_manager.load.model_loader_registry import ModelLoaderRegistry @@ -21,7 +21,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if not isinstance(config, DiffusersConfigBase): + if not isinstance(config, Diffusers_Config_Base): raise ValueError("Only DiffusersConfigBase models are currently supported here.") if submodel_type is not None: diff --git a/invokeai/backend/model_manager/load/model_loaders/cogview4.py b/invokeai/backend/model_manager/load/model_loaders/cogview4.py index e7669a33c42..a1a9269edbe 100644 --- a/invokeai/backend/model_manager/load/model_loaders/cogview4.py +++ b/invokeai/backend/model_manager/load/model_loaders/cogview4.py @@ -5,8 +5,8 @@ from invokeai.backend.model_manager.config import ( AnyModelConfig, - CheckpointConfigBase, - DiffusersConfigBase, + Checkpoint_Config_Base, + Diffusers_Config_Base, ) from invokeai.backend.model_manager.load.model_loader_registry import ModelLoaderRegistry from invokeai.backend.model_manager.load.model_loaders.generic_diffusers import GenericDiffusersLoader @@ -28,7 +28,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if isinstance(config, CheckpointConfigBase): + if isinstance(config, Checkpoint_Config_Base): raise NotImplementedError("CheckpointConfigBase is not implemented for CogView4 models.") if submodel_type is None: @@ -36,7 +36,7 @@ def _load_model( model_path = Path(config.path) load_class = self.get_hf_load_class(model_path, submodel_type) - repo_variant = config.repo_variant if isinstance(config, DiffusersConfigBase) else None + 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 diff --git a/invokeai/backend/model_manager/load/model_loaders/flux.py b/invokeai/backend/model_manager/load/model_loaders/flux.py index 9340cdd21a0..07967c7c565 100644 --- a/invokeai/backend/model_manager/load/model_loaders/flux.py +++ b/invokeai/backend/model_manager/load/model_loaders/flux.py @@ -36,7 +36,7 @@ from invokeai.backend.flux.util import get_flux_ae_params, get_flux_transformers_params from invokeai.backend.model_manager.config import ( AnyModelConfig, - CheckpointConfigBase, + Checkpoint_Config_Base, CLIPEmbed_Diffusers_Config_Base, ControlNet_Checkpoint_Config_Base, ControlNet_Diffusers_Config_Base, @@ -211,7 +211,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if not isinstance(config, CheckpointConfigBase): + if not isinstance(config, Checkpoint_Config_Base): raise ValueError("Only CheckpointConfigBase models are currently supported here.") match submodel_type: @@ -253,7 +253,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if not isinstance(config, CheckpointConfigBase): + if not isinstance(config, Checkpoint_Config_Base): raise ValueError("Only CheckpointConfigBase models are currently supported here.") match submodel_type: @@ -299,7 +299,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if not isinstance(config, CheckpointConfigBase): + if not isinstance(config, Checkpoint_Config_Base): raise ValueError("Only CheckpointConfigBase models are currently supported here.") match submodel_type: diff --git a/invokeai/backend/model_manager/load/model_loaders/generic_diffusers.py b/invokeai/backend/model_manager/load/model_loaders/generic_diffusers.py index 8a690583d5d..407a116b681 100644 --- a/invokeai/backend/model_manager/load/model_loaders/generic_diffusers.py +++ b/invokeai/backend/model_manager/load/model_loaders/generic_diffusers.py @@ -8,7 +8,7 @@ from diffusers.configuration_utils import ConfigMixin from diffusers.models.modeling_utils import ModelMixin -from invokeai.backend.model_manager.config import AnyModelConfig, DiffusersConfigBase, InvalidModelConfigException +from invokeai.backend.model_manager.config import AnyModelConfig, Diffusers_Config_Base, InvalidModelConfigException 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.taxonomy import ( @@ -33,7 +33,7 @@ def _load_model( model_class = self.get_hf_load_class(model_path) if submodel_type is not None: raise Exception(f"There are no submodels in models of type {model_class}") - repo_variant = config.repo_variant if isinstance(config, DiffusersConfigBase) else None + repo_variant = config.repo_variant if isinstance(config, Diffusers_Config_Base) else None variant = repo_variant.value if repo_variant else None try: result: AnyModel = model_class.from_pretrained(model_path, torch_dtype=self._torch_dtype, variant=variant) diff --git a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py index c8c751134c8..647ad4dbf43 100644 --- a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py +++ b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py @@ -13,8 +13,8 @@ from invokeai.backend.model_manager.config import ( AnyModelConfig, - CheckpointConfigBase, - DiffusersConfigBase, + Checkpoint_Config_Base, + Diffusers_Config_Base, Main_Checkpoint_SD1_Config, Main_Checkpoint_SD2_Config, Main_Checkpoint_SDXL_Config, @@ -65,7 +65,7 @@ def _load_model( config: AnyModelConfig, submodel_type: Optional[SubModelType] = None, ) -> AnyModel: - if isinstance(config, CheckpointConfigBase): + if isinstance(config, Checkpoint_Config_Base): return self._load_from_singlefile(config, submodel_type) if submodel_type is None: @@ -73,7 +73,7 @@ def _load_model( model_path = Path(config.path) load_class = self.get_hf_load_class(model_path, submodel_type) - repo_variant = config.repo_variant if isinstance(config, DiffusersConfigBase) else None + 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 try: diff --git a/tests/test_model_probe.py b/tests/test_model_probe.py index e24a3ac8bd7..03a7428382a 100644 --- a/tests/test_model_probe.py +++ b/tests/test_model_probe.py @@ -15,7 +15,7 @@ AnyModelConfig, InvalidModelConfigException, MainDiffusersConfig, - ModelConfigBase, + Config_Base, ModelConfigFactory, get_model_discriminator_value, ) @@ -109,7 +109,7 @@ def test_probe_sd1_diffusers_inpainting(datadir: Path): assert config.repo_variant is ModelRepoVariant.FP16 -class MinimalConfigExample(ModelConfigBase): +class MinimalConfigExample(Config_Base): type: ModelType = ModelType.Main format: ModelFormat = ModelFormat.Checkpoint fun_quote: str @@ -175,10 +175,10 @@ def test_regression_against_model_probe(datadir: Path, override_model_loading): assert legacy_config.model_dump_json() == new_config.model_dump_json() elif legacy_config: - assert type(legacy_config) in ModelConfigBase.USING_LEGACY_PROBE + assert type(legacy_config) in Config_Base.USING_LEGACY_PROBE elif new_config: - assert type(new_config) in ModelConfigBase.USING_CLASSIFY_API + assert type(new_config) in Config_Base.USING_CLASSIFY_API else: raise ValueError(f"Both probe and classify failed to classify model at path {path}.") @@ -186,7 +186,7 @@ def test_regression_against_model_probe(datadir: Path, override_model_loading): config_type = type(legacy_config or new_config) configs_with_tests.add(config_type) - untested_configs = ModelConfigBase.all_config_classes() - configs_with_tests - {MinimalConfigExample} + untested_configs = Config_Base.all_config_classes() - configs_with_tests - {MinimalConfigExample} logger.warning(f"Function test_regression_against_model_probe missing test case for: {untested_configs}") @@ -206,7 +206,7 @@ def test_serialisation_roundtrip(): We need to ensure they are de-serialised into the original config with all relevant fields restored. """ excluded = {MinimalConfigExample} - for config_cls in ModelConfigBase.all_config_classes() - excluded: + for config_cls in Config_Base.all_config_classes() - excluded: trials_per_class = 50 configs_with_random_data = create_fake_configs(config_cls, trials_per_class) @@ -221,7 +221,7 @@ def test_serialisation_roundtrip(): def test_discriminator_tagging_for_config_instances(): """Verify that each ModelConfig instance is assigned the correct, unique Pydantic discriminator tag.""" excluded = {MinimalConfigExample} - config_classes = ModelConfigBase.all_config_classes() - excluded + config_classes = Config_Base.all_config_classes() - excluded tags = {c.get_tag() for c in config_classes} assert len(tags) == len(config_classes), "Each config should have its own unique tag" @@ -246,10 +246,10 @@ def test_inheritance_order(): It may be worth rethinking our config taxonomy in the future, but in the meantime this test can help prevent debugging effort. """ - for config_cls in ModelConfigBase.all_config_classes(): + for config_cls in Config_Base.all_config_classes(): excluded = {abc.ABC, pydantic.BaseModel, object} inheritance_list = [cls for cls in config_cls.mro() if cls not in excluded] - assert inheritance_list[-1] is ModelConfigBase + assert inheritance_list[-1] is Config_Base def test_any_model_config_includes_all_config_classes(): @@ -262,7 +262,7 @@ def test_any_model_config_includes_all_config_classes(): config_class, _ = get_args(annotated_pair) extracted.add(config_class) - expected = set(ModelConfigBase.all_config_classes()) - {MinimalConfigExample} + expected = set(Config_Base.all_config_classes()) - {MinimalConfigExample} assert extracted == expected @@ -270,7 +270,7 @@ def test_config_uniquely_matches_model(datadir: Path): model_paths = ModelSearch().search(datadir / "stripped_models") for path in model_paths: mod = StrippedModelOnDisk(path) - matches = {cls for cls in ModelConfigBase.USING_CLASSIFY_API if cls.matches(mod)} + matches = {cls for cls in Config_Base.USING_CLASSIFY_API if cls.matches(mod)} assert len(matches) <= 1, f"Model at path {path} matches multiple config classes: {matches}" if not matches: logger.warning(f"Model at path {path} does not match any config classes using classify API.") From aea7e0fea430ea3af669e4acfa85f4aa2cd79442 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 3 Oct 2025 19:24:29 +1000 Subject: [PATCH 46/62] refactor(mm): split configs into separate files --- .../backend/model_manager/configs/__init__.py | 0 .../backend/model_manager/configs/base.py | 243 ++++++ .../model_manager/configs/clip_embed.py | 91 +++ .../model_manager/configs/clip_vision.py | 44 ++ .../model_manager/configs/controlnet.py | 195 +++++ .../model_manager/configs/external_api.py | 0 .../backend/model_manager/configs/factory.py | 340 +++++++++ .../model_manager/configs/flux_redux.py | 40 + .../configs/identification_utils.py | 182 +++++ .../model_manager/configs/ip_adapter.py | 180 +++++ .../model_manager/configs/llava_onevision.py | 42 ++ .../backend/model_manager/configs/lora.py | 323 ++++++++ .../backend/model_manager/configs/main.py | 692 ++++++++++++++++++ .../backend/model_manager/configs/siglip.py | 44 ++ .../backend/model_manager/configs/spandrel.py | 54 ++ .../model_manager/configs/t2i_adapter.py | 79 ++ .../model_manager/configs/t5_encoder.py | 87 +++ .../configs/textual_inversion.py | 156 ++++ .../backend/model_manager/configs/unknown.py | 24 + invokeai/backend/model_manager/configs/vae.py | 166 +++++ 20 files changed, 2982 insertions(+) create mode 100644 invokeai/backend/model_manager/configs/__init__.py create mode 100644 invokeai/backend/model_manager/configs/base.py create mode 100644 invokeai/backend/model_manager/configs/clip_embed.py create mode 100644 invokeai/backend/model_manager/configs/clip_vision.py create mode 100644 invokeai/backend/model_manager/configs/controlnet.py create mode 100644 invokeai/backend/model_manager/configs/external_api.py create mode 100644 invokeai/backend/model_manager/configs/factory.py create mode 100644 invokeai/backend/model_manager/configs/flux_redux.py create mode 100644 invokeai/backend/model_manager/configs/identification_utils.py create mode 100644 invokeai/backend/model_manager/configs/ip_adapter.py create mode 100644 invokeai/backend/model_manager/configs/llava_onevision.py create mode 100644 invokeai/backend/model_manager/configs/lora.py create mode 100644 invokeai/backend/model_manager/configs/main.py create mode 100644 invokeai/backend/model_manager/configs/siglip.py create mode 100644 invokeai/backend/model_manager/configs/spandrel.py create mode 100644 invokeai/backend/model_manager/configs/t2i_adapter.py create mode 100644 invokeai/backend/model_manager/configs/t5_encoder.py create mode 100644 invokeai/backend/model_manager/configs/textual_inversion.py create mode 100644 invokeai/backend/model_manager/configs/unknown.py create mode 100644 invokeai/backend/model_manager/configs/vae.py diff --git a/invokeai/backend/model_manager/configs/__init__.py b/invokeai/backend/model_manager/configs/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/invokeai/backend/model_manager/configs/base.py b/invokeai/backend/model_manager/configs/base.py new file mode 100644 index 00000000000..9e997e4bcd9 --- /dev/null +++ b/invokeai/backend/model_manager/configs/base.py @@ -0,0 +1,243 @@ +from abc import ABC, abstractmethod +from enum import Enum +from inspect import isabstract +from typing import ( + TYPE_CHECKING, + Any, + ClassVar, + Literal, + Self, + Type, +) + +from pydantic import BaseModel, ConfigDict, Field, Tag +from pydantic_core import PydanticUndefined + +from invokeai.app.util.misc import uuid_string +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + AnyVariant, + BaseModelType, + ModelFormat, + ModelRepoVariant, + ModelSourceType, + ModelType, +) + +if TYPE_CHECKING: + pass + + +class Config_Base(ABC, BaseModel): + """ + Abstract base class for model configurations. A model config describes a specific combination of model base, type and + format, along with other metadata about the model. For example, a Stable Diffusion 1.x main model in checkpoint format + would have base=sd-1, type=main, format=checkpoint. + + To create a new config type, inherit from this class and implement its interface: + - Define method 'from_model_on_disk' that returns an instance of the class or raises NotAMatch. This method will be + called during model installation to determine the correct config class for a model. + - Define fields 'type', 'base' and 'format' as pydantic fields. These should be Literals with a single value. A + default must be provided for each of these fields. + + If multiple combinations of base, type and format need to be supported, create a separate subclass for each. + + See MinimalConfigExample in test_model_probe.py for an example implementation. + """ + + # These fields are common to all model configs. + + key: str = Field( + default_factory=uuid_string, + description="A unique key for this model.", + ) + hash: str = Field( + description="The hash of the model file(s).", + ) + path: str = Field( + description="Path to the model on the filesystem. Relative paths are relative to the Invoke root directory.", + ) + file_size: int = Field( + description="The size of the model in bytes.", + ) + name: str = Field( + description="Name of the model.", + ) + description: str | None = Field( + default=None, + description="Model description", + ) + source: str = Field( + description="The original source of the model (path, URL or repo_id).", + ) + source_type: ModelSourceType = Field( + description="The type of source", + ) + source_api_response: str | None = Field( + default=None, + description="The original API response from the source, as stringified JSON.", + ) + cover_image: str | None = Field( + default=None, + description="Url for image to preview model", + ) + usage_info: str | None = Field( + default=None, + description="Usage information for this model", + ) + + CONFIG_CLASSES: ClassVar[set[Type["Config_Base"]]] = set() + """Set of all non-abstract subclasses of Config_Base, for use during model probing. In other words, this is the set + of all known model config types.""" + + model_config = ConfigDict( + validate_assignment=True, + json_schema_serialization_defaults_required=True, + json_schema_mode_override="serialization", + ) + + @classmethod + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + # Register non-abstract subclasses so we can iterate over them later during model probing. Note that + # isabstract() will return False if the class does not have any abstract methods, even if it inherits from ABC. + # We must check for ABC lest we unintentionally register some abstract model config classes. + if not isabstract(cls) and ABC not in cls.__bases__: + cls.CONFIG_CLASSES.add(cls) + + @classmethod + def __pydantic_init_subclass__(cls, **kwargs): + # Ensure that model configs define 'base', 'type' and 'format' fields and provide defaults for them. Each + # subclass is expected to represent a single combination of base, type and format. + # + # This pydantic dunder method is called after the pydantic model for a class is created. The normal + # __init_subclass__ is too early to do this check. + for name in ("type", "base", "format"): + if name not in cls.model_fields: + raise NotImplementedError(f"{cls.__name__} must define a '{name}' field") + if cls.model_fields[name].default is PydanticUndefined: + raise NotImplementedError(f"{cls.__name__} must define a default for the '{name}' field") + + @classmethod + def get_tag(cls) -> Tag: + """Constructs a pydantic discriminated union tag for this model config class. When a config is deserialized, + pydantic uses the tag to determine which subclass to instantiate. + + The tag is a dot-separated string of the type, format, base and variant (if applicable). + """ + tag_strings: list[str] = [] + for name in ("type", "format", "base", "variant"): + if field := cls.model_fields.get(name): + # The check in __pydantic_init_subclass__ ensures that type, format and base are always present with + # defaults. variant does not require a default, but if it has one, we need to add it to the tag. We can + # check for the presence of a default by seeing if it's not PydanticUndefined, a sentinel value used by + # pydantic to indicate that no default was provided. + if field.default is not PydanticUndefined: + # We expect each of these fields has an Enum for its default; we want the value of the enum. + tag_strings.append(field.default.value) + return Tag(".".join(tag_strings)) + + @staticmethod + def get_model_discriminator_value(v: Any) -> str: + """Computes the discriminator value for a model config discriminated union.""" + # This is called by pydantic during deserialization and serialization to determine which model the data + # represents. It can get either a dict (during deserialization) or an instance of a Config_Base subclass + # (during serialization). + # + # See: https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions-with-callable-discriminator + if isinstance(v, Config_Base): + # We have an instance of a ModelConfigBase subclass - use its tag directly. + return v.get_tag().tag + if isinstance(v, dict): + # We have a dict - attempt to compute a tag from its fields. + tag_strings: list[str] = [] + if type_ := v.get("type"): + if isinstance(type_, Enum): + type_ = str(type_.value) + elif not isinstance(type_, str): + raise TypeError("Model config dict 'type' field must be a string or Enum") + tag_strings.append(type_) + + if format_ := v.get("format"): + if isinstance(format_, Enum): + format_ = str(format_.value) + elif not isinstance(format_, str): + raise TypeError("Model config dict 'format' field must be a string or Enum") + tag_strings.append(format_) + + if base_ := v.get("base"): + if isinstance(base_, Enum): + base_ = str(base_.value) + elif not isinstance(base_, str): + raise TypeError("Model config dict 'base' field must be a string or Enum") + tag_strings.append(base_) + + # Special case: CLIP Embed models also need the variant to distinguish them. + if ( + type_ == ModelType.CLIPEmbed.value + and format_ == ModelFormat.Diffusers.value + and base_ == BaseModelType.Any.value + ): + if variant_ := v.get("variant"): + if isinstance(variant_, Enum): + variant_ = variant_.value + elif not isinstance(variant_, str): + raise TypeError("Model config dict 'variant' field must be a string or Enum") + tag_strings.append(variant_) + else: + raise ValueError("CLIP Embed model config dict must include a 'variant' field") + + return ".".join(tag_strings) + else: + raise TypeError("Model config discriminator value must be computed from a dict or ModelConfigBase instance") + + @abstractmethod + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + """Given the model on disk and any override fields, attempt to construct an instance of this config class. + + This method serves to identify whether the model on disk matches this config class, and if so, to extract any + additional metadata needed to instantiate the config. + + Implementations should raise a NotAMatchError if the model does not match this config class.""" + raise NotImplementedError(f"from_model_on_disk not implemented for {cls.__name__}") + + +class Checkpoint_Config_Base(ABC, BaseModel): + """Base class for checkpoint-style models.""" + + config_path: str | None = Field( + description="Path to the config for this model, if any.", + default=None, + ) + + +class Diffusers_Config_Base(ABC, BaseModel): + """Base class for diffusers-style models.""" + + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + repo_variant: ModelRepoVariant = Field(ModelRepoVariant.Default) + + @classmethod + def _get_repo_variant_or_raise(cls, mod: ModelOnDisk) -> ModelRepoVariant: + # get all files ending in .bin or .safetensors + weight_files = list(mod.path.glob("**/*.safetensors")) + weight_files.extend(list(mod.path.glob("**/*.bin"))) + for x in weight_files: + if ".fp16" in x.suffixes: + return ModelRepoVariant.FP16 + if "openvino_model" in x.name: + return ModelRepoVariant.OpenVINO + if "flax_model" in x.name: + return ModelRepoVariant.Flax + if x.suffix == ".onnx": + return ModelRepoVariant.ONNX + return ModelRepoVariant.Default + + +class SubmodelDefinition(BaseModel): + path_or_prefix: str + model_type: ModelType + variant: AnyVariant | None = None + + model_config = ConfigDict(protected_namespaces=()) diff --git a/invokeai/backend/model_manager/configs/clip_embed.py b/invokeai/backend/model_manager/configs/clip_embed.py new file mode 100644 index 00000000000..4bb24a0a637 --- /dev/null +++ b/invokeai/backend/model_manager/configs/clip_embed.py @@ -0,0 +1,91 @@ +from typing import ( + Literal, + Self, +) + +from pydantic import Field +from typing_extensions import Any + +from invokeai.backend.model_manager.configs.base import Config_Base, Diffusers_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, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ClipVariantType, + ModelFormat, + ModelType, +) + + +def get_clip_variant_type_from_config(config: dict[str, Any]) -> ClipVariantType | None: + try: + hidden_size = config.get("hidden_size") + match hidden_size: + case 1280: + return ClipVariantType.G + case 768: + return ClipVariantType.L + case _: + return None + except Exception: + return None + + +class CLIPEmbed_Diffusers_Config_Base(Diffusers_Config_Base): + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + type: Literal[ModelType.CLIPEmbed] = Field(default=ModelType.CLIPEmbed) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + + @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) + + raise_for_class_name( + { + mod.path / "config.json", + mod.path / "text_encoder" / "config.json", + }, + { + "CLIPModel", + "CLIPTextModel", + "CLIPTextModelWithProjection", + }, + ) + + cls._validate_variant(mod) + + return cls(**override_fields) + + @classmethod + def _validate_variant(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model variant does not match this config class.""" + expected_variant = cls.model_fields["variant"].default + config = get_config_dict_or_raise( + { + mod.path / "config.json", + mod.path / "text_encoder" / "config.json", + }, + ) + recognized_variant = get_clip_variant_type_from_config(config) + + if recognized_variant is None: + raise NotAMatchError("unable to determine CLIP variant from config") + + if expected_variant is not recognized_variant: + raise NotAMatchError(f"variant is {recognized_variant}, not {expected_variant}") + + +class CLIPEmbed_Diffusers_G_Config(CLIPEmbed_Diffusers_Config_Base, Config_Base): + variant: Literal[ClipVariantType.G] = Field(default=ClipVariantType.G) + + +class CLIPEmbed_Diffusers_L_Config(CLIPEmbed_Diffusers_Config_Base, Config_Base): + variant: Literal[ClipVariantType.L] = Field(default=ClipVariantType.L) diff --git a/invokeai/backend/model_manager/configs/clip_vision.py b/invokeai/backend/model_manager/configs/clip_vision.py new file mode 100644 index 00000000000..eb17fb9e338 --- /dev/null +++ b/invokeai/backend/model_manager/configs/clip_vision.py @@ -0,0 +1,44 @@ +from typing import ( + Literal, + Self, +) + +from pydantic import Field +from typing_extensions import Any + +from invokeai.backend.model_manager.configs.base import Config_Base, Diffusers_Config_Base +from invokeai.backend.model_manager.configs.identification_utils import ( + common_config_paths, + raise_for_class_name, + raise_for_override_fields, + raise_if_not_dir, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ModelFormat, + ModelType, +) + + +class CLIPVision_Diffusers_Config(Diffusers_Config_Base, Config_Base): + """Model config for CLIPVision.""" + + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + type: Literal[ModelType.CLIPVision] = Field(default=ModelType.CLIPVision) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + + @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) + + raise_for_class_name( + common_config_paths(mod.path), + { + "CLIPVisionModelWithProjection", + }, + ) + + return cls(**override_fields) diff --git a/invokeai/backend/model_manager/configs/controlnet.py b/invokeai/backend/model_manager/configs/controlnet.py new file mode 100644 index 00000000000..7db782a862c --- /dev/null +++ b/invokeai/backend/model_manager/configs/controlnet.py @@ -0,0 +1,195 @@ +from typing import ( + Literal, + Self, +) + +from pydantic import Field +from typing_extensions import Any + +from invokeai.backend.flux.controlnet.state_dict_utils import ( + is_state_dict_instantx_controlnet, + is_state_dict_xlabs_controlnet, +) +from invokeai.backend.model_manager.config import ControlAdapterDefaultSettings +from invokeai.backend.model_manager.configs.base import Checkpoint_Config_Base, Config_Base, Diffusers_Config_Base +from invokeai.backend.model_manager.configs.identification_utils import ( + NotAMatchError, + common_config_paths, + get_config_dict_or_raise, + raise_for_class_name, + raise_for_override_fields, + raise_if_not_dir, + raise_if_not_file, + state_dict_has_any_keys_starting_with, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ModelFormat, + ModelType, +) + + +class ControlNet_Diffusers_Config_Base(Diffusers_Config_Base): + """Model config for ControlNet models (diffusers version).""" + + type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + default_settings: ControlAdapterDefaultSettings | None = Field(None) + + @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) + + raise_for_class_name( + common_config_paths(mod.path), + { + "ControlNetModel", + "FluxControlNetModel", + }, + ) + + cls._validate_base(mod) + + return cls(**override_fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + config_dict = get_config_dict_or_raise(common_config_paths(mod.path)) + + if config_dict.get("_class_name") == "FluxControlNetModel": + return BaseModelType.Flux + + dimension = config_dict.get("cross_attention_dim") + + match dimension: + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + # No obvious way to distinguish between sd2-base and sd2-768, but we don't really differentiate them + # anyway. + return BaseModelType.StableDiffusion2 + case 2048: + return BaseModelType.StableDiffusionXL + case _: + raise NotAMatchError(f"unrecognized cross_attention_dim {dimension}") + + +class ControlNet_Diffusers_SD1_Config(ControlNet_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class ControlNet_Diffusers_SD2_Config(ControlNet_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class ControlNet_Diffusers_SDXL_Config(ControlNet_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class ControlNet_Diffusers_FLUX_Config(ControlNet_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + + +class ControlNet_Checkpoint_Config_Base(Checkpoint_Config_Base): + """Model config for ControlNet models (diffusers version).""" + + type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + default_settings: ControlAdapterDefaultSettings | None = Field(None) + + @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_controlnet(mod) + + cls._validate_base(mod) + + return cls(**override_fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _validate_looks_like_controlnet(cls, mod: ModelOnDisk) -> None: + if not state_dict_has_any_keys_starting_with( + mod.load_state_dict(), + { + "controlnet", + "control_model", + "input_blocks", + # XLabs FLUX ControlNet models have keys starting with "controlnet_blocks." + # For example: https://huggingface.co/XLabs-AI/flux-controlnet-collections/blob/86ab1e915a389d5857135c00e0d350e9e38a9048/flux-canny-controlnet_v2.safetensors + # TODO(ryand): This is very fragile. XLabs FLUX ControlNet models also contain keys starting with + # "double_blocks.", which we check for above. But, I'm afraid to modify this logic because it is so + # delicate. + "controlnet_blocks", + }, + ): + raise NotAMatchError("state dict does not look like a ControlNet checkpoint") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + state_dict = mod.load_state_dict() + + if is_state_dict_xlabs_controlnet(state_dict) or is_state_dict_instantx_controlnet(state_dict): + # TODO(ryand): Should I distinguish between XLabs, InstantX and other ControlNet models by implementing + # get_format()? + return BaseModelType.Flux + + for key in ( + "control_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight", + "controlnet_mid_block.bias", + "input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight", + "down_blocks.1.attentions.0.transformer_blocks.0.attn2.to_k.weight", + ): + if key not in state_dict: + continue + width = state_dict[key].shape[-1] + match width: + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case 2048: + return BaseModelType.StableDiffusionXL + case 1280: + return BaseModelType.StableDiffusionXL + case _: + pass + + raise NotAMatchError("unable to determine base type from state dict") + + +class ControlNet_Checkpoint_SD1_Config(ControlNet_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class ControlNet_Checkpoint_SD2_Config(ControlNet_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class ControlNet_Checkpoint_SDXL_Config(ControlNet_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class ControlNet_Checkpoint_FLUX_Config(ControlNet_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) diff --git a/invokeai/backend/model_manager/configs/external_api.py b/invokeai/backend/model_manager/configs/external_api.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/invokeai/backend/model_manager/configs/factory.py b/invokeai/backend/model_manager/configs/factory.py new file mode 100644 index 00000000000..27b6f252f1d --- /dev/null +++ b/invokeai/backend/model_manager/configs/factory.py @@ -0,0 +1,340 @@ +import logging +from pathlib import Path +from typing import ( + Union, +) + +from pydantic import Discriminator, TypeAdapter, ValidationError +from typing_extensions import Annotated, Any + +from invokeai.app.services.config.config_default import get_config +from invokeai.app.util.misc import uuid_string +from invokeai.backend.model_hash.model_hash import HASHING_ALGORITHMS +from invokeai.backend.model_manager.configs.base import Config_Base +from invokeai.backend.model_manager.configs.clip_embed import CLIPEmbed_Diffusers_G_Config, CLIPEmbed_Diffusers_L_Config +from invokeai.backend.model_manager.configs.clip_vision import CLIPVision_Diffusers_Config +from invokeai.backend.model_manager.configs.controlnet import ( + ControlNet_Checkpoint_FLUX_Config, + ControlNet_Checkpoint_SD1_Config, + ControlNet_Checkpoint_SD2_Config, + ControlNet_Checkpoint_SDXL_Config, + ControlNet_Diffusers_FLUX_Config, + ControlNet_Diffusers_SD1_Config, + ControlNet_Diffusers_SD2_Config, + ControlNet_Diffusers_SDXL_Config, +) +from invokeai.backend.model_manager.configs.flux_redux import FLUXRedux_Checkpoint_Config +from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError +from invokeai.backend.model_manager.configs.ip_adapter import ( + IPAdapter_Checkpoint_FLUX_Config, + IPAdapter_Checkpoint_SD1_Config, + IPAdapter_Checkpoint_SD2_Config, + IPAdapter_Checkpoint_SDXL_Config, + IPAdapter_InvokeAI_SD1_Config, + IPAdapter_InvokeAI_SD2_Config, + IPAdapter_InvokeAI_SDXL_Config, +) +from invokeai.backend.model_manager.configs.llava_onevision import LlavaOnevision_Diffusers_Config +from invokeai.backend.model_manager.configs.lora import ( + ControlLoRA_LyCORIS_FLUX_Config, + LoRA_Diffusers_FLUX_Config, + LoRA_Diffusers_SD1_Config, + LoRA_Diffusers_SD2_Config, + LoRA_Diffusers_SDXL_Config, + LoRA_LyCORIS_FLUX_Config, + LoRA_LyCORIS_SD1_Config, + LoRA_LyCORIS_SD2_Config, + LoRA_LyCORIS_SDXL_Config, + LoRA_OMI_FLUX_Config, + LoRA_OMI_SDXL_Config, +) +from invokeai.backend.model_manager.configs.main import ( + Main_BnBNF4_FLUX_Config, + Main_Checkpoint_FLUX_Config, + Main_Checkpoint_SD1_Config, + Main_Checkpoint_SD2_Config, + Main_Checkpoint_SDXL_Config, + Main_Checkpoint_SDXLRefiner_Config, + Main_Diffusers_CogView4_Config, + Main_Diffusers_SD1_Config, + Main_Diffusers_SD2_Config, + Main_Diffusers_SD3_Config, + Main_Diffusers_SDXL_Config, + Main_Diffusers_SDXLRefiner_Config, + Main_ExternalAPI_ChatGPT4o_Config, + Main_ExternalAPI_FluxKontext_Config, + Main_ExternalAPI_Gemini2_5_Config, + Main_ExternalAPI_Imagen3_Config, + Main_ExternalAPI_Imagen4_Config, + Main_GGUF_FLUX_Config, + Video_ExternalAPI_Runway_Config, + Video_ExternalAPI_Veo3_Config, +) +from invokeai.backend.model_manager.configs.siglip import SigLIP_Diffusers_Config +from invokeai.backend.model_manager.configs.spandrel import Spandrel_Checkpoint_Config +from invokeai.backend.model_manager.configs.t2i_adapter import ( + T2IAdapter_Diffusers_SD1_Config, + T2IAdapter_Diffusers_SDXL_Config, +) +from invokeai.backend.model_manager.configs.t5_encoder import T5Encoder_BnBLLMint8_Config, T5Encoder_T5Encoder_Config +from invokeai.backend.model_manager.configs.textual_inversion import ( + TI_File_SD1_Config, + TI_File_SD2_Config, + TI_File_SDXL_Config, + TI_Folder_SD1_Config, + TI_Folder_SD2_Config, + TI_Folder_SDXL_Config, +) +from invokeai.backend.model_manager.configs.unknown import Unknown_Config +from invokeai.backend.model_manager.configs.vae import ( + VAE_Checkpoint_FLUX_Config, + VAE_Checkpoint_SD1_Config, + VAE_Checkpoint_SD2_Config, + VAE_Checkpoint_SDXL_Config, + VAE_Diffusers_SD1_Config, + VAE_Diffusers_SDXL_Config, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ModelFormat, + ModelSourceType, + ModelType, + variant_type_adapter, +) + +logger = logging.getLogger(__name__) +app_config = get_config() + + +# The types are listed explicitly because IDEs/LSPs can't identify the correct types +# when AnyModelConfig is constructed dynamically using ModelConfigBase.all_config_classes +AnyModelConfig = Annotated[ + Union[ + # Main (Pipeline) - diffusers format + Annotated[Main_Diffusers_SD1_Config, Main_Diffusers_SD1_Config.get_tag()], + Annotated[Main_Diffusers_SD2_Config, Main_Diffusers_SD2_Config.get_tag()], + Annotated[Main_Diffusers_SDXL_Config, Main_Diffusers_SDXL_Config.get_tag()], + Annotated[Main_Diffusers_SDXLRefiner_Config, Main_Diffusers_SDXLRefiner_Config.get_tag()], + Annotated[Main_Diffusers_SD3_Config, Main_Diffusers_SD3_Config.get_tag()], + Annotated[Main_Diffusers_CogView4_Config, Main_Diffusers_CogView4_Config.get_tag()], + # Main (Pipeline) - checkpoint format + Annotated[Main_Checkpoint_SD1_Config, Main_Checkpoint_SD1_Config.get_tag()], + Annotated[Main_Checkpoint_SD2_Config, Main_Checkpoint_SD2_Config.get_tag()], + Annotated[Main_Checkpoint_SDXL_Config, Main_Checkpoint_SDXL_Config.get_tag()], + Annotated[Main_Checkpoint_SDXLRefiner_Config, Main_Checkpoint_SDXLRefiner_Config.get_tag()], + Annotated[Main_Checkpoint_FLUX_Config, Main_Checkpoint_FLUX_Config.get_tag()], + # Main (Pipeline) - quantized formats + Annotated[Main_BnBNF4_FLUX_Config, Main_BnBNF4_FLUX_Config.get_tag()], + Annotated[Main_GGUF_FLUX_Config, Main_GGUF_FLUX_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()], + Annotated[VAE_Checkpoint_SDXL_Config, VAE_Checkpoint_SDXL_Config.get_tag()], + Annotated[VAE_Checkpoint_FLUX_Config, VAE_Checkpoint_FLUX_Config.get_tag()], + # VAE - diffusers format + Annotated[VAE_Diffusers_SD1_Config, VAE_Diffusers_SD1_Config.get_tag()], + Annotated[VAE_Diffusers_SDXL_Config, VAE_Diffusers_SDXL_Config.get_tag()], + # ControlNet - checkpoint format + Annotated[ControlNet_Checkpoint_SD1_Config, ControlNet_Checkpoint_SD1_Config.get_tag()], + Annotated[ControlNet_Checkpoint_SD2_Config, ControlNet_Checkpoint_SD2_Config.get_tag()], + Annotated[ControlNet_Checkpoint_SDXL_Config, ControlNet_Checkpoint_SDXL_Config.get_tag()], + Annotated[ControlNet_Checkpoint_FLUX_Config, ControlNet_Checkpoint_FLUX_Config.get_tag()], + # ControlNet - diffusers format + Annotated[ControlNet_Diffusers_SD1_Config, ControlNet_Diffusers_SD1_Config.get_tag()], + Annotated[ControlNet_Diffusers_SD2_Config, ControlNet_Diffusers_SD2_Config.get_tag()], + Annotated[ControlNet_Diffusers_SDXL_Config, ControlNet_Diffusers_SDXL_Config.get_tag()], + Annotated[ControlNet_Diffusers_FLUX_Config, ControlNet_Diffusers_FLUX_Config.get_tag()], + # LoRA - LyCORIS format + Annotated[LoRA_LyCORIS_SD1_Config, LoRA_LyCORIS_SD1_Config.get_tag()], + Annotated[LoRA_LyCORIS_SD2_Config, LoRA_LyCORIS_SD2_Config.get_tag()], + Annotated[LoRA_LyCORIS_SDXL_Config, LoRA_LyCORIS_SDXL_Config.get_tag()], + Annotated[LoRA_LyCORIS_FLUX_Config, LoRA_LyCORIS_FLUX_Config.get_tag()], + # LoRA - OMI format + Annotated[LoRA_OMI_SDXL_Config, LoRA_OMI_SDXL_Config.get_tag()], + Annotated[LoRA_OMI_FLUX_Config, LoRA_OMI_FLUX_Config.get_tag()], + # LoRA - diffusers format + Annotated[LoRA_Diffusers_SD1_Config, LoRA_Diffusers_SD1_Config.get_tag()], + Annotated[LoRA_Diffusers_SD2_Config, LoRA_Diffusers_SD2_Config.get_tag()], + Annotated[LoRA_Diffusers_SDXL_Config, LoRA_Diffusers_SDXL_Config.get_tag()], + Annotated[LoRA_Diffusers_FLUX_Config, LoRA_Diffusers_FLUX_Config.get_tag()], + # ControlLoRA - diffusers format + Annotated[ControlLoRA_LyCORIS_FLUX_Config, ControlLoRA_LyCORIS_FLUX_Config.get_tag()], + # T5 Encoder - all formats + Annotated[T5Encoder_T5Encoder_Config, T5Encoder_T5Encoder_Config.get_tag()], + Annotated[T5Encoder_BnBLLMint8_Config, T5Encoder_BnBLLMint8_Config.get_tag()], + # TI - file format + Annotated[TI_File_SD1_Config, TI_File_SD1_Config.get_tag()], + Annotated[TI_File_SD2_Config, TI_File_SD2_Config.get_tag()], + Annotated[TI_File_SDXL_Config, TI_File_SDXL_Config.get_tag()], + # TI - folder format + Annotated[TI_Folder_SD1_Config, TI_Folder_SD1_Config.get_tag()], + Annotated[TI_Folder_SD2_Config, TI_Folder_SD2_Config.get_tag()], + Annotated[TI_Folder_SDXL_Config, TI_Folder_SDXL_Config.get_tag()], + # IP Adapter - InvokeAI format + Annotated[IPAdapter_InvokeAI_SD1_Config, IPAdapter_InvokeAI_SD1_Config.get_tag()], + Annotated[IPAdapter_InvokeAI_SD2_Config, IPAdapter_InvokeAI_SD2_Config.get_tag()], + Annotated[IPAdapter_InvokeAI_SDXL_Config, IPAdapter_InvokeAI_SDXL_Config.get_tag()], + # IP Adapter - checkpoint format + Annotated[IPAdapter_Checkpoint_SD1_Config, IPAdapter_Checkpoint_SD1_Config.get_tag()], + Annotated[IPAdapter_Checkpoint_SD2_Config, IPAdapter_Checkpoint_SD2_Config.get_tag()], + Annotated[IPAdapter_Checkpoint_SDXL_Config, IPAdapter_Checkpoint_SDXL_Config.get_tag()], + Annotated[IPAdapter_Checkpoint_FLUX_Config, IPAdapter_Checkpoint_FLUX_Config.get_tag()], + # T2I Adapter - diffusers format + Annotated[T2IAdapter_Diffusers_SD1_Config, T2IAdapter_Diffusers_SD1_Config.get_tag()], + Annotated[T2IAdapter_Diffusers_SDXL_Config, T2IAdapter_Diffusers_SDXL_Config.get_tag()], + # Misc models + Annotated[Spandrel_Checkpoint_Config, Spandrel_Checkpoint_Config.get_tag()], + Annotated[CLIPEmbed_Diffusers_G_Config, CLIPEmbed_Diffusers_G_Config.get_tag()], + Annotated[CLIPEmbed_Diffusers_L_Config, CLIPEmbed_Diffusers_L_Config.get_tag()], + Annotated[CLIPVision_Diffusers_Config, CLIPVision_Diffusers_Config.get_tag()], + Annotated[SigLIP_Diffusers_Config, SigLIP_Diffusers_Config.get_tag()], + Annotated[FLUXRedux_Checkpoint_Config, FLUXRedux_Checkpoint_Config.get_tag()], + Annotated[LlavaOnevision_Diffusers_Config, LlavaOnevision_Diffusers_Config.get_tag()], + # Main - external API + Annotated[Main_ExternalAPI_ChatGPT4o_Config, Main_ExternalAPI_ChatGPT4o_Config.get_tag()], + Annotated[Main_ExternalAPI_Gemini2_5_Config, Main_ExternalAPI_Gemini2_5_Config.get_tag()], + Annotated[Main_ExternalAPI_Imagen3_Config, Main_ExternalAPI_Imagen3_Config.get_tag()], + Annotated[Main_ExternalAPI_Imagen4_Config, Main_ExternalAPI_Imagen4_Config.get_tag()], + Annotated[Main_ExternalAPI_FluxKontext_Config, Main_ExternalAPI_FluxKontext_Config.get_tag()], + # Video - external API + Annotated[Video_ExternalAPI_Veo3_Config, Video_ExternalAPI_Veo3_Config.get_tag()], + Annotated[Video_ExternalAPI_Runway_Config, Video_ExternalAPI_Runway_Config.get_tag()], + # Unknown model (fallback) + Annotated[Unknown_Config, Unknown_Config.get_tag()], + ], + Discriminator(Config_Base.get_model_discriminator_value), +] + +AnyModelConfigValidator = TypeAdapter[AnyModelConfig](AnyModelConfig) + + +class ModelConfigFactory: + @staticmethod + def from_dict(fields: dict[str, Any]) -> AnyModelConfig: + """Return the appropriate config object from raw dict values.""" + model = AnyModelConfigValidator.validate_python(fields) + return model + + @staticmethod + def build_common_fields( + mod: ModelOnDisk, + override_fields: dict[str, Any] | None = None, + ) -> dict[str, Any]: + """Builds the common fields for all model configs. + + Args: + mod: The model on disk to extract fields from. + overrides: A optional dictionary of fields to override. These fields will take precedence over the values + extracted from the model on disk. + + - Casts string fields to their Enum types. + - Does not validate the fields against the model config schema. + """ + + _overrides: dict[str, Any] = override_fields or {} + fields: dict[str, Any] = {} + + if "type" in _overrides: + fields["type"] = ModelType(_overrides["type"]) + + if "format" in _overrides: + fields["format"] = ModelFormat(_overrides["format"]) + + if "base" in _overrides: + fields["base"] = BaseModelType(_overrides["base"]) + + if "source_type" in _overrides: + fields["source_type"] = ModelSourceType(_overrides["source_type"]) + + if "variant" in _overrides: + fields["variant"] = variant_type_adapter.validate_strings(_overrides["variant"]) + + fields["path"] = mod.path.as_posix() + fields["source"] = _overrides.get("source") or fields["path"] + fields["source_type"] = _overrides.get("source_type") or ModelSourceType.Path + fields["name"] = _overrides.get("name") or mod.name + fields["hash"] = _overrides.get("hash") or mod.hash() + fields["key"] = _overrides.get("key") or uuid_string() + fields["description"] = _overrides.get("description") + fields["file_size"] = _overrides.get("file_size") or mod.size() + + return fields + + @staticmethod + def from_model_on_disk( + mod: str | Path | ModelOnDisk, + override_fields: dict[str, Any] | None = None, + hash_algo: HASHING_ALGORITHMS = "blake3_single", + ) -> AnyModelConfig: + """ + Returns the best matching ModelConfig instance from a model's file/folder path. + Raises InvalidModelConfigException if no valid configuration is found. + Created to deprecate ModelProbe.probe + """ + if isinstance(mod, Path | str): + mod = ModelOnDisk(Path(mod), hash_algo) + + # We will always need these fields to build any model config. + fields = ModelConfigFactory.build_common_fields(mod, override_fields) + + # Store results as a mapping of config class to either an instance of that class or an exception + # that was raised when trying to build it. + results: dict[str, AnyModelConfig | Exception] = {} + + # Try to build an instance of each model config class that uses the classify API. + # Each class will either return an instance of itself or raise NotAMatch if it doesn't match. + # Other exceptions may be raised if something unexpected happens during matching or building. + for config_class in Config_Base.CONFIG_CLASSES: + class_name = config_class.__name__ + try: + instance = config_class.from_model_on_disk(mod, fields) + # Technically, from_model_on_disk returns a Config_Base, but in practice it will always be a member of + # the AnyModelConfig union. + results[class_name] = instance # type: ignore + except NotAMatchError as e: + results[class_name] = e + logger.debug(f"No match for {config_class.__name__} on model {mod.name}") + except ValidationError as e: + # This means the model matched, but we couldn't create the pydantic model instance for the config. + # Maybe invalid overrides were provided? + results[class_name] = e + logger.warning(f"Schema validation error for {config_class.__name__} on model {mod.name}: {e}") + except Exception as e: + results[class_name] = e + logger.warning(f"Unexpected exception while matching {mod.name} to {config_class.__name__}: {e}") + + matches = [r for r in results.values() if isinstance(r, Config_Base)] + + if not matches and app_config.allow_unknown_models: + logger.warning(f"Unable to identify model {mod.name}, falling back to Unknown_Config") + return Unknown_Config(**fields) + + if len(matches) > 1: + # We have multiple matches, in which case at most 1 is correct. We need to pick one. + # + # Known cases: + # - SD main models can look like a LoRA when they have merged in LoRA weights. Prefer the main model. + # - SD main models in diffusers format can look like a CLIP Embed; they have a text_encoder folder with + # a config.json file. Prefer the main model. + + # Sort the matching according to known special cases. + def sort_key(m: AnyModelConfig) -> int: + match m.type: + case ModelType.Main: + return 0 + case ModelType.LoRA: + return 1 + case ModelType.CLIPEmbed: + return 2 + case _: + return 3 + + matches.sort(key=sort_key) + logger.warning( + f"Multiple model config classes matched for model {mod.name}: {[type(m).__name__ for m in matches]}. Using {type(matches[0]).__name__}." + ) + + instance = matches[0] + logger.info(f"Model {mod.name} classified as {type(instance).__name__}") + return instance diff --git a/invokeai/backend/model_manager/configs/flux_redux.py b/invokeai/backend/model_manager/configs/flux_redux.py new file mode 100644 index 00000000000..6eb76116fba --- /dev/null +++ b/invokeai/backend/model_manager/configs/flux_redux.py @@ -0,0 +1,40 @@ +from typing import ( + Literal, + Self, +) + +from pydantic import Field +from typing_extensions import Any + +from invokeai.backend.flux.redux.flux_redux_state_dict_utils import is_state_dict_likely_flux_redux +from invokeai.backend.model_manager.configs.base import Config_Base +from invokeai.backend.model_manager.configs.identification_utils import ( + NotAMatchError, + raise_for_override_fields, + raise_if_not_file, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ModelFormat, + ModelType, +) + + +class FLUXRedux_Checkpoint_Config(Config_Base): + """Model config for FLUX Tools Redux model.""" + + type: Literal[ModelType.FluxRedux] = Field(default=ModelType.FluxRedux) + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + + @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 not is_state_dict_likely_flux_redux(mod.load_state_dict()): + raise NotAMatchError("model does not match FLUX Tools Redux heuristics") + + return cls(**override_fields) diff --git a/invokeai/backend/model_manager/configs/identification_utils.py b/invokeai/backend/model_manager/configs/identification_utils.py new file mode 100644 index 00000000000..58d545cb64d --- /dev/null +++ b/invokeai/backend/model_manager/configs/identification_utils.py @@ -0,0 +1,182 @@ +import json +from functools import cache +from pathlib import Path + +from pydantic import BaseModel, ValidationError +from pydantic_core import CoreSchema, SchemaValidator +from typing_extensions import Any + +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk + + +class NotAMatchError(Exception): + """Exception for when a model does not match a config class. + + Args: + reason: The reason why the model did not match. + """ + + def __init__(self, reason: str): + super().__init__(reason) + + +def get_config_dict_or_raise(config_path: Path | set[Path]) -> dict[str, Any]: + paths_to_check = config_path if isinstance(config_path, set) else {config_path} + + problems: dict[Path, str] = {} + + for p in paths_to_check: + if not p.exists(): + problems[p] = "file does not exist" + continue + + try: + with open(p, "r") as file: + config = json.load(file) + + return config + except Exception as e: + problems[p] = str(e) + continue + + raise NotAMatchError(f"unable to load config file(s): {problems}") + + +def get_class_name_from_config_dict_or_raise(config_path: Path | set[Path]) -> str: + """Load the diffusers/transformers model config file and return the class name. + + Raises: + NotAMatch if the config file is missing or does not contain a valid class name. + """ + + config = get_config_dict_or_raise(config_path) + + try: + if "_class_name" in config: + # This is a diffusers-style config + config_class_name = config["_class_name"] + elif "architectures" in config: + # This is a transformers-style config + config_class_name = config["architectures"][0] + else: + raise ValueError("missing _class_name or architectures field") + except Exception as e: + raise NotAMatchError(f"unable to determine class name from config file: {config_path}") from e + + if not isinstance(config_class_name, str): + raise NotAMatchError(f"_class_name or architectures field is not a string: {config_class_name}") + + return config_class_name + + +def raise_for_class_name(config_path: Path | set[Path], expected: set[str]) -> None: + """Get the class name from the config file and raise NotAMatch if it is not in the expected set. + + Args: + config_path: The path to the config file. + expected: The expected class names. + + Raises: + NotAMatch if the class name is not in the expected set. + """ + + class_name = get_class_name_from_config_dict_or_raise(config_path) + if class_name not in expected: + raise NotAMatchError(f"invalid class name from config: {class_name}") + + +def raise_for_override_fields(candidate_config_class: type[BaseModel], override_fields: dict[str, Any]) -> None: + """Check if the provided override fields are valid for the config class using pydantic. + + For example, if the candidate config class has a field "base" of type Literal[BaseModelType.StableDiffusion1], and + the override fields contain "base": BaseModelType.Flux, this function will raise NotAMatch. + + Args: + candidate_config_class: The config class that is being tested. + override_fields: The override fields provided by the user. + + Raises: + NotAMatch if any override field is invalid for the config class. + """ + for field_name, override_value in override_fields.items(): + if field_name not in candidate_config_class.model_fields: + raise NotAMatchError(f"unknown override field: {field_name}") + try: + PydanticFieldValidator.validate_field(candidate_config_class, field_name, override_value) + except ValidationError as e: + raise NotAMatchError(f"invalid override for field '{field_name}': {e}") from e + + +def raise_if_not_file(mod: ModelOnDisk) -> None: + """Raise NotAMatch if the model path is not a file.""" + if not mod.path.is_file(): + raise NotAMatchError("model path is not a file") + + +def raise_if_not_dir(mod: ModelOnDisk) -> None: + """Raise NotAMatch if the model path is not a directory.""" + if not mod.path.is_dir(): + raise NotAMatchError("model path is not a directory") + + +def state_dict_has_any_keys_exact(state_dict: dict[str | int, Any], keys: str | set[str]) -> bool: + """Returns true if the state dict has any of the specified keys.""" + _keys = {keys} if isinstance(keys, str) else keys + return any(key in state_dict for key in _keys) + + +def state_dict_has_any_keys_starting_with(state_dict: dict[str | int, Any], prefixes: str | set[str]) -> bool: + """Returns true if the state dict has any keys starting with any of the specified prefixes.""" + _prefixes = {prefixes} if isinstance(prefixes, str) else prefixes + return any(any(key.startswith(prefix) for prefix in _prefixes) for key in state_dict.keys() if isinstance(key, str)) + + +def state_dict_has_any_keys_ending_with(state_dict: dict[str | int, Any], suffixes: str | set[str]) -> bool: + """Returns true if the state dict has any keys ending with any of the specified suffixes.""" + _suffixes = {suffixes} if isinstance(suffixes, str) else suffixes + return any(any(key.endswith(suffix) for suffix in _suffixes) for key in state_dict.keys() if isinstance(key, str)) + + +def common_config_paths(path: Path) -> set[Path]: + """Returns common config file paths for models stored in directories.""" + return {path / "config.json", path / "model_index.json"} + + +class PydanticFieldValidator: + """Utility class for validating individual fields of a Pydantic model without instantiating the whole model. + + See: https://github.com/pydantic/pydantic/discussions/7367#discussioncomment-14213144 + """ + + @staticmethod + def find_field_schema(model: type[BaseModel], field_name: str) -> CoreSchema: + """Find the Pydantic core schema for a specific field in a model.""" + schema: CoreSchema = model.__pydantic_core_schema__.copy() + # we shallow copied, be careful not to mutate the original schema! + + assert schema["type"] in ["definitions", "model"] + + # find the field schema + field_schema = schema["schema"] # type: ignore + while "fields" not in field_schema: + field_schema = field_schema["schema"] # type: ignore + + field_schema = field_schema["fields"][field_name]["schema"] # type: ignore + + # if the original schema is a definition schema, replace the model schema with the field schema + if schema["type"] == "definitions": + schema["schema"] = field_schema + return schema + else: + return field_schema + + @cache + @staticmethod + def get_validator(model: type[BaseModel], field_name: str) -> SchemaValidator: + """Get a SchemaValidator for a specific field in a model.""" + return SchemaValidator(PydanticFieldValidator.find_field_schema(model, field_name)) + + @staticmethod + def validate_field(model: type[BaseModel], field_name: str, value: Any) -> Any: + """Validate a value for a specific field in a model.""" + return PydanticFieldValidator.get_validator(model, field_name).validate_python(value) diff --git a/invokeai/backend/model_manager/configs/ip_adapter.py b/invokeai/backend/model_manager/configs/ip_adapter.py new file mode 100644 index 00000000000..dc6f80d922b --- /dev/null +++ b/invokeai/backend/model_manager/configs/ip_adapter.py @@ -0,0 +1,180 @@ +from abc import ABC +from typing import ( + Literal, + Self, +) + +from pydantic import BaseModel, Field +from typing_extensions import Any + +from invokeai.backend.flux.ip_adapter.state_dict_utils import is_state_dict_xlabs_ip_adapter +from invokeai.backend.model_manager.configs.base import Config_Base +from invokeai.backend.model_manager.configs.identification_utils import ( + NotAMatchError, + raise_for_override_fields, + raise_if_not_dir, + raise_if_not_file, + state_dict_has_any_keys_starting_with, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ModelFormat, + ModelType, +) + + +class IPAdapter_Config_Base(ABC, BaseModel): + type: Literal[ModelType.IPAdapter] = Field(default=ModelType.IPAdapter) + + +class IPAdapter_InvokeAI_Config_Base(IPAdapter_Config_Base): + """Model config for IP Adapter diffusers format models.""" + + format: Literal[ModelFormat.InvokeAI] = Field(default=ModelFormat.InvokeAI) + + # TODO(ryand): Should we deprecate this field? From what I can tell, it hasn't been probed correctly for a long + # time. Need to go through the history to make sure I'm understanding this fully. + image_encoder_model_id: str = 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) + + cls._validate_has_weights_file(mod) + + cls._validate_has_image_encoder_metadata_file(mod) + + cls._validate_base(mod) + + return cls(**override_fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _validate_has_weights_file(cls, mod: ModelOnDisk) -> None: + weights_file = mod.path / "ip_adapter.bin" + if not weights_file.exists(): + raise NotAMatchError("missing ip_adapter.bin weights file") + + @classmethod + def _validate_has_image_encoder_metadata_file(cls, mod: ModelOnDisk) -> None: + image_encoder_metadata_file = mod.path / "image_encoder.txt" + if not image_encoder_metadata_file.exists(): + raise NotAMatchError("missing image_encoder.txt metadata file") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + state_dict = mod.load_state_dict() + + try: + cross_attention_dim = state_dict["ip_adapter"]["1.to_k_ip.weight"].shape[-1] + except Exception as e: + raise NotAMatchError(f"unable to determine cross attention dimension: {e}") from e + + match cross_attention_dim: + case 1280: + return BaseModelType.StableDiffusionXL + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case _: + raise NotAMatchError(f"unrecognized cross attention dimension {cross_attention_dim}") + + +class IPAdapter_InvokeAI_SD1_Config(IPAdapter_InvokeAI_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class IPAdapter_InvokeAI_SD2_Config(IPAdapter_InvokeAI_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class IPAdapter_InvokeAI_SDXL_Config(IPAdapter_InvokeAI_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class IPAdapter_Checkpoint_Config_Base(IPAdapter_Config_Base): + """Model config for IP Adapter checkpoint format models.""" + + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + + @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_ip_adapter(mod) + + cls._validate_base(mod) + + return cls(**override_fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _validate_looks_like_ip_adapter(cls, mod: ModelOnDisk) -> None: + if not state_dict_has_any_keys_starting_with( + mod.load_state_dict(), + { + "image_proj.", + "ip_adapter.", + # XLabs FLUX IP-Adapter models have keys startinh with "ip_adapter_proj_model.". + "ip_adapter_proj_model.", + }, + ): + raise NotAMatchError("model does not match Checkpoint IP Adapter heuristics") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + state_dict = mod.load_state_dict() + + if is_state_dict_xlabs_ip_adapter(state_dict): + return BaseModelType.Flux + + try: + cross_attention_dim = state_dict["ip_adapter.1.to_k_ip.weight"].shape[-1] + except Exception as e: + raise NotAMatchError(f"unable to determine cross attention dimension: {e}") from e + + match cross_attention_dim: + case 1280: + return BaseModelType.StableDiffusionXL + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case _: + raise NotAMatchError(f"unrecognized cross attention dimension {cross_attention_dim}") + + +class IPAdapter_Checkpoint_SD1_Config(IPAdapter_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class IPAdapter_Checkpoint_SD2_Config(IPAdapter_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class IPAdapter_Checkpoint_SDXL_Config(IPAdapter_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class IPAdapter_Checkpoint_FLUX_Config(IPAdapter_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) diff --git a/invokeai/backend/model_manager/configs/llava_onevision.py b/invokeai/backend/model_manager/configs/llava_onevision.py new file mode 100644 index 00000000000..c6ceb43ca9d --- /dev/null +++ b/invokeai/backend/model_manager/configs/llava_onevision.py @@ -0,0 +1,42 @@ +from typing import ( + Literal, + Self, +) + +from pydantic import Field +from typing_extensions import Any + +from invokeai.backend.model_manager.configs.base import Config_Base, Diffusers_Config_Base +from invokeai.backend.model_manager.configs.identification_utils import ( + common_config_paths, + raise_for_class_name, + raise_for_override_fields, + raise_if_not_dir, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ModelType, +) + + +class LlavaOnevision_Diffusers_Config(Diffusers_Config_Base, Config_Base): + """Model config for Llava Onevision models.""" + + type: Literal[ModelType.LlavaOnevision] = Field(default=ModelType.LlavaOnevision) + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + + @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) + + raise_for_class_name( + common_config_paths(mod.path), + { + "LlavaOnevisionForConditionalGeneration", + }, + ) + + return cls(**override_fields) diff --git a/invokeai/backend/model_manager/configs/lora.py b/invokeai/backend/model_manager/configs/lora.py new file mode 100644 index 00000000000..512137b4c3c --- /dev/null +++ b/invokeai/backend/model_manager/configs/lora.py @@ -0,0 +1,323 @@ +from abc import ABC +from pathlib import Path +from typing import ( + Any, + Literal, + Self, +) + +from pydantic import BaseModel, ConfigDict, Field +from typing_extensions import Any + +from invokeai.backend.model_manager.config import ControlAdapterDefaultSettings +from invokeai.backend.model_manager.configs.base import ( + Config_Base, +) +from invokeai.backend.model_manager.configs.identification_utils import ( + NotAMatchError, + raise_for_override_fields, + raise_if_not_dir, + raise_if_not_file, + state_dict_has_any_keys_ending_with, + state_dict_has_any_keys_starting_with, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.omi import flux_dev_1_lora, stable_diffusion_xl_1_lora +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + FluxLoRAFormat, + ModelFormat, + ModelType, +) +from invokeai.backend.model_manager.util.model_util import lora_token_vector_length +from invokeai.backend.patches.lora_conversions.flux_control_lora_utils import is_state_dict_likely_flux_control + + +class LoraModelDefaultSettings(BaseModel): + weight: float | None = Field(default=None, ge=-1, le=2, description="Default weight for this model") + model_config = ConfigDict(extra="forbid") + + +class LoRA_Config_Base(ABC, BaseModel): + """Base class for LoRA models.""" + + type: Literal[ModelType.LoRA] = Field(default=ModelType.LoRA) + trigger_phrases: set[str] | None = Field( + default=None, + description="Set of trigger phrases for this model", + ) + default_settings: LoraModelDefaultSettings | None = Field( + default=None, + description="Default settings for this model", + ) + + +def _get_flux_lora_format(mod: ModelOnDisk) -> FluxLoRAFormat | None: + # TODO(psyche): Moving this import to the function to avoid circular imports. Refactor later. + from invokeai.backend.patches.lora_conversions.formats import flux_format_from_state_dict + + state_dict = mod.load_state_dict(mod.path) + value = flux_format_from_state_dict(state_dict, mod.metadata()) + return value + + +class LoRA_OMI_Config_Base(LoRA_Config_Base): + format: Literal[ModelFormat.OMI] = Field(default=ModelFormat.OMI) + + @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_omi_lora(mod) + + cls._validate_base(mod) + + return cls(**override_fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _validate_looks_like_omi_lora(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model metadata does not look like an OMI LoRA.""" + flux_format = _get_flux_lora_format(mod) + if flux_format in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: + raise NotAMatchError("model looks like ControlLoRA or Diffusers LoRA") + + metadata = mod.metadata() + + metadata_looks_like_omi_lora = ( + bool(metadata.get("modelspec.sai_model_spec")) + and metadata.get("ot_branch") == "omi_format" + and metadata.get("modelspec.architecture", "").split("/")[1].lower() == "lora" + ) + + if not metadata_looks_like_omi_lora: + raise NotAMatchError("metadata does not look like OMI LoRA") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> Literal[BaseModelType.Flux, BaseModelType.StableDiffusionXL]: + metadata = mod.metadata() + architecture = metadata["modelspec.architecture"] + + if architecture == stable_diffusion_xl_1_lora: + return BaseModelType.StableDiffusionXL + elif architecture == flux_dev_1_lora: + return BaseModelType.Flux + else: + raise NotAMatchError(f"unrecognised/unsupported architecture for OMI LoRA: {architecture}") + + +class LoRA_OMI_SDXL_Config(LoRA_OMI_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class LoRA_OMI_FLUX_Config(LoRA_OMI_Config_Base, Config_Base): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + + +class LoRA_LyCORIS_Config_Base(LoRA_Config_Base): + """Model config for LoRA/Lycoris models.""" + + type: Literal[ModelType.LoRA] = Field(default=ModelType.LoRA) + format: Literal[ModelFormat.LyCORIS] = Field(default=ModelFormat.LyCORIS) + + @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_lora(mod) + + cls._validate_base(mod) + + return cls(**override_fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: + # First rule out ControlLoRA and Diffusers LoRA + flux_format = _get_flux_lora_format(mod) + if flux_format in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: + raise NotAMatchError("model looks like ControlLoRA or Diffusers LoRA") + + # Note: Existence of these key prefixes/suffixes does not guarantee that this is a LoRA. + # Some main models have these keys, likely due to the creator merging in a LoRA. + has_key_with_lora_prefix = state_dict_has_any_keys_starting_with( + mod.load_state_dict(), + { + "lora_te_", + "lora_unet_", + "lora_te1_", + "lora_te2_", + "lora_transformer_", + }, + ) + + has_key_with_lora_suffix = state_dict_has_any_keys_ending_with( + mod.load_state_dict(), + { + "to_k_lora.up.weight", + "to_q_lora.down.weight", + "lora_A.weight", + "lora_B.weight", + }, + ) + + if not has_key_with_lora_prefix and not has_key_with_lora_suffix: + raise NotAMatchError("model does not match LyCORIS LoRA heuristics") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + if _get_flux_lora_format(mod): + return BaseModelType.Flux + + state_dict = mod.load_state_dict() + # If we've gotten here, we assume that the model is a Stable Diffusion model + token_vector_length = lora_token_vector_length(state_dict) + if token_vector_length == 768: + return BaseModelType.StableDiffusion1 + elif token_vector_length == 1024: + return BaseModelType.StableDiffusion2 + elif token_vector_length == 1280: + return BaseModelType.StableDiffusionXL # recognizes format at https://civitai.com/models/224641 + elif token_vector_length == 2048: + return BaseModelType.StableDiffusionXL + else: + raise NotAMatchError(f"unrecognized token vector length {token_vector_length}") + + +class LoRA_LyCORIS_SD1_Config(LoRA_LyCORIS_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class LoRA_LyCORIS_SD2_Config(LoRA_LyCORIS_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class LoRA_LyCORIS_SDXL_Config(LoRA_LyCORIS_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class LoRA_LyCORIS_FLUX_Config(LoRA_LyCORIS_Config_Base, Config_Base): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + + +class ControlAdapter_Config_Base(ABC, BaseModel): + default_settings: ControlAdapterDefaultSettings | None = Field(None) + + +class ControlLoRA_LyCORIS_FLUX_Config(ControlAdapter_Config_Base, Config_Base): + """Model config for Control LoRA models.""" + + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + type: Literal[ModelType.ControlLoRa] = Field(default=ModelType.ControlLoRa) + format: Literal[ModelFormat.LyCORIS] = Field(default=ModelFormat.LyCORIS) + + trigger_phrases: set[str] | None = Field(None) + + @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_control_lora(mod) + + return cls(**override_fields) + + @classmethod + def _validate_looks_like_control_lora(cls, mod: ModelOnDisk) -> None: + state_dict = mod.load_state_dict() + + if not is_state_dict_likely_flux_control(state_dict): + raise NotAMatchError("model state dict does not look like a Flux Control LoRA") + + +class LoRA_Diffusers_Config_Base(LoRA_Config_Base): + """Model config for LoRA/Diffusers models.""" + + # TODO(psyche): Needs base handling. For FLUX, the Diffusers format does not indicate a folder model; it indicates + # the weights format. FLUX Diffusers LoRAs are single files. + + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + + @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) + + cls._validate_base(mod) + + return cls(**override_fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + if _get_flux_lora_format(mod): + return BaseModelType.Flux + + # If we've gotten here, we assume that the LoRA is a Stable Diffusion LoRA + path_to_weight_file = cls._get_weight_file_or_raise(mod) + state_dict = mod.load_state_dict(path_to_weight_file) + token_vector_length = lora_token_vector_length(state_dict) + + match token_vector_length: + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case 1280: + return BaseModelType.StableDiffusionXL # recognizes format at https://civitai.com/models/224641 + case 2048: + return BaseModelType.StableDiffusionXL + case _: + raise NotAMatchError(f"unrecognized token vector length {token_vector_length}") + + @classmethod + def _get_weight_file_or_raise(cls, mod: ModelOnDisk) -> Path: + suffixes = ["bin", "safetensors"] + weight_files = [mod.path / f"pytorch_lora_weights.{sfx}" for sfx in suffixes] + for wf in weight_files: + if wf.exists(): + return wf + raise NotAMatchError("missing pytorch_lora_weights.bin or pytorch_lora_weights.safetensors") + + +class LoRA_Diffusers_SD1_Config(LoRA_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class LoRA_Diffusers_SD2_Config(LoRA_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class LoRA_Diffusers_SDXL_Config(LoRA_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class LoRA_Diffusers_FLUX_Config(LoRA_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) diff --git a/invokeai/backend/model_manager/configs/main.py b/invokeai/backend/model_manager/configs/main.py new file mode 100644 index 00000000000..ef1ab1fe77f --- /dev/null +++ b/invokeai/backend/model_manager/configs/main.py @@ -0,0 +1,692 @@ +from abc import ABC +from typing import Any, Literal, Self + +from pydantic import BaseModel, ConfigDict, Field + +from invokeai.backend.model_manager.configs.base import ( + Checkpoint_Config_Base, + Config_Base, + Diffusers_Config_Base, + SubmodelDefinition, +) +from invokeai.backend.model_manager.configs.clip_embed import get_clip_variant_type_from_config +from invokeai.backend.model_manager.configs.identification_utils import ( + NotAMatchError, + common_config_paths, + get_config_dict_or_raise, + raise_for_class_name, + raise_for_override_fields, + raise_if_not_dir, + raise_if_not_file, + state_dict_has_any_keys_exact, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + FluxVariantType, + ModelFormat, + ModelType, + ModelVariantType, + SchedulerPredictionType, + SubModelType, +) +from invokeai.backend.quantization.gguf.ggml_tensor import GGMLTensor +from invokeai.backend.stable_diffusion.schedulers.schedulers import SCHEDULER_NAME_VALUES + +DEFAULTS_PRECISION = Literal["fp16", "fp32"] + + +class MainModelDefaultSettings(BaseModel): + vae: str | None = Field(default=None, description="Default VAE for this model (model key)") + vae_precision: DEFAULTS_PRECISION | None = Field(default=None, description="Default VAE precision for this model") + scheduler: SCHEDULER_NAME_VALUES | None = Field(default=None, description="Default scheduler for this model") + steps: int | None = Field(default=None, gt=0, description="Default number of steps for this model") + cfg_scale: float | None = Field(default=None, ge=1, description="Default CFG Scale for this model") + cfg_rescale_multiplier: float | None = Field( + default=None, ge=0, lt=1, description="Default CFG Rescale Multiplier for this model" + ) + width: int | None = Field(default=None, multiple_of=8, ge=64, description="Default width for this model") + height: int | None = Field(default=None, multiple_of=8, ge=64, description="Default height for this model") + guidance: float | None = Field(default=None, ge=1, description="Default Guidance for this model") + + model_config = ConfigDict(extra="forbid") + + +class Main_Config_Base(ABC, BaseModel): + type: Literal[ModelType.Main] = Field(default=ModelType.Main) + trigger_phrases: set[str] | None = Field( + default=None, + description="Set of trigger phrases for this model", + ) + default_settings: MainModelDefaultSettings | None = Field( + default=None, + description="Default settings for this model", + ) + + +def _has_bnb_nf4_keys(state_dict: dict[str | int, Any]) -> bool: + bnb_nf4_keys = { + "double_blocks.0.img_attn.proj.weight.quant_state.bitsandbytes__nf4", + "model.diffusion_model.double_blocks.0.img_attn.proj.weight.quant_state.bitsandbytes__nf4", + } + return any(key in state_dict for key in bnb_nf4_keys) + + +def _has_ggml_tensors(state_dict: dict[str | int, Any]) -> bool: + return any(isinstance(v, GGMLTensor) for v in state_dict.values()) + + +def _has_main_keys(state_dict: dict[str | int, Any]) -> bool: + for key in state_dict.keys(): + if isinstance(key, int): + continue + elif key.startswith( + ( + "cond_stage_model.", + "first_stage_model.", + "model.diffusion_model.", + # Some FLUX checkpoint files contain transformer keys prefixed with "model.diffusion_model". + # This prefix is typically used to distinguish between multiple models bundled in a single file. + "model.diffusion_model.double_blocks.", + ) + ): + return True + elif key.startswith("double_blocks.") and "ip_adapter" not in key: + # FLUX models in the official BFL format contain keys with the "double_blocks." prefix, but we must be + # careful to avoid false positives on XLabs FLUX IP-Adapter models. + return True + return False + + +class Main_Checkpoint_Config_Base(Checkpoint_Config_Base, Main_Config_Base): + """Model config for main checkpoint models.""" + + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + + prediction_type: SchedulerPredictionType = Field() + variant: ModelVariantType = 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_main_model(mod) + + cls._validate_base(mod) + + prediction_type = override_fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod) + + variant = override_fields.get("variant") or cls._get_variant_or_raise(mod) + + return cls(**override_fields, prediction_type=prediction_type, variant=variant) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + state_dict = mod.load_state_dict() + + key_name = "model.diffusion_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight" + if key_name in state_dict and state_dict[key_name].shape[-1] == 768: + return BaseModelType.StableDiffusion1 + if key_name in state_dict and state_dict[key_name].shape[-1] == 1024: + return BaseModelType.StableDiffusion2 + + key_name = "model.diffusion_model.input_blocks.4.1.transformer_blocks.0.attn2.to_k.weight" + if key_name in state_dict and state_dict[key_name].shape[-1] == 2048: + return BaseModelType.StableDiffusionXL + elif key_name in state_dict and state_dict[key_name].shape[-1] == 1280: + return BaseModelType.StableDiffusionXLRefiner + + raise NotAMatchError("unable to determine base type from state dict") + + @classmethod + def _get_scheduler_prediction_type_or_raise(cls, mod: ModelOnDisk) -> SchedulerPredictionType: + base = cls.model_fields["base"].default + + if base is BaseModelType.StableDiffusion2: + state_dict = mod.load_state_dict() + key_name = "model.diffusion_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight" + if key_name in state_dict and state_dict[key_name].shape[-1] == 1024: + if "global_step" in state_dict: + if state_dict["global_step"] == 220000: + return SchedulerPredictionType.Epsilon + elif state_dict["global_step"] == 110000: + return SchedulerPredictionType.VPrediction + return SchedulerPredictionType.VPrediction + else: + return SchedulerPredictionType.Epsilon + + @classmethod + def _get_variant_or_raise(cls, mod: ModelOnDisk) -> ModelVariantType: + base = cls.model_fields["base"].default + + state_dict = mod.load_state_dict() + key_name = "model.diffusion_model.input_blocks.0.0.weight" + + if key_name not in state_dict: + raise NotAMatchError("unable to determine model variant from state dict") + + in_channels = state_dict["model.diffusion_model.input_blocks.0.0.weight"].shape[1] + + match in_channels: + case 4: + return ModelVariantType.Normal + case 5: + # Only SD2 has a depth variant + assert base is BaseModelType.StableDiffusion2, f"unexpected unet in_channels 5 for base '{base}'" + return ModelVariantType.Depth + case 9: + return ModelVariantType.Inpaint + case _: + raise NotAMatchError(f"unrecognized unet in_channels {in_channels} for base '{base}'") + + @classmethod + def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: + has_main_model_keys = _has_main_keys(mod.load_state_dict()) + if not has_main_model_keys: + raise NotAMatchError("state dict does not look like a main model") + + +class Main_Checkpoint_SD1_Config(Main_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class Main_Checkpoint_SD2_Config(Main_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class Main_Checkpoint_SDXL_Config(Main_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class Main_Checkpoint_SDXLRefiner_Config(Main_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXLRefiner] = Field(default=BaseModelType.StableDiffusionXLRefiner) + + +def _get_flux_variant(state_dict: dict[str | int, Any]) -> FluxVariantType | None: + # FLUX Model variant types are distinguished by input channels and the presence of certain keys. + + # Input channels are derived from the shape of either "img_in.weight" or "model.diffusion_model.img_in.weight". + # + # Known models that use the latter key: + # - https://civitai.com/models/885098?modelVersionId=990775 + # - https://civitai.com/models/1018060?modelVersionId=1596255 + # - https://civitai.com/models/978314/ultrareal-fine-tune?modelVersionId=1413133 + # + # Input channels for known FLUX models: + # - Unquantized Dev and Schnell have in_channels=64 + # - BNB-NF4 Dev and Schnell have in_channels=1 + # - FLUX Fill has in_channels=384 + # - Unsure of quantized FLUX Fill models + # - Unsure of GGUF-quantized models + + in_channels = None + for key in {"img_in.weight", "model.diffusion_model.img_in.weight"}: + if key in state_dict: + in_channels = state_dict[key].shape[1] + break + + if in_channels is None: + # TODO(psyche): Should we have a graceful fallback here? Previously we fell back to the "normal" variant, + # but this variant is no longer used for FLUX models. If we get here, but the model is definitely a FLUX + # model, we should figure out a good fallback value. + return None + + # Because FLUX Dev and Schnell models have the same in_channels, we need to check for the presence of + # certain keys to distinguish between them. + is_flux_dev = ( + "guidance_in.out_layer.weight" in state_dict + or "model.diffusion_model.guidance_in.out_layer.weight" in state_dict + ) + + if is_flux_dev and in_channels == 384: + return FluxVariantType.DevFill + elif is_flux_dev: + return FluxVariantType.Dev + else: + # Must be a Schnell model...? + return FluxVariantType.Schnell + + +class Main_Checkpoint_FLUX_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base): + """Model config for main checkpoint models.""" + + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + + variant: FluxVariantType = 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_main_model(mod) + + cls._validate_is_flux(mod) + + cls._validate_does_not_look_like_bnb_quantized(mod) + + cls._validate_does_not_look_like_gguf_quantized(mod) + + variant = override_fields.get("variant") or cls._get_variant_or_raise(mod) + + return cls(**override_fields, variant=variant) + + @classmethod + def _validate_is_flux(cls, mod: ModelOnDisk) -> None: + if not state_dict_has_any_keys_exact( + mod.load_state_dict(), + { + "double_blocks.0.img_attn.norm.key_norm.scale", + "model.diffusion_model.double_blocks.0.img_attn.norm.key_norm.scale", + }, + ): + raise NotAMatchError("state dict does not look like a FLUX checkpoint") + + @classmethod + def _get_variant_or_raise(cls, mod: ModelOnDisk) -> FluxVariantType: + # FLUX Model variant types are distinguished by input channels and the presence of certain keys. + state_dict = mod.load_state_dict() + variant = _get_flux_variant(state_dict) + + if variant is None: + # TODO(psyche): Should we have a graceful fallback here? Previously we fell back to the "normal" variant, + # but this variant is no longer used for FLUX models. If we get here, but the model is definitely a FLUX + # model, we should figure out a good fallback value. + raise NotAMatchError("unable to determine model variant from state dict") + + return variant + + @classmethod + def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: + has_main_model_keys = _has_main_keys(mod.load_state_dict()) + if not has_main_model_keys: + raise NotAMatchError("state dict does not look like a main model") + + @classmethod + def _validate_does_not_look_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: + has_bnb_nf4_keys = _has_bnb_nf4_keys(mod.load_state_dict()) + if has_bnb_nf4_keys: + raise NotAMatchError("state dict looks like bnb quantized nf4") + + @classmethod + def _validate_does_not_look_like_gguf_quantized(cls, mod: ModelOnDisk): + has_ggml_tensors = _has_ggml_tensors(mod.load_state_dict()) + if has_ggml_tensors: + raise NotAMatchError("state dict looks like GGUF quantized") + + +class Main_BnBNF4_FLUX_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base): + """Model config for main checkpoint models.""" + + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + format: Literal[ModelFormat.BnbQuantizednf4b] = Field(default=ModelFormat.BnbQuantizednf4b) + + variant: FluxVariantType = 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_main_model(mod) + + cls._validate_model_looks_like_bnb_quantized(mod) + + variant = override_fields.get("variant") or cls._get_variant_or_raise(mod) + + return cls(**override_fields, variant=variant) + + @classmethod + def _get_variant_or_raise(cls, mod: ModelOnDisk) -> FluxVariantType: + # FLUX Model variant types are distinguished by input channels and the presence of certain keys. + state_dict = mod.load_state_dict() + variant = _get_flux_variant(state_dict) + + if variant is None: + # TODO(psyche): Should we have a graceful fallback here? Previously we fell back to the "normal" variant, + # but this variant is no longer used for FLUX models. If we get here, but the model is definitely a FLUX + # model, we should figure out a good fallback value. + raise NotAMatchError("unable to determine model variant from state dict") + + return variant + + @classmethod + def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: + has_main_model_keys = _has_main_keys(mod.load_state_dict()) + if not has_main_model_keys: + raise NotAMatchError("state dict does not look like a main model") + + @classmethod + def _validate_model_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: + has_bnb_nf4_keys = _has_bnb_nf4_keys(mod.load_state_dict()) + if not has_bnb_nf4_keys: + raise NotAMatchError("state dict does not look like bnb quantized nf4") + + +class Main_GGUF_FLUX_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base): + """Model config for main checkpoint models.""" + + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + format: Literal[ModelFormat.GGUFQuantized] = Field(default=ModelFormat.GGUFQuantized) + + variant: FluxVariantType = 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_main_model(mod) + + cls._validate_looks_like_gguf_quantized(mod) + + variant = override_fields.get("variant") or cls._get_variant_or_raise(mod) + + return cls(**override_fields, variant=variant) + + @classmethod + def _get_variant_or_raise(cls, mod: ModelOnDisk) -> FluxVariantType: + # FLUX Model variant types are distinguished by input channels and the presence of certain keys. + state_dict = mod.load_state_dict() + variant = _get_flux_variant(state_dict) + + if variant is None: + # TODO(psyche): Should we have a graceful fallback here? Previously we fell back to the "normal" variant, + # but this variant is no longer used for FLUX models. If we get here, but the model is definitely a FLUX + # model, we should figure out a good fallback value. + raise NotAMatchError("unable to determine model variant from state dict") + + return variant + + @classmethod + def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: + has_main_model_keys = _has_main_keys(mod.load_state_dict()) + if not has_main_model_keys: + raise NotAMatchError("state dict does not look like a main model") + + @classmethod + def _validate_looks_like_gguf_quantized(cls, mod: ModelOnDisk) -> None: + has_ggml_tensors = _has_ggml_tensors(mod.load_state_dict()) + if not has_ggml_tensors: + raise NotAMatchError("state dict does not look like GGUF quantized") + + +class Main_Diffusers_Config_Base(Diffusers_Config_Base, Main_Config_Base): + prediction_type: SchedulerPredictionType = Field() + variant: ModelVariantType = 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) + + raise_for_class_name( + common_config_paths(mod.path), + { + # SD 1.x and 2.x + "StableDiffusionPipeline", + "StableDiffusionInpaintPipeline", + # SDXL + "StableDiffusionXLPipeline", + "StableDiffusionXLInpaintPipeline", + # SDXL Refiner + "StableDiffusionXLImg2ImgPipeline", + # TODO(psyche): Do we actually support LCM models? I don't see using this class anywhere in the codebase. + "LatentConsistencyModelPipeline", + }, + ) + + cls._validate_base(mod) + + variant = override_fields.get("variant") or cls._get_variant_or_raise(mod) + + prediction_type = override_fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod) + + repo_variant = override_fields.get("repo_variant") or cls._get_repo_variant_or_raise(mod) + + return cls( + **override_fields, + variant=variant, + prediction_type=prediction_type, + repo_variant=repo_variant, + ) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + # Handle pipelines with a UNet (i.e SD 1.x, SD2.x, SDXL). + unet_conf = get_config_dict_or_raise(mod.path / "unet" / "config.json") + cross_attention_dim = unet_conf.get("cross_attention_dim") + match cross_attention_dim: + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case 1280: + return BaseModelType.StableDiffusionXLRefiner + case 2048: + return BaseModelType.StableDiffusionXL + case _: + raise NotAMatchError(f"unrecognized cross_attention_dim {cross_attention_dim}") + + @classmethod + def _get_scheduler_prediction_type_or_raise(cls, mod: ModelOnDisk) -> SchedulerPredictionType: + scheduler_conf = get_config_dict_or_raise(mod.path / "scheduler" / "scheduler_config.json") + + # TODO(psyche): Is epsilon the right default or should we raise if it's not present? + prediction_type = scheduler_conf.get("prediction_type", "epsilon") + + match prediction_type: + case "v_prediction": + return SchedulerPredictionType.VPrediction + case "epsilon": + return SchedulerPredictionType.Epsilon + case _: + raise NotAMatchError(f"unrecognized scheduler prediction_type {prediction_type}") + + @classmethod + def _get_variant_or_raise(cls, mod: ModelOnDisk) -> ModelVariantType: + base = cls.model_fields["base"].default + unet_config = get_config_dict_or_raise(mod.path / "unet" / "config.json") + in_channels = unet_config.get("in_channels") + + match in_channels: + case 4: + return ModelVariantType.Normal + case 5: + # Only SD2 has a depth variant + assert base is BaseModelType.StableDiffusion2, f"unexpected unet in_channels 5 for base '{base}'" + return ModelVariantType.Depth + case 9: + return ModelVariantType.Inpaint + case _: + raise NotAMatchError(f"unrecognized unet in_channels {in_channels} for base '{base}'") + + +class Main_Diffusers_SD1_Config(Main_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(BaseModelType.StableDiffusion1) + + +class Main_Diffusers_SD2_Config(Main_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion2] = Field(BaseModelType.StableDiffusion2) + + +class Main_Diffusers_SDXL_Config(Main_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(BaseModelType.StableDiffusionXL) + + +class Main_Diffusers_SDXLRefiner_Config(Main_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXLRefiner] = Field(BaseModelType.StableDiffusionXLRefiner) + + +class Main_Diffusers_SD3_Config(Diffusers_Config_Base, Main_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion3] = Field(BaseModelType.StableDiffusion3) + submodels: dict[SubModelType, SubmodelDefinition] | None = Field( + description="Loadable submodels in this model", + default=None, + ) + + @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), + { + "StableDiffusion3Pipeline", + "SD3Transformer2DModel", + }, + ) + + submodels = override_fields.get("submodels") or cls._get_submodels_or_raise(mod) + + repo_variant = override_fields.get("repo_variant") or cls._get_repo_variant_or_raise(mod) + + return cls( + **override_fields, + submodels=submodels, + repo_variant=repo_variant, + ) + + @classmethod + def _get_submodels_or_raise(cls, mod: ModelOnDisk) -> dict[SubModelType, SubmodelDefinition]: + # Example: https://huggingface.co/stabilityai/stable-diffusion-3.5-medium/blob/main/model_index.json + config = get_config_dict_or_raise(common_config_paths(mod.path)) + + submodels: dict[SubModelType, SubmodelDefinition] = {} + + for key, value in config.items(): + # Anything that starts with an underscore is top-level metadata, not a submodel + if key.startswith("_") or not (isinstance(value, list) and len(value) == 2): + continue + # The key is something like "transformer" and is a submodel - it will be in a dir of the same name. + # The value value is something like ["diffusers", "SD3Transformer2DModel"] + _library_name, class_name = value + + match class_name: + case "CLIPTextModelWithProjection": + model_type = ModelType.CLIPEmbed + path_or_prefix = (mod.path / key).resolve().as_posix() + + # We need to read the config to determine the variant of the CLIP model. + clip_embed_config = get_config_dict_or_raise( + { + mod.path / key / "config.json", + mod.path / key / "model_index.json", + } + ) + variant = get_clip_variant_type_from_config(clip_embed_config) + submodels[SubModelType(key)] = SubmodelDefinition( + path_or_prefix=path_or_prefix, + model_type=model_type, + variant=variant, + ) + case "SD3Transformer2DModel": + model_type = ModelType.Main + path_or_prefix = (mod.path / key).resolve().as_posix() + variant = None + submodels[SubModelType(key)] = SubmodelDefinition( + path_or_prefix=path_or_prefix, + model_type=model_type, + variant=variant, + ) + case _: + pass + + return submodels + + +class Main_Diffusers_CogView4_Config(Diffusers_Config_Base, Main_Config_Base, Config_Base): + base: Literal[BaseModelType.CogView4] = Field(BaseModelType.CogView4) + + @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), + { + "CogView4Pipeline", + }, + ) + + repo_variant = override_fields.get("repo_variant") or cls._get_repo_variant_or_raise(mod) + + return cls( + **override_fields, + repo_variant=repo_variant, + ) + + +class ExternalAPI_Config_Base(ABC, BaseModel): + """Model config for API-based models.""" + + format: Literal[ModelFormat.Api] = Field(default=ModelFormat.Api) + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + raise NotAMatchError("External API models cannot be built from disk") + + +class Main_ExternalAPI_ChatGPT4o_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): + base: Literal[BaseModelType.ChatGPT4o] = Field(default=BaseModelType.ChatGPT4o) + + +class Main_ExternalAPI_Gemini2_5_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): + base: Literal[BaseModelType.Gemini2_5] = Field(default=BaseModelType.Gemini2_5) + + +class Main_ExternalAPI_Imagen3_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): + base: Literal[BaseModelType.Imagen3] = Field(default=BaseModelType.Imagen3) + + +class Main_ExternalAPI_Imagen4_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): + base: Literal[BaseModelType.Imagen4] = Field(default=BaseModelType.Imagen4) + + +class Main_ExternalAPI_FluxKontext_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): + base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) + + +class Video_Config_Base(ABC, BaseModel): + type: Literal[ModelType.Video] = Field(default=ModelType.Video) + trigger_phrases: set[str] | None = Field(description="Set of trigger phrases for this model", default=None) + default_settings: MainModelDefaultSettings | None = Field( + description="Default settings for this model", default=None + ) + + +class Video_ExternalAPI_Veo3_Config(ExternalAPI_Config_Base, Video_Config_Base, Config_Base): + base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) + + +class Video_ExternalAPI_Runway_Config(ExternalAPI_Config_Base, Video_Config_Base, Config_Base): + base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) diff --git a/invokeai/backend/model_manager/configs/siglip.py b/invokeai/backend/model_manager/configs/siglip.py new file mode 100644 index 00000000000..62ca9494e27 --- /dev/null +++ b/invokeai/backend/model_manager/configs/siglip.py @@ -0,0 +1,44 @@ +from typing import ( + Literal, + Self, +) + +from pydantic import Field +from typing_extensions import Any + +from invokeai.backend.model_manager.configs.base import Config_Base, Diffusers_Config_Base +from invokeai.backend.model_manager.configs.identification_utils import ( + common_config_paths, + raise_for_class_name, + raise_for_override_fields, + raise_if_not_dir, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ModelFormat, + ModelType, +) + + +class SigLIP_Diffusers_Config(Diffusers_Config_Base, Config_Base): + """Model config for SigLIP.""" + + type: Literal[ModelType.SigLIP] = Field(default=ModelType.SigLIP) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + + @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) + + raise_for_class_name( + common_config_paths(mod.path), + { + "SiglipModel", + }, + ) + + return cls(**override_fields) diff --git a/invokeai/backend/model_manager/configs/spandrel.py b/invokeai/backend/model_manager/configs/spandrel.py new file mode 100644 index 00000000000..8ca8ad5f603 --- /dev/null +++ b/invokeai/backend/model_manager/configs/spandrel.py @@ -0,0 +1,54 @@ +from typing import ( + Literal, + Self, +) + +from pydantic import Field +from typing_extensions import Any + +from invokeai.backend.model_manager.configs.base import Config_Base +from invokeai.backend.model_manager.configs.identification_utils import ( + NotAMatchError, + raise_for_override_fields, + raise_if_not_file, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ModelFormat, + ModelType, +) +from invokeai.backend.spandrel_image_to_image_model import SpandrelImageToImageModel + + +class Spandrel_Checkpoint_Config(Config_Base): + """Model config for Spandrel Image to Image models.""" + + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + type: Literal[ModelType.SpandrelImageToImage] = Field(default=ModelType.SpandrelImageToImage) + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + + @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_spandrel_loads_model(mod) + + return cls(**override_fields) + + @classmethod + def _validate_spandrel_loads_model(cls, mod: ModelOnDisk) -> None: + try: + # It would be nice to avoid having to load the Spandrel model from disk here. A couple of options were + # explored to avoid this: + # 1. Call `SpandrelImageToImageModel.load_from_state_dict(ckpt)`, where `ckpt` is a state_dict on the meta + # device. Unfortunately, some Spandrel models perform operations during initialization that are not + # supported on meta tensors. + # 2. Spandrel has internal logic to determine a model's type from its state_dict before loading the model. + # This logic is not exposed in spandrel's public API. We could copy the logic here, but then we have to + # maintain it, and the risk of false positive detections is higher. + SpandrelImageToImageModel.load_from_file(mod.path) + except Exception as e: + raise NotAMatchError("model does not match SpandrelImageToImage heuristics") from e diff --git a/invokeai/backend/model_manager/configs/t2i_adapter.py b/invokeai/backend/model_manager/configs/t2i_adapter.py new file mode 100644 index 00000000000..865c4dc7638 --- /dev/null +++ b/invokeai/backend/model_manager/configs/t2i_adapter.py @@ -0,0 +1,79 @@ +from typing import ( + Literal, + Self, +) + +from pydantic import Field +from typing_extensions import Any + +from invokeai.backend.model_manager.config import ControlAdapterDefaultSettings +from invokeai.backend.model_manager.configs.base import Config_Base, Diffusers_Config_Base +from invokeai.backend.model_manager.configs.identification_utils import ( + NotAMatchError, + common_config_paths, + get_config_dict_or_raise, + raise_for_class_name, + raise_for_override_fields, + raise_if_not_dir, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ModelFormat, + ModelType, +) + + +class T2IAdapter_Diffusers_Config_Base(Diffusers_Config_Base): + """Model config for T2I.""" + + type: Literal[ModelType.T2IAdapter] = Field(default=ModelType.T2IAdapter) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + default_settings: ControlAdapterDefaultSettings | None = Field(None) + + @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) + + raise_for_class_name( + common_config_paths(mod.path), + { + "T2IAdapter", + }, + ) + + cls._validate_base(mod) + + return cls(**override_fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + config_dict = get_config_dict_or_raise(common_config_paths(mod.path)) + + adapter_type = config_dict.get("adapter_type") + + match adapter_type: + case "full_adapter_xl": + return BaseModelType.StableDiffusionXL + case "full_adapter" | "light_adapter": + return BaseModelType.StableDiffusion1 + case _: + raise NotAMatchError(f"unrecognized adapter_type '{adapter_type}'") + + +class T2IAdapter_Diffusers_SD1_Config(T2IAdapter_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class T2IAdapter_Diffusers_SDXL_Config(T2IAdapter_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) diff --git a/invokeai/backend/model_manager/configs/t5_encoder.py b/invokeai/backend/model_manager/configs/t5_encoder.py new file mode 100644 index 00000000000..3b6f2e733dd --- /dev/null +++ b/invokeai/backend/model_manager/configs/t5_encoder.py @@ -0,0 +1,87 @@ +from typing import Any, Literal, Self + +from pydantic import Field + +from invokeai.backend.model_manager.configs.base import Config_Base +from invokeai.backend.model_manager.configs.identification_utils import ( + NotAMatchError, + common_config_paths, + raise_for_class_name, + raise_for_override_fields, + raise_if_not_dir, + state_dict_has_any_keys_ending_with, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType + + +class T5Encoder_T5Encoder_Config(Config_Base): + """Configuration for T5 Encoder models in a bespoke, diffusers-like format. The model weights are expected to be in + a folder called text_encoder_2 inside the model directory, with a config file named model.safetensors.index.json.""" + + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + type: Literal[ModelType.T5Encoder] = Field(default=ModelType.T5Encoder) + format: Literal[ModelFormat.T5Encoder] = Field(default=ModelFormat.T5Encoder) + + @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) + + raise_for_class_name( + common_config_paths(mod.path), + { + "T5EncoderModel", + }, + ) + + cls.raise_if_doesnt_have_unquantized_config_file(mod) + + return cls(**override_fields) + + @classmethod + def raise_if_doesnt_have_unquantized_config_file(cls, mod: ModelOnDisk) -> None: + has_unquantized_config = (mod.path / "text_encoder_2" / "model.safetensors.index.json").exists() + + if not has_unquantized_config: + raise NotAMatchError("missing text_encoder_2/model.safetensors.index.json") + + +class T5Encoder_BnBLLMint8_Config(Config_Base): + """Configuration for T5 Encoder models quantized by bitsandbytes' LLM.int8.""" + + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + type: Literal[ModelType.T5Encoder] = Field(default=ModelType.T5Encoder) + format: Literal[ModelFormat.BnbQuantizedLlmInt8b] = Field(default=ModelFormat.BnbQuantizedLlmInt8b) + + @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) + + raise_for_class_name( + common_config_paths(mod.path), + { + "T5EncoderModel", + }, + ) + + cls.raise_if_filename_doesnt_look_like_bnb_quantized(mod) + + cls.raise_if_state_dict_doesnt_look_like_bnb_quantized(mod) + + return cls(**override_fields) + + @classmethod + def raise_if_filename_doesnt_look_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: + filename_looks_like_bnb = any(x for x in mod.weight_files() if "llm_int8" in x.as_posix()) + if not filename_looks_like_bnb: + raise NotAMatchError("filename does not look like bnb quantized llm_int8") + + @classmethod + def raise_if_state_dict_doesnt_look_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: + has_scb_key_suffix = state_dict_has_any_keys_ending_with(mod.load_state_dict(), "SCB") + if not has_scb_key_suffix: + raise NotAMatchError("state dict does not look like bnb quantized llm_int8") diff --git a/invokeai/backend/model_manager/configs/textual_inversion.py b/invokeai/backend/model_manager/configs/textual_inversion.py new file mode 100644 index 00000000000..c827f5234d5 --- /dev/null +++ b/invokeai/backend/model_manager/configs/textual_inversion.py @@ -0,0 +1,156 @@ +from abc import ABC +from pathlib import Path +from typing import ( + Literal, + Self, +) + +import torch +from pydantic import BaseModel, Field +from typing_extensions import Any + +from invokeai.backend.model_manager.configs.base import Config_Base +from invokeai.backend.model_manager.configs.identification_utils import ( + NotAMatchError, + 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, +) + + +class TI_Config_Base(ABC, BaseModel): + type: Literal[ModelType.TextualInversion] = Field(default=ModelType.TextualInversion) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk, path: Path | None = None) -> None: + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod, path) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) -> bool: + try: + p = path or mod.path + + if not p.exists(): + return False + + if p.is_dir(): + return False + + if p.name in [f"learned_embeds.{s}" for s in mod.weight_files()]: + return True + + state_dict = mod.load_state_dict(p) + + # Heuristic: textual inversion embeddings have these keys + if any(key in {"string_to_param", "emb_params", "clip_g"} for key in state_dict.keys()): + return True + + # Heuristic: small state dict with all tensor values + if (len(state_dict)) < 10 and all(isinstance(v, torch.Tensor) for v in state_dict.values()): + return True + + return False + except Exception: + return False + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseModelType: + p = path or mod.path + + try: + state_dict = mod.load_state_dict(p) + except Exception as e: + raise NotAMatchError(f"unable to load state dict from {p}: {e}") from e + + try: + if "string_to_token" in state_dict: + token_dim = list(state_dict["string_to_param"].values())[0].shape[-1] + elif "emb_params" in state_dict: + token_dim = state_dict["emb_params"].shape[-1] + elif "clip_g" in state_dict: + token_dim = state_dict["clip_g"].shape[-1] + else: + token_dim = list(state_dict.values())[0].shape[0] + except Exception as e: + raise NotAMatchError(f"unable to determine token dimension from state dict in {p}: {e}") from e + + match token_dim: + case 768: + return BaseModelType.StableDiffusion1 + case 1024: + return BaseModelType.StableDiffusion2 + case 1280: + return BaseModelType.StableDiffusionXL + case _: + raise NotAMatchError(f"unrecognized token dimension {token_dim}") + + +class TI_File_Config_Base(TI_Config_Base): + """Model config for textual inversion embeddings.""" + + format: Literal[ModelFormat.EmbeddingFile] = Field(default=ModelFormat.EmbeddingFile) + + @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 not cls._file_looks_like_embedding(mod): + raise NotAMatchError("model does not look like a textual inversion embedding file") + + cls._validate_base(mod) + + return cls(**override_fields) + + +class TI_File_SD1_Config(TI_File_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class TI_File_SD2_Config(TI_File_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class TI_File_SDXL_Config(TI_File_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class TI_Folder_Config_Base(TI_Config_Base): + """Model config for textual inversion embeddings.""" + + format: Literal[ModelFormat.EmbeddingFolder] = Field(default=ModelFormat.EmbeddingFolder) + + @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) + + for p in mod.weight_files(): + if cls._file_looks_like_embedding(mod, p): + cls._validate_base(mod, p) + return cls(**override_fields) + + raise NotAMatchError("model does not look like a textual inversion embedding folder") + + +class TI_Folder_SD1_Config(TI_Folder_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class TI_Folder_SD2_Config(TI_Folder_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class TI_Folder_SDXL_Config(TI_Folder_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) diff --git a/invokeai/backend/model_manager/configs/unknown.py b/invokeai/backend/model_manager/configs/unknown.py new file mode 100644 index 00000000000..10aad755664 --- /dev/null +++ b/invokeai/backend/model_manager/configs/unknown.py @@ -0,0 +1,24 @@ +from typing import Any, Literal, Self + +from pydantic import Field + +from invokeai.backend.model_manager.configs.base import Config_Base +from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ModelFormat, + ModelType, +) + + +class Unknown_Config(Config_Base): + """Model config for unknown models, used as a fallback when we cannot identify a model.""" + + base: Literal[BaseModelType.Unknown] = Field(default=BaseModelType.Unknown) + type: Literal[ModelType.Unknown] = Field(default=ModelType.Unknown) + format: Literal[ModelFormat.Unknown] = Field(default=ModelFormat.Unknown) + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: + raise NotAMatchError("unknown model config cannot match any model") diff --git a/invokeai/backend/model_manager/configs/vae.py b/invokeai/backend/model_manager/configs/vae.py new file mode 100644 index 00000000000..6c51fd770ee --- /dev/null +++ b/invokeai/backend/model_manager/configs/vae.py @@ -0,0 +1,166 @@ +import re +from typing import ( + Literal, + Self, +) + +from pydantic import Field +from typing_extensions import Any + +from invokeai.backend.model_manager.configs.base import Checkpoint_Config_Base, Config_Base, Diffusers_Config_Base +from invokeai.backend.model_manager.configs.identification_utils import ( + NotAMatchError, + common_config_paths, + get_config_dict_or_raise, + raise_for_class_name, + raise_for_override_fields, + raise_if_not_dir, + state_dict_has_any_keys_starting_with, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + ModelFormat, + ModelType, +) + +REGEX_TO_BASE: dict[str, BaseModelType] = { + r"xl": BaseModelType.StableDiffusionXL, + r"sd2": BaseModelType.StableDiffusion2, + r"vae": BaseModelType.StableDiffusion1, + r"FLUX.1-schnell_ae": BaseModelType.Flux, +} + + +class VAE_Checkpoint_Config_Base(Checkpoint_Config_Base): + """Model config for standalone VAE models.""" + + type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + + @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) + + cls._validate_looks_like_vae(mod) + + cls._validate_base(mod) + + return cls(**override_fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _validate_looks_like_vae(cls, mod: ModelOnDisk) -> None: + if not state_dict_has_any_keys_starting_with( + mod.load_state_dict(), + { + "encoder.conv_in", + "decoder.conv_in", + }, + ): + raise NotAMatchError("model does not match Checkpoint VAE heuristics") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + # Heuristic: VAEs of all architectures have a similar structure; the best we can do is guess based on name + for regexp, base in REGEX_TO_BASE.items(): + if re.search(regexp, mod.path.name, re.IGNORECASE): + return base + + raise NotAMatchError("cannot determine base type") + + +class VAE_Checkpoint_SD1_Config(VAE_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class VAE_Checkpoint_SD2_Config(VAE_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) + + +class VAE_Checkpoint_SDXL_Config(VAE_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) + + +class VAE_Checkpoint_FLUX_Config(VAE_Checkpoint_Config_Base, Config_Base): + base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) + + +class VAE_Diffusers_Config_Base(Diffusers_Config_Base): + """Model config for standalone VAE models (diffusers version).""" + + type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) + format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) + + @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) + + raise_for_class_name( + common_config_paths(mod.path), + { + "AutoencoderKL", + "AutoencoderTiny", + }, + ) + + cls._validate_base(mod) + + return cls(**override_fields) + + @classmethod + def _validate_base(cls, mod: ModelOnDisk) -> None: + """Raise `NotAMatch` if the model base does not match this config class.""" + expected_base = cls.model_fields["base"].default + recognized_base = cls._get_base_or_raise(mod) + if expected_base is not recognized_base: + raise NotAMatchError(f"base is {recognized_base}, not {expected_base}") + + @classmethod + def _config_looks_like_sdxl(cls, config: dict[str, Any]) -> bool: + # Heuristic: These config values that distinguish Stability's SD 1.x VAE from their SDXL VAE. + return config.get("scaling_factor", 0) == 0.13025 and config.get("sample_size") in [512, 1024] + + @classmethod + def _name_looks_like_sdxl(cls, mod: ModelOnDisk) -> bool: + # Heuristic: SD and SDXL VAE are the same shape (3-channel RGB to 4-channel float scaled down + # by a factor of 8), so we can't necessarily tell them apart by config hyperparameters. Best + # we can do is guess based on name. + return bool(re.search(r"xl\b", cls._guess_name(mod), re.IGNORECASE)) + + @classmethod + def _guess_name(cls, mod: ModelOnDisk) -> str: + name = mod.path.name + if name == "vae": + name = mod.path.parent.name + return name + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + config_dict = get_config_dict_or_raise(common_config_paths(mod.path)) + if cls._config_looks_like_sdxl(config_dict): + return BaseModelType.StableDiffusionXL + elif cls._name_looks_like_sdxl(mod): + return BaseModelType.StableDiffusionXL + else: + # TODO(psyche): Figure out how to positively identify SD1 here, and raise if we can't. Until then, YOLO. + return BaseModelType.StableDiffusion1 + + +class VAE_Diffusers_SD1_Config(VAE_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) + + +class VAE_Diffusers_SDXL_Config(VAE_Diffusers_Config_Base, Config_Base): + base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) From 03b3191e1dbfaf7cdc02492d1d61f1b8bb7a93c3 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 6 Oct 2025 13:36:58 +1100 Subject: [PATCH 47/62] docs(mm): add comments for identification utils --- .../configs/identification_utils.py | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/invokeai/backend/model_manager/configs/identification_utils.py b/invokeai/backend/model_manager/configs/identification_utils.py index 58d545cb64d..c2c310a332e 100644 --- a/invokeai/backend/model_manager/configs/identification_utils.py +++ b/invokeai/backend/model_manager/configs/identification_utils.py @@ -21,6 +21,18 @@ def __init__(self, reason: str): def get_config_dict_or_raise(config_path: Path | set[Path]) -> dict[str, Any]: + """Load the diffusers/transformers model config file and return it as a dictionary. The config file is expected + to be in JSON format. + + Args: + config_path: The path to the config file, or a set of paths to try. + + Returns: + The config file as a dictionary. + + Raises: + NotAMatch if the config file is missing or cannot be loaded. + """ paths_to_check = config_path if isinstance(config_path, set) else {config_path} problems: dict[Path, str] = {} @@ -45,6 +57,12 @@ def get_config_dict_or_raise(config_path: Path | set[Path]) -> dict[str, Any]: def get_class_name_from_config_dict_or_raise(config_path: Path | set[Path]) -> str: """Load the diffusers/transformers model config file and return the class name. + Args: + config_path: The path to the config file, or a set of paths to try. + + Returns: + The class name from the config file. + Raises: NotAMatch if the config file is missing or does not contain a valid class name. """ @@ -69,20 +87,22 @@ def get_class_name_from_config_dict_or_raise(config_path: Path | set[Path]) -> s return config_class_name -def raise_for_class_name(config_path: Path | set[Path], expected: set[str]) -> None: +def raise_for_class_name(config_path: Path | set[Path], class_name: str | set[str]) -> None: """Get the class name from the config file and raise NotAMatch if it is not in the expected set. Args: - config_path: The path to the config file. - expected: The expected class names. + config_path: The path to the config file, or a set of paths to try. + class_name: The expected class name, or a set of expected class names. Raises: NotAMatch if the class name is not in the expected set. """ - class_name = get_class_name_from_config_dict_or_raise(config_path) - if class_name not in expected: - raise NotAMatchError(f"invalid class name from config: {class_name}") + class_name = {class_name} if isinstance(class_name, str) else class_name + + actual_class_name = get_class_name_from_config_dict_or_raise(config_path) + if actual_class_name not in class_name: + raise NotAMatchError(f"invalid class name from config: {actual_class_name}") def raise_for_override_fields(candidate_config_class: type[BaseModel], override_fields: dict[str, Any]) -> None: @@ -91,6 +111,9 @@ def raise_for_override_fields(candidate_config_class: type[BaseModel], override_ For example, if the candidate config class has a field "base" of type Literal[BaseModelType.StableDiffusion1], and the override fields contain "base": BaseModelType.Flux, this function will raise NotAMatch. + Internally, this function extracts the pydantic schema for each individual override field from the candidate config + class and validates the override value against that schema. Post-instantiation validators are not run. + Args: candidate_config_class: The config class that is being tested. override_fields: The override fields provided by the user. From c0fff3a44a13701836d26440d466b111330ffbbb Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 6 Oct 2025 13:38:28 +1100 Subject: [PATCH 48/62] chore(ui): typegen --- .../frontend/web/src/services/api/schema.ts | 21980 +++++++++------- 1 file changed, 12629 insertions(+), 9351 deletions(-) diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts index 388263e6ece..8ef99103f35 100644 --- a/invokeai/frontend/web/src/services/api/schema.ts +++ b/invokeai/frontend/web/src/services/api/schema.ts @@ -2200,98 +2200,7 @@ export type components = { */ type: "alpha_mask_to_tensor"; }; - AnyModelConfig: components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"] | components["schemas"]["MainBnbQuantized4bCheckpointConfig"] | components["schemas"]["MainGGUFCheckpointConfig"] | components["schemas"]["VAEDiffusersConfig"] | components["schemas"]["VAECheckpointConfig"] | components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"] | components["schemas"]["LoRALyCORISConfig"] | components["schemas"]["LoRAOmiConfig"] | components["schemas"]["ControlLoRALyCORISConfig"] | components["schemas"]["ControlLoRADiffusersConfig"] | components["schemas"]["LoRADiffusersConfig"] | components["schemas"]["T5EncoderConfig"] | components["schemas"]["T5EncoderBnbQuantizedLlmInt8bConfig"] | components["schemas"]["TextualInversionFileConfig"] | components["schemas"]["TextualInversionFolderConfig"] | components["schemas"]["IPAdapterInvokeAIConfig"] | components["schemas"]["IPAdapterCheckpointConfig"] | components["schemas"]["T2IAdapterConfig"] | components["schemas"]["SpandrelImageToImageConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["CLIPLEmbedDiffusersConfig"] | components["schemas"]["CLIPGEmbedDiffusersConfig"] | components["schemas"]["SigLIPConfig"] | components["schemas"]["FluxReduxConfig"] | components["schemas"]["LlavaOnevisionConfig"] | components["schemas"]["ApiModelConfig"] | components["schemas"]["VideoApiModelConfig"] | components["schemas"]["UnknownModelConfig"]; - /** - * ApiModelConfig - * @description Model config for API-based models. - */ - ApiModelConfig: { - /** - * 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; - /** - * Cover Image - * @description Url for image to preview model - */ - cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; - /** - * Usage Info - * @description Usage information for this model - */ - usage_info: 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; - /** Variant */ - variant: components["schemas"]["ModelVariantType"] | components["schemas"]["FluxVariantType"]; - /** - * Format - * @default api - * @constant - */ - format: "api"; - /** - * Base - * @enum {string} - */ - base: "chatgpt-4o" | "gemini-2.5" | "imagen3" | "imagen4" | "flux-kontext"; - }; + 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; /** * AppConfig * @description App Config Response @@ -3464,28 +3373,8 @@ export type components = { */ bulk_download_item_name: string; }; - /** CLIPField */ - CLIPField: { - /** @description Info to load tokenizer submodel */ - tokenizer: components["schemas"]["ModelIdentifierField"]; - /** @description Info to load text_encoder submodel */ - text_encoder: components["schemas"]["ModelIdentifierField"]; - /** - * Skipped Layers - * @description Number of skipped layers in text_encoder - */ - skipped_layers: number; - /** - * Loras - * @description LoRAs to apply on model loading - */ - loras: components["schemas"]["LoRAField"][]; - }; - /** - * CLIPGEmbedDiffusersConfig - * @description Model config for CLIP-G Embeddings. - */ - CLIPGEmbedDiffusersConfig: { + /** CLIPEmbed_Diffusers_G_Config */ + CLIPEmbed_Diffusers_G_Config: { /** * Key * @description A unique key for this model. @@ -3572,11 +3461,8 @@ export type components = { */ variant: "gigantic"; }; - /** - * CLIPLEmbedDiffusersConfig - * @description Model config for CLIP-L Embeddings. - */ - CLIPLEmbedDiffusersConfig: { + /** CLIPEmbed_Diffusers_L_Config */ + CLIPEmbed_Diffusers_L_Config: { /** * Key * @description A unique key for this model. @@ -3663,6 +3549,23 @@ export type components = { */ variant: "large"; }; + /** CLIPField */ + CLIPField: { + /** @description Info to load tokenizer submodel */ + tokenizer: components["schemas"]["ModelIdentifierField"]; + /** @description Info to load text_encoder submodel */ + text_encoder: components["schemas"]["ModelIdentifierField"]; + /** + * Skipped Layers + * @description Number of skipped layers in text_encoder + */ + skipped_layers: number; + /** + * Loras + * @description LoRAs to apply on model loading + */ + loras: components["schemas"]["LoRAField"][]; + }; /** * CLIPOutput * @description Base class for invocations that output a CLIP field @@ -3740,10 +3643,10 @@ export type components = { type: "clip_skip_output"; }; /** - * CLIPVisionDiffusersConfig + * CLIPVision_Diffusers_Config * @description Model config for CLIPVision. */ - CLIPVisionDiffusersConfig: { + CLIPVision_Diffusers_Config: { /** * Key * @description A unique key for this model. @@ -5239,91 +5142,6 @@ export type components = { */ resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; }; - /** - * ControlLoRADiffusersConfig - * @description Model config for Control LoRA models. - */ - ControlLoRADiffusersConfig: { - /** - * 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; - /** - * Cover Image - * @description Url for image to preview model - */ - cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; - /** - * Usage Info - * @description Usage information for this model - */ - usage_info: string | null; - default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; - /** - * Base - * @constant - */ - base: "flux"; - /** - * Type - * @default control_lora - * @constant - */ - type: "control_lora"; - /** - * Format - * @default diffusers - * @constant - */ - format: "diffusers"; - /** Trigger Phrases */ - trigger_phrases: string[] | null; - }; /** ControlLoRAField */ ControlLoRAField: { /** @description Info to load lora model */ @@ -5337,10 +5155,10 @@ export type components = { img: components["schemas"]["ImageField"]; }; /** - * ControlLoRALyCORISConfig + * ControlLoRA_LyCORIS_FLUX_Config * @description Model config for Control LoRA models. */ - ControlLoRALyCORISConfig: { + ControlLoRA_LyCORIS_FLUX_Config: { /** * Key * @description A unique key for this model. @@ -5403,6 +5221,7 @@ export type components = { default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** * Base + * @default flux * @constant */ base: "flux"; @@ -5422,45 +5241,157 @@ export type components = { trigger_phrases: string[] | null; }; /** - * ControlNetCheckpointConfig - * @description Model config for ControlNet models (diffusers version). + * ControlNet - SD1.5, SD2, SDXL + * @description Collects ControlNet info to pass to other nodes */ - ControlNetCheckpointConfig: { + ControlNetInvocation: { /** - * Key - * @description A unique key for this model. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - key: string; + id: string; /** - * Hash - * @description The hash of the model file(s). + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - hash: string; + is_intermediate?: boolean; /** - * Path - * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + * Use Cache + * @description Whether or not to use the cache + * @default true */ - path: string; + use_cache?: boolean; /** - * File Size - * @description The size of the model in bytes. + * @description The control image + * @default null */ - file_size: number; + image?: components["schemas"]["ImageField"] | null; /** - * Name - * @description Name of the model. + * @description ControlNet model to load + * @default null */ - name: string; + control_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Description - * @description Model description + * Control Weight + * @description The weight given to the ControlNet + * @default 1 */ - description: string | null; + control_weight?: number | number[]; /** - * Source - * @description The original source of the model (path, URL or repo_id). + * Begin Step Percent + * @description When the ControlNet is first applied (% of total steps) + * @default 0 */ - source: string; + begin_step_percent?: number; + /** + * End Step Percent + * @description When the ControlNet is last applied (% of total steps) + * @default 1 + */ + end_step_percent?: number; + /** + * Control Mode + * @description The control mode used + * @default balanced + * @enum {string} + */ + control_mode?: "balanced" | "more_prompt" | "more_control" | "unbalanced"; + /** + * Resize Mode + * @description The resize mode used + * @default just_resize + * @enum {string} + */ + resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; + /** + * type + * @default controlnet + * @constant + */ + type: "controlnet"; + }; + /** ControlNetMetadataField */ + ControlNetMetadataField: { + /** @description The control image */ + image: components["schemas"]["ImageField"]; + /** + * @description The control image, after processing. + * @default null + */ + processed_image?: components["schemas"]["ImageField"] | null; + /** @description The ControlNet model to use */ + control_model: components["schemas"]["ModelIdentifierField"]; + /** + * Control Weight + * @description The weight given to the ControlNet + * @default 1 + */ + control_weight?: number | number[]; + /** + * Begin Step Percent + * @description When the ControlNet is first applied (% of total steps) + * @default 0 + */ + begin_step_percent?: number; + /** + * End Step Percent + * @description When the ControlNet is last applied (% of total steps) + * @default 1 + */ + end_step_percent?: number; + /** + * Control Mode + * @description The control mode to use + * @default balanced + * @enum {string} + */ + control_mode?: "balanced" | "more_prompt" | "more_control" | "unbalanced"; + /** + * Resize Mode + * @description The resize mode to use + * @default just_resize + * @enum {string} + */ + resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; + }; + /** ControlNet_Checkpoint_FLUX_Config */ + ControlNet_Checkpoint_FLUX_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"]; /** @@ -5496,11 +5427,6 @@ export type components = { * @description When this model was last converted to diffusers */ converted_at: number | null; - /** - * Base - * @enum {string} - */ - base: "sd-1" | "sd-2" | "sdxl" | "flux"; /** * Type * @default controlnet @@ -5513,12 +5439,15 @@ export type components = { * @constant */ format: "checkpoint"; + /** + * Base + * @default flux + * @constant + */ + base: "flux"; }; - /** - * ControlNetDiffusersConfig - * @description Model config for ControlNet models (diffusers version). - */ - ControlNetDiffusersConfig: { + /** ControlNet_Checkpoint_SD1_Config */ + ControlNet_Checkpoint_SD1_Config: { /** * Key * @description A unique key for this model. @@ -5580,579 +5509,567 @@ export type components = { usage_info: string | null; default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** - * Format - * @default diffusers - * @constant + * Config Path + * @description Path to the config for this model, if any. */ - format: "diffusers"; - /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + config_path: string | null; /** - * Base - * @enum {string} + * Converted At + * @description When this model was last converted to diffusers */ - base: "sd-1" | "sd-2" | "sdxl" | "flux"; + converted_at: number | null; /** * Type * @default controlnet * @constant */ type: "controlnet"; - }; - /** - * ControlNet - SD1.5, SD2, SDXL - * @description Collects ControlNet info to pass to other nodes - */ - ControlNetInvocation: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Format + * @default checkpoint + * @constant */ - id: string; + format: "checkpoint"; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Base + * @default sd-1 + * @constant */ - is_intermediate?: boolean; + base: "sd-1"; + }; + /** ControlNet_Checkpoint_SD2_Config */ + ControlNet_Checkpoint_SD2_Config: { /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Key + * @description A unique key for this model. */ - use_cache?: boolean; + key: string; /** - * @description The control image - * @default null + * Hash + * @description The hash of the model file(s). */ - image?: components["schemas"]["ImageField"] | null; + hash: string; /** - * @description ControlNet model to load - * @default null + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - control_model?: components["schemas"]["ModelIdentifierField"] | null; + path: string; /** - * Control Weight - * @description The weight given to the ControlNet - * @default 1 + * File Size + * @description The size of the model in bytes. */ - control_weight?: number | number[]; + file_size: number; /** - * Begin Step Percent - * @description When the ControlNet is first applied (% of total steps) - * @default 0 + * Name + * @description Name of the model. */ - begin_step_percent?: number; + name: string; /** - * End Step Percent - * @description When the ControlNet is last applied (% of total steps) - * @default 1 + * Description + * @description Model description */ - end_step_percent?: number; + description: string | null; /** - * Control Mode - * @description The control mode used - * @default balanced - * @enum {string} + * Source + * @description The original source of the model (path, URL or repo_id). */ - control_mode?: "balanced" | "more_prompt" | "more_control" | "unbalanced"; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Resize Mode - * @description The resize mode used - * @default just_resize - * @enum {string} + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; + source_api_response: string | null; /** - * type - * @default controlnet - * @constant + * Cover Image + * @description Url for image to preview model */ - type: "controlnet"; - }; - /** ControlNetMetadataField */ - ControlNetMetadataField: { - /** @description The control image */ - image: components["schemas"]["ImageField"]; + cover_image: string | null; /** - * @description The control image, after processing. - * @default null + * Submodels + * @description Loadable submodels in this model */ - processed_image?: components["schemas"]["ImageField"] | null; - /** @description The ControlNet model to use */ - control_model: components["schemas"]["ModelIdentifierField"]; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Control Weight - * @description The weight given to the ControlNet - * @default 1 + * Usage Info + * @description Usage information for this model */ - control_weight?: number | number[]; + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** - * Begin Step Percent - * @description When the ControlNet is first applied (% of total steps) - * @default 0 + * Config Path + * @description Path to the config for this model, if any. */ - begin_step_percent?: number; + config_path: string | null; /** - * End Step Percent - * @description When the ControlNet is last applied (% of total steps) - * @default 1 + * Converted At + * @description When this model was last converted to diffusers */ - end_step_percent?: number; + converted_at: number | null; /** - * Control Mode - * @description The control mode to use - * @default balanced - * @enum {string} + * Type + * @default controlnet + * @constant */ - control_mode?: "balanced" | "more_prompt" | "more_control" | "unbalanced"; + type: "controlnet"; /** - * Resize Mode - * @description The resize mode to use - * @default just_resize - * @enum {string} + * Format + * @default checkpoint + * @constant */ - resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; - }; - /** - * ControlOutput - * @description node output for ControlNet info - */ - ControlOutput: { - /** @description ControlNet(s) to apply */ - control: components["schemas"]["ControlField"]; + format: "checkpoint"; /** - * type - * @default control_output + * Base + * @default sd-2 * @constant */ - type: "control_output"; + base: "sd-2"; }; - /** - * Core Metadata - * @description Used internally by Invoke to collect metadata for generations. - */ - CoreMetadataInvocation: { + /** ControlNet_Checkpoint_SDXL_Config */ + ControlNet_Checkpoint_SDXL_Config: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Key + * @description A unique key for this model. */ - id: string; + key: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Hash + * @description The hash of the model file(s). */ - is_intermediate?: boolean; + hash: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - use_cache?: boolean; + path: string; /** - * Generation Mode - * @description The generation mode that output this image - * @default null + * File Size + * @description The size of the model in bytes. */ - generation_mode?: ("txt2img" | "img2img" | "inpaint" | "outpaint" | "sdxl_txt2img" | "sdxl_img2img" | "sdxl_inpaint" | "sdxl_outpaint" | "flux_txt2img" | "flux_img2img" | "flux_inpaint" | "flux_outpaint" | "sd3_txt2img" | "sd3_img2img" | "sd3_inpaint" | "sd3_outpaint" | "cogview4_txt2img" | "cogview4_img2img" | "cogview4_inpaint" | "cogview4_outpaint") | null; + file_size: number; /** - * Positive Prompt - * @description The positive prompt parameter - * @default null + * Name + * @description Name of the model. */ - positive_prompt?: string | null; + name: string; /** - * Negative Prompt - * @description The negative prompt parameter - * @default null + * Description + * @description Model description */ - negative_prompt?: string | null; + description: string | null; /** - * Width - * @description The width parameter - * @default null + * Source + * @description The original source of the model (path, URL or repo_id). */ - width?: number | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Height - * @description The height parameter - * @default null + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - height?: number | null; + source_api_response: string | null; /** - * Seed - * @description The seed used for noise generation - * @default null + * Cover Image + * @description Url for image to preview model */ - seed?: number | null; + cover_image: string | null; /** - * Rand Device - * @description The device used for random number generation - * @default null + * Submodels + * @description Loadable submodels in this model */ - rand_device?: string | null; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Cfg Scale - * @description The classifier-free guidance scale parameter - * @default null + * Usage Info + * @description Usage information for this model */ - cfg_scale?: number | null; + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** - * Cfg Rescale Multiplier - * @description Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR - * @default null + * Config Path + * @description Path to the config for this model, if any. */ - cfg_rescale_multiplier?: number | null; + config_path: string | null; /** - * Steps - * @description The number of steps used for inference - * @default null + * Converted At + * @description When this model was last converted to diffusers */ - steps?: number | null; + converted_at: number | null; /** - * Scheduler - * @description The scheduler used for inference - * @default null + * Type + * @default controlnet + * @constant */ - scheduler?: string | null; + type: "controlnet"; /** - * Seamless X - * @description Whether seamless tiling was used on the X axis - * @default null + * Format + * @default checkpoint + * @constant */ - seamless_x?: boolean | null; + format: "checkpoint"; /** - * Seamless Y - * @description Whether seamless tiling was used on the Y axis - * @default null + * Base + * @default sdxl + * @constant */ - seamless_y?: boolean | null; + base: "sdxl"; + }; + /** ControlNet_Diffusers_FLUX_Config */ + ControlNet_Diffusers_FLUX_Config: { /** - * Clip Skip - * @description The number of skipped CLIP layers - * @default null + * Key + * @description A unique key for this model. */ - clip_skip?: number | null; + key: string; /** - * @description The main model used for inference - * @default null + * Hash + * @description The hash of the model file(s). */ - model?: components["schemas"]["ModelIdentifierField"] | null; + hash: string; /** - * Controlnets - * @description The ControlNets used for inference - * @default null + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - controlnets?: components["schemas"]["ControlNetMetadataField"][] | null; + path: string; /** - * Ipadapters - * @description The IP Adapters used for inference - * @default null + * File Size + * @description The size of the model in bytes. */ - ipAdapters?: components["schemas"]["IPAdapterMetadataField"][] | null; + file_size: number; /** - * T2Iadapters - * @description The IP Adapters used for inference - * @default null + * Name + * @description Name of the model. */ - t2iAdapters?: components["schemas"]["T2IAdapterMetadataField"][] | null; + name: string; /** - * Loras - * @description The LoRAs used for inference - * @default null + * Description + * @description Model description */ - loras?: components["schemas"]["LoRAMetadataField"][] | null; + description: string | null; /** - * Strength - * @description The strength used for latents-to-latents - * @default null + * Source + * @description The original source of the model (path, URL or repo_id). */ - strength?: number | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Init Image - * @description The name of the initial image - * @default null + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - init_image?: string | null; + source_api_response: string | null; /** - * @description The VAE used for decoding, if the main model's default was not used - * @default null + * Cover Image + * @description Url for image to preview model */ - vae?: components["schemas"]["ModelIdentifierField"] | null; + cover_image: string | null; /** - * Hrf Enabled - * @description Whether or not high resolution fix was enabled. - * @default null + * Submodels + * @description Loadable submodels in this model */ - hrf_enabled?: boolean | null; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Hrf Method - * @description The high resolution fix upscale method. - * @default null + * Usage Info + * @description Usage information for this model */ - hrf_method?: string | null; + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** - * Hrf Strength - * @description The high resolution fix img2img strength used in the upscale pass. - * @default null + * Format + * @default diffusers + * @constant */ - hrf_strength?: number | null; + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; /** - * Positive Style Prompt - * @description The positive style prompt parameter - * @default null + * Type + * @default controlnet + * @constant */ - positive_style_prompt?: string | null; + type: "controlnet"; /** - * Negative Style Prompt - * @description The negative style prompt parameter - * @default null + * Base + * @default flux + * @constant */ - negative_style_prompt?: string | null; - /** - * @description The SDXL Refiner model used - * @default null - */ - refiner_model?: components["schemas"]["ModelIdentifierField"] | null; - /** - * Refiner Cfg Scale - * @description The classifier-free guidance scale parameter used for the refiner - * @default null - */ - refiner_cfg_scale?: number | null; - /** - * Refiner Steps - * @description The number of steps used for the refiner - * @default null - */ - refiner_steps?: number | null; + base: "flux"; + }; + /** ControlNet_Diffusers_SD1_Config */ + ControlNet_Diffusers_SD1_Config: { /** - * Refiner Scheduler - * @description The scheduler used for the refiner - * @default null + * Key + * @description A unique key for this model. */ - refiner_scheduler?: string | null; + key: string; /** - * Refiner Positive Aesthetic Score - * @description The aesthetic score used for the refiner - * @default null + * Hash + * @description The hash of the model file(s). */ - refiner_positive_aesthetic_score?: number | null; + hash: string; /** - * Refiner Negative Aesthetic Score - * @description The aesthetic score used for the refiner - * @default null + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - refiner_negative_aesthetic_score?: number | null; + path: string; /** - * Refiner Start - * @description The start value used for refiner denoising - * @default null + * File Size + * @description The size of the model in bytes. */ - refiner_start?: number | null; + file_size: number; /** - * type - * @default core_metadata - * @constant + * Name + * @description Name of the model. */ - type: "core_metadata"; - } & { - [key: string]: unknown; - }; - /** - * Create Denoise Mask - * @description Creates mask for denoising model run. - */ - CreateDenoiseMaskInvocation: { + name: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Description + * @description Model description */ - id: string; + description: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Source + * @description The original source of the model (path, URL or repo_id). */ - is_intermediate?: boolean; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - use_cache?: boolean; + source_api_response: string | null; /** - * @description VAE - * @default null + * Cover Image + * @description Url for image to preview model */ - vae?: components["schemas"]["VAEField"] | null; + cover_image: string | null; /** - * @description Image which will be masked - * @default null + * Submodels + * @description Loadable submodels in this model */ - image?: components["schemas"]["ImageField"] | null; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * @description The mask to use when pasting - * @default null + * Usage Info + * @description Usage information for this model */ - mask?: components["schemas"]["ImageField"] | null; + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** - * Tiled - * @description Processing using overlapping tiles (reduce memory consumption) - * @default false + * Format + * @default diffusers + * @constant */ - tiled?: boolean; + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; /** - * Fp32 - * @description Whether or not to use full float32 precision - * @default false + * Type + * @default controlnet + * @constant */ - fp32?: boolean; + type: "controlnet"; /** - * type - * @default create_denoise_mask + * Base + * @default sd-1 * @constant */ - type: "create_denoise_mask"; + base: "sd-1"; }; - /** - * Create Gradient Mask - * @description Creates mask for denoising. - */ - CreateGradientMaskInvocation: { + /** ControlNet_Diffusers_SD2_Config */ + ControlNet_Diffusers_SD2_Config: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Key + * @description A unique key for this model. */ - id: string; + key: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Hash + * @description The hash of the model file(s). */ - is_intermediate?: boolean; + hash: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - use_cache?: boolean; + path: string; /** - * @description Image which will be masked - * @default null + * File Size + * @description The size of the model in bytes. */ - mask?: components["schemas"]["ImageField"] | null; + file_size: number; /** - * Edge Radius - * @description How far to expand the edges of the mask - * @default 16 + * Name + * @description Name of the model. */ - edge_radius?: number; + name: string; /** - * Coherence Mode - * @default Gaussian Blur - * @enum {string} + * Description + * @description Model description */ - coherence_mode?: "Gaussian Blur" | "Box Blur" | "Staged"; + description: string | null; /** - * Minimum Denoise - * @description Minimum denoise level for the coherence region - * @default 0 + * Source + * @description The original source of the model (path, URL or repo_id). */ - minimum_denoise?: number; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * [OPTIONAL] Image - * @description OPTIONAL: Only connect for specialized Inpainting models, masked_latents will be generated from the image with the VAE - * @default null + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - image?: components["schemas"]["ImageField"] | null; + source_api_response: string | null; /** - * [OPTIONAL] UNet - * @description OPTIONAL: If the Unet is a specialized Inpainting model, masked_latents will be generated from the image with the VAE - * @default null + * Cover Image + * @description Url for image to preview model */ - unet?: components["schemas"]["UNetField"] | null; + cover_image: string | null; /** - * [OPTIONAL] VAE - * @description OPTIONAL: Only connect for specialized Inpainting models, masked_latents will be generated from the image with the VAE - * @default null + * Submodels + * @description Loadable submodels in this model */ - vae?: components["schemas"]["VAEField"] | null; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Tiled - * @description Processing using overlapping tiles (reduce memory consumption) - * @default false + * Usage Info + * @description Usage information for this model */ - tiled?: boolean; + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** - * Fp32 - * @description Whether or not to use full float32 precision - * @default false + * Format + * @default diffusers + * @constant */ - fp32?: boolean; + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; /** - * type - * @default create_gradient_mask + * Type + * @default controlnet * @constant */ - type: "create_gradient_mask"; + type: "controlnet"; + /** + * Base + * @default sd-2 + * @constant + */ + base: "sd-2"; }; - /** - * Crop Image to Bounding Box - * @description Crop an image to the given bounding box. If the bounding box is omitted, the image is cropped to the non-transparent pixels. - */ - CropImageToBoundingBoxInvocation: { + /** ControlNet_Diffusers_SDXL_Config */ + ControlNet_Diffusers_SDXL_Config: { /** - * @description The board to save the image to - * @default null + * Key + * @description A unique key for this model. */ - board?: components["schemas"]["BoardField"] | null; + key: string; /** - * @description Optional metadata to be saved with the image - * @default null + * Hash + * @description The hash of the model file(s). */ - metadata?: components["schemas"]["MetadataField"] | null; + hash: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - id: string; + path: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * File Size + * @description The size of the model in bytes. */ - is_intermediate?: boolean; + file_size: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Name + * @description Name of the model. */ - use_cache?: boolean; + name: string; /** - * @description The image to crop - * @default null + * Description + * @description Model description */ - image?: components["schemas"]["ImageField"] | null; + description: string | null; /** - * @description The bounding box to crop the image to - * @default null + * Source + * @description The original source of the model (path, URL or repo_id). */ - bounding_box?: components["schemas"]["BoundingBoxField"] | null; + 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; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Submodels + * @description Loadable submodels in this model + */ + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; + /** + * Usage Info + * @description Usage information for this model + */ + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Type + * @default controlnet + * @constant + */ + type: "controlnet"; + /** + * Base + * @default sdxl + * @constant + */ + base: "sdxl"; + }; + /** + * ControlOutput + * @description node output for ControlNet info + */ + ControlOutput: { + /** @description ControlNet(s) to apply */ + control: components["schemas"]["ControlField"]; /** * type - * @default crop_image_to_bounding_box + * @default control_output * @constant */ - type: "crop_image_to_bounding_box"; + type: "control_output"; }; /** - * Crop Latents - * @description Crops a latent-space tensor to a box specified in image-space. The box dimensions and coordinates must be - * divisible by the latent scale factor of 8. + * Core Metadata + * @description Used internally by Invoke to collect metadata for generations. */ - CropLatentsCoreInvocation: { + CoreMetadataInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -6171,220 +6088,220 @@ export type components = { */ use_cache?: boolean; /** - * @description Latents tensor + * Generation Mode + * @description The generation mode that output this image * @default null */ - latents?: components["schemas"]["LatentsField"] | null; + generation_mode?: ("txt2img" | "img2img" | "inpaint" | "outpaint" | "sdxl_txt2img" | "sdxl_img2img" | "sdxl_inpaint" | "sdxl_outpaint" | "flux_txt2img" | "flux_img2img" | "flux_inpaint" | "flux_outpaint" | "sd3_txt2img" | "sd3_img2img" | "sd3_inpaint" | "sd3_outpaint" | "cogview4_txt2img" | "cogview4_img2img" | "cogview4_inpaint" | "cogview4_outpaint") | null; /** - * X - * @description The left x coordinate (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space. + * Positive Prompt + * @description The positive prompt parameter * @default null */ - x?: number | null; + positive_prompt?: string | null; /** - * Y - * @description The top y coordinate (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space. + * Negative Prompt + * @description The negative prompt parameter * @default null */ - y?: number | null; + negative_prompt?: string | null; /** * Width - * @description The width (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space. + * @description The width parameter * @default null */ width?: number | null; /** * Height - * @description The height (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space. + * @description The height parameter * @default null */ height?: number | null; /** - * type - * @default crop_latents - * @constant + * Seed + * @description The seed used for noise generation + * @default null */ - type: "crop_latents"; - }; - /** - * OpenCV Inpaint - * @description Simple inpaint using opencv. - */ - CvInpaintInvocation: { + seed?: number | null; /** - * @description The board to save the image to + * Rand Device + * @description The device used for random number generation * @default null */ - board?: components["schemas"]["BoardField"] | null; + rand_device?: string | null; /** - * @description Optional metadata to be saved with the image + * Cfg Scale + * @description The classifier-free guidance scale parameter * @default null */ - metadata?: components["schemas"]["MetadataField"] | null; + cfg_scale?: number | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Cfg Rescale Multiplier + * @description Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR + * @default null */ - id: string; + cfg_rescale_multiplier?: number | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Steps + * @description The number of steps used for inference + * @default null */ - is_intermediate?: boolean; + steps?: number | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Scheduler + * @description The scheduler used for inference + * @default null */ - use_cache?: boolean; + scheduler?: string | null; /** - * @description The image to inpaint + * Seamless X + * @description Whether seamless tiling was used on the X axis * @default null */ - image?: components["schemas"]["ImageField"] | null; + seamless_x?: boolean | null; /** - * @description The mask to use when inpainting + * Seamless Y + * @description Whether seamless tiling was used on the Y axis * @default null */ - mask?: components["schemas"]["ImageField"] | null; + seamless_y?: boolean | null; /** - * type - * @default cv_inpaint - * @constant + * Clip Skip + * @description The number of skipped CLIP layers + * @default null */ - type: "cv_inpaint"; - }; - /** - * DW Openpose Detection - * @description Generates an openpose pose from an image using DWPose - */ - DWOpenposeDetectionInvocation: { + clip_skip?: number | null; /** - * @description The board to save the image to + * @description The main model used for inference * @default null */ - board?: components["schemas"]["BoardField"] | null; + model?: components["schemas"]["ModelIdentifierField"] | null; /** - * @description Optional metadata to be saved with the image + * Controlnets + * @description The ControlNets used for inference * @default null */ - metadata?: components["schemas"]["MetadataField"] | null; + controlnets?: components["schemas"]["ControlNetMetadataField"][] | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Ipadapters + * @description The IP Adapters used for inference + * @default null */ - id: string; + ipAdapters?: components["schemas"]["IPAdapterMetadataField"][] | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * T2Iadapters + * @description The IP Adapters used for inference + * @default null */ - is_intermediate?: boolean; + t2iAdapters?: components["schemas"]["T2IAdapterMetadataField"][] | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Loras + * @description The LoRAs used for inference + * @default null */ - use_cache?: boolean; + loras?: components["schemas"]["LoRAMetadataField"][] | null; /** - * @description The image to process + * Strength + * @description The strength used for latents-to-latents * @default null */ - image?: components["schemas"]["ImageField"] | null; + strength?: number | null; /** - * Draw Body - * @default true + * Init Image + * @description The name of the initial image + * @default null */ - draw_body?: boolean; + init_image?: string | null; /** - * Draw Face - * @default false + * @description The VAE used for decoding, if the main model's default was not used + * @default null */ - draw_face?: boolean; + vae?: components["schemas"]["ModelIdentifierField"] | null; /** - * Draw Hands - * @default false + * Hrf Enabled + * @description Whether or not high resolution fix was enabled. + * @default null */ - draw_hands?: boolean; + hrf_enabled?: boolean | null; /** - * type - * @default dw_openpose_detection - * @constant + * Hrf Method + * @description The high resolution fix upscale method. + * @default null */ - type: "dw_openpose_detection"; - }; - /** - * DeleteAllExceptCurrentResult - * @description Result of deleting all except current - */ - DeleteAllExceptCurrentResult: { + hrf_method?: string | null; /** - * Deleted - * @description Number of queue items deleted + * Hrf Strength + * @description The high resolution fix img2img strength used in the upscale pass. + * @default null */ - deleted: number; - }; - /** DeleteBoardResult */ - DeleteBoardResult: { + hrf_strength?: number | null; /** - * Board Id - * @description The id of the board that was deleted. + * Positive Style Prompt + * @description The positive style prompt parameter + * @default null */ - board_id: string; + positive_style_prompt?: string | null; /** - * Deleted Board Images - * @description The image names of the board-images relationships that were deleted. + * Negative Style Prompt + * @description The negative style prompt parameter + * @default null */ - deleted_board_images: string[]; + negative_style_prompt?: string | null; /** - * Deleted Images - * @description The names of the images that were deleted. + * @description The SDXL Refiner model used + * @default null */ - deleted_images: string[]; - }; - /** - * DeleteByDestinationResult - * @description Result of deleting by a destination - */ - DeleteByDestinationResult: { + refiner_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Deleted - * @description Number of queue items deleted + * Refiner Cfg Scale + * @description The classifier-free guidance scale parameter used for the refiner + * @default null */ - deleted: number; - }; - /** DeleteImagesResult */ - DeleteImagesResult: { + refiner_cfg_scale?: number | null; /** - * Affected Boards - * @description The ids of boards affected by the delete operation + * Refiner Steps + * @description The number of steps used for the refiner + * @default null */ - affected_boards: string[]; + refiner_steps?: number | null; /** - * Deleted Images - * @description The names of the images that were deleted + * Refiner Scheduler + * @description The scheduler used for the refiner + * @default null */ - deleted_images: string[]; - }; - /** DeleteVideosResult */ - DeleteVideosResult: { + refiner_scheduler?: string | null; /** - * Affected Boards - * @description The ids of boards affected by the delete operation + * Refiner Positive Aesthetic Score + * @description The aesthetic score used for the refiner + * @default null */ - affected_boards: string[]; + refiner_positive_aesthetic_score?: number | null; /** - * Deleted Videos - * @description The ids of the videos that were deleted + * Refiner Negative Aesthetic Score + * @description The aesthetic score used for the refiner + * @default null */ - deleted_videos: string[]; + refiner_negative_aesthetic_score?: number | null; + /** + * Refiner Start + * @description The start value used for refiner denoising + * @default null + */ + refiner_start?: number | null; + /** + * type + * @default core_metadata + * @constant + */ + type: "core_metadata"; + } & { + [key: string]: unknown; }; /** - * Denoise - SD1.5, SDXL - * @description Denoises noisy latents to decodable images + * Create Denoise Mask + * @description Creates mask for denoising model run. */ - DenoiseLatentsInvocation: { + CreateDenoiseMaskInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -6403,101 +6320,131 @@ export type components = { */ use_cache?: boolean; /** - * Positive Conditioning - * @description Positive conditioning tensor + * @description VAE * @default null */ - positive_conditioning?: components["schemas"]["ConditioningField"] | components["schemas"]["ConditioningField"][] | null; + vae?: components["schemas"]["VAEField"] | null; /** - * Negative Conditioning - * @description Negative conditioning tensor + * @description Image which will be masked * @default null */ - negative_conditioning?: components["schemas"]["ConditioningField"] | components["schemas"]["ConditioningField"][] | null; + image?: components["schemas"]["ImageField"] | null; /** - * @description Noise tensor + * @description The mask to use when pasting * @default null */ - noise?: components["schemas"]["LatentsField"] | null; + mask?: components["schemas"]["ImageField"] | null; /** - * Steps - * @description Number of steps to run - * @default 10 + * Tiled + * @description Processing using overlapping tiles (reduce memory consumption) + * @default false */ - steps?: number; + tiled?: boolean; /** - * CFG Scale - * @description Classifier-Free Guidance scale - * @default 7.5 + * Fp32 + * @description Whether or not to use full float32 precision + * @default false */ - cfg_scale?: number | number[]; + fp32?: boolean; /** - * Denoising Start - * @description When to start denoising, expressed a percentage of total steps - * @default 0 + * type + * @default create_denoise_mask + * @constant */ - denoising_start?: number; + type: "create_denoise_mask"; + }; + /** + * Create Gradient Mask + * @description Creates mask for denoising. + */ + CreateGradientMaskInvocation: { /** - * Denoising End - * @description When to stop denoising, expressed a percentage of total steps - * @default 1 + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - denoising_end?: number; + id: string; /** - * Scheduler - * @description Scheduler to use during inference - * @default euler - * @enum {string} + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - scheduler?: "ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd"; + is_intermediate?: boolean; /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null + * Use Cache + * @description Whether or not to use the cache + * @default true */ - unet?: components["schemas"]["UNetField"] | null; + use_cache?: boolean; /** - * Control + * @description Image which will be masked * @default null */ - control?: components["schemas"]["ControlField"] | components["schemas"]["ControlField"][] | null; + mask?: components["schemas"]["ImageField"] | null; /** - * IP-Adapter - * @description IP-Adapter to apply - * @default null + * Edge Radius + * @description How far to expand the edges of the mask + * @default 16 */ - ip_adapter?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; + edge_radius?: number; /** - * T2I-Adapter - * @description T2I-Adapter(s) to apply - * @default null + * Coherence Mode + * @default Gaussian Blur + * @enum {string} */ - t2i_adapter?: components["schemas"]["T2IAdapterField"] | components["schemas"]["T2IAdapterField"][] | null; + coherence_mode?: "Gaussian Blur" | "Box Blur" | "Staged"; /** - * CFG Rescale Multiplier - * @description Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR + * Minimum Denoise + * @description Minimum denoise level for the coherence region * @default 0 */ - cfg_rescale_multiplier?: number; + minimum_denoise?: number; /** - * @description Latents tensor + * [OPTIONAL] Image + * @description OPTIONAL: Only connect for specialized Inpainting models, masked_latents will be generated from the image with the VAE * @default null */ - latents?: components["schemas"]["LatentsField"] | null; + image?: components["schemas"]["ImageField"] | 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. + * [OPTIONAL] UNet + * @description OPTIONAL: If the Unet is a specialized Inpainting model, masked_latents will be generated from the image with the VAE * @default null */ - denoise_mask?: components["schemas"]["DenoiseMaskField"] | null; + unet?: components["schemas"]["UNetField"] | null; + /** + * [OPTIONAL] VAE + * @description OPTIONAL: Only connect for specialized Inpainting models, masked_latents will be generated from the image with the VAE + * @default null + */ + vae?: components["schemas"]["VAEField"] | null; + /** + * Tiled + * @description Processing using overlapping tiles (reduce memory consumption) + * @default false + */ + tiled?: boolean; + /** + * Fp32 + * @description Whether or not to use full float32 precision + * @default false + */ + fp32?: boolean; /** * type - * @default denoise_latents + * @default create_gradient_mask * @constant */ - type: "denoise_latents"; + type: "create_gradient_mask"; }; - /** Denoise - SD1.5, SDXL + Metadata */ - DenoiseLatentsMetaInvocation: { + /** + * Crop Image to Bounding Box + * @description Crop an image to the given bounding box. If the bounding box is omitted, the image is cropped to the non-transparent pixels. + */ + CropImageToBoundingBoxInvocation: { + /** + * @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 @@ -6521,141 +6468,86 @@ export type components = { */ use_cache?: boolean; /** - * Positive Conditioning - * @description Positive conditioning tensor + * @description The image to crop * @default null */ - positive_conditioning?: components["schemas"]["ConditioningField"] | components["schemas"]["ConditioningField"][] | null; + image?: components["schemas"]["ImageField"] | null; /** - * Negative Conditioning - * @description Negative conditioning tensor + * @description The bounding box to crop the image to * @default null */ - negative_conditioning?: components["schemas"]["ConditioningField"] | components["schemas"]["ConditioningField"][] | null; + bounding_box?: components["schemas"]["BoundingBoxField"] | null; /** - * @description Noise tensor - * @default null + * type + * @default crop_image_to_bounding_box + * @constant */ - noise?: components["schemas"]["LatentsField"] | null; + type: "crop_image_to_bounding_box"; + }; + /** + * Crop Latents + * @description Crops a latent-space tensor to a box specified in image-space. The box dimensions and coordinates must be + * divisible by the latent scale factor of 8. + */ + CropLatentsCoreInvocation: { /** - * Steps - * @description Number of steps to run - * @default 10 - */ - steps?: number; - /** - * CFG Scale - * @description Classifier-Free Guidance scale - * @default 7.5 - */ - cfg_scale?: number | number[]; - /** - * 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; - /** - * Scheduler - * @description Scheduler to use during inference - * @default euler - * @enum {string} - */ - scheduler?: "ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd"; - /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null - */ - unet?: components["schemas"]["UNetField"] | null; - /** - * Control - * @default null - */ - control?: components["schemas"]["ControlField"] | components["schemas"]["ControlField"][] | null; - /** - * IP-Adapter - * @description IP-Adapter to apply - * @default null + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - ip_adapter?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; + id: string; /** - * T2I-Adapter - * @description T2I-Adapter(s) to apply - * @default null + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - t2i_adapter?: components["schemas"]["T2IAdapterField"] | components["schemas"]["T2IAdapterField"][] | null; + is_intermediate?: boolean; /** - * CFG Rescale Multiplier - * @description Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR - * @default 0 + * Use Cache + * @description Whether or not to use the cache + * @default true */ - cfg_rescale_multiplier?: number; + 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. + * X + * @description The left x coordinate (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space. * @default null */ - denoise_mask?: components["schemas"]["DenoiseMaskField"] | null; - /** - * type - * @default denoise_latents_meta - * @constant - */ - type: "denoise_latents_meta"; - }; - /** - * DenoiseMaskField - * @description An inpaint mask field - */ - DenoiseMaskField: { + x?: number | null; /** - * Mask Name - * @description The name of the mask image + * Y + * @description The top y coordinate (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space. + * @default null */ - mask_name: string; + y?: number | null; /** - * Masked Latents Name - * @description The name of the masked image latents + * Width + * @description The width (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space. * @default null */ - masked_latents_name?: string | null; + width?: number | null; /** - * Gradient - * @description Used for gradient inpainting - * @default false + * Height + * @description The height (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space. + * @default null */ - gradient?: boolean; - }; - /** - * DenoiseMaskOutput - * @description Base class for nodes that output a single image - */ - DenoiseMaskOutput: { - /** @description Mask for denoise model run */ - denoise_mask: components["schemas"]["DenoiseMaskField"]; + height?: number | null; /** * type - * @default denoise_mask_output + * @default crop_latents * @constant */ - type: "denoise_mask_output"; + type: "crop_latents"; }; /** - * Depth Anything Depth Estimation - * @description Generates a depth map using a Depth Anything model. + * OpenCV Inpaint + * @description Simple inpaint using opencv. */ - DepthAnythingDepthEstimationInvocation: { + CvInpaintInvocation: { /** * @description The board to save the image to * @default null @@ -6684,29 +6576,37 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to process + * @description The image to inpaint * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Model Size - * @description The size of the depth model to use - * @default small_v2 - * @enum {string} + * @description The mask to use when inpainting + * @default null */ - model_size?: "large" | "base" | "small" | "small_v2"; + mask?: components["schemas"]["ImageField"] | null; /** * type - * @default depth_anything_depth_estimation + * @default cv_inpaint * @constant */ - type: "depth_anything_depth_estimation"; + type: "cv_inpaint"; }; /** - * Divide Integers - * @description Divides two numbers + * DW Openpose Detection + * @description Generates an openpose pose from an image using DWPose */ - DivideInvocation: { + DWOpenposeDetectionInvocation: { + /** + * @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. @@ -6725,237 +6625,221 @@ export type components = { */ use_cache?: boolean; /** - * A - * @description The first number - * @default 0 + * @description The image to process + * @default null */ - a?: number; + image?: components["schemas"]["ImageField"] | null; /** - * B - * @description The second number - * @default 0 + * Draw Body + * @default true */ - b?: number; + draw_body?: boolean; /** - * type - * @default div - * @constant + * Draw Face + * @default false */ - type: "div"; - }; - /** - * DownloadCancelledEvent - * @description Event model for download_cancelled - */ - DownloadCancelledEvent: { + draw_face?: boolean; /** - * Timestamp - * @description The timestamp of the event + * Draw Hands + * @default false */ - timestamp: number; + draw_hands?: boolean; /** - * Source - * @description The source of the download + * type + * @default dw_openpose_detection + * @constant */ - source: string; + type: "dw_openpose_detection"; }; /** - * DownloadCompleteEvent - * @description Event model for download_complete + * DeleteAllExceptCurrentResult + * @description Result of deleting all except current */ - DownloadCompleteEvent: { + DeleteAllExceptCurrentResult: { /** - * Timestamp - * @description The timestamp of the event + * Deleted + * @description Number of queue items deleted */ - timestamp: number; + deleted: number; + }; + /** DeleteBoardResult */ + DeleteBoardResult: { /** - * Source - * @description The source of the download + * Board Id + * @description The id of the board that was deleted. */ - source: string; + board_id: string; /** - * Download Path - * @description The local path where the download is saved + * Deleted Board Images + * @description The image names of the board-images relationships that were deleted. */ - download_path: string; + deleted_board_images: string[]; /** - * Total Bytes - * @description The total number of bytes downloaded + * Deleted Images + * @description The names of the images that were deleted. */ - total_bytes: number; + deleted_images: string[]; }; /** - * DownloadErrorEvent - * @description Event model for download_error + * DeleteByDestinationResult + * @description Result of deleting by a destination */ - DownloadErrorEvent: { + DeleteByDestinationResult: { /** - * Timestamp - * @description The timestamp of the event + * Deleted + * @description Number of queue items deleted */ - timestamp: number; + deleted: number; + }; + /** DeleteImagesResult */ + DeleteImagesResult: { /** - * Source - * @description The source of the download + * Affected Boards + * @description The ids of boards affected by the delete operation */ - source: string; + affected_boards: string[]; /** - * Error Type - * @description The type of error + * Deleted Images + * @description The names of the images that were deleted */ - error_type: string; + deleted_images: string[]; + }; + /** DeleteVideosResult */ + DeleteVideosResult: { /** - * Error - * @description The error message + * Affected Boards + * @description The ids of boards affected by the delete operation */ - error: string; + affected_boards: string[]; + /** + * Deleted Videos + * @description The ids of the videos that were deleted + */ + deleted_videos: string[]; }; /** - * DownloadJob - * @description Class to monitor and control a model download request. + * Denoise - SD1.5, SDXL + * @description Denoises noisy latents to decodable images */ - DownloadJob: { + DenoiseLatentsInvocation: { /** * Id - * @description Numeric ID of this job - * @default -1 + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - id?: number; + id: string; /** - * Dest - * Format: path - * @description Initial destination of downloaded model on local disk; a directory or file path + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - dest: string; + is_intermediate?: boolean; /** - * Download Path - * @description Final location of downloaded file or directory + * Use Cache + * @description Whether or not to use the cache + * @default true */ - download_path?: string | null; + use_cache?: boolean; /** - * @description Status of the download - * @default waiting + * Positive Conditioning + * @description Positive conditioning tensor + * @default null */ - status?: components["schemas"]["DownloadJobStatus"]; + positive_conditioning?: components["schemas"]["ConditioningField"] | components["schemas"]["ConditioningField"][] | null; /** - * Bytes - * @description Bytes downloaded so far - * @default 0 + * Negative Conditioning + * @description Negative conditioning tensor + * @default null */ - bytes?: number; + negative_conditioning?: components["schemas"]["ConditioningField"] | components["schemas"]["ConditioningField"][] | null; /** - * Total Bytes - * @description Total file size (bytes) - * @default 0 + * @description Noise tensor + * @default null */ - total_bytes?: number; + noise?: components["schemas"]["LatentsField"] | null; /** - * Error Type - * @description Name of exception that caused an error + * Steps + * @description Number of steps to run + * @default 10 */ - error_type?: string | null; + steps?: number; /** - * Error - * @description Traceback of the exception that caused an error + * CFG Scale + * @description Classifier-Free Guidance scale + * @default 7.5 */ - error?: string | null; + cfg_scale?: number | number[]; /** - * Source - * Format: uri - * @description Where to download from. Specific types specified in child classes. + * Denoising Start + * @description When to start denoising, expressed a percentage of total steps + * @default 0 */ - source: string; + denoising_start?: number; /** - * Access Token - * @description authorization token for protected resources + * Denoising End + * @description When to stop denoising, expressed a percentage of total steps + * @default 1 */ - access_token?: string | null; + denoising_end?: number; /** - * Priority - * @description Queue priority; lower values are higher priority - * @default 10 + * Scheduler + * @description Scheduler to use during inference + * @default euler + * @enum {string} */ - priority?: number; + scheduler?: "ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd"; /** - * Job Started - * @description Timestamp for when the download job started + * UNet + * @description UNet (scheduler, LoRAs) + * @default null */ - job_started?: string | null; + unet?: components["schemas"]["UNetField"] | null; /** - * Job Ended - * @description Timestamp for when the download job ende1d (completed or errored) + * Control + * @default null */ - job_ended?: string | null; + control?: components["schemas"]["ControlField"] | components["schemas"]["ControlField"][] | null; /** - * Content Type - * @description Content type of downloaded file + * IP-Adapter + * @description IP-Adapter to apply + * @default null */ - content_type?: string | null; - }; - /** - * DownloadJobStatus - * @description State of a download job. - * @enum {string} - */ - DownloadJobStatus: "waiting" | "running" | "completed" | "cancelled" | "error"; - /** - * DownloadProgressEvent - * @description Event model for download_progress - */ - DownloadProgressEvent: { + ip_adapter?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; /** - * Timestamp - * @description The timestamp of the event + * T2I-Adapter + * @description T2I-Adapter(s) to apply + * @default null */ - timestamp: number; + t2i_adapter?: components["schemas"]["T2IAdapterField"] | components["schemas"]["T2IAdapterField"][] | null; /** - * Source - * @description The source of the download + * CFG Rescale Multiplier + * @description Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR + * @default 0 */ - source: string; + cfg_rescale_multiplier?: number; /** - * Download Path - * @description The local path where the download is saved + * @description Latents tensor + * @default null */ - download_path: string; + latents?: components["schemas"]["LatentsField"] | null; /** - * Current Bytes - * @description The number of bytes downloaded so far + * @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 */ - current_bytes: number; + denoise_mask?: components["schemas"]["DenoiseMaskField"] | null; /** - * Total Bytes - * @description The total number of bytes to be downloaded + * type + * @default denoise_latents + * @constant */ - total_bytes: number; + type: "denoise_latents"; }; - /** - * DownloadStartedEvent - * @description Event model for download_started - */ - DownloadStartedEvent: { - /** - * Timestamp - * @description The timestamp of the event - */ - timestamp: number; - /** - * Source - * @description The source of the download - */ - source: string; + /** Denoise - SD1.5, SDXL + Metadata */ + DenoiseLatentsMetaInvocation: { /** - * Download Path - * @description The local path where the download is saved + * @description Optional metadata to be saved with the image + * @default null */ - download_path: string; - }; - /** - * Dynamic Prompt - * @description Parses a prompt using adieyal/dynamicprompts' random or combinatorial generator - */ - DynamicPromptInvocation: { + metadata?: components["schemas"]["MetadataField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -6970,156 +6854,145 @@ export type components = { /** * Use Cache * @description Whether or not to use the cache - * @default false + * @default true */ use_cache?: boolean; /** - * Prompt - * @description The prompt to parse with dynamicprompts + * Positive Conditioning + * @description Positive conditioning tensor * @default null */ - prompt?: string | null; + positive_conditioning?: components["schemas"]["ConditioningField"] | components["schemas"]["ConditioningField"][] | null; /** - * Max Prompts - * @description The number of prompts to generate - * @default 1 + * Negative Conditioning + * @description Negative conditioning tensor + * @default null */ - max_prompts?: number; + negative_conditioning?: components["schemas"]["ConditioningField"] | components["schemas"]["ConditioningField"][] | null; /** - * Combinatorial - * @description Whether to use the combinatorial generator - * @default false + * @description Noise tensor + * @default null */ - combinatorial?: boolean; + noise?: components["schemas"]["LatentsField"] | null; /** - * type - * @default dynamic_prompt - * @constant + * Steps + * @description Number of steps to run + * @default 10 */ - type: "dynamic_prompt"; - }; - /** DynamicPromptsResponse */ - DynamicPromptsResponse: { - /** Prompts */ - prompts: string[]; - /** Error */ - error?: string | null; - }; - /** - * Upscale (RealESRGAN) - * @description Upscales an image using RealESRGAN. - */ - ESRGANInvocation: { + steps?: number; /** - * @description The board to save the image to - * @default null + * CFG Scale + * @description Classifier-Free Guidance scale + * @default 7.5 */ - board?: components["schemas"]["BoardField"] | null; + cfg_scale?: number | number[]; /** - * @description Optional metadata to be saved with the image - * @default null + * Denoising Start + * @description When to start denoising, expressed a percentage of total steps + * @default 0 */ - metadata?: components["schemas"]["MetadataField"] | null; + denoising_start?: number; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Denoising End + * @description When to stop denoising, expressed a percentage of total steps + * @default 1 */ - id: string; + denoising_end?: number; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false - */ - is_intermediate?: boolean; + * Scheduler + * @description Scheduler to use during inference + * @default euler + * @enum {string} + */ + scheduler?: "ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd"; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * UNet + * @description UNet (scheduler, LoRAs) + * @default null */ - use_cache?: boolean; + unet?: components["schemas"]["UNetField"] | null; /** - * @description The input image + * Control * @default null */ - image?: components["schemas"]["ImageField"] | null; + control?: components["schemas"]["ControlField"] | components["schemas"]["ControlField"][] | null; /** - * Model Name - * @description The Real-ESRGAN model to use - * @default RealESRGAN_x4plus.pth - * @enum {string} + * IP-Adapter + * @description IP-Adapter to apply + * @default null */ - model_name?: "RealESRGAN_x4plus.pth" | "RealESRGAN_x4plus_anime_6B.pth" | "ESRGAN_SRx4_DF2KOST_official-ff704c30.pth" | "RealESRGAN_x2plus.pth"; + ip_adapter?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; /** - * Tile Size - * @description Tile size for tiled ESRGAN upscaling (0=tiling disabled) - * @default 400 + * T2I-Adapter + * @description T2I-Adapter(s) to apply + * @default null */ - tile_size?: number; + t2i_adapter?: components["schemas"]["T2IAdapterField"] | components["schemas"]["T2IAdapterField"][] | null; /** - * type - * @default esrgan - * @constant + * CFG Rescale Multiplier + * @description Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR + * @default 0 */ - type: "esrgan"; - }; - /** Edge */ - Edge: { - /** @description The connection for the edge's from node and field */ - source: components["schemas"]["EdgeConnection"]; - /** @description The connection for the edge's to node and field */ - destination: components["schemas"]["EdgeConnection"]; - }; - /** EdgeConnection */ - EdgeConnection: { + cfg_rescale_multiplier?: number; /** - * Node Id - * @description The id of the node for this edge connection + * @description Latents tensor + * @default null */ - node_id: string; + latents?: components["schemas"]["LatentsField"] | null; /** - * Field - * @description The field for this connection + * @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 */ - field: string; - }; - /** EnqueueBatchResult */ - EnqueueBatchResult: { + denoise_mask?: components["schemas"]["DenoiseMaskField"] | null; /** - * Queue Id - * @description The ID of the queue + * type + * @default denoise_latents_meta + * @constant */ - queue_id: string; + type: "denoise_latents_meta"; + }; + /** + * DenoiseMaskField + * @description An inpaint mask field + */ + DenoiseMaskField: { /** - * Enqueued - * @description The total number of queue items enqueued + * Mask Name + * @description The name of the mask image */ - enqueued: number; + mask_name: string; /** - * Requested - * @description The total number of queue items requested to be enqueued + * Masked Latents Name + * @description The name of the masked image latents + * @default null */ - requested: number; - /** @description The batch that was enqueued */ - batch: components["schemas"]["Batch"]; + masked_latents_name?: string | null; /** - * Priority - * @description The priority of the enqueued batch + * Gradient + * @description Used for gradient inpainting + * @default false */ - priority: number; + gradient?: boolean; + }; + /** + * DenoiseMaskOutput + * @description Base class for nodes that output a single image + */ + DenoiseMaskOutput: { + /** @description Mask for denoise model run */ + denoise_mask: components["schemas"]["DenoiseMaskField"]; /** - * Item Ids - * @description The IDs of the queue items that were enqueued + * type + * @default denoise_mask_output + * @constant */ - item_ids: number[]; + type: "denoise_mask_output"; }; /** - * Expand Mask with Fade - * @description Expands a mask with a fade effect. The mask uses black to indicate areas to keep from the generated image and white for areas to discard. - * The mask is thresholded to create a binary mask, and then a distance transform is applied to create a fade effect. - * The fade size is specified in pixels, and the mask is expanded by that amount. The result is a mask with a smooth transition from black to white. - * If the fade size is 0, the mask is returned as-is. + * Depth Anything Depth Estimation + * @description Generates a depth map using a Depth Anything model. */ - ExpandMaskWithFadeInvocation: { + DepthAnythingDepthEstimationInvocation: { /** * @description The board to save the image to * @default null @@ -7148,41 +7021,29 @@ export type components = { */ use_cache?: boolean; /** - * @description The mask to expand + * @description The image to process * @default null */ - mask?: components["schemas"]["ImageField"] | null; - /** - * Threshold - * @description The threshold for the binary mask (0-255) - * @default 0 - */ - threshold?: number; + image?: components["schemas"]["ImageField"] | null; /** - * Fade Size Px - * @description The size of the fade in pixels - * @default 32 + * Model Size + * @description The size of the depth model to use + * @default small_v2 + * @enum {string} */ - fade_size_px?: number; + model_size?: "large" | "base" | "small" | "small_v2"; /** * type - * @default expand_mask_with_fade + * @default depth_anything_depth_estimation * @constant */ - type: "expand_mask_with_fade"; - }; - /** ExposedField */ - ExposedField: { - /** Nodeid */ - nodeId: string; - /** Fieldname */ - fieldName: string; + type: "depth_anything_depth_estimation"; }; /** - * Apply LoRA Collection - FLUX - * @description Applies a collection of LoRAs to a FLUX transformer. + * Divide Integers + * @description Divides two numbers */ - FLUXLoRACollectionLoader: { + DivideInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -7201,203 +7062,237 @@ export type components = { */ use_cache?: boolean; /** - * LoRAs - * @description LoRA models and weights. May be a single LoRA or collection. - * @default null + * A + * @description The first number + * @default 0 */ - loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null; + a?: number; /** - * Transformer - * @description Transformer - * @default null + * B + * @description The second number + * @default 0 */ - transformer?: components["schemas"]["TransformerField"] | null; + b?: number; /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * type + * @default div + * @constant */ - clip?: components["schemas"]["CLIPField"] | null; + type: "div"; + }; + /** + * DownloadCancelledEvent + * @description Event model for download_cancelled + */ + DownloadCancelledEvent: { /** - * T5 Encoder - * @description T5 tokenizer and text encoder - * @default null + * Timestamp + * @description The timestamp of the event */ - t5_encoder?: components["schemas"]["T5EncoderField"] | null; + timestamp: number; /** - * type - * @default flux_lora_collection_loader - * @constant + * Source + * @description The source of the download */ - type: "flux_lora_collection_loader"; + source: string; }; /** - * FaceIdentifier - * @description Outputs an image with detected face IDs printed on each face. For use with other FaceTools. + * DownloadCompleteEvent + * @description Event model for download_complete */ - FaceIdentifierInvocation: { + DownloadCompleteEvent: { /** - * @description The board to save the image to - * @default null + * Timestamp + * @description The timestamp of the event */ - board?: components["schemas"]["BoardField"] | null; + timestamp: number; /** - * @description Optional metadata to be saved with the image - * @default null + * Source + * @description The source of the download */ - metadata?: components["schemas"]["MetadataField"] | null; + source: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Download Path + * @description The local path where the download is saved */ - id: string; + download_path: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Total Bytes + * @description The total number of bytes downloaded */ - is_intermediate?: boolean; + total_bytes: number; + }; + /** + * DownloadErrorEvent + * @description Event model for download_error + */ + DownloadErrorEvent: { /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Timestamp + * @description The timestamp of the event */ - use_cache?: boolean; + timestamp: number; /** - * @description Image to face detect - * @default null + * Source + * @description The source of the download */ - image?: components["schemas"]["ImageField"] | null; - /** - * Minimum Confidence - * @description Minimum confidence for face detection (lower if detection is failing) - * @default 0.5 - */ - minimum_confidence?: number; + source: string; /** - * Chunk - * @description Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image. - * @default false + * Error Type + * @description The type of error */ - chunk?: boolean; + error_type: string; /** - * type - * @default face_identifier - * @constant + * Error + * @description The error message */ - type: "face_identifier"; + error: string; }; /** - * FaceMask - * @description Face mask creation using mediapipe face detection + * DownloadJob + * @description Class to monitor and control a model download request. */ - FaceMaskInvocation: { + DownloadJob: { /** - * @description Optional metadata to be saved with the image - * @default null + * Id + * @description Numeric ID of this job + * @default -1 */ - metadata?: components["schemas"]["MetadataField"] | null; + id?: number; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Dest + * Format: path + * @description Initial destination of downloaded model on local disk; a directory or file path */ - id: string; + dest: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Download Path + * @description Final location of downloaded file or directory */ - is_intermediate?: boolean; + download_path?: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * @description Status of the download + * @default waiting */ - use_cache?: boolean; + status?: components["schemas"]["DownloadJobStatus"]; /** - * @description Image to face detect - * @default null + * Bytes + * @description Bytes downloaded so far + * @default 0 */ - image?: components["schemas"]["ImageField"] | null; + bytes?: number; /** - * Face Ids - * @description Comma-separated list of face ids to mask eg '0,2,7'. Numbered from 0. Leave empty to mask all. Find face IDs with FaceIdentifier node. - * @default + * Total Bytes + * @description Total file size (bytes) + * @default 0 */ - face_ids?: string; + total_bytes?: number; /** - * Minimum Confidence - * @description Minimum confidence for face detection (lower if detection is failing) - * @default 0.5 + * Error Type + * @description Name of exception that caused an error */ - minimum_confidence?: number; + error_type?: string | null; /** - * X Offset - * @description Offset for the X-axis of the face mask - * @default 0 + * Error + * @description Traceback of the exception that caused an error */ - x_offset?: number; + error?: string | null; /** - * Y Offset - * @description Offset for the Y-axis of the face mask - * @default 0 + * Source + * Format: uri + * @description Where to download from. Specific types specified in child classes. */ - y_offset?: number; + source: string; /** - * Chunk - * @description Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image. - * @default false + * Access Token + * @description authorization token for protected resources */ - chunk?: boolean; + access_token?: string | null; /** - * Invert Mask - * @description Toggle to invert the mask - * @default false + * Priority + * @description Queue priority; lower values are higher priority + * @default 10 */ - invert_mask?: boolean; + priority?: number; /** - * type - * @default face_mask_detection - * @constant + * Job Started + * @description Timestamp for when the download job started */ - type: "face_mask_detection"; + job_started?: string | null; + /** + * Job Ended + * @description Timestamp for when the download job ende1d (completed or errored) + */ + job_ended?: string | null; + /** + * Content Type + * @description Content type of downloaded file + */ + content_type?: string | null; }; /** - * FaceMaskOutput - * @description Base class for FaceMask output + * DownloadJobStatus + * @description State of a download job. + * @enum {string} */ - FaceMaskOutput: { - /** @description The output image */ - image: components["schemas"]["ImageField"]; + DownloadJobStatus: "waiting" | "running" | "completed" | "cancelled" | "error"; + /** + * DownloadProgressEvent + * @description Event model for download_progress + */ + DownloadProgressEvent: { /** - * Width - * @description The width of the image in pixels + * Timestamp + * @description The timestamp of the event */ - width: number; + timestamp: number; /** - * Height - * @description The height of the image in pixels + * Source + * @description The source of the download */ - height: number; + source: string; /** - * type - * @default face_mask_output - * @constant + * Download Path + * @description The local path where the download is saved */ - type: "face_mask_output"; - /** @description The output mask */ - mask: components["schemas"]["ImageField"]; + download_path: string; + /** + * Current Bytes + * @description The number of bytes downloaded so far + */ + current_bytes: number; + /** + * Total Bytes + * @description The total number of bytes to be downloaded + */ + total_bytes: number; }; /** - * FaceOff - * @description Bound, extract, and mask a face from an image using MediaPipe detection + * DownloadStartedEvent + * @description Event model for download_started */ - FaceOffInvocation: { + DownloadStartedEvent: { /** - * @description Optional metadata to be saved with the image - * @default null + * Timestamp + * @description The timestamp of the event */ - metadata?: components["schemas"]["MetadataField"] | null; + timestamp: number; + /** + * Source + * @description The source of the download + */ + source: string; + /** + * Download Path + * @description The local path where the download is saved + */ + download_path: string; + }; + /** + * Dynamic Prompt + * @description Parses a prompt using adieyal/dynamicprompts' random or combinatorial generator + */ + DynamicPromptInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -7412,140 +7307,56 @@ export type components = { /** * Use Cache * @description Whether or not to use the cache - * @default true + * @default false */ use_cache?: boolean; /** - * @description Image for face detection + * Prompt + * @description The prompt to parse with dynamicprompts * @default null */ - image?: components["schemas"]["ImageField"] | null; - /** - * Face Id - * @description The face ID to process, numbered from 0. Multiple faces not supported. Find a face's ID with FaceIdentifier node. - * @default 0 - */ - face_id?: number; - /** - * Minimum Confidence - * @description Minimum confidence for face detection (lower if detection is failing) - * @default 0.5 - */ - minimum_confidence?: number; - /** - * X Offset - * @description X-axis offset of the mask - * @default 0 - */ - x_offset?: number; - /** - * Y Offset - * @description Y-axis offset of the mask - * @default 0 - */ - y_offset?: number; + prompt?: string | null; /** - * Padding - * @description All-axis padding around the mask in pixels - * @default 0 + * Max Prompts + * @description The number of prompts to generate + * @default 1 */ - padding?: number; + max_prompts?: number; /** - * Chunk - * @description Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image. + * Combinatorial + * @description Whether to use the combinatorial generator * @default false */ - chunk?: boolean; + combinatorial?: boolean; /** * type - * @default face_off + * @default dynamic_prompt * @constant */ - type: "face_off"; + type: "dynamic_prompt"; + }; + /** DynamicPromptsResponse */ + DynamicPromptsResponse: { + /** Prompts */ + prompts: string[]; + /** Error */ + error?: string | null; }; /** - * FaceOffOutput - * @description Base class for FaceOff Output + * Upscale (RealESRGAN) + * @description Upscales an image using RealESRGAN. */ - FaceOffOutput: { - /** @description The output image */ - image: components["schemas"]["ImageField"]; - /** - * Width - * @description The width of the image in pixels - */ - width: number; - /** - * Height - * @description The height of the image in pixels - */ - height: number; - /** - * type - * @default face_off_output - * @constant - */ - type: "face_off_output"; - /** @description The output mask */ - mask: components["schemas"]["ImageField"]; - /** - * X - * @description The x coordinate of the bounding box's left side - */ - x: number; - /** - * Y - * @description The y coordinate of the bounding box's top side - */ - y: number; - }; - /** FieldIdentifier */ - FieldIdentifier: { - /** - * Kind - * @description The kind of field - * @enum {string} - */ - kind: "input" | "output"; - /** - * Node Id - * @description The ID of the node - */ - node_id: string; + ESRGANInvocation: { /** - * Field Name - * @description The name of the field + * @description The board to save the image to + * @default null */ - field_name: string; + board?: components["schemas"]["BoardField"] | null; /** - * User Label - * @description The user label of the field, if any + * @description Optional metadata to be saved with the image + * @default null */ - user_label: string | null; - }; - /** - * FieldKind - * @description The kind of field. - * - `Input`: An input field on a node. - * - `Output`: An output field on a node. - * - `Internal`: A field which is treated as an input, but cannot be used in node definitions. Metadata is - * one example. It is provided to nodes via the WithMetadata class, and we want to reserve the field name - * "metadata" for this on all nodes. `FieldKind` is used to short-circuit the field name validation logic, - * allowing "metadata" for that field. - * - `NodeAttribute`: The field is a node attribute. These are fields which are not inputs or outputs, - * but which are used to store information about the node. For example, the `id` and `type` fields are node - * attributes. - * - * The presence of this in `json_schema_extra["field_kind"]` is used when initializing node schemas on app - * startup, and when generating the OpenAPI schema for the workflow editor. - * @enum {string} - */ - FieldKind: "input" | "output" | "internal" | "node_attribute"; - /** - * Float Batch - * @description Create a batched generation, where the workflow is executed once for each float in the batch. - */ - FloatBatchInvocation: { + metadata?: components["schemas"]["MetadataField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -7564,82 +7375,98 @@ export type components = { */ use_cache?: boolean; /** - * Batch Group - * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. - * @default None + * @description The input image + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Model Name + * @description The Real-ESRGAN model to use + * @default RealESRGAN_x4plus.pth * @enum {string} */ - batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; + model_name?: "RealESRGAN_x4plus.pth" | "RealESRGAN_x4plus_anime_6B.pth" | "ESRGAN_SRx4_DF2KOST_official-ff704c30.pth" | "RealESRGAN_x2plus.pth"; /** - * Floats - * @description The floats to batch over - * @default null + * Tile Size + * @description Tile size for tiled ESRGAN upscaling (0=tiling disabled) + * @default 400 */ - floats?: number[] | null; + tile_size?: number; /** * type - * @default float_batch + * @default esrgan * @constant */ - type: "float_batch"; + type: "esrgan"; }; - /** - * Float Collection Primitive - * @description A collection of float primitive values - */ - FloatCollectionInvocation: { + /** Edge */ + Edge: { + /** @description The connection for the edge's from node and field */ + source: components["schemas"]["EdgeConnection"]; + /** @description The connection for the edge's to node and field */ + destination: components["schemas"]["EdgeConnection"]; + }; + /** EdgeConnection */ + EdgeConnection: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Node Id + * @description The id of the node for this edge connection */ - id: string; + node_id: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Field + * @description The field for this connection */ - is_intermediate?: boolean; + field: string; + }; + /** EnqueueBatchResult */ + EnqueueBatchResult: { /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Queue Id + * @description The ID of the queue */ - use_cache?: boolean; + queue_id: string; /** - * Collection - * @description The collection of float values - * @default [] + * Enqueued + * @description The total number of queue items enqueued */ - collection?: number[]; + enqueued: number; /** - * type - * @default float_collection - * @constant + * Requested + * @description The total number of queue items requested to be enqueued */ - type: "float_collection"; - }; - /** - * FloatCollectionOutput - * @description Base class for nodes that output a collection of floats - */ - FloatCollectionOutput: { + requested: number; + /** @description The batch that was enqueued */ + batch: components["schemas"]["Batch"]; /** - * Collection - * @description The float collection + * Priority + * @description The priority of the enqueued batch */ - collection: number[]; + priority: number; /** - * type - * @default float_collection_output - * @constant + * Item Ids + * @description The IDs of the queue items that were enqueued */ - type: "float_collection_output"; + item_ids: number[]; }; /** - * Float Generator - * @description Generated a range of floats for use in a batched generation + * Expand Mask with Fade + * @description Expands a mask with a fade effect. The mask uses black to indicate areas to keep from the generated image and white for areas to discard. + * The mask is thresholded to create a binary mask, and then a distance transform is applied to create a fade effect. + * The fade size is specified in pixels, and the mask is expanded by that amount. The result is a mask with a smooth transition from black to white. + * If the fade size is 0, the mask is returned as-is. */ - FloatGenerator: { + ExpandMaskWithFadeInvocation: { + /** + * @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. @@ -7658,637 +7485,563 @@ export type components = { */ use_cache?: boolean; /** - * Generator Type - * @description The float generator. + * @description The mask to expand + * @default null */ - generator: components["schemas"]["FloatGeneratorField"]; + mask?: components["schemas"]["ImageField"] | null; /** - * type - * @default float_generator - * @constant + * Threshold + * @description The threshold for the binary mask (0-255) + * @default 0 */ - type: "float_generator"; - }; - /** FloatGeneratorField */ - FloatGeneratorField: Record; - /** - * FloatGeneratorOutput - * @description Base class for nodes that output a collection of floats - */ - FloatGeneratorOutput: { + threshold?: number; /** - * Floats - * @description The generated floats + * Fade Size Px + * @description The size of the fade in pixels + * @default 32 */ - floats: number[]; + fade_size_px?: number; /** * type - * @default float_generator_output + * @default expand_mask_with_fade * @constant */ - type: "float_generator_output"; + type: "expand_mask_with_fade"; }; - /** - * Float Primitive - * @description A float primitive value - */ - FloatInvocation: { + /** ExposedField */ + ExposedField: { + /** Nodeid */ + nodeId: string; + /** Fieldname */ + fieldName: string; + }; + /** ExternalAPI_ChatGPT4o_Config */ + ExternalAPI_ChatGPT4o_Config: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Key + * @description A unique key for this model. */ - id: string; + key: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Hash + * @description The hash of the model file(s). */ - is_intermediate?: boolean; + hash: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - use_cache?: boolean; + path: string; /** - * Value - * @description The float value - * @default 0 + * File Size + * @description The size of the model in bytes. */ - value?: number; + file_size: number; /** - * type - * @default float - * @constant + * Name + * @description Name of the model. */ - type: "float"; - }; - /** - * Float Range - * @description Creates a range - */ - FloatLinearRangeInvocation: { + name: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Description + * @description Model description */ - id: string; + description: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Source + * @description The original source of the model (path, URL or repo_id). */ - is_intermediate?: boolean; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - use_cache?: boolean; + source_api_response: string | null; /** - * Start - * @description The first value of the range - * @default 5 + * Cover Image + * @description Url for image to preview model */ - start?: number; + cover_image: string | null; /** - * Stop - * @description The last value of the range - * @default 10 + * Submodels + * @description Loadable submodels in this model */ - stop?: number; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Steps - * @description number of values to interpolate over (including start and stop) - * @default 30 + * Usage Info + * @description Usage information for this model */ - steps?: number; + usage_info: string | null; /** - * type - * @default float_range + * Type + * @default main * @constant */ - type: "float_range"; - }; - /** - * Float Math - * @description Performs floating point math. - */ - FloatMathInvocation: { + type: "main"; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Trigger Phrases + * @description Set of trigger phrases for this model */ - id: string; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Format + * @default api + * @constant */ - is_intermediate?: boolean; + format: "api"; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Base + * @default chatgpt-4o + * @constant */ - use_cache?: boolean; + base: "chatgpt-4o"; + }; + /** ExternalAPI_FluxKontext_Config */ + ExternalAPI_FluxKontext_Config: { /** - * Operation - * @description The operation to perform - * @default ADD - * @enum {string} + * Key + * @description A unique key for this model. */ - operation?: "ADD" | "SUB" | "MUL" | "DIV" | "EXP" | "ABS" | "SQRT" | "MIN" | "MAX"; + key: string; /** - * A - * @description The first number - * @default 1 + * Hash + * @description The hash of the model file(s). */ - a?: number; + hash: string; /** - * B - * @description The second number - * @default 1 + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - b?: number; + path: string; /** - * type - * @default float_math - * @constant + * File Size + * @description The size of the model in bytes. */ - type: "float_math"; - }; - /** - * FloatOutput - * @description Base class for nodes that output a single float - */ - FloatOutput: { + file_size: number; /** - * Value - * @description The output float + * Name + * @description Name of the model. */ - value: number; + name: string; /** - * type - * @default float_output - * @constant + * Description + * @description Model description */ - type: "float_output"; - }; - /** - * Float To Integer - * @description Rounds a float number to (a multiple of) an integer. - */ - FloatToIntegerInvocation: { + description: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Source + * @description The original source of the model (path, URL or repo_id). */ - id: string; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - is_intermediate?: boolean; + source_api_response: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Cover Image + * @description Url for image to preview model */ - use_cache?: boolean; + cover_image: string | null; /** - * Value - * @description The value to round - * @default 0 + * Submodels + * @description Loadable submodels in this model */ - value?: number; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Multiple of - * @description The multiple to round to - * @default 1 + * Usage Info + * @description Usage information for this model */ - multiple?: number; + usage_info: string | null; /** - * Method - * @description The method to use for rounding - * @default Nearest - * @enum {string} + * Type + * @default main + * @constant */ - method?: "Nearest" | "Floor" | "Ceiling" | "Truncate"; + type: "main"; /** - * type - * @default float_to_int - * @constant + * Trigger Phrases + * @description Set of trigger phrases for this model */ - type: "float_to_int"; - }; - /** - * FluxConditioningCollectionOutput - * @description Base class for nodes that output a collection of conditioning tensors - */ - FluxConditioningCollectionOutput: { + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * Collection - * @description The output conditioning tensors + * Format + * @default api + * @constant */ - collection: components["schemas"]["FluxConditioningField"][]; + format: "api"; /** - * type - * @default flux_conditioning_collection_output + * Base + * @default flux-kontext * @constant */ - type: "flux_conditioning_collection_output"; + base: "flux-kontext"; }; - /** - * FluxConditioningField - * @description A conditioning tensor primitive value - */ - FluxConditioningField: { + /** ExternalAPI_Gemini2_5_Config */ + ExternalAPI_Gemini2_5_Config: { /** - * Conditioning Name - * @description The name of conditioning tensor + * Key + * @description A unique key for this model. */ - conditioning_name: string; + key: string; /** - * @description The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True. - * @default null + * Hash + * @description The hash of the model file(s). */ - mask?: components["schemas"]["TensorField"] | null; - }; - /** - * FluxConditioningOutput - * @description Base class for nodes that output a single conditioning tensor - */ - FluxConditioningOutput: { - /** @description Conditioning tensor */ - conditioning: components["schemas"]["FluxConditioningField"]; + hash: string; /** - * type - * @default flux_conditioning_output - * @constant + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - type: "flux_conditioning_output"; - }; - /** - * Control LoRA - FLUX - * @description LoRA model and Image to use with FLUX transformer generation. - */ - FluxControlLoRALoaderInvocation: { + path: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * File Size + * @description The size of the model in bytes. */ - id: string; + file_size: number; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Name + * @description Name of the model. */ - is_intermediate?: boolean; + name: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Description + * @description Model description */ - use_cache?: boolean; + description: string | null; /** - * Control LoRA - * @description Control LoRA model to load - * @default null + * Source + * @description The original source of the model (path, URL or repo_id). */ - lora?: components["schemas"]["ModelIdentifierField"] | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * @description The image to encode. - * @default null + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - image?: components["schemas"]["ImageField"] | null; + source_api_response: string | null; /** - * Weight - * @description The weight of the LoRA. - * @default 1 + * Cover Image + * @description Url for image to preview model */ - weight?: number; + cover_image: string | null; /** - * type - * @default flux_control_lora_loader - * @constant + * Submodels + * @description Loadable submodels in this model */ - type: "flux_control_lora_loader"; - }; - /** - * FluxControlLoRALoaderOutput - * @description Flux Control LoRA Loader Output - */ - FluxControlLoRALoaderOutput: { + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Flux Control LoRA - * @description Control LoRAs to apply on model loading - * @default null + * Usage Info + * @description Usage information for this model */ - control_lora: components["schemas"]["ControlLoRAField"]; + usage_info: string | null; /** - * type - * @default flux_control_lora_loader_output + * Type + * @default main * @constant */ - type: "flux_control_lora_loader_output"; - }; - /** FluxControlNetField */ - FluxControlNetField: { - /** @description The control image */ - image: components["schemas"]["ImageField"]; - /** @description The ControlNet model to use */ - control_model: components["schemas"]["ModelIdentifierField"]; + type: "main"; /** - * Control Weight - * @description The weight given to the ControlNet - * @default 1 + * Trigger Phrases + * @description Set of trigger phrases for this model */ - control_weight?: number | number[]; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * Begin Step Percent - * @description When the ControlNet is first applied (% of total steps) - * @default 0 + * Format + * @default api + * @constant */ - begin_step_percent?: number; + format: "api"; /** - * End Step Percent - * @description When the ControlNet is last applied (% of total steps) - * @default 1 + * Base + * @default gemini-2.5 + * @constant */ - end_step_percent?: number; + base: "gemini-2.5"; + }; + /** ExternalAPI_Imagen3_Config */ + ExternalAPI_Imagen3_Config: { /** - * Resize Mode - * @description The resize mode to use - * @default just_resize - * @enum {string} + * Key + * @description A unique key for this model. */ - resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; + key: string; /** - * Instantx Control Mode - * @description The control mode for InstantX ControlNet union models. Ignored for other ControlNet models. The standard mapping is: canny (0), tile (1), depth (2), blur (3), pose (4), gray (5), low quality (6). Negative values will be treated as 'None'. - * @default -1 + * Hash + * @description The hash of the model file(s). */ - instantx_control_mode?: number | null; - }; - /** - * FLUX ControlNet - * @description Collect FLUX ControlNet info to pass to other nodes. - */ - FluxControlNetInvocation: { + hash: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - id: string; + path: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * File Size + * @description The size of the model in bytes. */ - is_intermediate?: boolean; + file_size: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Name + * @description Name of the model. */ - use_cache?: boolean; + name: string; /** - * @description The control image - * @default null + * Description + * @description Model description */ - image?: components["schemas"]["ImageField"] | null; + description: string | null; /** - * @description ControlNet model to load - * @default null + * Source + * @description The original source of the model (path, URL or repo_id). */ - control_model?: components["schemas"]["ModelIdentifierField"] | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Control Weight - * @description The weight given to the ControlNet - * @default 1 + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - control_weight?: number | number[]; + source_api_response: string | null; /** - * Begin Step Percent - * @description When the ControlNet is first applied (% of total steps) - * @default 0 + * Cover Image + * @description Url for image to preview model */ - begin_step_percent?: number; + cover_image: string | null; /** - * End Step Percent - * @description When the ControlNet is last applied (% of total steps) - * @default 1 + * Submodels + * @description Loadable submodels in this model */ - end_step_percent?: number; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Resize Mode - * @description The resize mode used - * @default just_resize - * @enum {string} + * Usage Info + * @description Usage information for this model */ - resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; + usage_info: string | null; /** - * Instantx Control Mode - * @description The control mode for InstantX ControlNet union models. Ignored for other ControlNet models. The standard mapping is: canny (0), tile (1), depth (2), blur (3), pose (4), gray (5), low quality (6). Negative values will be treated as 'None'. - * @default -1 + * Type + * @default main + * @constant */ - instantx_control_mode?: number | null; + type: "main"; /** - * type - * @default flux_controlnet + * 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 api * @constant */ - type: "flux_controlnet"; - }; - /** - * FluxControlNetOutput - * @description FLUX ControlNet info - */ - FluxControlNetOutput: { - /** @description ControlNet(s) to apply */ - control: components["schemas"]["FluxControlNetField"]; + format: "api"; /** - * type - * @default flux_controlnet_output + * Base + * @default imagen3 * @constant */ - type: "flux_controlnet_output"; + base: "imagen3"; }; - /** - * FLUX Denoise - * @description Run denoising process with a FLUX transformer model. - */ - FluxDenoiseInvocation: { + /** ExternalAPI_Imagen4_Config */ + ExternalAPI_Imagen4_Config: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Key + * @description A unique key for this model. */ - id: string; + key: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Hash + * @description The hash of the model file(s). */ - is_intermediate?: boolean; + hash: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - use_cache?: boolean; + path: string; /** - * @description Latents tensor - * @default null + * File Size + * @description The size of the model in bytes. */ - latents?: components["schemas"]["LatentsField"] | null; + file_size: number; /** - * @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 + * Name + * @description Name of the model. */ - denoise_mask?: components["schemas"]["DenoiseMaskField"] | null; + name: string; /** - * Denoising Start - * @description When to start denoising, expressed a percentage of total steps - * @default 0 + * Description + * @description Model description */ - denoising_start?: number; + description: string | null; /** - * Denoising End - * @description When to stop denoising, expressed a percentage of total steps - * @default 1 + * Source + * @description The original source of the model (path, URL or repo_id). */ - denoising_end?: number; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Add Noise - * @description Add noise based on denoising start. - * @default true + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - add_noise?: boolean; + source_api_response: string | null; /** - * Transformer - * @description Flux model (Transformer) to load - * @default null + * Cover Image + * @description Url for image to preview model */ - transformer?: components["schemas"]["TransformerField"] | null; + cover_image: string | null; /** - * Control LoRA - * @description Control LoRA model to load - * @default null + * Submodels + * @description Loadable submodels in this model */ - control_lora?: components["schemas"]["ControlLoRAField"] | null; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Positive Text Conditioning - * @description Positive conditioning tensor - * @default null + * Usage Info + * @description Usage information for this model */ - positive_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; + usage_info: string | null; /** - * Negative Text Conditioning - * @description Negative conditioning tensor. Can be None if cfg_scale is 1.0. - * @default null + * Type + * @default main + * @constant */ - negative_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; + type: "main"; /** - * Redux Conditioning - * @description FLUX Redux conditioning tensor. - * @default null + * Trigger Phrases + * @description Set of trigger phrases for this model */ - redux_conditioning?: components["schemas"]["FluxReduxConditioningField"] | components["schemas"]["FluxReduxConditioningField"][] | null; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * @description FLUX Fill conditioning. - * @default null + * Format + * @default api + * @constant */ - fill_conditioning?: components["schemas"]["FluxFillConditioningField"] | null; + format: "api"; /** - * CFG Scale - * @description Classifier-Free Guidance scale - * @default 1 + * Base + * @default imagen4 + * @constant */ - cfg_scale?: number | number[]; + base: "imagen4"; + }; + /** ExternalAPI_Runway_Config */ + ExternalAPI_Runway_Config: { /** - * CFG Scale Start Step - * @description Index of the first step to apply cfg_scale. Negative indices count backwards from the the last step (e.g. a value of -1 refers to the final step). - * @default 0 + * Key + * @description A unique key for this model. */ - cfg_scale_start_step?: number; + key: string; /** - * CFG Scale End Step - * @description Index of the last step to apply cfg_scale. Negative indices count backwards from the last step (e.g. a value of -1 refers to the final step). - * @default -1 + * Hash + * @description The hash of the model file(s). */ - cfg_scale_end_step?: number; + hash: string; /** - * Width - * @description Width of the generated image. - * @default 1024 + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - width?: number; + path: string; /** - * Height - * @description Height of the generated image. - * @default 1024 + * File Size + * @description The size of the model in bytes. */ - height?: number; + file_size: number; /** - * Num Steps - * @description Number of diffusion steps. Recommended values are schnell: 4, dev: 50. - * @default 4 + * Name + * @description Name of the model. */ - num_steps?: number; + name: string; /** - * Guidance - * @description The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell. - * @default 4 + * Description + * @description Model description */ - guidance?: number; + description: string | null; /** - * Seed - * @description Randomness seed for reproducibility. - * @default 0 + * Source + * @description The original source of the model (path, URL or repo_id). */ - seed?: number; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Control - * @description ControlNet models. - * @default null + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - control?: components["schemas"]["FluxControlNetField"] | components["schemas"]["FluxControlNetField"][] | null; + source_api_response: string | null; /** - * @description VAE - * @default null + * Cover Image + * @description Url for image to preview model */ - controlnet_vae?: components["schemas"]["VAEField"] | null; + cover_image: string | null; /** - * IP-Adapter - * @description IP-Adapter to apply - * @default null + * Submodels + * @description Loadable submodels in this model */ - ip_adapter?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Kontext Conditioning - * @description FLUX Kontext conditioning (reference image). - * @default null + * Usage Info + * @description Usage information for this model */ - kontext_conditioning?: components["schemas"]["FluxKontextConditioningField"] | components["schemas"]["FluxKontextConditioningField"][] | null; + usage_info: string | null; /** - * type - * @default flux_denoise + * Type + * @default video * @constant */ - type: "flux_denoise"; + type: "video"; + /** + * 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 api + * @constant + */ + format: "api"; + /** + * Base + * @default flux-kontext + * @constant + */ + base: "flux-kontext"; }; /** - * FLUX Denoise + Metadata - * @description Run denoising process with a FLUX transformer model + metadata. + * Apply LoRA Collection - FLUX + * @description Applies a collection of LoRAs to a FLUX transformer. */ - FluxDenoiseLatentsMetaInvocation: { - /** - * @description Optional metadata to be saved with the image - * @default null - */ - metadata?: components["schemas"]["MetadataField"] | null; + FLUXLoRACollectionLoader: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -8307,161 +8060,134 @@ export type components = { */ 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. + * LoRAs + * @description LoRA models and weights. May be a single LoRA or collection. * @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; - /** - * Add Noise - * @description Add noise based on denoising start. - * @default true - */ - add_noise?: boolean; + loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null; /** * Transformer - * @description Flux model (Transformer) to load + * @description Transformer * @default null */ transformer?: components["schemas"]["TransformerField"] | null; /** - * Control LoRA - * @description Control LoRA model to load + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count * @default null */ - control_lora?: components["schemas"]["ControlLoRAField"] | null; + clip?: components["schemas"]["CLIPField"] | null; /** - * Positive Text Conditioning - * @description Positive conditioning tensor + * T5 Encoder + * @description T5 tokenizer and text encoder * @default null */ - positive_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; + t5_encoder?: components["schemas"]["T5EncoderField"] | null; /** - * Negative Text Conditioning - * @description Negative conditioning tensor. Can be None if cfg_scale is 1.0. - * @default null - */ - negative_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; - /** - * Redux Conditioning - * @description FLUX Redux conditioning tensor. - * @default null + * type + * @default flux_lora_collection_loader + * @constant */ - redux_conditioning?: components["schemas"]["FluxReduxConditioningField"] | components["schemas"]["FluxReduxConditioningField"][] | null; + type: "flux_lora_collection_loader"; + }; + /** + * FLUXRedux_Checkpoint_Config + * @description Model config for FLUX Tools Redux model. + */ + FLUXRedux_Checkpoint_Config: { /** - * @description FLUX Fill conditioning. - * @default null + * Key + * @description A unique key for this model. */ - fill_conditioning?: components["schemas"]["FluxFillConditioningField"] | null; + key: string; /** - * CFG Scale - * @description Classifier-Free Guidance scale - * @default 1 + * Hash + * @description The hash of the model file(s). */ - cfg_scale?: number | number[]; + hash: string; /** - * CFG Scale Start Step - * @description Index of the first step to apply cfg_scale. Negative indices count backwards from the the last step (e.g. a value of -1 refers to the final step). - * @default 0 + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - cfg_scale_start_step?: number; + path: string; /** - * CFG Scale End Step - * @description Index of the last step to apply cfg_scale. Negative indices count backwards from the last step (e.g. a value of -1 refers to the final step). - * @default -1 + * File Size + * @description The size of the model in bytes. */ - cfg_scale_end_step?: number; + file_size: number; /** - * Width - * @description Width of the generated image. - * @default 1024 + * Name + * @description Name of the model. */ - width?: number; + name: string; /** - * Height - * @description Height of the generated image. - * @default 1024 + * Description + * @description Model description */ - height?: number; + description: string | null; /** - * Num Steps - * @description Number of diffusion steps. Recommended values are schnell: 4, dev: 50. - * @default 4 + * Source + * @description The original source of the model (path, URL or repo_id). */ - num_steps?: number; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Guidance - * @description The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell. - * @default 4 + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - guidance?: number; + source_api_response: string | null; /** - * Seed - * @description Randomness seed for reproducibility. - * @default 0 + * Cover Image + * @description Url for image to preview model */ - seed?: number; + cover_image: string | null; /** - * Control - * @description ControlNet models. - * @default null + * Submodels + * @description Loadable submodels in this model */ - control?: components["schemas"]["FluxControlNetField"] | components["schemas"]["FluxControlNetField"][] | null; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * @description VAE - * @default null + * Usage Info + * @description Usage information for this model */ - controlnet_vae?: components["schemas"]["VAEField"] | null; + usage_info: string | null; /** - * IP-Adapter - * @description IP-Adapter to apply - * @default null + * Type + * @default flux_redux + * @constant */ - ip_adapter?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; + type: "flux_redux"; /** - * Kontext Conditioning - * @description FLUX Kontext conditioning (reference image). - * @default null + * Format + * @default checkpoint + * @constant */ - kontext_conditioning?: components["schemas"]["FluxKontextConditioningField"] | components["schemas"]["FluxKontextConditioningField"][] | null; + format: "checkpoint"; /** - * type - * @default flux_denoise_meta + * Base + * @default flux * @constant */ - type: "flux_denoise_meta"; - }; - /** - * FluxFillConditioningField - * @description A FLUX Fill conditioning field. - */ - FluxFillConditioningField: { - /** @description The FLUX Fill reference image. */ - image: components["schemas"]["ImageField"]; - /** @description The FLUX Fill inpaint mask. */ - mask: components["schemas"]["TensorField"]; + base: "flux"; }; /** - * FLUX Fill Conditioning - * @description Prepare the FLUX Fill conditioning data. + * FaceIdentifier + * @description Outputs an image with detected face IDs printed on each face. For use with other FaceTools. */ - FluxFillInvocation: { + FaceIdentifierInvocation: { + /** + * @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. @@ -8480,44 +8206,39 @@ export type components = { */ use_cache?: boolean; /** - * @description The FLUX Fill reference image. + * @description Image to face detect * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * @description The bool inpainting mask. Excluded regions should be set to False, included regions should be set to True. - * @default null - */ - mask?: components["schemas"]["TensorField"] | null; - /** - * type - * @default flux_fill - * @constant + * Minimum Confidence + * @description Minimum confidence for face detection (lower if detection is failing) + * @default 0.5 */ - type: "flux_fill"; - }; - /** - * FluxFillOutput - * @description The conditioning output of a FLUX Fill invocation. - */ - FluxFillOutput: { + minimum_confidence?: number; /** - * Conditioning - * @description FLUX Redux conditioning tensor + * Chunk + * @description Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image. + * @default false */ - fill_cond: components["schemas"]["FluxFillConditioningField"]; + chunk?: boolean; /** * type - * @default flux_fill_output + * @default face_identifier * @constant */ - type: "flux_fill_output"; + type: "face_identifier"; }; /** - * FLUX IP-Adapter - * @description Collects FLUX IP-Adapter info to pass to other nodes. + * FaceMask + * @description Face mask creation using mediapipe face detection */ - FluxIPAdapterInvocation: { + FaceMaskInvocation: { + /** + * @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. @@ -8536,64 +8257,89 @@ export type components = { */ use_cache?: boolean; /** - * @description The IP-Adapter image prompt(s). + * @description Image to face detect * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * IP-Adapter Model - * @description The IP-Adapter model. - * @default null + * Face Ids + * @description Comma-separated list of face ids to mask eg '0,2,7'. Numbered from 0. Leave empty to mask all. Find face IDs with FaceIdentifier node. + * @default */ - ip_adapter_model?: components["schemas"]["ModelIdentifierField"] | null; + face_ids?: string; /** - * Clip Vision Model - * @description CLIP Vision model to use. - * @default ViT-L - * @constant + * Minimum Confidence + * @description Minimum confidence for face detection (lower if detection is failing) + * @default 0.5 */ - clip_vision_model?: "ViT-L"; + minimum_confidence?: number; /** - * Weight - * @description The weight given to the IP-Adapter - * @default 1 + * X Offset + * @description Offset for the X-axis of the face mask + * @default 0 */ - weight?: number | number[]; + x_offset?: number; /** - * Begin Step Percent - * @description When the IP-Adapter is first applied (% of total steps) + * Y Offset + * @description Offset for the Y-axis of the face mask * @default 0 */ - begin_step_percent?: number; + y_offset?: number; /** - * End Step Percent - * @description When the IP-Adapter is last applied (% of total steps) - * @default 1 + * Chunk + * @description Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image. + * @default false */ - end_step_percent?: number; + chunk?: boolean; + /** + * Invert Mask + * @description Toggle to invert the mask + * @default false + */ + invert_mask?: boolean; /** * type - * @default flux_ip_adapter + * @default face_mask_detection * @constant */ - type: "flux_ip_adapter"; + type: "face_mask_detection"; }; /** - * FLUX Kontext Image Prep - * @description Prepares an image or images for use with FLUX Kontext. The first/single image is resized to the nearest - * preferred Kontext resolution. All other images are concatenated horizontally, maintaining their aspect ratio. + * FaceMaskOutput + * @description Base class for FaceMask output */ - FluxKontextConcatenateImagesInvocation: { + FaceMaskOutput: { + /** @description The output image */ + image: components["schemas"]["ImageField"]; /** - * @description The board to save the image to - * @default null + * Width + * @description The width of the image in pixels */ - board?: components["schemas"]["BoardField"] | null; + width: number; /** - * @description Optional metadata to be saved with the image - * @default null + * Height + * @description The height of the image in pixels */ - metadata?: components["schemas"]["MetadataField"] | null; + height: number; + /** + * type + * @default face_mask_output + * @constant + */ + type: "face_mask_output"; + /** @description The output mask */ + mask: components["schemas"]["ImageField"]; + }; + /** + * FaceOff + * @description Bound, extract, and mask a face from an image using MediaPipe detection + */ + FaceOffInvocation: { + /** + * @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. @@ -8612,88 +8358,136 @@ export type components = { */ use_cache?: boolean; /** - * Images - * @description The images to concatenate + * @description Image for face detection * @default null */ - images?: components["schemas"]["ImageField"][] | null; + image?: components["schemas"]["ImageField"] | null; /** - * Use Preferred Resolution - * @description Use FLUX preferred resolutions for the first image - * @default true + * Face Id + * @description The face ID to process, numbered from 0. Multiple faces not supported. Find a face's ID with FaceIdentifier node. + * @default 0 */ - use_preferred_resolution?: boolean; + face_id?: number; /** - * type - * @default flux_kontext_image_prep - * @constant + * Minimum Confidence + * @description Minimum confidence for face detection (lower if detection is failing) + * @default 0.5 */ - type: "flux_kontext_image_prep"; - }; - /** - * FluxKontextConditioningField - * @description A conditioning field for FLUX Kontext (reference image). - */ - FluxKontextConditioningField: { - /** @description The Kontext reference image. */ - image: components["schemas"]["ImageField"]; - }; - /** - * Kontext Conditioning - FLUX - * @description Prepares a reference image for FLUX Kontext conditioning. - */ - FluxKontextInvocation: { + minimum_confidence?: number; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * X Offset + * @description X-axis offset of the mask + * @default 0 */ - id: string; + x_offset?: number; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Y Offset + * @description Y-axis offset of the mask + * @default 0 */ - is_intermediate?: boolean; + y_offset?: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Padding + * @description All-axis padding around the mask in pixels + * @default 0 */ - use_cache?: boolean; + padding?: number; /** - * @description The Kontext reference image. - * @default null + * Chunk + * @description Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image. + * @default false */ - image?: components["schemas"]["ImageField"] | null; + chunk?: boolean; /** * type - * @default flux_kontext + * @default face_off * @constant */ - type: "flux_kontext"; + type: "face_off"; }; /** - * FluxKontextOutput - * @description The conditioning output of a FLUX Kontext invocation. + * FaceOffOutput + * @description Base class for FaceOff Output */ - FluxKontextOutput: { + FaceOffOutput: { + /** @description The output image */ + image: components["schemas"]["ImageField"]; /** - * Kontext Conditioning - * @description FLUX Kontext conditioning (reference image) + * Width + * @description The width of the image in pixels */ - kontext_cond: components["schemas"]["FluxKontextConditioningField"]; + width: number; + /** + * Height + * @description The height of the image in pixels + */ + height: number; /** * type - * @default flux_kontext_output + * @default face_off_output * @constant */ - type: "flux_kontext_output"; + type: "face_off_output"; + /** @description The output mask */ + mask: components["schemas"]["ImageField"]; + /** + * X + * @description The x coordinate of the bounding box's left side + */ + x: number; + /** + * Y + * @description The y coordinate of the bounding box's top side + */ + y: number; + }; + /** FieldIdentifier */ + FieldIdentifier: { + /** + * Kind + * @description The kind of field + * @enum {string} + */ + kind: "input" | "output"; + /** + * Node Id + * @description The ID of the node + */ + node_id: string; + /** + * Field Name + * @description The name of the field + */ + field_name: string; + /** + * User Label + * @description The user label of the field, if any + */ + user_label: string | null; }; /** - * Apply LoRA - FLUX - * @description Apply a LoRA model to a FLUX transformer and/or text encoder. + * FieldKind + * @description The kind of field. + * - `Input`: An input field on a node. + * - `Output`: An output field on a node. + * - `Internal`: A field which is treated as an input, but cannot be used in node definitions. Metadata is + * one example. It is provided to nodes via the WithMetadata class, and we want to reserve the field name + * "metadata" for this on all nodes. `FieldKind` is used to short-circuit the field name validation logic, + * allowing "metadata" for that field. + * - `NodeAttribute`: The field is a node attribute. These are fields which are not inputs or outputs, + * but which are used to store information about the node. For example, the `id` and `type` fields are node + * attributes. + * + * The presence of this in `json_schema_extra["field_kind"]` is used when initializing node schemas on app + * startup, and when generating the OpenAPI schema for the workflow editor. + * @enum {string} */ - FluxLoRALoaderInvocation: { + FieldKind: "input" | "output" | "internal" | "node_attribute"; + /** + * Float Batch + * @description Create a batched generation, where the workflow is executed once for each float in the batch. + */ + FloatBatchInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -8712,77 +8506,30 @@ export type components = { */ 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; - /** - * FLUX Transformer - * @description Transformer - * @default null - */ - transformer?: components["schemas"]["TransformerField"] | null; - /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null - */ - clip?: components["schemas"]["CLIPField"] | null; - /** - * T5 Encoder - * @description T5 tokenizer and text encoder - * @default null - */ - t5_encoder?: components["schemas"]["T5EncoderField"] | null; - /** - * type - * @default flux_lora_loader - * @constant - */ - type: "flux_lora_loader"; - }; - /** - * FluxLoRALoaderOutput - * @description FLUX LoRA Loader Output - */ - FluxLoRALoaderOutput: { - /** - * FLUX Transformer - * @description Transformer - * @default null - */ - transformer: components["schemas"]["TransformerField"] | null; - /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * Batch Group + * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. + * @default None + * @enum {string} */ - clip: components["schemas"]["CLIPField"] | null; + batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; /** - * T5 Encoder - * @description T5 tokenizer and text encoder + * Floats + * @description The floats to batch over * @default null */ - t5_encoder: components["schemas"]["T5EncoderField"] | null; + floats?: number[] | null; /** * type - * @default flux_lora_loader_output + * @default float_batch * @constant */ - type: "flux_lora_loader_output"; + type: "float_batch"; }; /** - * Main Model - FLUX - * @description Loads a flux base model, outputting its submodels. + * Float Collection Primitive + * @description A collection of float primitive values */ - FluxModelLoaderInvocation: { + FloatCollectionInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -8800,170 +8547,176 @@ export type components = { * @default true */ use_cache?: boolean; - /** @description Flux model (Transformer) to load */ - model: components["schemas"]["ModelIdentifierField"]; /** - * T5 Encoder - * @description T5 tokenizer and text encoder + * Collection + * @description The collection of float values + * @default [] */ - t5_encoder_model: components["schemas"]["ModelIdentifierField"]; + collection?: number[]; /** - * CLIP Embed - * @description CLIP Embed loader - */ - clip_embed_model: components["schemas"]["ModelIdentifierField"]; + * type + * @default float_collection + * @constant + */ + type: "float_collection"; + }; + /** + * FloatCollectionOutput + * @description Base class for nodes that output a collection of floats + */ + FloatCollectionOutput: { /** - * VAE - * @description VAE model to load - * @default null + * Collection + * @description The float collection */ - vae_model?: components["schemas"]["ModelIdentifierField"] | null; + collection: number[]; /** * type - * @default flux_model_loader + * @default float_collection_output * @constant */ - type: "flux_model_loader"; + type: "float_collection_output"; }; /** - * FluxModelLoaderOutput - * @description Flux base model loader output + * Float Generator + * @description Generated a range of floats for use in a batched generation */ - FluxModelLoaderOutput: { - /** - * Transformer - * @description Transformer - */ - transformer: components["schemas"]["TransformerField"]; + FloatGenerator: { /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - clip: components["schemas"]["CLIPField"]; + id: string; /** - * T5 Encoder - * @description T5 tokenizer and text encoder + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - t5_encoder: components["schemas"]["T5EncoderField"]; + is_intermediate?: boolean; /** - * VAE - * @description VAE + * Use Cache + * @description Whether or not to use the cache + * @default true */ - vae: components["schemas"]["VAEField"]; + use_cache?: boolean; /** - * Max Seq Length - * @description The max sequence length to used for the T5 encoder. (256 for schnell transformer, 512 for dev transformer) - * @enum {integer} + * Generator Type + * @description The float generator. */ - max_seq_len: 256 | 512; + generator: components["schemas"]["FloatGeneratorField"]; /** * type - * @default flux_model_loader_output + * @default float_generator * @constant */ - type: "flux_model_loader_output"; + type: "float_generator"; }; + /** FloatGeneratorField */ + FloatGeneratorField: Record; /** - * FluxReduxConditioningField - * @description A FLUX Redux conditioning tensor primitive value + * FloatGeneratorOutput + * @description Base class for nodes that output a collection of floats */ - FluxReduxConditioningField: { - /** @description The Redux image conditioning tensor. */ - conditioning: components["schemas"]["TensorField"]; + FloatGeneratorOutput: { /** - * @description The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True. - * @default null + * Floats + * @description The generated floats */ - mask?: components["schemas"]["TensorField"] | null; + floats: number[]; + /** + * type + * @default float_generator_output + * @constant + */ + type: "float_generator_output"; }; /** - * FluxReduxConfig - * @description Model config for FLUX Tools Redux model. + * Float Primitive + * @description A float primitive value */ - FluxReduxConfig: { - /** - * Key - * @description A unique key for this model. - */ - key: string; - /** - * Hash - * @description The hash of the model file(s). - */ - hash: string; + FloatInvocation: { /** - * Path - * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - path: string; + id: string; /** - * File Size - * @description The size of the model in bytes. + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - file_size: number; + is_intermediate?: boolean; /** - * Name - * @description Name of the model. + * Use Cache + * @description Whether or not to use the cache + * @default true */ - name: string; + use_cache?: boolean; /** - * Description - * @description Model description + * Value + * @description The float value + * @default 0 */ - description: string | null; + value?: number; /** - * Source - * @description The original source of the model (path, URL or repo_id). + * type + * @default float + * @constant */ - source: string; - /** @description The type of source */ - source_type: components["schemas"]["ModelSourceType"]; + type: "float"; + }; + /** + * Float Range + * @description Creates a range + */ + FloatLinearRangeInvocation: { /** - * Source Api Response - * @description The original API response from the source, as stringified JSON. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - source_api_response: string | null; + id: string; /** - * Cover Image - * @description Url for image to preview model + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - cover_image: string | null; + is_intermediate?: boolean; /** - * Submodels - * @description Loadable submodels in this model + * Use Cache + * @description Whether or not to use the cache + * @default true */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + use_cache?: boolean; /** - * Usage Info - * @description Usage information for this model + * Start + * @description The first value of the range + * @default 5 */ - usage_info: string | null; + start?: number; /** - * Type - * @default flux_redux - * @constant + * Stop + * @description The last value of the range + * @default 10 */ - type: "flux_redux"; + stop?: number; /** - * Format - * @default checkpoint - * @constant + * Steps + * @description number of values to interpolate over (including start and stop) + * @default 30 */ - format: "checkpoint"; + steps?: number; /** - * Base - * @default flux + * type + * @default float_range * @constant */ - base: "flux"; + type: "float_range"; }; /** - * FLUX Redux - * @description Runs a FLUX Redux model to generate a conditioning tensor. + * Float Math + * @description Performs floating point math. */ - FluxReduxInvocation: { + FloatMathInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -8982,69 +8735,53 @@ export type components = { */ use_cache?: boolean; /** - * @description The FLUX Redux image prompt. - * @default null - */ - image?: components["schemas"]["ImageField"] | null; - /** - * @description The bool mask associated with this FLUX Redux image prompt. Excluded regions should be set to False, included regions should be set to True. - * @default null - */ - mask?: components["schemas"]["TensorField"] | null; - /** - * FLUX Redux Model - * @description The FLUX Redux model to use. - * @default null + * Operation + * @description The operation to perform + * @default ADD + * @enum {string} */ - redux_model?: components["schemas"]["ModelIdentifierField"] | null; + operation?: "ADD" | "SUB" | "MUL" | "DIV" | "EXP" | "ABS" | "SQRT" | "MIN" | "MAX"; /** - * Downsampling Factor - * @description Redux Downsampling Factor (1-9) + * A + * @description The first number * @default 1 */ - downsampling_factor?: number; - /** - * Downsampling Function - * @description Redux Downsampling Function - * @default area - * @enum {string} - */ - downsampling_function?: "nearest" | "bilinear" | "bicubic" | "area" | "nearest-exact"; + a?: number; /** - * Weight - * @description Redux weight (0.0-1.0) + * B + * @description The second number * @default 1 */ - weight?: number; + b?: number; /** * type - * @default flux_redux + * @default float_math * @constant */ - type: "flux_redux"; + type: "float_math"; }; /** - * FluxReduxOutput - * @description The conditioning output of a FLUX Redux invocation. + * FloatOutput + * @description Base class for nodes that output a single float */ - FluxReduxOutput: { + FloatOutput: { /** - * Conditioning - * @description FLUX Redux conditioning tensor + * Value + * @description The output float */ - redux_cond: components["schemas"]["FluxReduxConditioningField"]; + value: number; /** * type - * @default flux_redux_output + * @default float_output * @constant */ - type: "flux_redux_output"; + type: "float_output"; }; /** - * Prompt - FLUX - * @description Encodes and preps a prompt for a flux image. + * Float To Integer + * @description Rounds a float number to (a multiple of) an integer. */ - FluxTextEncoderInvocation: { + FloatToIntegerInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9063,95 +8800,83 @@ export type components = { */ use_cache?: boolean; /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null - */ - clip?: components["schemas"]["CLIPField"] | null; - /** - * T5Encoder - * @description T5 tokenizer and text encoder - * @default null - */ - t5_encoder?: components["schemas"]["T5EncoderField"] | null; - /** - * T5 Max Seq Len - * @description Max sequence length for the T5 encoder. Expected to be 256 for FLUX schnell models and 512 for FLUX dev models. - * @default null + * Value + * @description The value to round + * @default 0 */ - t5_max_seq_len?: (256 | 512) | null; + value?: number; /** - * Prompt - * @description Text prompt to encode. - * @default null + * Multiple of + * @description The multiple to round to + * @default 1 */ - prompt?: string | null; + multiple?: number; /** - * @description A mask defining the region that this conditioning prompt applies to. - * @default null + * Method + * @description The method to use for rounding + * @default Nearest + * @enum {string} */ - mask?: components["schemas"]["TensorField"] | null; + method?: "Nearest" | "Floor" | "Ceiling" | "Truncate"; /** * type - * @default flux_text_encoder + * @default float_to_int * @constant */ - type: "flux_text_encoder"; + type: "float_to_int"; }; /** - * Latents to Image - FLUX - * @description Generates an image from latents. + * FluxConditioningCollectionOutput + * @description Base class for nodes that output a collection of conditioning tensors */ - FluxVaeDecodeInvocation: { - /** - * @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; + FluxConditioningCollectionOutput: { /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Collection + * @description The output conditioning tensors */ - is_intermediate?: boolean; + collection: components["schemas"]["FluxConditioningField"][]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * type + * @default flux_conditioning_collection_output + * @constant */ - use_cache?: boolean; + type: "flux_conditioning_collection_output"; + }; + /** + * FluxConditioningField + * @description A conditioning tensor primitive value + */ + FluxConditioningField: { /** - * @description Latents tensor - * @default null + * Conditioning Name + * @description The name of conditioning tensor */ - latents?: components["schemas"]["LatentsField"] | null; + conditioning_name: string; /** - * @description VAE + * @description The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True. * @default null */ - vae?: components["schemas"]["VAEField"] | null; + mask?: components["schemas"]["TensorField"] | null; + }; + /** + * FluxConditioningOutput + * @description Base class for nodes that output a single conditioning tensor + */ + FluxConditioningOutput: { + /** @description Conditioning tensor */ + conditioning: components["schemas"]["FluxConditioningField"]; /** * type - * @default flux_vae_decode + * @default flux_conditioning_output * @constant */ - type: "flux_vae_decode"; + type: "flux_conditioning_output"; }; /** - * Image to Latents - FLUX - * @description Encodes an image into latents. + * Control LoRA - FLUX + * @description LoRA model and Image to use with FLUX transformer generation. */ - FluxVaeEncodeInvocation: { + FluxControlLoRALoaderInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9169,78 +8894,91 @@ export type components = { * @default true */ use_cache?: boolean; + /** + * Control LoRA + * @description Control LoRA model to load + * @default null + */ + lora?: components["schemas"]["ModelIdentifierField"] | null; /** * @description The image to encode. * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * @description VAE - * @default null + * Weight + * @description The weight of the LoRA. + * @default 1 */ - vae?: components["schemas"]["VAEField"] | null; + weight?: number; /** * type - * @default flux_vae_encode + * @default flux_control_lora_loader * @constant */ - type: "flux_vae_encode"; + type: "flux_control_lora_loader"; }; /** - * FluxVariantType - * @enum {string} + * FluxControlLoRALoaderOutput + * @description Flux Control LoRA Loader Output */ - FluxVariantType: "schnell" | "dev" | "dev_fill"; - /** FoundModel */ - FoundModel: { + FluxControlLoRALoaderOutput: { /** - * Path - * @description Path to the model + * Flux Control LoRA + * @description Control LoRAs to apply on model loading + * @default null */ - path: string; + control_lora: components["schemas"]["ControlLoRAField"]; /** - * Is Installed - * @description Whether or not the model is already installed + * type + * @default flux_control_lora_loader_output + * @constant */ - is_installed: boolean; + type: "flux_control_lora_loader_output"; }; - /** - * FreeUConfig - * @description Configuration for the FreeU hyperparameters. - * - https://huggingface.co/docs/diffusers/main/en/using-diffusers/freeu - * - https://github.com/ChenyangSi/FreeU - */ - FreeUConfig: { + /** FluxControlNetField */ + FluxControlNetField: { + /** @description The control image */ + image: components["schemas"]["ImageField"]; + /** @description The ControlNet model to use */ + control_model: components["schemas"]["ModelIdentifierField"]; /** - * S1 - * @description Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. + * Control Weight + * @description The weight given to the ControlNet + * @default 1 */ - s1: number; + control_weight?: number | number[]; /** - * S2 - * @description Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. + * Begin Step Percent + * @description When the ControlNet is first applied (% of total steps) + * @default 0 */ - s2: number; + begin_step_percent?: number; /** - * B1 - * @description Scaling factor for stage 1 to amplify the contributions of backbone features. + * End Step Percent + * @description When the ControlNet is last applied (% of total steps) + * @default 1 */ - b1: number; + end_step_percent?: number; /** - * B2 - * @description Scaling factor for stage 2 to amplify the contributions of backbone features. + * Resize Mode + * @description The resize mode to use + * @default just_resize + * @enum {string} */ - b2: number; + resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; + /** + * Instantx Control Mode + * @description The control mode for InstantX ControlNet union models. Ignored for other ControlNet models. The standard mapping is: canny (0), tile (1), depth (2), blur (3), pose (4), gray (5), low quality (6). Negative values will be treated as 'None'. + * @default -1 + */ + instantx_control_mode?: number | null; }; /** - * Apply FreeU - SD1.5, SDXL - * @description Applies FreeU to the UNet. Suggested values (b1/b2/s1/s2): - * - * SD1.5: 1.2/1.4/0.9/0.2, - * SD2: 1.1/1.2/0.9/0.2, - * SDXL: 1.1/1.2/0.6/0.4, + * FLUX ControlNet + * @description Collect FLUX ControlNet info to pass to other nodes. */ - FreeUInvocation: { + FluxControlNetInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9259,47 +8997,72 @@ export type components = { */ use_cache?: boolean; /** - * UNet - * @description UNet (scheduler, LoRAs) + * @description The control image * @default null */ - unet?: components["schemas"]["UNetField"] | null; + image?: components["schemas"]["ImageField"] | null; /** - * B1 - * @description Scaling factor for stage 1 to amplify the contributions of backbone features. - * @default 1.2 + * @description ControlNet model to load + * @default null */ - b1?: number; + control_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * B2 - * @description Scaling factor for stage 2 to amplify the contributions of backbone features. - * @default 1.4 + * Control Weight + * @description The weight given to the ControlNet + * @default 1 */ - b2?: number; + control_weight?: number | number[]; /** - * S1 - * @description Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. - * @default 0.9 + * Begin Step Percent + * @description When the ControlNet is first applied (% of total steps) + * @default 0 */ - s1?: number; + begin_step_percent?: number; /** - * S2 - * @description Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. - * @default 0.2 + * End Step Percent + * @description When the ControlNet is last applied (% of total steps) + * @default 1 */ - s2?: number; + end_step_percent?: number; /** - * type - * @default freeu - * @constant - */ - type: "freeu"; + * Resize Mode + * @description The resize mode used + * @default just_resize + * @enum {string} + */ + resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; + /** + * Instantx Control Mode + * @description The control mode for InstantX ControlNet union models. Ignored for other ControlNet models. The standard mapping is: canny (0), tile (1), depth (2), blur (3), pose (4), gray (5), low quality (6). Negative values will be treated as 'None'. + * @default -1 + */ + instantx_control_mode?: number | null; + /** + * type + * @default flux_controlnet + * @constant + */ + type: "flux_controlnet"; }; /** - * Get Image Mask Bounding Box - * @description Gets the bounding box of the given mask image. + * FluxControlNetOutput + * @description FLUX ControlNet info */ - GetMaskBoundingBoxInvocation: { + FluxControlNetOutput: { + /** @description ControlNet(s) to apply */ + control: components["schemas"]["FluxControlNetField"]; + /** + * type + * @default flux_controlnet_output + * @constant + */ + type: "flux_controlnet_output"; + }; + /** + * FLUX Denoise + * @description Run denoising process with a FLUX transformer model. + */ + FluxDenoiseInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9318,193 +9081,153 @@ export type components = { */ use_cache?: boolean; /** - * @description The mask to crop. + * @description Latents tensor * @default null */ - mask?: components["schemas"]["ImageField"] | null; + latents?: components["schemas"]["LatentsField"] | null; /** - * Margin - * @description Margin to add to the bounding box. + * @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 */ - margin?: number; + denoising_start?: number; /** - * @description Color of the mask in the image. - * @default { - * "r": 255, - * "g": 255, - * "b": 255, - * "a": 255 - * } + * Denoising End + * @description When to stop denoising, expressed a percentage of total steps + * @default 1 */ - mask_color?: components["schemas"]["ColorField"]; + denoising_end?: number; /** - * type - * @default get_image_mask_bounding_box - * @constant + * Add Noise + * @description Add noise based on denoising start. + * @default true */ - type: "get_image_mask_bounding_box"; - }; - /** GlmEncoderField */ - GlmEncoderField: { - /** @description Info to load tokenizer submodel */ - tokenizer: components["schemas"]["ModelIdentifierField"]; - /** @description Info to load text_encoder submodel */ - text_encoder: components["schemas"]["ModelIdentifierField"]; - }; - /** - * GradientMaskOutput - * @description Outputs a denoise mask and an image representing the total gradient of the mask. - */ - GradientMaskOutput: { - /** @description Mask for denoise model run. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved. */ - denoise_mask: components["schemas"]["DenoiseMaskField"]; - /** @description Image representing the total gradient area of the mask. For paste-back purposes. */ - expanded_mask_area: components["schemas"]["ImageField"]; + add_noise?: boolean; /** - * type - * @default gradient_mask_output - * @constant + * Transformer + * @description Flux model (Transformer) to load + * @default null */ - type: "gradient_mask_output"; - }; - /** Graph */ - Graph: { + transformer?: components["schemas"]["TransformerField"] | null; /** - * Id - * @description The id of this graph + * Control LoRA + * @description Control LoRA model to load + * @default null */ - id?: string; + control_lora?: components["schemas"]["ControlLoRAField"] | null; /** - * Nodes - * @description The nodes in this graph + * Positive Text Conditioning + * @description Positive conditioning tensor + * @default null */ - nodes?: { - [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | 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"]["CannyEdgeDetectionInvocation"] | 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"]["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"]["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"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | 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"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["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"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | 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"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"]; - }; + positive_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; /** - * Edges - * @description The connections between nodes and their fields in this graph + * Negative Text Conditioning + * @description Negative conditioning tensor. Can be None if cfg_scale is 1.0. + * @default null */ - edges?: components["schemas"]["Edge"][]; - }; - /** - * GraphExecutionState - * @description Tracks the state of a graph execution - */ - GraphExecutionState: { + negative_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; /** - * Id - * @description The id of the execution state + * Redux Conditioning + * @description FLUX Redux conditioning tensor. + * @default null */ - id: string; - /** @description The graph being executed */ - graph: components["schemas"]["Graph"]; - /** @description The expanded graph of activated and executed nodes */ - execution_graph: components["schemas"]["Graph"]; + redux_conditioning?: components["schemas"]["FluxReduxConditioningField"] | components["schemas"]["FluxReduxConditioningField"][] | null; /** - * Executed - * @description The set of node ids that have been executed + * @description FLUX Fill conditioning. + * @default null */ - executed: string[]; + fill_conditioning?: components["schemas"]["FluxFillConditioningField"] | null; /** - * Executed History - * @description The list of node ids that have been executed, in order of execution + * CFG Scale + * @description Classifier-Free Guidance scale + * @default 1 */ - executed_history: string[]; + cfg_scale?: number | number[]; /** - * Results - * @description The results of node executions + * CFG Scale Start Step + * @description Index of the first step to apply cfg_scale. Negative indices count backwards from the the last step (e.g. a value of -1 refers to the final step). + * @default 0 */ - results: { - [key: string]: 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"]["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"]["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"]["PairTileImageOutput"] | 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"]["VideoOutput"]; - }; + cfg_scale_start_step?: number; /** - * Errors - * @description Errors raised when executing nodes + * CFG Scale End Step + * @description Index of the last step to apply cfg_scale. Negative indices count backwards from the last step (e.g. a value of -1 refers to the final step). + * @default -1 */ - errors: { - [key: string]: string; - }; + cfg_scale_end_step?: number; /** - * Prepared Source Mapping - * @description The map of prepared nodes to original graph nodes + * Width + * @description Width of the generated image. + * @default 1024 */ - prepared_source_mapping: { - [key: string]: string; - }; + width?: number; /** - * Source Prepared Mapping - * @description The map of original graph nodes to prepared nodes + * Height + * @description Height of the generated image. + * @default 1024 */ - source_prepared_mapping: { - [key: string]: string[]; - }; - }; - /** - * Grounding DINO (Text Prompt Object Detection) - * @description Runs a Grounding DINO model. Performs zero-shot bounding-box object detection from a text prompt. - */ - GroundingDinoInvocation: { + height?: number; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Num Steps + * @description Number of diffusion steps. Recommended values are schnell: 4, dev: 50. + * @default 4 */ - id: string; + num_steps?: number; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Guidance + * @description The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell. + * @default 4 */ - is_intermediate?: boolean; + guidance?: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Seed + * @description Randomness seed for reproducibility. + * @default 0 */ - use_cache?: boolean; + seed?: number; /** - * Model - * @description The Grounding DINO model to use. + * Control + * @description ControlNet models. * @default null */ - model?: ("grounding-dino-tiny" | "grounding-dino-base") | null; + control?: components["schemas"]["FluxControlNetField"] | components["schemas"]["FluxControlNetField"][] | null; /** - * Prompt - * @description The prompt describing the object to segment. + * @description VAE * @default null */ - prompt?: string | null; + controlnet_vae?: components["schemas"]["VAEField"] | null; /** - * @description The image to segment. + * IP-Adapter + * @description IP-Adapter to apply * @default null */ - image?: components["schemas"]["ImageField"] | null; + ip_adapter?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; /** - * Detection Threshold - * @description The detection threshold for the Grounding DINO model. All detected bounding boxes with scores above this threshold will be returned. - * @default 0.3 + * Kontext Conditioning + * @description FLUX Kontext conditioning (reference image). + * @default null */ - detection_threshold?: number; + kontext_conditioning?: components["schemas"]["FluxKontextConditioningField"] | components["schemas"]["FluxKontextConditioningField"][] | null; /** * type - * @default grounding_dino + * @default flux_denoise * @constant */ - type: "grounding_dino"; + type: "flux_denoise"; }; /** - * HED Edge Detection - * @description Geneartes an edge map using the HED (softedge) model. + * FLUX Denoise + Metadata + * @description Run denoising process with a FLUX transformer model + metadata. */ - HEDEdgeDetectionInvocation: { + FluxDenoiseLatentsMetaInvocation: { /** - * @description The board to save the image to - * @default null - */ - board?: components["schemas"]["BoardField"] | null; - /** - * @description Optional metadata to be saved with the image + * @description Optional metadata to be saved with the image * @default null */ metadata?: components["schemas"]["MetadataField"] | null; @@ -9526,59 +9249,161 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to process + * @description Latents tensor * @default null */ - image?: components["schemas"]["ImageField"] | null; + latents?: components["schemas"]["LatentsField"] | null; /** - * Scribble - * @description Whether or not to use scribble mode - * @default false + * @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 */ - scribble?: boolean; + denoise_mask?: components["schemas"]["DenoiseMaskField"] | null; /** - * type - * @default hed_edge_detection - * @constant + * Denoising Start + * @description When to start denoising, expressed a percentage of total steps + * @default 0 */ - type: "hed_edge_detection"; - }; - /** - * HFModelSource - * @description A HuggingFace repo_id with optional variant, sub-folder and access token. - * Note that the variant option, if not provided to the constructor, will default to fp16, which is - * what people (almost) always want. - */ - HFModelSource: { - /** Repo Id */ - repo_id: string; - /** @default fp16 */ - variant?: components["schemas"]["ModelRepoVariant"] | null; - /** Subfolder */ - subfolder?: string | null; - /** Access Token */ - access_token?: string | null; + denoising_start?: number; /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} + * Denoising End + * @description When to stop denoising, expressed a percentage of total steps + * @default 1 */ - type: "hf"; + denoising_end?: number; + /** + * Add Noise + * @description Add noise based on denoising start. + * @default true + */ + add_noise?: boolean; + /** + * Transformer + * @description Flux model (Transformer) to load + * @default null + */ + transformer?: components["schemas"]["TransformerField"] | null; + /** + * Control LoRA + * @description Control LoRA model to load + * @default null + */ + control_lora?: components["schemas"]["ControlLoRAField"] | null; + /** + * Positive Text Conditioning + * @description Positive conditioning tensor + * @default null + */ + positive_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; + /** + * Negative Text Conditioning + * @description Negative conditioning tensor. Can be None if cfg_scale is 1.0. + * @default null + */ + negative_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; + /** + * Redux Conditioning + * @description FLUX Redux conditioning tensor. + * @default null + */ + redux_conditioning?: components["schemas"]["FluxReduxConditioningField"] | components["schemas"]["FluxReduxConditioningField"][] | null; + /** + * @description FLUX Fill conditioning. + * @default null + */ + fill_conditioning?: components["schemas"]["FluxFillConditioningField"] | null; + /** + * CFG Scale + * @description Classifier-Free Guidance scale + * @default 1 + */ + cfg_scale?: number | number[]; + /** + * CFG Scale Start Step + * @description Index of the first step to apply cfg_scale. Negative indices count backwards from the the last step (e.g. a value of -1 refers to the final step). + * @default 0 + */ + cfg_scale_start_step?: number; + /** + * CFG Scale End Step + * @description Index of the last step to apply cfg_scale. Negative indices count backwards from the last step (e.g. a value of -1 refers to the final step). + * @default -1 + */ + cfg_scale_end_step?: number; + /** + * Width + * @description Width of the generated image. + * @default 1024 + */ + width?: number; + /** + * Height + * @description Height of the generated image. + * @default 1024 + */ + height?: number; + /** + * Num Steps + * @description Number of diffusion steps. Recommended values are schnell: 4, dev: 50. + * @default 4 + */ + num_steps?: number; + /** + * Guidance + * @description The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell. + * @default 4 + */ + guidance?: number; + /** + * Seed + * @description Randomness seed for reproducibility. + * @default 0 + */ + seed?: number; + /** + * Control + * @description ControlNet models. + * @default null + */ + control?: components["schemas"]["FluxControlNetField"] | components["schemas"]["FluxControlNetField"][] | null; + /** + * @description VAE + * @default null + */ + controlnet_vae?: components["schemas"]["VAEField"] | null; + /** + * IP-Adapter + * @description IP-Adapter to apply + * @default null + */ + ip_adapter?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; + /** + * Kontext Conditioning + * @description FLUX Kontext conditioning (reference image). + * @default null + */ + kontext_conditioning?: components["schemas"]["FluxKontextConditioningField"] | components["schemas"]["FluxKontextConditioningField"][] | null; + /** + * type + * @default flux_denoise_meta + * @constant + */ + type: "flux_denoise_meta"; }; /** - * HFTokenStatus - * @enum {string} + * FluxFillConditioningField + * @description A FLUX Fill conditioning field. */ - HFTokenStatus: "valid" | "invalid" | "unknown"; - /** HTTPValidationError */ - HTTPValidationError: { - /** Detail */ - detail?: components["schemas"]["ValidationError"][]; + FluxFillConditioningField: { + /** @description The FLUX Fill reference image. */ + image: components["schemas"]["ImageField"]; + /** @description The FLUX Fill inpaint mask. */ + mask: components["schemas"]["TensorField"]; }; /** - * Heuristic Resize - * @description Resize an image using a heuristic method. Preserves edge maps. + * FLUX Fill Conditioning + * @description Prepare the FLUX Fill conditioning data. */ - HeuristicResizeInvocation: { + FluxFillInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9597,218 +9422,220 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to resize + * @description The FLUX Fill reference image. * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Width - * @description The width to resize to (px) - * @default 512 - */ - width?: number; - /** - * Height - * @description The height to resize to (px) - * @default 512 + * @description The bool inpainting mask. Excluded regions should be set to False, included regions should be set to True. + * @default null */ - height?: number; + mask?: components["schemas"]["TensorField"] | null; /** * type - * @default heuristic_resize + * @default flux_fill * @constant */ - type: "heuristic_resize"; + type: "flux_fill"; }; /** - * HuggingFaceMetadata - * @description Extended metadata fields provided by HuggingFace. + * FluxFillOutput + * @description The conditioning output of a FLUX Fill invocation. */ - HuggingFaceMetadata: { - /** - * Name - * @description model's name - */ - name: string; + FluxFillOutput: { /** - * Files - * @description model files and their sizes + * Conditioning + * @description FLUX Redux conditioning tensor */ - files?: components["schemas"]["RemoteModelFile"][]; + fill_cond: components["schemas"]["FluxFillConditioningField"]; /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} + * type + * @default flux_fill_output + * @constant */ - type: "huggingface"; + type: "flux_fill_output"; + }; + /** + * FLUX IP-Adapter + * @description Collects FLUX IP-Adapter info to pass to other nodes. + */ + FluxIPAdapterInvocation: { /** * Id - * @description The HF model id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ id: string; /** - * Api Response - * @description Response from the HF API as stringified JSON - */ - api_response?: string | null; - /** - * Is Diffusers - * @description Whether the metadata is for a Diffusers format model + * Is Intermediate + * @description Whether or not this is an intermediate invocation. * @default false */ - is_diffusers?: boolean; + is_intermediate?: boolean; /** - * Ckpt Urls - * @description URLs for all checkpoint format models in the metadata + * Use Cache + * @description Whether or not to use the cache + * @default true */ - ckpt_urls?: string[] | null; - }; - /** HuggingFaceModels */ - HuggingFaceModels: { + use_cache?: boolean; /** - * Urls - * @description URLs for all checkpoint format models in the metadata - */ - urls: string[] | null; - /** - * Is Diffusers - * @description Whether the metadata is for a Diffusers format model + * @description The IP-Adapter image prompt(s). + * @default null */ - is_diffusers: boolean; - }; - /** - * IPAdapterCheckpointConfig - * @description Model config for IP Adapter checkpoint format models. - */ - IPAdapterCheckpointConfig: { + image?: components["schemas"]["ImageField"] | null; /** - * Key - * @description A unique key for this model. + * IP-Adapter Model + * @description The IP-Adapter model. + * @default null */ - key: string; + ip_adapter_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Hash - * @description The hash of the model file(s). + * Clip Vision Model + * @description CLIP Vision model to use. + * @default ViT-L + * @constant */ - hash: string; + clip_vision_model?: "ViT-L"; /** - * Path - * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + * Weight + * @description The weight given to the IP-Adapter + * @default 1 */ - path: string; + weight?: number | number[]; /** - * File Size - * @description The size of the model in bytes. + * Begin Step Percent + * @description When the IP-Adapter is first applied (% of total steps) + * @default 0 */ - file_size: number; + begin_step_percent?: number; /** - * Name - * @description Name of the model. + * End Step Percent + * @description When the IP-Adapter is last applied (% of total steps) + * @default 1 */ - name: string; + end_step_percent?: number; /** - * Description - * @description Model description + * type + * @default flux_ip_adapter + * @constant */ - description: string | null; + type: "flux_ip_adapter"; + }; + /** + * FLUX Kontext Image Prep + * @description Prepares an image or images for use with FLUX Kontext. The first/single image is resized to the nearest + * preferred Kontext resolution. All other images are concatenated horizontally, maintaining their aspect ratio. + */ + FluxKontextConcatenateImagesInvocation: { /** - * Source - * @description The original source of the model (path, URL or repo_id). + * @description The board to save the image to + * @default null */ - source: string; - /** @description The type of source */ - source_type: components["schemas"]["ModelSourceType"]; + board?: components["schemas"]["BoardField"] | null; /** - * Source Api Response - * @description The original API response from the source, as stringified JSON. + * @description Optional metadata to be saved with the image + * @default null */ - source_api_response: string | null; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Cover Image - * @description Url for image to preview model + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - cover_image: string | null; + id: string; /** - * Submodels - * @description Loadable submodels in this model + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + is_intermediate?: boolean; /** - * Usage Info - * @description Usage information for this model + * Use Cache + * @description Whether or not to use the cache + * @default true */ - usage_info: string | null; + use_cache?: boolean; /** - * Type - * @default ip_adapter - * @constant + * Images + * @description The images to concatenate + * @default null */ - type: "ip_adapter"; + images?: components["schemas"]["ImageField"][] | null; /** - * Base - * @enum {string} + * Use Preferred Resolution + * @description Use FLUX preferred resolutions for the first image + * @default true */ - base: "sd-1" | "sd-2" | "sdxl" | "flux"; + use_preferred_resolution?: boolean; /** - * Format - * @default checkpoint + * type + * @default flux_kontext_image_prep * @constant */ - format: "checkpoint"; + type: "flux_kontext_image_prep"; }; - /** IPAdapterField */ - IPAdapterField: { + /** + * FluxKontextConditioningField + * @description A conditioning field for FLUX Kontext (reference image). + */ + FluxKontextConditioningField: { + /** @description The Kontext reference image. */ + image: components["schemas"]["ImageField"]; + }; + /** + * Kontext Conditioning - FLUX + * @description Prepares a reference image for FLUX Kontext conditioning. + */ + FluxKontextInvocation: { /** - * Image - * @description The IP-Adapter image prompt(s). + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - image: components["schemas"]["ImageField"] | components["schemas"]["ImageField"][]; - /** @description The IP-Adapter model to use. */ - ip_adapter_model: components["schemas"]["ModelIdentifierField"]; - /** @description The name of the CLIP image encoder model. */ - image_encoder_model: components["schemas"]["ModelIdentifierField"]; + id: string; /** - * Weight - * @description The weight given to the IP-Adapter. - * @default 1 + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - weight?: number | number[]; + is_intermediate?: boolean; /** - * Target Blocks - * @description The IP Adapter blocks to apply - * @default [] + * Use Cache + * @description Whether or not to use the cache + * @default true */ - target_blocks?: string[]; + use_cache?: boolean; /** - * Method - * @description Weight apply method - * @default full + * @description The Kontext reference image. + * @default null */ - method?: string; + image?: components["schemas"]["ImageField"] | null; /** - * Begin Step Percent - * @description When the IP-Adapter is first applied (% of total steps) - * @default 0 + * type + * @default flux_kontext + * @constant */ - begin_step_percent?: number; + type: "flux_kontext"; + }; + /** + * FluxKontextOutput + * @description The conditioning output of a FLUX Kontext invocation. + */ + FluxKontextOutput: { /** - * End Step Percent - * @description When the IP-Adapter is last applied (% of total steps) - * @default 1 + * Kontext Conditioning + * @description FLUX Kontext conditioning (reference image) */ - end_step_percent?: number; + kontext_cond: components["schemas"]["FluxKontextConditioningField"]; /** - * @description The bool mask associated with this IP-Adapter. Excluded regions should be set to False, included regions should be set to True. - * @default null + * type + * @default flux_kontext_output + * @constant */ - mask?: components["schemas"]["TensorField"] | null; + type: "flux_kontext_output"; }; /** - * IP-Adapter - SD1.5, SDXL - * @description Collects IP-Adapter info to pass to other nodes. + * Apply LoRA - FLUX + * @description Apply a LoRA model to a FLUX transformer and/or text encoder. */ - IPAdapterInvocation: { + FluxLoRALoaderInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9827,201 +9654,175 @@ export type components = { */ use_cache?: boolean; /** - * Image - * @description The IP-Adapter image prompt(s). - * @default null - */ - image?: components["schemas"]["ImageField"] | components["schemas"]["ImageField"][] | null; - /** - * IP-Adapter Model - * @description The IP-Adapter model. + * LoRA + * @description LoRA model to load * @default null */ - ip_adapter_model?: components["schemas"]["ModelIdentifierField"] | null; - /** - * Clip Vision Model - * @description CLIP Vision model to use. Overrides model settings. Mandatory for checkpoint models. - * @default ViT-H - * @enum {string} - */ - clip_vision_model?: "ViT-H" | "ViT-G" | "ViT-L"; + lora?: components["schemas"]["ModelIdentifierField"] | null; /** * Weight - * @description The weight given to the IP-Adapter - * @default 1 - */ - weight?: number | number[]; - /** - * Method - * @description The method to apply the IP-Adapter - * @default full - * @enum {string} + * @description The weight at which the LoRA is applied to each model + * @default 0.75 */ - method?: "full" | "style" | "composition" | "style_strong" | "style_precise"; + weight?: number; /** - * Begin Step Percent - * @description When the IP-Adapter is first applied (% of total steps) - * @default 0 + * FLUX Transformer + * @description Transformer + * @default null */ - begin_step_percent?: number; + transformer?: components["schemas"]["TransformerField"] | null; /** - * End Step Percent - * @description When the IP-Adapter is last applied (% of total steps) - * @default 1 + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null */ - end_step_percent?: number; + clip?: components["schemas"]["CLIPField"] | null; /** - * @description A mask defining the region that this IP-Adapter applies to. + * T5 Encoder + * @description T5 tokenizer and text encoder * @default null */ - mask?: components["schemas"]["TensorField"] | null; + t5_encoder?: components["schemas"]["T5EncoderField"] | null; /** * type - * @default ip_adapter + * @default flux_lora_loader * @constant */ - type: "ip_adapter"; + type: "flux_lora_loader"; }; /** - * IPAdapterInvokeAIConfig - * @description Model config for IP Adapter diffusers format models. + * FluxLoRALoaderOutput + * @description FLUX LoRA Loader Output */ - IPAdapterInvokeAIConfig: { - /** - * 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; + FluxLoRALoaderOutput: { /** - * File Size - * @description The size of the model in bytes. + * FLUX Transformer + * @description Transformer + * @default null */ - file_size: number; + transformer: components["schemas"]["TransformerField"] | null; /** - * Name - * @description Name of the model. + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null */ - name: string; + clip: components["schemas"]["CLIPField"] | null; /** - * Description - * @description Model description + * T5 Encoder + * @description T5 tokenizer and text encoder + * @default null */ - description: string | null; + t5_encoder: components["schemas"]["T5EncoderField"] | null; /** - * Source - * @description The original source of the model (path, URL or repo_id). + * type + * @default flux_lora_loader_output + * @constant */ - source: string; - /** @description The type of source */ - source_type: components["schemas"]["ModelSourceType"]; + type: "flux_lora_loader_output"; + }; + /** + * Main Model - FLUX + * @description Loads a flux base model, outputting its submodels. + */ + FluxModelLoaderInvocation: { /** - * Source Api Response - * @description The original API response from the source, as stringified JSON. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - source_api_response: string | null; + id: string; /** - * Cover Image - * @description Url for image to preview model + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - cover_image: string | null; + is_intermediate?: boolean; /** - * Submodels - * @description Loadable submodels in this model + * Use Cache + * @description Whether or not to use the cache + * @default true */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + use_cache?: boolean; + /** @description Flux model (Transformer) to load */ + model: components["schemas"]["ModelIdentifierField"]; /** - * Usage Info - * @description Usage information for this model + * T5 Encoder + * @description T5 tokenizer and text encoder */ - usage_info: string | null; + t5_encoder_model: components["schemas"]["ModelIdentifierField"]; /** - * Type - * @default ip_adapter - * @constant + * CLIP Embed + * @description CLIP Embed loader */ - type: "ip_adapter"; + clip_embed_model: components["schemas"]["ModelIdentifierField"]; /** - * Base - * @enum {string} + * VAE + * @description VAE model to load + * @default null */ - base: "sd-1" | "sd-2" | "sdxl"; + vae_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Format - * @default invokeai + * type + * @default flux_model_loader * @constant */ - format: "invokeai"; - /** Image Encoder Model Id */ - image_encoder_model_id: string; + type: "flux_model_loader"; }; /** - * IPAdapterMetadataField - * @description IP Adapter Field, minus the CLIP Vision Encoder model + * FluxModelLoaderOutput + * @description Flux base model loader output */ - IPAdapterMetadataField: { - /** @description The IP-Adapter image prompt. */ - image: components["schemas"]["ImageField"]; - /** @description The IP-Adapter model. */ - ip_adapter_model: components["schemas"]["ModelIdentifierField"]; - /** - * Clip Vision Model - * @description The CLIP Vision model - * @enum {string} - */ - clip_vision_model: "ViT-L" | "ViT-H" | "ViT-G"; + FluxModelLoaderOutput: { /** - * Method - * @description Method to apply IP Weights with - * @enum {string} + * Transformer + * @description Transformer */ - method: "full" | "style" | "composition" | "style_strong" | "style_precise"; + transformer: components["schemas"]["TransformerField"]; /** - * Weight - * @description The weight given to the IP-Adapter + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count */ - weight: number | number[]; + clip: components["schemas"]["CLIPField"]; /** - * Begin Step Percent - * @description When the IP-Adapter is first applied (% of total steps) + * T5 Encoder + * @description T5 tokenizer and text encoder */ - begin_step_percent: number; + t5_encoder: components["schemas"]["T5EncoderField"]; /** - * End Step Percent - * @description When the IP-Adapter is last applied (% of total steps) + * VAE + * @description VAE */ - end_step_percent: number; - }; - /** IPAdapterOutput */ - IPAdapterOutput: { + vae: components["schemas"]["VAEField"]; /** - * IP-Adapter - * @description IP-Adapter to apply + * Max Seq Length + * @description The max sequence length to used for the T5 encoder. (256 for schnell transformer, 512 for dev transformer) + * @enum {integer} */ - ip_adapter: components["schemas"]["IPAdapterField"]; + max_seq_len: 256 | 512; /** * type - * @default ip_adapter_output + * @default flux_model_loader_output * @constant */ - type: "ip_adapter_output"; + type: "flux_model_loader_output"; }; /** - * Ideal Size - SD1.5, SDXL - * @description Calculates the ideal size for generation to avoid duplication + * FluxReduxConditioningField + * @description A FLUX Redux conditioning tensor primitive value */ - IdealSizeInvocation: { + FluxReduxConditioningField: { + /** @description The Redux image conditioning tensor. */ + conditioning: components["schemas"]["TensorField"]; + /** + * @description The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True. + * @default null + */ + mask?: components["schemas"]["TensorField"] | null; + }; + /** + * FLUX Redux + * @description Runs a FLUX Redux model to generate a conditioning tensor. + */ + FluxReduxInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -10040,62 +9841,69 @@ export type components = { */ use_cache?: boolean; /** - * Width - * @description Final image width - * @default 1024 + * @description The FLUX Redux image prompt. + * @default null */ - width?: number; + image?: components["schemas"]["ImageField"] | null; /** - * Height - * @description Final image height - * @default 576 + * @description The bool mask associated with this FLUX Redux image prompt. Excluded regions should be set to False, included regions should be set to True. + * @default null */ - height?: number; + mask?: components["schemas"]["TensorField"] | null; /** - * @description UNet (scheduler, LoRAs) + * FLUX Redux Model + * @description The FLUX Redux model to use. * @default null */ - unet?: components["schemas"]["UNetField"] | null; + redux_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Multiplier - * @description Amount to multiply the model's dimensions by when calculating the ideal size (may result in initial generation artifacts if too large) + * Downsampling Factor + * @description Redux Downsampling Factor (1-9) * @default 1 */ - multiplier?: number; + downsampling_factor?: number; + /** + * Downsampling Function + * @description Redux Downsampling Function + * @default area + * @enum {string} + */ + downsampling_function?: "nearest" | "bilinear" | "bicubic" | "area" | "nearest-exact"; + /** + * Weight + * @description Redux weight (0.0-1.0) + * @default 1 + */ + weight?: number; /** * type - * @default ideal_size + * @default flux_redux * @constant */ - type: "ideal_size"; + type: "flux_redux"; }; /** - * IdealSizeOutput - * @description Base class for invocations that output an image + * FluxReduxOutput + * @description The conditioning output of a FLUX Redux invocation. */ - IdealSizeOutput: { - /** - * Width - * @description The ideal width of the image (in pixels) - */ - width: number; + FluxReduxOutput: { /** - * Height - * @description The ideal height of the image (in pixels) + * Conditioning + * @description FLUX Redux conditioning tensor */ - height: number; + redux_cond: components["schemas"]["FluxReduxConditioningField"]; /** * type - * @default ideal_size_output + * @default flux_redux_output * @constant */ - type: "ideal_size_output"; + type: "flux_redux_output"; }; /** - * Image Batch - * @description Create a batched generation, where the workflow is executed once for each image in the batch. + * Prompt - FLUX + * @description Encodes and preps a prompt for a flux image. */ - ImageBatchInvocation: { + FluxTextEncoderInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -10114,99 +9922,46 @@ export type components = { */ use_cache?: boolean; /** - * Batch Group - * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. - * @default None - * @enum {string} - */ - batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; - /** - * Images - * @description The images to batch over + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count * @default null */ - images?: components["schemas"]["ImageField"][] | null; - /** - * type - * @default image_batch - * @constant - */ - type: "image_batch"; - }; - /** - * Blur Image - * @description Blurs an image - */ - ImageBlurInvocation: { + clip?: components["schemas"]["CLIPField"] | null; /** - * @description The board to save the image to + * T5Encoder + * @description T5 tokenizer and text encoder * @default null */ - board?: components["schemas"]["BoardField"] | null; + t5_encoder?: components["schemas"]["T5EncoderField"] | null; /** - * @description Optional metadata to be saved with the image + * T5 Max Seq Len + * @description Max sequence length for the T5 encoder. Expected to be 256 for FLUX schnell models and 512 for FLUX dev models. * @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; + t5_max_seq_len?: (256 | 512) | null; /** - * @description The image to blur + * Prompt + * @description Text prompt to encode. * @default null */ - image?: components["schemas"]["ImageField"] | null; - /** - * Radius - * @description The blur radius - * @default 8 - */ - radius?: number; + prompt?: string | null; /** - * Blur Type - * @description The type of blur - * @default gaussian - * @enum {string} + * @description A mask defining the region that this conditioning prompt applies to. + * @default null */ - blur_type?: "gaussian" | "box"; + mask?: components["schemas"]["TensorField"] | null; /** * type - * @default img_blur + * @default flux_text_encoder * @constant */ - type: "img_blur"; + type: "flux_text_encoder"; }; /** - * ImageCategory - * @description The category of an image. - * - * - GENERAL: The image is an output, init image, or otherwise an image without a specialized purpose. - * - MASK: The image is a mask image. - * - CONTROL: The image is a ControlNet control image. - * - USER: The image is a user-provide image. - * - OTHER: The image is some other type of image with a specialized purpose. To be used by external nodes. - * @enum {string} - */ - ImageCategory: "general" | "mask" | "control" | "user" | "other"; - /** - * Extract Image Channel - * @description Gets a channel from an image. + * Latents to Image - FLUX + * @description Generates an image from latents. */ - ImageChannelInvocation: { + FluxVaeDecodeInvocation: { /** * @description The board to save the image to * @default null @@ -10235,39 +9990,27 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to get the channel from + * @description Latents tensor * @default null */ - image?: components["schemas"]["ImageField"] | null; + latents?: components["schemas"]["LatentsField"] | null; /** - * Channel - * @description The channel to get - * @default A - * @enum {string} + * @description VAE + * @default null */ - channel?: "A" | "R" | "G" | "B"; + vae?: components["schemas"]["VAEField"] | null; /** * type - * @default img_chan + * @default flux_vae_decode * @constant */ - type: "img_chan"; + type: "flux_vae_decode"; }; /** - * Multiply Image Channel - * @description Scale a specific color channel of an image. + * Image to Latents - FLUX + * @description Encodes an image into latents. */ - ImageChannelMultiplyInvocation: { - /** - * @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; + FluxVaeEncodeInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -10286,50 +10029,77 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to adjust + * @description The image to encode. * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Channel - * @description Which channel to adjust + * @description VAE * @default null */ - channel?: ("Red (RGBA)" | "Green (RGBA)" | "Blue (RGBA)" | "Alpha (RGBA)" | "Cyan (CMYK)" | "Magenta (CMYK)" | "Yellow (CMYK)" | "Black (CMYK)" | "Hue (HSV)" | "Saturation (HSV)" | "Value (HSV)" | "Luminosity (LAB)" | "A (LAB)" | "B (LAB)" | "Y (YCbCr)" | "Cb (YCbCr)" | "Cr (YCbCr)") | null; + vae?: components["schemas"]["VAEField"] | null; /** - * Scale - * @description The amount to scale the channel by. - * @default 1 + * type + * @default flux_vae_encode + * @constant */ - scale?: number; + type: "flux_vae_encode"; + }; + /** + * FluxVariantType + * @enum {string} + */ + FluxVariantType: "schnell" | "dev" | "dev_fill"; + /** FoundModel */ + FoundModel: { /** - * Invert Channel - * @description Invert the channel after scaling - * @default false + * Path + * @description Path to the model */ - invert_channel?: boolean; + path: string; /** - * type - * @default img_channel_multiply - * @constant + * Is Installed + * @description Whether or not the model is already installed */ - type: "img_channel_multiply"; + is_installed: boolean; }; /** - * Offset Image Channel - * @description Add or subtract a value from a specific color channel of an image. + * FreeUConfig + * @description Configuration for the FreeU hyperparameters. + * - https://huggingface.co/docs/diffusers/main/en/using-diffusers/freeu + * - https://github.com/ChenyangSi/FreeU */ - ImageChannelOffsetInvocation: { + FreeUConfig: { /** - * @description The board to save the image to - * @default null + * S1 + * @description Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. */ - board?: components["schemas"]["BoardField"] | null; + s1: number; /** - * @description Optional metadata to be saved with the image - * @default null + * S2 + * @description Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. */ - metadata?: components["schemas"]["MetadataField"] | null; + s2: number; + /** + * B1 + * @description Scaling factor for stage 1 to amplify the contributions of backbone features. + */ + b1: number; + /** + * B2 + * @description Scaling factor for stage 2 to amplify the contributions of backbone features. + */ + b2: number; + }; + /** + * Apply FreeU - SD1.5, SDXL + * @description Applies FreeU to the UNet. Suggested values (b1/b2/s1/s2): + * + * SD1.5: 1.2/1.4/0.9/0.2, + * SD2: 1.1/1.2/0.9/0.2, + * SDXL: 1.1/1.2/0.6/0.4, + */ + FreeUInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -10348,34 +10118,47 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to adjust + * UNet + * @description UNet (scheduler, LoRAs) * @default null */ - image?: components["schemas"]["ImageField"] | null; + unet?: components["schemas"]["UNetField"] | null; /** - * Channel - * @description Which channel to adjust - * @default null + * B1 + * @description Scaling factor for stage 1 to amplify the contributions of backbone features. + * @default 1.2 */ - channel?: ("Red (RGBA)" | "Green (RGBA)" | "Blue (RGBA)" | "Alpha (RGBA)" | "Cyan (CMYK)" | "Magenta (CMYK)" | "Yellow (CMYK)" | "Black (CMYK)" | "Hue (HSV)" | "Saturation (HSV)" | "Value (HSV)" | "Luminosity (LAB)" | "A (LAB)" | "B (LAB)" | "Y (YCbCr)" | "Cb (YCbCr)" | "Cr (YCbCr)") | null; + b1?: number; /** - * Offset - * @description The amount to adjust the channel by - * @default 0 + * B2 + * @description Scaling factor for stage 2 to amplify the contributions of backbone features. + * @default 1.4 */ - offset?: number; + b2?: number; + /** + * S1 + * @description Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. + * @default 0.9 + */ + s1?: number; + /** + * S2 + * @description Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. + * @default 0.2 + */ + s2?: number; /** * type - * @default img_channel_offset + * @default freeu * @constant */ - type: "img_channel_offset"; + type: "freeu"; }; /** - * Image Collection Primitive - * @description A collection of image primitive values + * Get Image Mask Bounding Box + * @description Gets the bounding box of the given mask image. */ - ImageCollectionInvocation: { + GetMaskBoundingBoxInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -10394,50 +10177,134 @@ export type components = { */ use_cache?: boolean; /** - * Collection - * @description The collection of image values + * @description The mask to crop. * @default null */ - collection?: components["schemas"]["ImageField"][] | null; + mask?: components["schemas"]["ImageField"] | null; /** - * type - * @default image_collection - * @constant + * Margin + * @description Margin to add to the bounding box. + * @default 0 */ - type: "image_collection"; - }; - /** - * ImageCollectionOutput - * @description Base class for nodes that output a collection of images - */ - ImageCollectionOutput: { + margin?: number; /** - * Collection - * @description The output images + * @description Color of the mask in the image. + * @default { + * "r": 255, + * "g": 255, + * "b": 255, + * "a": 255 + * } */ - collection: components["schemas"]["ImageField"][]; + mask_color?: components["schemas"]["ColorField"]; /** * type - * @default image_collection_output + * @default get_image_mask_bounding_box * @constant */ - type: "image_collection_output"; + type: "get_image_mask_bounding_box"; + }; + /** GlmEncoderField */ + GlmEncoderField: { + /** @description Info to load tokenizer submodel */ + tokenizer: components["schemas"]["ModelIdentifierField"]; + /** @description Info to load text_encoder submodel */ + text_encoder: components["schemas"]["ModelIdentifierField"]; }; /** - * Convert Image Mode - * @description Converts an image to a different mode. + * GradientMaskOutput + * @description Outputs a denoise mask and an image representing the total gradient of the mask. */ - ImageConvertInvocation: { + GradientMaskOutput: { + /** @description Mask for denoise model run. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved. */ + denoise_mask: components["schemas"]["DenoiseMaskField"]; + /** @description Image representing the total gradient area of the mask. For paste-back purposes. */ + expanded_mask_area: components["schemas"]["ImageField"]; /** - * @description The board to save the image to - * @default null + * type + * @default gradient_mask_output + * @constant */ - board?: components["schemas"]["BoardField"] | null; + type: "gradient_mask_output"; + }; + /** Graph */ + Graph: { /** - * @description Optional metadata to be saved with the image - * @default null + * Id + * @description The id of this graph */ - metadata?: components["schemas"]["MetadataField"] | null; + id?: string; + /** + * Nodes + * @description The nodes in this graph + */ + nodes?: { + [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | 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"]["CannyEdgeDetectionInvocation"] | 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"]["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"]["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"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | 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"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["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"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | 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"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"]; + }; + /** + * Edges + * @description The connections between nodes and their fields in this graph + */ + edges?: components["schemas"]["Edge"][]; + }; + /** + * GraphExecutionState + * @description Tracks the state of a graph execution + */ + GraphExecutionState: { + /** + * Id + * @description The id of the execution state + */ + id: string; + /** @description The graph being executed */ + graph: components["schemas"]["Graph"]; + /** @description The expanded graph of activated and executed nodes */ + execution_graph: components["schemas"]["Graph"]; + /** + * Executed + * @description The set of node ids that have been executed + */ + executed: string[]; + /** + * Executed History + * @description The list of node ids that have been executed, in order of execution + */ + executed_history: string[]; + /** + * Results + * @description The results of node executions + */ + results: { + [key: string]: 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"]["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"]["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"]["PairTileImageOutput"] | 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"]["VideoOutput"]; + }; + /** + * Errors + * @description Errors raised when executing nodes + */ + errors: { + [key: string]: string; + }; + /** + * Prepared Source Mapping + * @description The map of prepared nodes to original graph nodes + */ + prepared_source_mapping: { + [key: string]: string; + }; + /** + * Source Prepared Mapping + * @description The map of original graph nodes to prepared nodes + */ + source_prepared_mapping: { + [key: string]: string[]; + }; + }; + /** + * Grounding DINO (Text Prompt Object Detection) + * @description Runs a Grounding DINO model. Performs zero-shot bounding-box object detection from a text prompt. + */ + GroundingDinoInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -10456,29 +10323,40 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to convert + * Model + * @description The Grounding DINO model to use. + * @default null + */ + model?: ("grounding-dino-tiny" | "grounding-dino-base") | null; + /** + * Prompt + * @description The prompt describing the object to segment. + * @default null + */ + prompt?: string | null; + /** + * @description The image to segment. * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Mode - * @description The mode to convert to - * @default L - * @enum {string} + * Detection Threshold + * @description The detection threshold for the Grounding DINO model. All detected bounding boxes with scores above this threshold will be returned. + * @default 0.3 */ - mode?: "L" | "RGB" | "RGBA" | "CMYK" | "YCbCr" | "LAB" | "HSV" | "I" | "F"; + detection_threshold?: number; /** * type - * @default img_conv + * @default grounding_dino * @constant */ - type: "img_conv"; + type: "grounding_dino"; }; /** - * Crop Image - * @description Crops an image to a specified box. The box can be outside of the image. + * HED Edge Detection + * @description Geneartes an edge map using the HED (softedge) model. */ - ImageCropInvocation: { + HEDEdgeDetectionInvocation: { /** * @description The board to save the image to * @default null @@ -10507,137 +10385,207 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to crop + * @description The image to process * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * X - * @description The left x coordinate of the crop rectangle - * @default 0 + * Scribble + * @description Whether or not to use scribble mode + * @default false */ - x?: number; + scribble?: boolean; /** - * Y - * @description The top y coordinate of the crop rectangle - * @default 0 + * type + * @default hed_edge_detection + * @constant */ - y?: number; + type: "hed_edge_detection"; + }; + /** + * HFModelSource + * @description A HuggingFace repo_id with optional variant, sub-folder and access token. + * Note that the variant option, if not provided to the constructor, will default to fp16, which is + * what people (almost) always want. + */ + HFModelSource: { + /** Repo Id */ + repo_id: string; + /** @default fp16 */ + variant?: components["schemas"]["ModelRepoVariant"] | null; + /** Subfolder */ + subfolder?: string | null; + /** Access Token */ + access_token?: string | null; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "hf"; + }; + /** + * HFTokenStatus + * @enum {string} + */ + HFTokenStatus: "valid" | "invalid" | "unknown"; + /** HTTPValidationError */ + HTTPValidationError: { + /** Detail */ + detail?: components["schemas"]["ValidationError"][]; + }; + /** + * Heuristic Resize + * @description Resize an image using a heuristic method. Preserves edge maps. + */ + HeuristicResizeInvocation: { + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to resize + * @default null + */ + image?: components["schemas"]["ImageField"] | null; /** * Width - * @description The width of the crop rectangle + * @description The width to resize to (px) * @default 512 */ width?: number; /** * Height - * @description The height of the crop rectangle + * @description The height to resize to (px) * @default 512 */ height?: number; /** * type - * @default img_crop + * @default heuristic_resize * @constant */ - type: "img_crop"; + type: "heuristic_resize"; }; /** - * ImageDTO - * @description Deserialized image record, enriched for the frontend. + * HuggingFaceMetadata + * @description Extended metadata fields provided by HuggingFace. */ - ImageDTO: { + HuggingFaceMetadata: { /** - * Image Name - * @description The unique name of the image. + * Name + * @description model's name */ - image_name: string; + name: string; /** - * Image Url - * @description The URL of the image. + * Files + * @description model files and their sizes */ - image_url: string; + files?: components["schemas"]["RemoteModelFile"][]; /** - * Thumbnail Url - * @description The URL of the image's thumbnail. + * @description discriminator enum property added by openapi-typescript + * @enum {string} */ - thumbnail_url: string; - /** @description The type of the image. */ - image_origin: components["schemas"]["ResourceOrigin"]; - /** @description The category of the image. */ - image_category: components["schemas"]["ImageCategory"]; + type: "huggingface"; /** - * Width - * @description The width of the image in px. + * Id + * @description The HF model id */ - width: number; + id: string; /** - * Height - * @description The height of the image in px. + * Api Response + * @description Response from the HF API as stringified JSON */ - height: number; + api_response?: string | null; /** - * Created At - * @description The created timestamp of the image. + * Is Diffusers + * @description Whether the metadata is for a Diffusers format model + * @default false */ - created_at: string; + is_diffusers?: boolean; /** - * Updated At - * @description The updated timestamp of the image. + * Ckpt Urls + * @description URLs for all checkpoint format models in the metadata */ - updated_at: string; + ckpt_urls?: string[] | null; + }; + /** HuggingFaceModels */ + HuggingFaceModels: { /** - * Deleted At - * @description The deleted timestamp of the image. + * Urls + * @description URLs for all checkpoint format models in the metadata */ - deleted_at?: string | null; + urls: string[] | null; /** - * Is Intermediate - * @description Whether this is an intermediate image. + * Is Diffusers + * @description Whether the metadata is for a Diffusers format model */ - is_intermediate: boolean; + is_diffusers: boolean; + }; + /** IPAdapterField */ + IPAdapterField: { /** - * Session Id - * @description The session ID that generated this image, if it is a generated image. + * Image + * @description The IP-Adapter image prompt(s). */ - session_id?: string | null; + image: components["schemas"]["ImageField"] | components["schemas"]["ImageField"][]; + /** @description The IP-Adapter model to use. */ + ip_adapter_model: components["schemas"]["ModelIdentifierField"]; + /** @description The name of the CLIP image encoder model. */ + image_encoder_model: components["schemas"]["ModelIdentifierField"]; /** - * Node Id - * @description The node ID that generated this image, if it is a generated image. + * Weight + * @description The weight given to the IP-Adapter. + * @default 1 */ - node_id?: string | null; + weight?: number | number[]; /** - * Starred - * @description Whether this image is starred. + * Target Blocks + * @description The IP Adapter blocks to apply + * @default [] */ - starred: boolean; + target_blocks?: string[]; /** - * Has Workflow - * @description Whether this image has a workflow. + * Method + * @description Weight apply method + * @default full */ - has_workflow: boolean; + method?: string; /** - * Board Id - * @description The id of the board the image belongs to, if one exists. + * Begin Step Percent + * @description When the IP-Adapter is first applied (% of total steps) + * @default 0 */ - board_id?: string | null; - }; - /** - * ImageField - * @description An image primitive field - */ - ImageField: { + begin_step_percent?: number; /** - * Image Name - * @description The name of the image + * End Step Percent + * @description When the IP-Adapter is last applied (% of total steps) + * @default 1 */ - image_name: string; + end_step_percent?: number; + /** + * @description The bool mask associated with this IP-Adapter. Excluded regions should be set to False, included regions should be set to True. + * @default null + */ + mask?: components["schemas"]["TensorField"] | null; }; /** - * Image Generator - * @description Generated a collection of images for use in a batched generation + * IP-Adapter - SD1.5, SDXL + * @description Collects IP-Adapter info to pass to other nodes. */ - ImageGenerator: { + IPAdapterInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -10656,777 +10604,683 @@ export type components = { */ use_cache?: boolean; /** - * Generator Type - * @description The image generator. - */ - generator: components["schemas"]["ImageGeneratorField"]; - /** - * type - * @default image_generator - * @constant - */ - type: "image_generator"; - }; - /** ImageGeneratorField */ - ImageGeneratorField: Record; - /** - * ImageGeneratorOutput - * @description Base class for nodes that output a collection of boards - */ - ImageGeneratorOutput: { - /** - * Images - * @description The generated images - */ - images: components["schemas"]["ImageField"][]; - /** - * type - * @default image_generator_output - * @constant + * Image + * @description The IP-Adapter image prompt(s). + * @default null */ - type: "image_generator_output"; - }; - /** - * Adjust Image Hue - * @description Adjusts the Hue of an image. - */ - ImageHueAdjustmentInvocation: { + image?: components["schemas"]["ImageField"] | components["schemas"]["ImageField"][] | null; /** - * @description The board to save the image to + * IP-Adapter Model + * @description The IP-Adapter model. * @default null */ - board?: components["schemas"]["BoardField"] | null; + ip_adapter_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * @description Optional metadata to be saved with the image - * @default null + * Clip Vision Model + * @description CLIP Vision model to use. Overrides model settings. Mandatory for checkpoint models. + * @default ViT-H + * @enum {string} */ - metadata?: components["schemas"]["MetadataField"] | null; + clip_vision_model?: "ViT-H" | "ViT-G" | "ViT-L"; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Weight + * @description The weight given to the IP-Adapter + * @default 1 */ - id: string; + weight?: number | number[]; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Method + * @description The method to apply the IP-Adapter + * @default full + * @enum {string} */ - is_intermediate?: boolean; + method?: "full" | "style" | "composition" | "style_strong" | "style_precise"; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Begin Step Percent + * @description When the IP-Adapter is first applied (% of total steps) + * @default 0 */ - use_cache?: boolean; + begin_step_percent?: number; /** - * @description The image to adjust - * @default null + * End Step Percent + * @description When the IP-Adapter is last applied (% of total steps) + * @default 1 */ - image?: components["schemas"]["ImageField"] | null; + end_step_percent?: number; /** - * Hue - * @description The degrees by which to rotate the hue, 0-360 - * @default 0 + * @description A mask defining the region that this IP-Adapter applies to. + * @default null */ - hue?: number; + mask?: components["schemas"]["TensorField"] | null; /** * type - * @default img_hue_adjust + * @default ip_adapter * @constant */ - type: "img_hue_adjust"; + type: "ip_adapter"; }; /** - * Inverse Lerp Image - * @description Inverse linear interpolation of all pixels of an image + * IPAdapterMetadataField + * @description IP Adapter Field, minus the CLIP Vision Encoder model */ - ImageInverseLerpInvocation: { + IPAdapterMetadataField: { + /** @description The IP-Adapter image prompt. */ + image: components["schemas"]["ImageField"]; + /** @description The IP-Adapter model. */ + ip_adapter_model: components["schemas"]["ModelIdentifierField"]; /** - * @description The board to save the image to - * @default null + * Clip Vision Model + * @description The CLIP Vision model + * @enum {string} */ - board?: components["schemas"]["BoardField"] | null; + clip_vision_model: "ViT-L" | "ViT-H" | "ViT-G"; /** - * @description Optional metadata to be saved with the image - * @default null + * Method + * @description Method to apply IP Weights with + * @enum {string} */ - metadata?: components["schemas"]["MetadataField"] | null; + method: "full" | "style" | "composition" | "style_strong" | "style_precise"; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Weight + * @description The weight given to the IP-Adapter */ - id: string; + weight: number | number[]; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Begin Step Percent + * @description When the IP-Adapter is first applied (% of total steps) */ - is_intermediate?: boolean; + begin_step_percent: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * End Step Percent + * @description When the IP-Adapter is last applied (% of total steps) */ - use_cache?: boolean; - /** - * @description The image to lerp - * @default null - */ - image?: components["schemas"]["ImageField"] | null; - /** - * Min - * @description The minimum input value - * @default 0 - */ - min?: number; + end_step_percent: number; + }; + /** IPAdapterOutput */ + IPAdapterOutput: { /** - * Max - * @description The maximum input value - * @default 255 + * IP-Adapter + * @description IP-Adapter to apply */ - max?: number; + ip_adapter: components["schemas"]["IPAdapterField"]; /** * type - * @default img_ilerp + * @default ip_adapter_output * @constant */ - type: "img_ilerp"; + type: "ip_adapter_output"; }; - /** - * Image Primitive - * @description An image primitive value - */ - ImageInvocation: { + /** IPAdapter_Checkpoint_FLUX_Config */ + IPAdapter_Checkpoint_FLUX_Config: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Key + * @description A unique key for this model. */ - id: string; + key: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Hash + * @description The hash of the model file(s). */ - is_intermediate?: boolean; + hash: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - use_cache?: boolean; + path: string; /** - * @description The image to load - * @default null + * File Size + * @description The size of the model in bytes. */ - image?: components["schemas"]["ImageField"] | null; + file_size: number; /** - * type - * @default image - * @constant + * Name + * @description Name of the model. */ - type: "image"; - }; - /** - * Lerp Image - * @description Linear interpolation of all pixels of an image - */ - ImageLerpInvocation: { + name: string; /** - * @description The board to save the image to - * @default null + * Description + * @description Model description */ - board?: components["schemas"]["BoardField"] | null; + description: string | null; /** - * @description Optional metadata to be saved with the image - * @default null + * Source + * @description The original source of the model (path, URL or repo_id). */ - metadata?: components["schemas"]["MetadataField"] | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - id: string; + source_api_response: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Cover Image + * @description Url for image to preview model */ - is_intermediate?: boolean; + cover_image: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Submodels + * @description Loadable submodels in this model */ - use_cache?: boolean; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * @description The image to lerp - * @default null + * Usage Info + * @description Usage information for this model */ - image?: components["schemas"]["ImageField"] | null; + usage_info: string | null; /** - * Min - * @description The minimum output value - * @default 0 + * Type + * @default ip_adapter + * @constant */ - min?: number; + type: "ip_adapter"; /** - * Max - * @description The maximum output value - * @default 255 + * Format + * @default checkpoint + * @constant */ - max?: number; + format: "checkpoint"; /** - * type - * @default img_lerp + * Base + * @default flux * @constant */ - type: "img_lerp"; + base: "flux"; }; - /** - * Image Mask to Tensor - * @description Convert a mask image to a tensor. Converts the image to grayscale and uses thresholding at the specified value. - */ - ImageMaskToTensorInvocation: { - /** - * @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; + /** IPAdapter_Checkpoint_SD1_Config */ + IPAdapter_Checkpoint_SD1_Config: { /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Key + * @description A unique key for this model. */ - is_intermediate?: boolean; + key: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Hash + * @description The hash of the model file(s). */ - use_cache?: boolean; + hash: string; /** - * @description The mask image to convert. - * @default null + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - image?: components["schemas"]["ImageField"] | null; + path: string; /** - * Cutoff - * @description Cutoff (<) - * @default 128 + * File Size + * @description The size of the model in bytes. */ - cutoff?: number; + file_size: number; /** - * Invert - * @description Whether to invert the mask. - * @default false + * Name + * @description Name of the model. */ - invert?: boolean; + name: string; /** - * type - * @default image_mask_to_tensor - * @constant + * Description + * @description Model description */ - type: "image_mask_to_tensor"; - }; - /** - * Multiply Images - * @description Multiplies two images together using `PIL.ImageChops.multiply()`. - */ - ImageMultiplyInvocation: { + description: string | null; /** - * @description The board to save the image to - * @default null + * Source + * @description The original source of the model (path, URL or repo_id). */ - board?: components["schemas"]["BoardField"] | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * @description Optional metadata to be saved with the image - * @default null + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - metadata?: components["schemas"]["MetadataField"] | null; + source_api_response: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Cover Image + * @description Url for image to preview model */ - id: string; + cover_image: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Submodels + * @description Loadable submodels in this model */ - is_intermediate?: boolean; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Usage Info + * @description Usage information for this model */ - use_cache?: boolean; + usage_info: string | null; /** - * @description The first image to multiply - * @default null + * Type + * @default ip_adapter + * @constant */ - image1?: components["schemas"]["ImageField"] | null; + type: "ip_adapter"; /** - * @description The second image to multiply - * @default null + * Format + * @default checkpoint + * @constant */ - image2?: components["schemas"]["ImageField"] | null; + format: "checkpoint"; /** - * type - * @default img_mul + * Base + * @default sd-1 * @constant */ - type: "img_mul"; + base: "sd-1"; }; - /** - * Blur NSFW Image - * @description Add blur to NSFW-flagged images - */ - ImageNSFWBlurInvocation: { + /** IPAdapter_Checkpoint_SD2_Config */ + IPAdapter_Checkpoint_SD2_Config: { /** - * @description The board to save the image to - * @default null + * Key + * @description A unique key for this model. */ - board?: components["schemas"]["BoardField"] | null; + key: string; /** - * @description Optional metadata to be saved with the image - * @default null + * Hash + * @description The hash of the model file(s). */ - metadata?: components["schemas"]["MetadataField"] | null; + hash: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - id: string; + path: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * File Size + * @description The size of the model in bytes. */ - is_intermediate?: boolean; + file_size: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Name + * @description Name of the model. */ - use_cache?: boolean; + name: string; /** - * @description The image to check - * @default null + * Description + * @description Model description */ - image?: components["schemas"]["ImageField"] | null; + description: string | null; /** - * type - * @default img_nsfw - * @constant + * Source + * @description The original source of the model (path, URL or repo_id). */ - type: "img_nsfw"; - }; - /** - * ImageNamesResult - * @description Response containing ordered image names with metadata for optimistic updates. - */ - ImageNamesResult: { + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Image Names - * @description Ordered list of image names + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - image_names: string[]; + source_api_response: string | null; /** - * Starred Count - * @description Number of starred images (when starred_first=True) + * Cover Image + * @description Url for image to preview model */ - starred_count: number; + cover_image: string | null; /** - * Total Count - * @description Total number of images matching the query + * Submodels + * @description Loadable submodels in this model */ - total_count: number; - }; - /** - * Add Image Noise - * @description Add noise to an image - */ - ImageNoiseInvocation: { + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * @description The board to save the image to - * @default null + * Usage Info + * @description Usage information for this model */ - board?: components["schemas"]["BoardField"] | null; + usage_info: string | null; /** - * @description Optional metadata to be saved with the image - * @default null + * Type + * @default ip_adapter + * @constant */ - metadata?: components["schemas"]["MetadataField"] | null; + type: "ip_adapter"; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Format + * @default checkpoint + * @constant */ - id: string; + format: "checkpoint"; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Base + * @default sd-2 + * @constant */ - is_intermediate?: boolean; + base: "sd-2"; + }; + /** IPAdapter_Checkpoint_SDXL_Config */ + IPAdapter_Checkpoint_SDXL_Config: { /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Key + * @description A unique key for this model. */ - use_cache?: boolean; + key: string; /** - * @description The image to add noise to - * @default null + * Hash + * @description The hash of the model file(s). */ - image?: components["schemas"]["ImageField"] | null; + hash: string; /** - * @description Optional mask determining where to apply noise (black=noise, white=no noise) - * @default null + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - mask?: components["schemas"]["ImageField"] | null; + path: string; /** - * Seed - * @description Seed for random number generation - * @default 0 + * File Size + * @description The size of the model in bytes. */ - seed?: number; + file_size: number; /** - * Noise Type - * @description The type of noise to add - * @default gaussian - * @enum {string} + * Name + * @description Name of the model. */ - noise_type?: "gaussian" | "salt_and_pepper"; + name: string; /** - * Amount - * @description The amount of noise to add - * @default 0.1 + * Description + * @description Model description */ - amount?: number; + description: string | null; /** - * Noise Color - * @description Whether to add colored noise - * @default true + * Source + * @description The original source of the model (path, URL or repo_id). */ - noise_color?: boolean; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Size - * @description The size of the noise points - * @default 1 + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - size?: number; + source_api_response: string | null; /** - * type - * @default img_noise - * @constant + * Cover Image + * @description Url for image to preview model */ - type: "img_noise"; - }; - /** - * ImageOutput - * @description Base class for nodes that output a single image - */ - ImageOutput: { - /** @description The output image */ - image: components["schemas"]["ImageField"]; + cover_image: string | null; /** - * Width - * @description The width of the image in pixels + * Submodels + * @description Loadable submodels in this model */ - width: number; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Height - * @description The height of the image in pixels + * Usage Info + * @description Usage information for this model */ - height: number; + usage_info: string | null; /** - * type - * @default image_output + * Type + * @default ip_adapter * @constant */ - type: "image_output"; - }; - /** ImagePanelCoordinateOutput */ - ImagePanelCoordinateOutput: { + type: "ip_adapter"; /** - * X Left - * @description The left x-coordinate of the panel. + * Format + * @default checkpoint + * @constant */ - x_left: number; + format: "checkpoint"; /** - * Y Top - * @description The top y-coordinate of the panel. + * Base + * @default sdxl + * @constant */ - y_top: number; + base: "sdxl"; + }; + /** IPAdapter_InvokeAI_SD1_Config */ + IPAdapter_InvokeAI_SD1_Config: { /** - * Width - * @description The width of the panel. + * Key + * @description A unique key for this model. */ - width: number; + key: string; /** - * Height - * @description The height of the panel. + * Hash + * @description The hash of the model file(s). */ - height: number; + hash: string; /** - * type - * @default image_panel_coordinate_output - * @constant + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - type: "image_panel_coordinate_output"; - }; - /** - * Image Panel Layout - * @description Get the coordinates of a single panel in a grid. (If the full image shape cannot be divided evenly into panels, - * then the grid may not cover the entire image.) - */ - ImagePanelLayoutInvocation: { + path: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * File Size + * @description The size of the model in bytes. */ - id: string; + file_size: number; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Name + * @description Name of the model. */ - is_intermediate?: boolean; + name: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Description + * @description Model description */ - use_cache?: boolean; + description: string | null; /** - * Width - * @description The width of the entire grid. - * @default null + * Source + * @description The original source of the model (path, URL or repo_id). */ - width?: number | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Height - * @description The height of the entire grid. - * @default null + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - height?: number | null; + source_api_response: string | null; /** - * Num Cols - * @description The number of columns in the grid. - * @default 1 + * Cover Image + * @description Url for image to preview model */ - num_cols?: number; + cover_image: string | null; /** - * Num Rows - * @description The number of rows in the grid. - * @default 1 + * Submodels + * @description Loadable submodels in this model */ - num_rows?: number; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Panel Col Idx - * @description The column index of the panel to be processed. - * @default 0 + * Usage Info + * @description Usage information for this model */ - panel_col_idx?: number; + usage_info: string | null; /** - * Panel Row Idx - * @description The row index of the panel to be processed. - * @default 0 + * Type + * @default ip_adapter + * @constant */ - panel_row_idx?: number; + type: "ip_adapter"; /** - * type - * @default image_panel_layout + * Format + * @default invokeai * @constant */ - type: "image_panel_layout"; - }; - /** - * Paste Image - * @description Pastes an image into another image. - */ - ImagePasteInvocation: { + format: "invokeai"; + /** Image Encoder Model Id */ + image_encoder_model_id: string; /** - * @description The board to save the image to - * @default null + * Base + * @default sd-1 + * @constant */ - board?: components["schemas"]["BoardField"] | null; + base: "sd-1"; + }; + /** IPAdapter_InvokeAI_SD2_Config */ + IPAdapter_InvokeAI_SD2_Config: { /** - * @description Optional metadata to be saved with the image - * @default null + * Key + * @description A unique key for this model. */ - metadata?: components["schemas"]["MetadataField"] | null; + key: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Hash + * @description The hash of the model file(s). */ - id: string; + hash: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - is_intermediate?: boolean; + path: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * File Size + * @description The size of the model in bytes. */ - use_cache?: boolean; + file_size: number; /** - * @description The base image - * @default null + * Name + * @description Name of the model. */ - base_image?: components["schemas"]["ImageField"] | null; + name: string; /** - * @description The image to paste - * @default null + * Description + * @description Model description */ - image?: components["schemas"]["ImageField"] | null; + description: string | null; /** - * @description The mask to use when pasting - * @default null + * Source + * @description The original source of the model (path, URL or repo_id). */ - mask?: components["schemas"]["ImageField"] | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * X - * @description The left x coordinate at which to paste the image - * @default 0 + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - x?: number; + source_api_response: string | null; /** - * Y - * @description The top y coordinate at which to paste the image - * @default 0 + * Cover Image + * @description Url for image to preview model */ - y?: number; + cover_image: string | null; /** - * Crop - * @description Crop to base image dimensions - * @default false + * Submodels + * @description Loadable submodels in this model */ - crop?: boolean; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * type - * @default img_paste - * @constant + * Usage Info + * @description Usage information for this model */ - type: "img_paste"; - }; - /** - * ImageRecordChanges - * @description A set of changes to apply to an image record. - * - * Only limited changes are valid: - * - `image_category`: change the category of an image - * - `session_id`: change the session associated with an image - * - `is_intermediate`: change the image's `is_intermediate` flag - * - `starred`: change whether the image is starred - */ - ImageRecordChanges: { - /** @description The image's new category. */ - image_category?: components["schemas"]["ImageCategory"] | null; + usage_info: string | null; /** - * Session Id - * @description The image's new session ID. + * Type + * @default ip_adapter + * @constant */ - session_id?: string | null; + type: "ip_adapter"; /** - * Is Intermediate - * @description The image's new `is_intermediate` flag. + * Format + * @default invokeai + * @constant */ - is_intermediate?: boolean | null; + format: "invokeai"; + /** Image Encoder Model Id */ + image_encoder_model_id: string; /** - * Starred - * @description The image's new `starred` state + * Base + * @default sd-2 + * @constant */ - starred?: boolean | null; - } & { - [key: string]: unknown; + base: "sd-2"; }; - /** - * Resize Image - * @description Resizes an image to specific dimensions - */ - ImageResizeInvocation: { + /** IPAdapter_InvokeAI_SDXL_Config */ + IPAdapter_InvokeAI_SDXL_Config: { /** - * @description The board to save the image to - * @default null + * Key + * @description A unique key for this model. */ - board?: components["schemas"]["BoardField"] | null; + key: string; /** - * @description Optional metadata to be saved with the image - * @default null + * Hash + * @description The hash of the model file(s). */ - metadata?: components["schemas"]["MetadataField"] | null; + hash: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - id: string; + path: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * File Size + * @description The size of the model in bytes. */ - is_intermediate?: boolean; + file_size: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Name + * @description Name of the model. */ - use_cache?: boolean; + name: string; /** - * @description The image to resize - * @default null + * Description + * @description Model description */ - image?: components["schemas"]["ImageField"] | null; + description: string | null; /** - * Width - * @description The width to resize to (px) - * @default 512 + * Source + * @description The original source of the model (path, URL or repo_id). */ - width?: number; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Height - * @description The height to resize to (px) - * @default 512 + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - height?: number; + source_api_response: string | null; /** - * Resample Mode - * @description The resampling mode - * @default bicubic - * @enum {string} + * Cover Image + * @description Url for image to preview model */ - resample_mode?: "nearest" | "box" | "bilinear" | "hamming" | "bicubic" | "lanczos"; + cover_image: string | null; /** - * type - * @default img_resize - * @constant + * Submodels + * @description Loadable submodels in this model */ - type: "img_resize"; - }; - /** - * Scale Image - * @description Scales an image by a factor - */ - ImageScaleInvocation: { + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * @description The board to save the image to - * @default null + * Usage Info + * @description Usage information for this model */ - board?: components["schemas"]["BoardField"] | null; + usage_info: string | null; /** - * @description Optional metadata to be saved with the image - * @default null + * Type + * @default ip_adapter + * @constant */ - metadata?: components["schemas"]["MetadataField"] | null; + type: "ip_adapter"; + /** + * Format + * @default invokeai + * @constant + */ + format: "invokeai"; + /** Image Encoder Model Id */ + image_encoder_model_id: string; + /** + * Base + * @default sdxl + * @constant + */ + base: "sdxl"; + }; + /** + * Ideal Size - SD1.5, SDXL + * @description Calculates the ideal size for generation to avoid duplication + */ + IdealSizeInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -11445,35 +11299,62 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to scale + * Width + * @description Final image width + * @default 1024 + */ + width?: number; + /** + * Height + * @description Final image height + * @default 576 + */ + height?: number; + /** + * @description UNet (scheduler, LoRAs) * @default null */ - image?: components["schemas"]["ImageField"] | null; + unet?: components["schemas"]["UNetField"] | null; /** - * Scale Factor - * @description The factor by which to scale the image - * @default 2 + * Multiplier + * @description Amount to multiply the model's dimensions by when calculating the ideal size (may result in initial generation artifacts if too large) + * @default 1 */ - scale_factor?: number; + multiplier?: number; /** - * Resample Mode - * @description The resampling mode - * @default bicubic - * @enum {string} + * type + * @default ideal_size + * @constant */ - resample_mode?: "nearest" | "box" | "bilinear" | "hamming" | "bicubic" | "lanczos"; + type: "ideal_size"; + }; + /** + * IdealSizeOutput + * @description Base class for invocations that output an image + */ + IdealSizeOutput: { + /** + * Width + * @description The ideal width of the image (in pixels) + */ + width: number; + /** + * Height + * @description The ideal height of the image (in pixels) + */ + height: number; /** * type - * @default img_scale + * @default ideal_size_output * @constant */ - type: "img_scale"; + type: "ideal_size_output"; }; /** - * Image to Latents - SD1.5, SDXL - * @description Encodes an image into latents. + * Image Batch + * @description Create a batched generation, where the workflow is executed once for each image in the batch. */ - ImageToLatentsInvocation: { + ImageBatchInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -11492,76 +11373,30 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to encode - * @default null + * Batch Group + * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. + * @default None + * @enum {string} */ - image?: components["schemas"]["ImageField"] | null; + batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; /** - * @description VAE + * Images + * @description The images to batch over * @default null */ - vae?: components["schemas"]["VAEField"] | null; - /** - * Tiled - * @description Processing using overlapping tiles (reduce memory consumption) - * @default false - */ - tiled?: boolean; - /** - * Tile Size - * @description The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the model will be used. Larger tile sizes generally produce better results at the cost of higher memory usage. - * @default 0 - */ - tile_size?: number; - /** - * Fp32 - * @description Whether or not to use full float32 precision - * @default false - */ - fp32?: boolean; + images?: components["schemas"]["ImageField"][] | null; /** * type - * @default i2l + * @default image_batch * @constant */ - type: "i2l"; - }; - /** ImageUploadEntry */ - ImageUploadEntry: { - /** @description The image DTO */ - image_dto: components["schemas"]["ImageDTO"]; - /** - * Presigned Url - * @description The URL to get the presigned URL for the image upload - */ - presigned_url: string; - }; - /** - * ImageUrlsDTO - * @description The URLs for an image and its thumbnail. - */ - ImageUrlsDTO: { - /** - * Image Name - * @description The unique name of the image. - */ - image_name: string; - /** - * Image Url - * @description The URL of the image. - */ - image_url: string; - /** - * Thumbnail Url - * @description The URL of the image's thumbnail. - */ - thumbnail_url: string; + type: "image_batch"; }; /** - * Add Invisible Watermark - * @description Add an invisible watermark to an image + * Blur Image + * @description Blurs an image */ - ImageWatermarkInvocation: { + ImageBlurInvocation: { /** * @description The board to save the image to * @default null @@ -11590,41 +11425,47 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to check + * @description The image to blur * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Text - * @description Watermark text - * @default InvokeAI - */ - text?: string; - /** - * type - * @default img_watermark - * @constant + * Radius + * @description The blur radius + * @default 8 */ - type: "img_watermark"; - }; - /** ImagesDownloaded */ - ImagesDownloaded: { + radius?: number; /** - * Response - * @description The message to display to the user when images begin downloading + * Blur Type + * @description The type of blur + * @default gaussian + * @enum {string} */ - response?: string | null; + blur_type?: "gaussian" | "box"; /** - * Bulk Download Item Name - * @description The name of the bulk download item for which events will be emitted + * type + * @default img_blur + * @constant */ - bulk_download_item_name?: string | null; + type: "img_blur"; }; /** - * Solid Color Infill - * @description Infills transparent areas of an image with a solid color + * ImageCategory + * @description The category of an image. + * + * - GENERAL: The image is an output, init image, or otherwise an image without a specialized purpose. + * - MASK: The image is a mask image. + * - CONTROL: The image is a ControlNet control image. + * - USER: The image is a user-provide image. + * - OTHER: The image is some other type of image with a specialized purpose. To be used by external nodes. + * @enum {string} */ - InfillColorInvocation: { + ImageCategory: "general" | "mask" | "control" | "user" | "other"; + /** + * Extract Image Channel + * @description Gets a channel from an image. + */ + ImageChannelInvocation: { /** * @description The board to save the image to * @default null @@ -11653,32 +11494,29 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to process + * @description The image to get the channel from * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * @description The color to use to infill - * @default { - * "r": 127, - * "g": 127, - * "b": 127, - * "a": 255 - * } + * Channel + * @description The channel to get + * @default A + * @enum {string} */ - color?: components["schemas"]["ColorField"]; + channel?: "A" | "R" | "G" | "B"; /** * type - * @default infill_rgba + * @default img_chan * @constant */ - type: "infill_rgba"; + type: "img_chan"; }; /** - * PatchMatch Infill - * @description Infills transparent areas of an image using the PatchMatch algorithm + * Multiply Image Channel + * @description Scale a specific color channel of an image. */ - InfillPatchMatchInvocation: { + ImageChannelMultiplyInvocation: { /** * @description The board to save the image to * @default null @@ -11707,35 +11545,40 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to process + * @description The image to adjust * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Downscale - * @description Run patchmatch on downscaled image to speedup infill - * @default 2 + * Channel + * @description Which channel to adjust + * @default null */ - downscale?: number; + channel?: ("Red (RGBA)" | "Green (RGBA)" | "Blue (RGBA)" | "Alpha (RGBA)" | "Cyan (CMYK)" | "Magenta (CMYK)" | "Yellow (CMYK)" | "Black (CMYK)" | "Hue (HSV)" | "Saturation (HSV)" | "Value (HSV)" | "Luminosity (LAB)" | "A (LAB)" | "B (LAB)" | "Y (YCbCr)" | "Cb (YCbCr)" | "Cr (YCbCr)") | null; /** - * Resample Mode - * @description The resampling mode - * @default bicubic - * @enum {string} + * Scale + * @description The amount to scale the channel by. + * @default 1 */ - resample_mode?: "nearest" | "box" | "bilinear" | "hamming" | "bicubic" | "lanczos"; + scale?: number; + /** + * Invert Channel + * @description Invert the channel after scaling + * @default false + */ + invert_channel?: boolean; /** * type - * @default infill_patchmatch + * @default img_channel_multiply * @constant */ - type: "infill_patchmatch"; + type: "img_channel_multiply"; }; /** - * Tile Infill - * @description Infills transparent areas of an image with tiles of the image + * Offset Image Channel + * @description Add or subtract a value from a specific color channel of an image. */ - InfillTileInvocation: { + ImageChannelOffsetInvocation: { /** * @description The board to save the image to * @default null @@ -11764,114 +11607,34 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to process + * @description The image to adjust * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Tile Size - * @description The tile size (px) - * @default 32 + * Channel + * @description Which channel to adjust + * @default null */ - tile_size?: number; + channel?: ("Red (RGBA)" | "Green (RGBA)" | "Blue (RGBA)" | "Alpha (RGBA)" | "Cyan (CMYK)" | "Magenta (CMYK)" | "Yellow (CMYK)" | "Black (CMYK)" | "Hue (HSV)" | "Saturation (HSV)" | "Value (HSV)" | "Luminosity (LAB)" | "A (LAB)" | "B (LAB)" | "Y (YCbCr)" | "Cb (YCbCr)" | "Cr (YCbCr)") | null; /** - * Seed - * @description The seed to use for tile generation (omit for random) + * Offset + * @description The amount to adjust the channel by * @default 0 */ - seed?: number; + offset?: number; /** * type - * @default infill_tile + * @default img_channel_offset * @constant */ - type: "infill_tile"; - }; - /** - * Input - * @description The type of input a field accepts. - * - `Input.Direct`: The field must have its value provided directly, when the invocation and field are instantiated. - * - `Input.Connection`: The field must have its value provided by a connection. - * - `Input.Any`: The field may have its value provided either directly or by a connection. - * @enum {string} - */ - Input: "connection" | "direct" | "any"; - /** - * InputFieldJSONSchemaExtra - * @description Extra attributes to be added to input fields and their OpenAPI schema. Used during graph execution, - * and by the workflow editor during schema parsing and UI rendering. - */ - InputFieldJSONSchemaExtra: { - input: components["schemas"]["Input"]; - field_kind: components["schemas"]["FieldKind"]; - /** - * Orig Required - * @default true - */ - orig_required: boolean; - /** - * Default - * @default null - */ - default: unknown | null; - /** - * Orig Default - * @default null - */ - orig_default: unknown | null; - /** - * Ui Hidden - * @default false - */ - ui_hidden: boolean; - /** @default null */ - ui_type: components["schemas"]["UIType"] | null; - /** @default null */ - ui_component: components["schemas"]["UIComponent"] | null; - /** - * Ui Order - * @default null - */ - ui_order: number | null; - /** - * Ui Choice Labels - * @default null - */ - ui_choice_labels: { - [key: string]: string; - } | null; - /** - * Ui Model Base - * @default null - */ - ui_model_base: components["schemas"]["BaseModelType"][] | null; - /** - * Ui Model Type - * @default null - */ - ui_model_type: components["schemas"]["ModelType"][] | null; - /** - * Ui Model Variant - * @default null - */ - ui_model_variant: (components["schemas"]["ClipVariantType"] | components["schemas"]["ModelVariantType"])[] | null; - /** - * Ui Model Format - * @default null - */ - ui_model_format: components["schemas"]["ModelFormat"][] | null; + type: "img_channel_offset"; }; /** - * InstallStatus - * @description State of an install job running in the background. - * @enum {string} - */ - InstallStatus: "waiting" | "downloading" | "downloads_done" | "running" | "completed" | "error" | "cancelled"; - /** - * Integer Batch - * @description Create a batched generation, where the workflow is executed once for each integer in the batch. + * Image Collection Primitive + * @description A collection of image primitive values */ - IntegerBatchInvocation: { + ImageCollectionInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -11890,82 +11653,50 @@ export type components = { */ use_cache?: boolean; /** - * Batch Group - * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. - * @default None - * @enum {string} - */ - batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; - /** - * Integers - * @description The integers to batch over + * Collection + * @description The collection of image values * @default null */ - integers?: number[] | null; + collection?: components["schemas"]["ImageField"][] | null; /** * type - * @default integer_batch + * @default image_collection * @constant */ - type: "integer_batch"; + type: "image_collection"; }; /** - * Integer Collection Primitive - * @description A collection of integer primitive values + * ImageCollectionOutput + * @description Base class for nodes that output a collection of images */ - IntegerCollectionInvocation: { - /** - * 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; + ImageCollectionOutput: { /** * Collection - * @description The collection of integer values - * @default [] + * @description The output images */ - collection?: number[]; + collection: components["schemas"]["ImageField"][]; /** * type - * @default integer_collection + * @default image_collection_output * @constant */ - type: "integer_collection"; + type: "image_collection_output"; }; /** - * IntegerCollectionOutput - * @description Base class for nodes that output a collection of integers + * Convert Image Mode + * @description Converts an image to a different mode. */ - IntegerCollectionOutput: { + ImageConvertInvocation: { /** - * Collection - * @description The int collection + * @description The board to save the image to + * @default null */ - collection: number[]; + board?: components["schemas"]["BoardField"] | null; /** - * type - * @default integer_collection_output - * @constant + * @description Optional metadata to be saved with the image + * @default null */ - type: "integer_collection_output"; - }; - /** - * Integer Generator - * @description Generated a range of integers for use in a batched generation - */ - IntegerGenerator: { + metadata?: components["schemas"]["MetadataField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -11984,38 +11715,39 @@ export type components = { */ use_cache?: boolean; /** - * Generator Type - * @description The integer generator. - */ - generator: components["schemas"]["IntegerGeneratorField"]; - /** - * type - * @default integer_generator - * @constant + * @description The image to convert + * @default null */ - type: "integer_generator"; - }; - /** IntegerGeneratorField */ - IntegerGeneratorField: Record; - /** IntegerGeneratorOutput */ - IntegerGeneratorOutput: { + image?: components["schemas"]["ImageField"] | null; /** - * Integers - * @description The generated integers + * Mode + * @description The mode to convert to + * @default L + * @enum {string} */ - integers: number[]; + mode?: "L" | "RGB" | "RGBA" | "CMYK" | "YCbCr" | "LAB" | "HSV" | "I" | "F"; /** * type - * @default integer_generator_output + * @default img_conv * @constant */ - type: "integer_generator_output"; + type: "img_conv"; }; /** - * Integer Primitive - * @description An integer primitive value + * Crop Image + * @description Crops an image to a specified box. The box can be outside of the image. */ - IntegerInvocation: { + ImageCropInvocation: { + /** + * @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. @@ -12034,23 +11766,137 @@ export type components = { */ use_cache?: boolean; /** - * Value - * @description The integer value - * @default 0 + * @description The image to crop + * @default null */ - value?: number; + image?: components["schemas"]["ImageField"] | null; + /** + * X + * @description The left x coordinate of the crop rectangle + * @default 0 + */ + x?: number; + /** + * Y + * @description The top y coordinate of the crop rectangle + * @default 0 + */ + y?: number; + /** + * Width + * @description The width of the crop rectangle + * @default 512 + */ + width?: number; + /** + * Height + * @description The height of the crop rectangle + * @default 512 + */ + height?: number; /** * type - * @default integer + * @default img_crop * @constant */ - type: "integer"; + type: "img_crop"; }; /** - * Integer Math - * @description Performs integer math. + * ImageDTO + * @description Deserialized image record, enriched for the frontend. */ - IntegerMathInvocation: { + ImageDTO: { + /** + * Image Name + * @description The unique name of the image. + */ + image_name: string; + /** + * Image Url + * @description The URL of the image. + */ + image_url: string; + /** + * Thumbnail Url + * @description The URL of the image's thumbnail. + */ + thumbnail_url: string; + /** @description The type of the image. */ + image_origin: components["schemas"]["ResourceOrigin"]; + /** @description The category of the image. */ + image_category: components["schemas"]["ImageCategory"]; + /** + * Width + * @description The width of the image in px. + */ + width: number; + /** + * Height + * @description The height of the image in px. + */ + height: number; + /** + * Created At + * @description The created timestamp of the image. + */ + created_at: string; + /** + * Updated At + * @description The updated timestamp of the image. + */ + updated_at: string; + /** + * Deleted At + * @description The deleted timestamp of the image. + */ + deleted_at?: string | null; + /** + * Is Intermediate + * @description Whether this is an intermediate image. + */ + is_intermediate: boolean; + /** + * Session Id + * @description The session ID that generated this image, if it is a generated image. + */ + session_id?: string | null; + /** + * Node Id + * @description The node ID that generated this image, if it is a generated image. + */ + node_id?: string | null; + /** + * Starred + * @description Whether this image is starred. + */ + starred: boolean; + /** + * Has Workflow + * @description Whether this image has a workflow. + */ + has_workflow: boolean; + /** + * Board Id + * @description The id of the board the image belongs to, if one exists. + */ + board_id?: string | null; + }; + /** + * ImageField + * @description An image primitive field + */ + ImageField: { + /** + * Image Name + * @description The name of the image + */ + image_name: string; + }; + /** + * Image Generator + * @description Generated a collection of images for use in a batched generation + */ + ImageGenerator: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -12069,53 +11915,51 @@ export type components = { */ use_cache?: boolean; /** - * Operation - * @description The operation to perform - * @default ADD - * @enum {string} - */ - operation?: "ADD" | "SUB" | "MUL" | "DIV" | "EXP" | "MOD" | "ABS" | "MIN" | "MAX"; - /** - * A - * @description The first number - * @default 1 - */ - a?: number; - /** - * B - * @description The second number - * @default 1 + * Generator Type + * @description The image generator. */ - b?: number; + generator: components["schemas"]["ImageGeneratorField"]; /** * type - * @default integer_math + * @default image_generator * @constant */ - type: "integer_math"; + type: "image_generator"; }; + /** ImageGeneratorField */ + ImageGeneratorField: Record; /** - * IntegerOutput - * @description Base class for nodes that output a single integer + * ImageGeneratorOutput + * @description Base class for nodes that output a collection of boards */ - IntegerOutput: { + ImageGeneratorOutput: { /** - * Value - * @description The output integer + * Images + * @description The generated images */ - value: number; + images: components["schemas"]["ImageField"][]; /** * type - * @default integer_output + * @default image_generator_output * @constant */ - type: "integer_output"; + type: "image_generator_output"; }; /** - * Invert Tensor Mask - * @description Inverts a tensor mask. + * Adjust Image Hue + * @description Adjusts the Hue of an image. */ - InvertTensorMaskInvocation: { + ImageHueAdjustmentInvocation: { + /** + * @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. @@ -12134,220 +11978,1635 @@ export type components = { */ use_cache?: boolean; /** - * @description The tensor mask to convert. + * @description The image to adjust * @default null */ - mask?: components["schemas"]["TensorField"] | null; + image?: components["schemas"]["ImageField"] | null; + /** + * Hue + * @description The degrees by which to rotate the hue, 0-360 + * @default 0 + */ + hue?: number; /** * type - * @default invert_tensor_mask + * @default img_hue_adjust * @constant */ - type: "invert_tensor_mask"; + type: "img_hue_adjust"; }; - /** InvocationCacheStatus */ - InvocationCacheStatus: { + /** + * Inverse Lerp Image + * @description Inverse linear interpolation of all pixels of an image + */ + ImageInverseLerpInvocation: { /** - * Size - * @description The current size of the invocation cache + * @description The board to save the image to + * @default null */ - size: number; + board?: components["schemas"]["BoardField"] | null; /** - * Hits - * @description The number of cache hits + * @description Optional metadata to be saved with the image + * @default null */ - hits: number; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Misses - * @description The number of cache misses + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - misses: number; + id: string; /** - * Enabled - * @description Whether the invocation cache is enabled + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - enabled: boolean; + is_intermediate?: boolean; /** - * Max Size - * @description The maximum size of the invocation cache + * Use Cache + * @description Whether or not to use the cache + * @default true */ - max_size: number; - }; - /** - * InvocationCompleteEvent - * @description Event model for invocation_complete - */ - InvocationCompleteEvent: { + use_cache?: boolean; /** - * Timestamp - * @description The timestamp of the event + * @description The image to lerp + * @default null */ - timestamp: number; + image?: components["schemas"]["ImageField"] | null; /** - * Queue Id - * @description The ID of the queue + * Min + * @description The minimum input value + * @default 0 */ - queue_id: string; + min?: number; /** - * Item Id - * @description The ID of the queue item + * Max + * @description The maximum input value + * @default 255 */ - item_id: number; + max?: number; /** - * Batch Id - * @description The ID of the queue batch + * type + * @default img_ilerp + * @constant */ - batch_id: string; + type: "img_ilerp"; + }; + /** + * Image Primitive + * @description An image primitive value + */ + ImageInvocation: { /** - * Origin - * @description The origin of the queue item + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to load * @default null */ - origin: string | null; + image?: components["schemas"]["ImageField"] | null; /** - * Destination - * @description The destination of the queue item + * type + * @default image + * @constant + */ + type: "image"; + }; + /** + * Lerp Image + * @description Linear interpolation of all pixels of an image + */ + ImageLerpInvocation: { + /** + * @description The board to save the image to * @default null */ - destination: string | null; + board?: components["schemas"]["BoardField"] | null; /** - * Session Id - * @description The ID of the session (aka graph execution state) + * @description Optional metadata to be saved with the image + * @default null */ - session_id: string; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Invocation - * @description The ID of the invocation + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | 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"]["CannyEdgeDetectionInvocation"] | 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"]["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"]["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"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | 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"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["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"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | 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"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"]; + id: string; /** - * Invocation Source Id - * @description The ID of the prepared invocation's source node + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - invocation_source_id: string; + is_intermediate?: boolean; /** - * Result - * @description The result of the invocation + * Use Cache + * @description Whether or not to use the cache + * @default true */ - result: 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"]["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"]["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"]["PairTileImageOutput"] | 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"]["VideoOutput"]; + use_cache?: boolean; + /** + * @description The image to lerp + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Min + * @description The minimum output value + * @default 0 + */ + min?: number; + /** + * Max + * @description The maximum output value + * @default 255 + */ + max?: number; + /** + * type + * @default img_lerp + * @constant + */ + type: "img_lerp"; }; /** - * InvocationErrorEvent - * @description Event model for invocation_error + * Image Mask to Tensor + * @description Convert a mask image to a tensor. Converts the image to grayscale and uses thresholding at the specified value. */ - InvocationErrorEvent: { + ImageMaskToTensorInvocation: { /** - * Timestamp - * @description The timestamp of the event + * @description Optional metadata to be saved with the image + * @default null */ - timestamp: number; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Queue Id - * @description The ID of the queue + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - queue_id: string; + id: string; /** - * Item Id - * @description The ID of the queue item + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - item_id: number; + is_intermediate?: boolean; /** - * Batch Id - * @description The ID of the queue batch + * Use Cache + * @description Whether or not to use the cache + * @default true */ - batch_id: string; + use_cache?: boolean; /** - * Origin - * @description The origin of the queue item + * @description The mask image to convert. * @default null */ - origin: string | null; + image?: components["schemas"]["ImageField"] | null; /** - * Destination - * @description The destination of the queue item - * @default null + * Cutoff + * @description Cutoff (<) + * @default 128 */ - destination: string | null; + cutoff?: number; /** - * Session Id - * @description The ID of the session (aka graph execution state) + * Invert + * @description Whether to invert the mask. + * @default false */ - session_id: string; + invert?: boolean; /** - * Invocation - * @description The ID of the invocation + * type + * @default image_mask_to_tensor + * @constant */ - invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | 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"]["CannyEdgeDetectionInvocation"] | 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"]["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"]["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"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | 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"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["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"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | 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"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"]; + type: "image_mask_to_tensor"; + }; + /** + * Multiply Images + * @description Multiplies two images together using `PIL.ImageChops.multiply()`. + */ + ImageMultiplyInvocation: { /** - * Invocation Source Id - * @description The ID of the prepared invocation's source node + * @description The board to save the image to + * @default null */ - invocation_source_id: string; + board?: components["schemas"]["BoardField"] | null; /** - * Error Type - * @description The error type + * @description Optional metadata to be saved with the image + * @default null */ - error_type: string; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Error Message - * @description The error message + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - error_message: string; + id: string; /** - * Error Traceback - * @description The error traceback + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - error_traceback: string; + is_intermediate?: boolean; /** - * User Id - * @description The ID of the user who created the invocation + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The first image to multiply * @default null */ - user_id: string | null; + image1?: components["schemas"]["ImageField"] | null; /** - * Project Id - * @description The ID of the user who created the invocation + * @description The second image to multiply * @default null */ - project_id: string | null; + image2?: components["schemas"]["ImageField"] | null; + /** + * type + * @default img_mul + * @constant + */ + type: "img_mul"; }; - InvocationOutputMap: { - add: components["schemas"]["IntegerOutput"]; - alpha_mask_to_tensor: components["schemas"]["MaskOutput"]; - apply_mask_to_image: components["schemas"]["ImageOutput"]; - apply_tensor_mask_to_image: components["schemas"]["ImageOutput"]; - blank_image: components["schemas"]["ImageOutput"]; - boolean: components["schemas"]["BooleanOutput"]; - boolean_collection: components["schemas"]["BooleanCollectionOutput"]; - bounding_box: components["schemas"]["BoundingBoxOutput"]; - calculate_image_tiles: components["schemas"]["CalculateImageTilesOutput"]; - calculate_image_tiles_even_split: components["schemas"]["CalculateImageTilesOutput"]; - calculate_image_tiles_min_overlap: components["schemas"]["CalculateImageTilesOutput"]; - canny_edge_detection: components["schemas"]["ImageOutput"]; - canvas_paste_back: components["schemas"]["ImageOutput"]; - canvas_v2_mask_and_crop: components["schemas"]["ImageOutput"]; - clip_skip: components["schemas"]["CLIPSkipInvocationOutput"]; - cogview4_denoise: components["schemas"]["LatentsOutput"]; - cogview4_i2l: components["schemas"]["LatentsOutput"]; - cogview4_l2i: components["schemas"]["ImageOutput"]; - cogview4_model_loader: components["schemas"]["CogView4ModelLoaderOutput"]; - cogview4_text_encoder: components["schemas"]["CogView4ConditioningOutput"]; - collect: components["schemas"]["CollectInvocationOutput"]; - color: components["schemas"]["ColorOutput"]; - color_correct: components["schemas"]["ImageOutput"]; - color_map: components["schemas"]["ImageOutput"]; - compel: components["schemas"]["ConditioningOutput"]; - conditioning: components["schemas"]["ConditioningOutput"]; - conditioning_collection: components["schemas"]["ConditioningCollectionOutput"]; - content_shuffle: components["schemas"]["ImageOutput"]; - controlnet: components["schemas"]["ControlOutput"]; - core_metadata: components["schemas"]["MetadataOutput"]; - create_denoise_mask: components["schemas"]["DenoiseMaskOutput"]; - create_gradient_mask: components["schemas"]["GradientMaskOutput"]; - crop_image_to_bounding_box: components["schemas"]["ImageOutput"]; - crop_latents: components["schemas"]["LatentsOutput"]; - cv_inpaint: components["schemas"]["ImageOutput"]; - denoise_latents: components["schemas"]["LatentsOutput"]; + /** + * Blur NSFW Image + * @description Add blur to NSFW-flagged images + */ + ImageNSFWBlurInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to check + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * type + * @default img_nsfw + * @constant + */ + type: "img_nsfw"; + }; + /** + * ImageNamesResult + * @description Response containing ordered image names with metadata for optimistic updates. + */ + ImageNamesResult: { + /** + * Image Names + * @description Ordered list of image names + */ + image_names: string[]; + /** + * Starred Count + * @description Number of starred images (when starred_first=True) + */ + starred_count: number; + /** + * Total Count + * @description Total number of images matching the query + */ + total_count: number; + }; + /** + * Add Image Noise + * @description Add noise to an image + */ + ImageNoiseInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to add noise to + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * @description Optional mask determining where to apply noise (black=noise, white=no noise) + * @default null + */ + mask?: components["schemas"]["ImageField"] | null; + /** + * Seed + * @description Seed for random number generation + * @default 0 + */ + seed?: number; + /** + * Noise Type + * @description The type of noise to add + * @default gaussian + * @enum {string} + */ + noise_type?: "gaussian" | "salt_and_pepper"; + /** + * Amount + * @description The amount of noise to add + * @default 0.1 + */ + amount?: number; + /** + * Noise Color + * @description Whether to add colored noise + * @default true + */ + noise_color?: boolean; + /** + * Size + * @description The size of the noise points + * @default 1 + */ + size?: number; + /** + * type + * @default img_noise + * @constant + */ + type: "img_noise"; + }; + /** + * ImageOutput + * @description Base class for nodes that output a single image + */ + ImageOutput: { + /** @description The output image */ + image: components["schemas"]["ImageField"]; + /** + * Width + * @description The width of the image in pixels + */ + width: number; + /** + * Height + * @description The height of the image in pixels + */ + height: number; + /** + * type + * @default image_output + * @constant + */ + type: "image_output"; + }; + /** ImagePanelCoordinateOutput */ + ImagePanelCoordinateOutput: { + /** + * X Left + * @description The left x-coordinate of the panel. + */ + x_left: number; + /** + * Y Top + * @description The top y-coordinate of the panel. + */ + y_top: number; + /** + * Width + * @description The width of the panel. + */ + width: number; + /** + * Height + * @description The height of the panel. + */ + height: number; + /** + * type + * @default image_panel_coordinate_output + * @constant + */ + type: "image_panel_coordinate_output"; + }; + /** + * Image Panel Layout + * @description Get the coordinates of a single panel in a grid. (If the full image shape cannot be divided evenly into panels, + * then the grid may not cover the entire image.) + */ + ImagePanelLayoutInvocation: { + /** + * 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; + /** + * Width + * @description The width of the entire grid. + * @default null + */ + width?: number | null; + /** + * Height + * @description The height of the entire grid. + * @default null + */ + height?: number | null; + /** + * Num Cols + * @description The number of columns in the grid. + * @default 1 + */ + num_cols?: number; + /** + * Num Rows + * @description The number of rows in the grid. + * @default 1 + */ + num_rows?: number; + /** + * Panel Col Idx + * @description The column index of the panel to be processed. + * @default 0 + */ + panel_col_idx?: number; + /** + * Panel Row Idx + * @description The row index of the panel to be processed. + * @default 0 + */ + panel_row_idx?: number; + /** + * type + * @default image_panel_layout + * @constant + */ + type: "image_panel_layout"; + }; + /** + * Paste Image + * @description Pastes an image into another image. + */ + ImagePasteInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The base image + * @default null + */ + base_image?: components["schemas"]["ImageField"] | null; + /** + * @description The image to paste + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * @description The mask to use when pasting + * @default null + */ + mask?: components["schemas"]["ImageField"] | null; + /** + * X + * @description The left x coordinate at which to paste the image + * @default 0 + */ + x?: number; + /** + * Y + * @description The top y coordinate at which to paste the image + * @default 0 + */ + y?: number; + /** + * Crop + * @description Crop to base image dimensions + * @default false + */ + crop?: boolean; + /** + * type + * @default img_paste + * @constant + */ + type: "img_paste"; + }; + /** + * ImageRecordChanges + * @description A set of changes to apply to an image record. + * + * Only limited changes are valid: + * - `image_category`: change the category of an image + * - `session_id`: change the session associated with an image + * - `is_intermediate`: change the image's `is_intermediate` flag + * - `starred`: change whether the image is starred + */ + ImageRecordChanges: { + /** @description The image's new category. */ + image_category?: components["schemas"]["ImageCategory"] | null; + /** + * Session Id + * @description The image's new session ID. + */ + session_id?: string | null; + /** + * Is Intermediate + * @description The image's new `is_intermediate` flag. + */ + is_intermediate?: boolean | null; + /** + * Starred + * @description The image's new `starred` state + */ + starred?: boolean | null; + } & { + [key: string]: unknown; + }; + /** + * Resize Image + * @description Resizes an image to specific dimensions + */ + ImageResizeInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to resize + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Width + * @description The width to resize to (px) + * @default 512 + */ + width?: number; + /** + * Height + * @description The height to resize to (px) + * @default 512 + */ + height?: number; + /** + * Resample Mode + * @description The resampling mode + * @default bicubic + * @enum {string} + */ + resample_mode?: "nearest" | "box" | "bilinear" | "hamming" | "bicubic" | "lanczos"; + /** + * type + * @default img_resize + * @constant + */ + type: "img_resize"; + }; + /** + * Scale Image + * @description Scales an image by a factor + */ + ImageScaleInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to scale + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Scale Factor + * @description The factor by which to scale the image + * @default 2 + */ + scale_factor?: number; + /** + * Resample Mode + * @description The resampling mode + * @default bicubic + * @enum {string} + */ + resample_mode?: "nearest" | "box" | "bilinear" | "hamming" | "bicubic" | "lanczos"; + /** + * type + * @default img_scale + * @constant + */ + type: "img_scale"; + }; + /** + * Image to Latents - SD1.5, SDXL + * @description Encodes an image into latents. + */ + ImageToLatentsInvocation: { + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to encode + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * @description VAE + * @default null + */ + vae?: components["schemas"]["VAEField"] | null; + /** + * Tiled + * @description Processing using overlapping tiles (reduce memory consumption) + * @default false + */ + tiled?: boolean; + /** + * Tile Size + * @description The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the model will be used. Larger tile sizes generally produce better results at the cost of higher memory usage. + * @default 0 + */ + tile_size?: number; + /** + * Fp32 + * @description Whether or not to use full float32 precision + * @default false + */ + fp32?: boolean; + /** + * type + * @default i2l + * @constant + */ + type: "i2l"; + }; + /** ImageUploadEntry */ + ImageUploadEntry: { + /** @description The image DTO */ + image_dto: components["schemas"]["ImageDTO"]; + /** + * Presigned Url + * @description The URL to get the presigned URL for the image upload + */ + presigned_url: string; + }; + /** + * ImageUrlsDTO + * @description The URLs for an image and its thumbnail. + */ + ImageUrlsDTO: { + /** + * Image Name + * @description The unique name of the image. + */ + image_name: string; + /** + * Image Url + * @description The URL of the image. + */ + image_url: string; + /** + * Thumbnail Url + * @description The URL of the image's thumbnail. + */ + thumbnail_url: string; + }; + /** + * Add Invisible Watermark + * @description Add an invisible watermark to an image + */ + ImageWatermarkInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to check + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Text + * @description Watermark text + * @default InvokeAI + */ + text?: string; + /** + * type + * @default img_watermark + * @constant + */ + type: "img_watermark"; + }; + /** ImagesDownloaded */ + ImagesDownloaded: { + /** + * Response + * @description The message to display to the user when images begin downloading + */ + response?: string | null; + /** + * Bulk Download Item Name + * @description The name of the bulk download item for which events will be emitted + */ + bulk_download_item_name?: string | null; + }; + /** + * Solid Color Infill + * @description Infills transparent areas of an image with a solid color + */ + InfillColorInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to process + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * @description The color to use to infill + * @default { + * "r": 127, + * "g": 127, + * "b": 127, + * "a": 255 + * } + */ + color?: components["schemas"]["ColorField"]; + /** + * type + * @default infill_rgba + * @constant + */ + type: "infill_rgba"; + }; + /** + * PatchMatch Infill + * @description Infills transparent areas of an image using the PatchMatch algorithm + */ + InfillPatchMatchInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to process + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Downscale + * @description Run patchmatch on downscaled image to speedup infill + * @default 2 + */ + downscale?: number; + /** + * Resample Mode + * @description The resampling mode + * @default bicubic + * @enum {string} + */ + resample_mode?: "nearest" | "box" | "bilinear" | "hamming" | "bicubic" | "lanczos"; + /** + * type + * @default infill_patchmatch + * @constant + */ + type: "infill_patchmatch"; + }; + /** + * Tile Infill + * @description Infills transparent areas of an image with tiles of the image + */ + InfillTileInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to process + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Tile Size + * @description The tile size (px) + * @default 32 + */ + tile_size?: number; + /** + * Seed + * @description The seed to use for tile generation (omit for random) + * @default 0 + */ + seed?: number; + /** + * type + * @default infill_tile + * @constant + */ + type: "infill_tile"; + }; + /** + * Input + * @description The type of input a field accepts. + * - `Input.Direct`: The field must have its value provided directly, when the invocation and field are instantiated. + * - `Input.Connection`: The field must have its value provided by a connection. + * - `Input.Any`: The field may have its value provided either directly or by a connection. + * @enum {string} + */ + Input: "connection" | "direct" | "any"; + /** + * InputFieldJSONSchemaExtra + * @description Extra attributes to be added to input fields and their OpenAPI schema. Used during graph execution, + * and by the workflow editor during schema parsing and UI rendering. + */ + InputFieldJSONSchemaExtra: { + input: components["schemas"]["Input"]; + field_kind: components["schemas"]["FieldKind"]; + /** + * Orig Required + * @default true + */ + orig_required: boolean; + /** + * Default + * @default null + */ + default: unknown | null; + /** + * Orig Default + * @default null + */ + orig_default: unknown | null; + /** + * Ui Hidden + * @default false + */ + ui_hidden: boolean; + /** @default null */ + ui_type: components["schemas"]["UIType"] | null; + /** @default null */ + ui_component: components["schemas"]["UIComponent"] | null; + /** + * Ui Order + * @default null + */ + ui_order: number | null; + /** + * Ui Choice Labels + * @default null + */ + ui_choice_labels: { + [key: string]: string; + } | null; + /** + * Ui Model Base + * @default null + */ + ui_model_base: components["schemas"]["BaseModelType"][] | null; + /** + * Ui Model Type + * @default null + */ + ui_model_type: components["schemas"]["ModelType"][] | null; + /** + * Ui Model Variant + * @default null + */ + ui_model_variant: (components["schemas"]["ClipVariantType"] | components["schemas"]["ModelVariantType"])[] | null; + /** + * Ui Model Format + * @default null + */ + ui_model_format: components["schemas"]["ModelFormat"][] | null; + }; + /** + * InstallStatus + * @description State of an install job running in the background. + * @enum {string} + */ + InstallStatus: "waiting" | "downloading" | "downloads_done" | "running" | "completed" | "error" | "cancelled"; + /** + * Integer Batch + * @description Create a batched generation, where the workflow is executed once for each integer in the batch. + */ + IntegerBatchInvocation: { + /** + * 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; + /** + * Batch Group + * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. + * @default None + * @enum {string} + */ + batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; + /** + * Integers + * @description The integers to batch over + * @default null + */ + integers?: number[] | null; + /** + * type + * @default integer_batch + * @constant + */ + type: "integer_batch"; + }; + /** + * Integer Collection Primitive + * @description A collection of integer primitive values + */ + IntegerCollectionInvocation: { + /** + * 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; + /** + * Collection + * @description The collection of integer values + * @default [] + */ + collection?: number[]; + /** + * type + * @default integer_collection + * @constant + */ + type: "integer_collection"; + }; + /** + * IntegerCollectionOutput + * @description Base class for nodes that output a collection of integers + */ + IntegerCollectionOutput: { + /** + * Collection + * @description The int collection + */ + collection: number[]; + /** + * type + * @default integer_collection_output + * @constant + */ + type: "integer_collection_output"; + }; + /** + * Integer Generator + * @description Generated a range of integers for use in a batched generation + */ + IntegerGenerator: { + /** + * 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; + /** + * Generator Type + * @description The integer generator. + */ + generator: components["schemas"]["IntegerGeneratorField"]; + /** + * type + * @default integer_generator + * @constant + */ + type: "integer_generator"; + }; + /** IntegerGeneratorField */ + IntegerGeneratorField: Record; + /** IntegerGeneratorOutput */ + IntegerGeneratorOutput: { + /** + * Integers + * @description The generated integers + */ + integers: number[]; + /** + * type + * @default integer_generator_output + * @constant + */ + type: "integer_generator_output"; + }; + /** + * Integer Primitive + * @description An integer primitive value + */ + IntegerInvocation: { + /** + * 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; + /** + * Value + * @description The integer value + * @default 0 + */ + value?: number; + /** + * type + * @default integer + * @constant + */ + type: "integer"; + }; + /** + * Integer Math + * @description Performs integer math. + */ + IntegerMathInvocation: { + /** + * 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; + /** + * Operation + * @description The operation to perform + * @default ADD + * @enum {string} + */ + operation?: "ADD" | "SUB" | "MUL" | "DIV" | "EXP" | "MOD" | "ABS" | "MIN" | "MAX"; + /** + * A + * @description The first number + * @default 1 + */ + a?: number; + /** + * B + * @description The second number + * @default 1 + */ + b?: number; + /** + * type + * @default integer_math + * @constant + */ + type: "integer_math"; + }; + /** + * IntegerOutput + * @description Base class for nodes that output a single integer + */ + IntegerOutput: { + /** + * Value + * @description The output integer + */ + value: number; + /** + * type + * @default integer_output + * @constant + */ + type: "integer_output"; + }; + /** + * Invert Tensor Mask + * @description Inverts a tensor mask. + */ + InvertTensorMaskInvocation: { + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The tensor mask to convert. + * @default null + */ + mask?: components["schemas"]["TensorField"] | null; + /** + * type + * @default invert_tensor_mask + * @constant + */ + type: "invert_tensor_mask"; + }; + /** InvocationCacheStatus */ + InvocationCacheStatus: { + /** + * Size + * @description The current size of the invocation cache + */ + size: number; + /** + * Hits + * @description The number of cache hits + */ + hits: number; + /** + * Misses + * @description The number of cache misses + */ + misses: number; + /** + * Enabled + * @description Whether the invocation cache is enabled + */ + enabled: boolean; + /** + * Max Size + * @description The maximum size of the invocation cache + */ + max_size: number; + }; + /** + * InvocationCompleteEvent + * @description Event model for invocation_complete + */ + InvocationCompleteEvent: { + /** + * Timestamp + * @description The timestamp of the event + */ + timestamp: number; + /** + * Queue Id + * @description The ID of the queue + */ + queue_id: string; + /** + * Item Id + * @description The ID of the queue item + */ + item_id: number; + /** + * Batch Id + * @description The ID of the queue batch + */ + batch_id: string; + /** + * Origin + * @description The origin of the queue item + * @default null + */ + origin: string | null; + /** + * Destination + * @description The destination of the queue item + * @default null + */ + destination: string | null; + /** + * Session Id + * @description The ID of the session (aka graph execution state) + */ + session_id: string; + /** + * Invocation + * @description The ID of the invocation + */ + invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | 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"]["CannyEdgeDetectionInvocation"] | 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"]["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"]["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"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | 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"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["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"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | 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"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"]; + /** + * Invocation Source Id + * @description The ID of the prepared invocation's source node + */ + invocation_source_id: string; + /** + * Result + * @description The result of the invocation + */ + result: 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"]["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"]["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"]["PairTileImageOutput"] | 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"]["VideoOutput"]; + }; + /** + * InvocationErrorEvent + * @description Event model for invocation_error + */ + InvocationErrorEvent: { + /** + * Timestamp + * @description The timestamp of the event + */ + timestamp: number; + /** + * Queue Id + * @description The ID of the queue + */ + queue_id: string; + /** + * Item Id + * @description The ID of the queue item + */ + item_id: number; + /** + * Batch Id + * @description The ID of the queue batch + */ + batch_id: string; + /** + * Origin + * @description The origin of the queue item + * @default null + */ + origin: string | null; + /** + * Destination + * @description The destination of the queue item + * @default null + */ + destination: string | null; + /** + * Session Id + * @description The ID of the session (aka graph execution state) + */ + session_id: string; + /** + * Invocation + * @description The ID of the invocation + */ + invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | 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"]["CannyEdgeDetectionInvocation"] | 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"]["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"]["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"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | 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"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["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"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | 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"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"]; + /** + * Invocation Source Id + * @description The ID of the prepared invocation's source node + */ + invocation_source_id: string; + /** + * Error Type + * @description The error type + */ + error_type: string; + /** + * Error Message + * @description The error message + */ + error_message: string; + /** + * Error Traceback + * @description The error traceback + */ + error_traceback: string; + /** + * User Id + * @description The ID of the user who created the invocation + * @default null + */ + user_id: string | null; + /** + * Project Id + * @description The ID of the user who created the invocation + * @default null + */ + project_id: string | null; + }; + InvocationOutputMap: { + add: components["schemas"]["IntegerOutput"]; + alpha_mask_to_tensor: components["schemas"]["MaskOutput"]; + apply_mask_to_image: components["schemas"]["ImageOutput"]; + apply_tensor_mask_to_image: components["schemas"]["ImageOutput"]; + blank_image: components["schemas"]["ImageOutput"]; + boolean: components["schemas"]["BooleanOutput"]; + boolean_collection: components["schemas"]["BooleanCollectionOutput"]; + bounding_box: components["schemas"]["BoundingBoxOutput"]; + calculate_image_tiles: components["schemas"]["CalculateImageTilesOutput"]; + calculate_image_tiles_even_split: components["schemas"]["CalculateImageTilesOutput"]; + calculate_image_tiles_min_overlap: components["schemas"]["CalculateImageTilesOutput"]; + canny_edge_detection: components["schemas"]["ImageOutput"]; + canvas_paste_back: components["schemas"]["ImageOutput"]; + canvas_v2_mask_and_crop: components["schemas"]["ImageOutput"]; + clip_skip: components["schemas"]["CLIPSkipInvocationOutput"]; + cogview4_denoise: components["schemas"]["LatentsOutput"]; + cogview4_i2l: components["schemas"]["LatentsOutput"]; + cogview4_l2i: components["schemas"]["ImageOutput"]; + cogview4_model_loader: components["schemas"]["CogView4ModelLoaderOutput"]; + cogview4_text_encoder: components["schemas"]["CogView4ConditioningOutput"]; + collect: components["schemas"]["CollectInvocationOutput"]; + color: components["schemas"]["ColorOutput"]; + color_correct: components["schemas"]["ImageOutput"]; + color_map: components["schemas"]["ImageOutput"]; + compel: components["schemas"]["ConditioningOutput"]; + conditioning: components["schemas"]["ConditioningOutput"]; + conditioning_collection: components["schemas"]["ConditioningCollectionOutput"]; + content_shuffle: components["schemas"]["ImageOutput"]; + controlnet: components["schemas"]["ControlOutput"]; + core_metadata: components["schemas"]["MetadataOutput"]; + create_denoise_mask: components["schemas"]["DenoiseMaskOutput"]; + create_gradient_mask: components["schemas"]["GradientMaskOutput"]; + crop_image_to_bounding_box: components["schemas"]["ImageOutput"]; + crop_latents: components["schemas"]["LatentsOutput"]; + cv_inpaint: components["schemas"]["ImageOutput"]; + denoise_latents: components["schemas"]["LatentsOutput"]; denoise_latents_meta: components["schemas"]["LatentsMetaOutput"]; depth_anything_depth_estimation: components["schemas"]["ImageOutput"]; div: components["schemas"]["IntegerOutput"]; @@ -12524,788 +13783,2814 @@ export type components = { unsharp_mask: components["schemas"]["ImageOutput"]; vae_loader: components["schemas"]["VAEOutput"]; }; - /** - * InvocationProgressEvent - * @description Event model for invocation_progress - */ - InvocationProgressEvent: { + /** + * InvocationProgressEvent + * @description Event model for invocation_progress + */ + InvocationProgressEvent: { + /** + * Timestamp + * @description The timestamp of the event + */ + timestamp: number; + /** + * Queue Id + * @description The ID of the queue + */ + queue_id: string; + /** + * Item Id + * @description The ID of the queue item + */ + item_id: number; + /** + * Batch Id + * @description The ID of the queue batch + */ + batch_id: string; + /** + * Origin + * @description The origin of the queue item + * @default null + */ + origin: string | null; + /** + * Destination + * @description The destination of the queue item + * @default null + */ + destination: string | null; + /** + * Session Id + * @description The ID of the session (aka graph execution state) + */ + session_id: string; + /** + * Invocation + * @description The ID of the invocation + */ + invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | 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"]["CannyEdgeDetectionInvocation"] | 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"]["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"]["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"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | 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"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["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"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | 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"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"]; + /** + * Invocation Source Id + * @description The ID of the prepared invocation's source node + */ + invocation_source_id: string; + /** + * Message + * @description A message to display + */ + message: string; + /** + * Percentage + * @description The percentage of the progress (omit to indicate indeterminate progress) + * @default null + */ + percentage: number | null; + /** + * @description An image representing the current state of the progress + * @default null + */ + image: components["schemas"]["ProgressImage"] | null; + }; + /** + * InvocationStartedEvent + * @description Event model for invocation_started + */ + InvocationStartedEvent: { + /** + * Timestamp + * @description The timestamp of the event + */ + timestamp: number; + /** + * Queue Id + * @description The ID of the queue + */ + queue_id: string; + /** + * Item Id + * @description The ID of the queue item + */ + item_id: number; + /** + * Batch Id + * @description The ID of the queue batch + */ + batch_id: string; + /** + * Origin + * @description The origin of the queue item + * @default null + */ + origin: string | null; + /** + * Destination + * @description The destination of the queue item + * @default null + */ + destination: string | null; + /** + * Session Id + * @description The ID of the session (aka graph execution state) + */ + session_id: string; + /** + * Invocation + * @description The ID of the invocation + */ + invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | 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"]["CannyEdgeDetectionInvocation"] | 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"]["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"]["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"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | 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"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["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"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | 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"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"]; + /** + * Invocation Source Id + * @description The ID of the prepared invocation's source node + */ + invocation_source_id: string; + }; + /** + * InvokeAIAppConfig + * @description Invoke's global app configuration. + * + * Typically, you won't need to interact with this class directly. Instead, use the `get_config` function from `invokeai.app.services.config` to get a singleton config object. + * + * Attributes: + * host: IP address to bind to. Use `0.0.0.0` to serve to your local network. + * port: Port to bind to. + * allow_origins: Allowed CORS origins. + * allow_credentials: Allow CORS credentials. + * allow_methods: Methods allowed for CORS. + * allow_headers: Headers allowed for CORS. + * ssl_certfile: SSL certificate file for HTTPS. See https://www.uvicorn.org/settings/#https. + * ssl_keyfile: SSL key file for HTTPS. See https://www.uvicorn.org/settings/#https. + * log_tokenization: Enable logging of parsed prompt tokens. + * patchmatch: Enable patchmatch inpaint code. + * models_dir: Path to the models directory. + * convert_cache_dir: Path to the converted models cache directory (DEPRECATED, but do not delete because it is needed for migration from previous versions). + * download_cache_dir: Path to the directory that contains dynamically downloaded models. + * legacy_conf_dir: Path to directory of legacy checkpoint config files. + * db_dir: Path to InvokeAI databases directory. + * outputs_dir: Path to directory for outputs. + * custom_nodes_dir: Path to directory for custom nodes. + * style_presets_dir: Path to directory for style presets. + * workflow_thumbnails_dir: Path to directory for workflow thumbnails. + * log_handlers: Log handler. Valid options are "console", "file=", "syslog=path|address:host:port", "http=". + * log_format: Log format. Use "plain" for text-only, "color" for colorized output, "legacy" for 2.3-style logging and "syslog" for syslog-style.
Valid values: `plain`, `color`, `syslog`, `legacy` + * log_level: Emit logging messages at this level or higher.
Valid values: `debug`, `info`, `warning`, `error`, `critical` + * log_sql: Log SQL queries. `log_level` must be `debug` for this to do anything. Extremely verbose. + * log_level_network: Log level for network-related messages. 'info' and 'debug' are very verbose.
Valid values: `debug`, `info`, `warning`, `error`, `critical` + * use_memory_db: Use in-memory database. Useful for development. + * dev_reload: Automatically reload when Python sources are changed. Does not reload node definitions. + * profile_graphs: Enable graph profiling using `cProfile`. + * profile_prefix: An optional prefix for profile output files. + * profiles_dir: Path to profiles output directory. + * max_cache_ram_gb: The maximum amount of CPU RAM to use for model caching in GB. If unset, the limit will be configured based on the available RAM. In most cases, it is recommended to leave this unset. + * max_cache_vram_gb: The amount of VRAM to use for model caching in GB. If unset, the limit will be configured based on the available VRAM and the device_working_mem_gb. In most cases, it is recommended to leave this unset. + * log_memory_usage: If True, a memory snapshot will be captured before and after every model cache operation, and the result will be logged (at debug level). There is a time cost to capturing the memory snapshots, so it is recommended to only enable this feature if you are actively inspecting the model cache's behaviour. + * device_working_mem_gb: The amount of working memory to keep available on the compute device (in GB). Has no effect if running on CPU. If you are experiencing OOM errors, try increasing this value. + * enable_partial_loading: Enable partial loading of models. This enables models to run with reduced VRAM requirements (at the cost of slower speed) by streaming the model from RAM to VRAM as its used. In some edge cases, partial loading can cause models to run more slowly if they were previously being fully loaded into VRAM. + * keep_ram_copy_of_weights: Whether to keep a full RAM copy of a model's weights when the model is loaded in VRAM. Keeping a RAM copy increases average RAM usage, but speeds up model switching and LoRA patching (assuming there is sufficient RAM). Set this to False if RAM pressure is consistently high. + * ram: DEPRECATED: This setting is no longer used. It has been replaced by `max_cache_ram_gb`, but most users will not need to use this config since automatic cache size limits should work well in most cases. This config setting will be removed once the new model cache behavior is stable. + * vram: DEPRECATED: This setting is no longer used. It has been replaced by `max_cache_vram_gb`, but most users will not need to use this config since automatic cache size limits should work well in most cases. This config setting will be removed once the new model cache behavior is stable. + * lazy_offload: DEPRECATED: This setting is no longer used. Lazy-offloading is enabled by default. This config setting will be removed once the new model cache behavior is stable. + * pytorch_cuda_alloc_conf: Configure the Torch CUDA memory allocator. This will impact peak reserved VRAM usage and performance. Setting to "backend:cudaMallocAsync" works well on many systems. The optimal configuration is highly dependent on the system configuration (device type, VRAM, CUDA driver version, etc.), so must be tuned experimentally. + * device: Preferred execution device. `auto` will choose the device depending on the hardware platform and the installed torch capabilities.
Valid values: `auto`, `cpu`, `cuda`, `mps`, `cuda:N` (where N is a device number) + * precision: Floating point precision. `float16` will consume half the memory of `float32` but produce slightly lower-quality images. The `auto` setting will guess the proper precision based on your video card and operating system.
Valid values: `auto`, `float16`, `bfloat16`, `float32` + * sequential_guidance: Whether to calculate guidance in serial instead of in parallel, lowering memory requirements. + * attention_type: Attention type.
Valid values: `auto`, `normal`, `xformers`, `sliced`, `torch-sdp` + * attention_slice_size: Slice size, valid when attention_type=="sliced".
Valid values: `auto`, `balanced`, `max`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8` + * force_tiled_decode: Whether to enable tiled VAE decode (reduces memory consumption with some performance penalty). + * pil_compress_level: The compress_level setting of PIL.Image.save(), used for PNG encoding. All settings are lossless. 0 = no compression, 1 = fastest with slightly larger filesize, 9 = slowest with smallest filesize. 1 is typically the best setting. + * max_queue_size: Maximum number of items in the session queue. + * clear_queue_on_startup: Empties session queue on startup. + * allow_nodes: List of nodes to allow. Omit to allow all. + * deny_nodes: List of nodes to deny. Omit to deny none. + * node_cache_size: How many cached nodes to keep in memory. + * hashing_algorithm: Model hashing algorthim for model installs. 'blake3_multi' is best for SSDs. 'blake3_single' is best for spinning disk HDDs. 'random' disables hashing, instead assigning a UUID to models. Useful when using a memory db to reduce model installation time, or if you don't care about storing stable hashes for models. Alternatively, any other hashlib algorithm is accepted, though these are not nearly as performant as blake3.
Valid values: `blake3_multi`, `blake3_single`, `random`, `md5`, `sha1`, `sha224`, `sha256`, `sha384`, `sha512`, `blake2b`, `blake2s`, `sha3_224`, `sha3_256`, `sha3_384`, `sha3_512`, `shake_128`, `shake_256` + * remote_api_tokens: List of regular expression and token pairs used when downloading models from URLs. The download URL is tested against the regex, and if it matches, the token is provided in as a Bearer token. + * scan_models_on_startup: Scan the models directory on startup, registering orphaned models. This is typically only used in conjunction with `use_memory_db` for testing purposes. + * unsafe_disable_picklescan: UNSAFE. Disable the picklescan security check during model installation. Recommended only for development and testing purposes. This will allow arbitrary code execution during model installation, so should never be used in production. + * allow_unknown_models: Allow installation of models that we are unable to identify. If enabled, models will be marked as `unknown` in the database, and will not have any metadata associated with them. If disabled, unknown models will be rejected during installation. + */ + InvokeAIAppConfig: { + /** + * Schema Version + * @description Schema version of the config file. This is not a user-configurable setting. + * @default 4.0.2 + */ + schema_version?: string; + /** + * Legacy Models Yaml Path + * @description Path to the legacy models.yaml file. This is not a user-configurable setting. + */ + legacy_models_yaml_path?: string | null; + /** + * Host + * @description IP address to bind to. Use `0.0.0.0` to serve to your local network. + * @default 127.0.0.1 + */ + host?: string; + /** + * Port + * @description Port to bind to. + * @default 9090 + */ + port?: number; + /** + * Allow Origins + * @description Allowed CORS origins. + * @default [] + */ + allow_origins?: string[]; + /** + * Allow Credentials + * @description Allow CORS credentials. + * @default true + */ + allow_credentials?: boolean; + /** + * Allow Methods + * @description Methods allowed for CORS. + * @default [ + * "*" + * ] + */ + allow_methods?: string[]; + /** + * Allow Headers + * @description Headers allowed for CORS. + * @default [ + * "*" + * ] + */ + allow_headers?: string[]; + /** + * Ssl Certfile + * @description SSL certificate file for HTTPS. See https://www.uvicorn.org/settings/#https. + */ + ssl_certfile?: string | null; + /** + * Ssl Keyfile + * @description SSL key file for HTTPS. See https://www.uvicorn.org/settings/#https. + */ + ssl_keyfile?: string | null; + /** + * Log Tokenization + * @description Enable logging of parsed prompt tokens. + * @default false + */ + log_tokenization?: boolean; + /** + * Patchmatch + * @description Enable patchmatch inpaint code. + * @default true + */ + patchmatch?: boolean; + /** + * Models Dir + * Format: path + * @description Path to the models directory. + * @default models + */ + models_dir?: string; + /** + * Convert Cache Dir + * Format: path + * @description Path to the converted models cache directory (DEPRECATED, but do not delete because it is needed for migration from previous versions). + * @default models/.convert_cache + */ + convert_cache_dir?: string; + /** + * Download Cache Dir + * Format: path + * @description Path to the directory that contains dynamically downloaded models. + * @default models/.download_cache + */ + download_cache_dir?: string; + /** + * Legacy Conf Dir + * Format: path + * @description Path to directory of legacy checkpoint config files. + * @default configs + */ + legacy_conf_dir?: string; + /** + * Db Dir + * Format: path + * @description Path to InvokeAI databases directory. + * @default databases + */ + db_dir?: string; + /** + * Outputs Dir + * Format: path + * @description Path to directory for outputs. + * @default outputs + */ + outputs_dir?: string; + /** + * Custom Nodes Dir + * Format: path + * @description Path to directory for custom nodes. + * @default nodes + */ + custom_nodes_dir?: string; + /** + * Style Presets Dir + * Format: path + * @description Path to directory for style presets. + * @default style_presets + */ + style_presets_dir?: string; + /** + * Workflow Thumbnails Dir + * Format: path + * @description Path to directory for workflow thumbnails. + * @default workflow_thumbnails + */ + workflow_thumbnails_dir?: string; + /** + * Log Handlers + * @description Log handler. Valid options are "console", "file=", "syslog=path|address:host:port", "http=". + * @default [ + * "console" + * ] + */ + log_handlers?: string[]; + /** + * Log Format + * @description Log format. Use "plain" for text-only, "color" for colorized output, "legacy" for 2.3-style logging and "syslog" for syslog-style. + * @default color + * @enum {string} + */ + log_format?: "plain" | "color" | "syslog" | "legacy"; + /** + * Log Level + * @description Emit logging messages at this level or higher. + * @default info + * @enum {string} + */ + log_level?: "debug" | "info" | "warning" | "error" | "critical"; + /** + * Log Sql + * @description Log SQL queries. `log_level` must be `debug` for this to do anything. Extremely verbose. + * @default false + */ + log_sql?: boolean; + /** + * Log Level Network + * @description Log level for network-related messages. 'info' and 'debug' are very verbose. + * @default warning + * @enum {string} + */ + log_level_network?: "debug" | "info" | "warning" | "error" | "critical"; + /** + * Use Memory Db + * @description Use in-memory database. Useful for development. + * @default false + */ + use_memory_db?: boolean; + /** + * Dev Reload + * @description Automatically reload when Python sources are changed. Does not reload node definitions. + * @default false + */ + dev_reload?: boolean; + /** + * Profile Graphs + * @description Enable graph profiling using `cProfile`. + * @default false + */ + profile_graphs?: boolean; + /** + * Profile Prefix + * @description An optional prefix for profile output files. + */ + profile_prefix?: string | null; + /** + * Profiles Dir + * Format: path + * @description Path to profiles output directory. + * @default profiles + */ + profiles_dir?: string; + /** + * Max Cache Ram Gb + * @description The maximum amount of CPU RAM to use for model caching in GB. If unset, the limit will be configured based on the available RAM. In most cases, it is recommended to leave this unset. + */ + max_cache_ram_gb?: number | null; + /** + * Max Cache Vram Gb + * @description The amount of VRAM to use for model caching in GB. If unset, the limit will be configured based on the available VRAM and the device_working_mem_gb. In most cases, it is recommended to leave this unset. + */ + max_cache_vram_gb?: number | null; + /** + * Log Memory Usage + * @description If True, a memory snapshot will be captured before and after every model cache operation, and the result will be logged (at debug level). There is a time cost to capturing the memory snapshots, so it is recommended to only enable this feature if you are actively inspecting the model cache's behaviour. + * @default false + */ + log_memory_usage?: boolean; + /** + * Device Working Mem Gb + * @description The amount of working memory to keep available on the compute device (in GB). Has no effect if running on CPU. If you are experiencing OOM errors, try increasing this value. + * @default 3 + */ + device_working_mem_gb?: number; + /** + * Enable Partial Loading + * @description Enable partial loading of models. This enables models to run with reduced VRAM requirements (at the cost of slower speed) by streaming the model from RAM to VRAM as its used. In some edge cases, partial loading can cause models to run more slowly if they were previously being fully loaded into VRAM. + * @default false + */ + enable_partial_loading?: boolean; + /** + * Keep Ram Copy Of Weights + * @description Whether to keep a full RAM copy of a model's weights when the model is loaded in VRAM. Keeping a RAM copy increases average RAM usage, but speeds up model switching and LoRA patching (assuming there is sufficient RAM). Set this to False if RAM pressure is consistently high. + * @default true + */ + keep_ram_copy_of_weights?: boolean; + /** + * Ram + * @description DEPRECATED: This setting is no longer used. It has been replaced by `max_cache_ram_gb`, but most users will not need to use this config since automatic cache size limits should work well in most cases. This config setting will be removed once the new model cache behavior is stable. + */ + ram?: number | null; + /** + * Vram + * @description DEPRECATED: This setting is no longer used. It has been replaced by `max_cache_vram_gb`, but most users will not need to use this config since automatic cache size limits should work well in most cases. This config setting will be removed once the new model cache behavior is stable. + */ + vram?: number | null; + /** + * Lazy Offload + * @description DEPRECATED: This setting is no longer used. Lazy-offloading is enabled by default. This config setting will be removed once the new model cache behavior is stable. + * @default true + */ + lazy_offload?: boolean; + /** + * Pytorch Cuda Alloc Conf + * @description Configure the Torch CUDA memory allocator. This will impact peak reserved VRAM usage and performance. Setting to "backend:cudaMallocAsync" works well on many systems. The optimal configuration is highly dependent on the system configuration (device type, VRAM, CUDA driver version, etc.), so must be tuned experimentally. + */ + pytorch_cuda_alloc_conf?: string | null; + /** + * Device + * @description Preferred execution device. `auto` will choose the device depending on the hardware platform and the installed torch capabilities.
Valid values: `auto`, `cpu`, `cuda`, `mps`, `cuda:N` (where N is a device number) + * @default auto + */ + device?: string; + /** + * Precision + * @description Floating point precision. `float16` will consume half the memory of `float32` but produce slightly lower-quality images. The `auto` setting will guess the proper precision based on your video card and operating system. + * @default auto + * @enum {string} + */ + precision?: "auto" | "float16" | "bfloat16" | "float32"; + /** + * Sequential Guidance + * @description Whether to calculate guidance in serial instead of in parallel, lowering memory requirements. + * @default false + */ + sequential_guidance?: boolean; + /** + * Attention Type + * @description Attention type. + * @default auto + * @enum {string} + */ + attention_type?: "auto" | "normal" | "xformers" | "sliced" | "torch-sdp"; + /** + * Attention Slice Size + * @description Slice size, valid when attention_type=="sliced". + * @default auto + * @enum {unknown} + */ + attention_slice_size?: "auto" | "balanced" | "max" | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; + /** + * Force Tiled Decode + * @description Whether to enable tiled VAE decode (reduces memory consumption with some performance penalty). + * @default false + */ + force_tiled_decode?: boolean; + /** + * Pil Compress Level + * @description The compress_level setting of PIL.Image.save(), used for PNG encoding. All settings are lossless. 0 = no compression, 1 = fastest with slightly larger filesize, 9 = slowest with smallest filesize. 1 is typically the best setting. + * @default 1 + */ + pil_compress_level?: number; + /** + * Max Queue Size + * @description Maximum number of items in the session queue. + * @default 10000 + */ + max_queue_size?: number; + /** + * Clear Queue On Startup + * @description Empties session queue on startup. + * @default false + */ + clear_queue_on_startup?: boolean; + /** + * Allow Nodes + * @description List of nodes to allow. Omit to allow all. + */ + allow_nodes?: string[] | null; + /** + * Deny Nodes + * @description List of nodes to deny. Omit to deny none. + */ + deny_nodes?: string[] | null; + /** + * Node Cache Size + * @description How many cached nodes to keep in memory. + * @default 512 + */ + node_cache_size?: number; + /** + * Hashing Algorithm + * @description Model hashing algorthim for model installs. 'blake3_multi' is best for SSDs. 'blake3_single' is best for spinning disk HDDs. 'random' disables hashing, instead assigning a UUID to models. Useful when using a memory db to reduce model installation time, or if you don't care about storing stable hashes for models. Alternatively, any other hashlib algorithm is accepted, though these are not nearly as performant as blake3. + * @default blake3_single + * @enum {string} + */ + hashing_algorithm?: "blake3_multi" | "blake3_single" | "random" | "md5" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512" | "blake2b" | "blake2s" | "sha3_224" | "sha3_256" | "sha3_384" | "sha3_512" | "shake_128" | "shake_256"; + /** + * Remote Api Tokens + * @description List of regular expression and token pairs used when downloading models from URLs. The download URL is tested against the regex, and if it matches, the token is provided in as a Bearer token. + */ + remote_api_tokens?: components["schemas"]["URLRegexTokenPair"][] | null; + /** + * Scan Models On Startup + * @description Scan the models directory on startup, registering orphaned models. This is typically only used in conjunction with `use_memory_db` for testing purposes. + * @default false + */ + scan_models_on_startup?: boolean; + /** + * Unsafe Disable Picklescan + * @description UNSAFE. Disable the picklescan security check during model installation. Recommended only for development and testing purposes. This will allow arbitrary code execution during model installation, so should never be used in production. + * @default false + */ + unsafe_disable_picklescan?: boolean; + /** + * Allow Unknown Models + * @description Allow installation of models that we are unable to identify. If enabled, models will be marked as `unknown` in the database, and will not have any metadata associated with them. If disabled, unknown models will be rejected during installation. + * @default true + */ + allow_unknown_models?: boolean; + }; + /** + * InvokeAIAppConfigWithSetFields + * @description InvokeAI App Config with model fields set + */ + InvokeAIAppConfigWithSetFields: { + /** + * Set Fields + * @description The set fields + */ + set_fields: string[]; + /** @description The InvokeAI App Config */ + config: components["schemas"]["InvokeAIAppConfig"]; + }; + /** + * Adjust Image Hue Plus + * @description Adjusts the Hue of an image by rotating it in the selected color space. Originally created by @dwringer + */ + InvokeAdjustImageHuePlusInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to adjust + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Space + * @description Color space in which to rotate hue by polar coords (*: non-invertible) + * @default HSV / HSL / RGB + * @enum {string} + */ + space?: "HSV / HSL / RGB" | "Okhsl" | "Okhsv" | "*Oklch / Oklab" | "*LCh / CIELab" | "*UPLab (w/CIELab_to_UPLab.icc)"; + /** + * Degrees + * @description Degrees by which to rotate image hue + * @default 0 + */ + degrees?: number; + /** + * Preserve Lightness + * @description Whether to preserve CIELAB lightness values + * @default false + */ + preserve_lightness?: boolean; + /** + * Ok Adaptive Gamut + * @description Higher preserves chroma at the expense of lightness (Oklab) + * @default 0.05 + */ + ok_adaptive_gamut?: number; + /** + * Ok High Precision + * @description Use more steps in computing gamut (Oklab/Okhsv/Okhsl) + * @default true + */ + ok_high_precision?: boolean; + /** + * type + * @default invokeai_img_hue_adjust_plus + * @constant + */ + type: "invokeai_img_hue_adjust_plus"; + }; + /** + * Equivalent Achromatic Lightness + * @description Calculate Equivalent Achromatic Lightness from image. Originally created by @dwringer + */ + InvokeEquivalentAchromaticLightnessInvocation: { + /** + * @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 Image from which to get channel + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * type + * @default invokeai_ealightness + * @constant + */ + type: "invokeai_ealightness"; + }; + /** + * Image Layer Blend + * @description Blend two images together, with optional opacity, mask, and blend modes. Originally created by @dwringer + */ + InvokeImageBlendInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The top image to blend + * @default null + */ + layer_upper?: components["schemas"]["ImageField"] | null; + /** + * Blend Mode + * @description Available blend modes + * @default Normal + * @enum {string} + */ + blend_mode?: "Normal" | "Lighten Only" | "Darken Only" | "Lighten Only (EAL)" | "Darken Only (EAL)" | "Hue" | "Saturation" | "Color" | "Luminosity" | "Linear Dodge (Add)" | "Subtract" | "Multiply" | "Divide" | "Screen" | "Overlay" | "Linear Burn" | "Difference" | "Hard Light" | "Soft Light" | "Vivid Light" | "Linear Light" | "Color Burn" | "Color Dodge"; + /** + * Opacity + * @description Desired opacity of the upper layer + * @default 1 + */ + opacity?: number; + /** + * @description Optional mask, used to restrict areas from blending + * @default null + */ + mask?: components["schemas"]["ImageField"] | null; + /** + * Fit To Width + * @description Scale upper layer to fit base width + * @default false + */ + fit_to_width?: boolean; + /** + * Fit To Height + * @description Scale upper layer to fit base height + * @default true + */ + fit_to_height?: boolean; + /** + * @description The bottom image to blend + * @default null + */ + layer_base?: components["schemas"]["ImageField"] | null; + /** + * Color Space + * @description Available color spaces for blend computations + * @default RGB + * @enum {string} + */ + color_space?: "RGB" | "Linear RGB" | "HSL (RGB)" | "HSV (RGB)" | "Okhsl" | "Okhsv" | "Oklch (Oklab)" | "LCh (CIELab)"; + /** + * Adaptive Gamut + * @description Adaptive gamut clipping (0=off). Higher prioritizes chroma over lightness + * @default 0 + */ + adaptive_gamut?: number; + /** + * High Precision + * @description Use more steps in computing gamut when possible + * @default true + */ + high_precision?: boolean; + /** + * type + * @default invokeai_img_blend + * @constant + */ + type: "invokeai_img_blend"; + }; + /** + * Image Compositor + * @description Removes backdrop from subject image then overlays subject on background image. Originally created by @dwringer + */ + InvokeImageCompositorInvocation: { + /** + * @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 Image of the subject on a plain monochrome background + * @default null + */ + image_subject?: components["schemas"]["ImageField"] | null; + /** + * @description Image of a background scene + * @default null + */ + image_background?: components["schemas"]["ImageField"] | null; + /** + * Chroma Key + * @description Can be empty for corner flood select, or CSS-3 color or tuple + * @default + */ + chroma_key?: string; + /** + * Threshold + * @description Subject isolation flood-fill threshold + * @default 50 + */ + threshold?: number; + /** + * Fill X + * @description Scale base subject image to fit background width + * @default false + */ + fill_x?: boolean; + /** + * Fill Y + * @description Scale base subject image to fit background height + * @default true + */ + fill_y?: boolean; + /** + * X Offset + * @description x-offset for the subject + * @default 0 + */ + x_offset?: number; + /** + * Y Offset + * @description y-offset for the subject + * @default 0 + */ + y_offset?: number; + /** + * type + * @default invokeai_img_composite + * @constant + */ + type: "invokeai_img_composite"; + }; + /** + * Image Dilate or Erode + * @description Dilate (expand) or erode (contract) an image. Originally created by @dwringer + */ + InvokeImageDilateOrErodeInvocation: { + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image from which to create a mask + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Lightness Only + * @description If true, only applies to image lightness (CIELa*b*) + * @default false + */ + lightness_only?: boolean; + /** + * Radius W + * @description Width (in pixels) by which to dilate(expand) or erode (contract) the image + * @default 4 + */ + radius_w?: number; + /** + * Radius H + * @description Height (in pixels) by which to dilate(expand) or erode (contract) the image + * @default 4 + */ + radius_h?: number; + /** + * Mode + * @description How to operate on the image + * @default Dilate + * @enum {string} + */ + mode?: "Dilate" | "Erode"; + /** + * type + * @default invokeai_img_dilate_erode + * @constant + */ + type: "invokeai_img_dilate_erode"; + }; + /** + * Enhance Image + * @description Applies processing from PIL's ImageEnhance module. Originally created by @dwringer + */ + InvokeImageEnhanceInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image for which to apply processing + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Invert + * @description Whether to invert the image colors + * @default false + */ + invert?: boolean; + /** + * Color + * @description Color enhancement factor + * @default 1 + */ + color?: number; + /** + * Contrast + * @description Contrast enhancement factor + * @default 1 + */ + contrast?: number; + /** + * Brightness + * @description Brightness enhancement factor + * @default 1 + */ + brightness?: number; + /** + * Sharpness + * @description Sharpness enhancement factor + * @default 1 + */ + sharpness?: number; + /** + * type + * @default invokeai_img_enhance + * @constant + */ + type: "invokeai_img_enhance"; + }; + /** + * Image Value Thresholds + * @description Clip image to pure black/white past specified thresholds. Originally created by @dwringer + */ + InvokeImageValueThresholdsInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image from which to create a mask + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Invert Output + * @description Make light areas dark and vice versa + * @default false + */ + invert_output?: boolean; + /** + * Renormalize Values + * @description Rescale remaining values from minimum to maximum + * @default false + */ + renormalize_values?: boolean; + /** + * Lightness Only + * @description If true, only applies to image lightness (CIELa*b*) + * @default false + */ + lightness_only?: boolean; + /** + * Threshold Upper + * @description Threshold above which will be set to full value + * @default 0.5 + */ + threshold_upper?: number; + /** + * Threshold Lower + * @description Threshold below which will be set to minimum value + * @default 0.5 + */ + threshold_lower?: number; + /** + * type + * @default invokeai_img_val_thresholds + * @constant + */ + type: "invokeai_img_val_thresholds"; + }; + /** + * ItemIdsResult + * @description Response containing ordered item ids with metadata for optimistic updates. + */ + ItemIdsResult: { + /** + * Item Ids + * @description Ordered list of item ids + */ + item_ids: number[]; + /** + * Total Count + * @description Total number of queue items matching the query + */ + total_count: number; + }; + /** + * IterateInvocation + * @description Iterates over a list of items + */ + IterateInvocation: { + /** + * 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; + /** + * Collection + * @description The list of items to iterate over + * @default [] + */ + collection?: unknown[]; + /** + * Index + * @description The index, will be provided on executed iterators + * @default 0 + */ + index?: number; + /** + * type + * @default iterate + * @constant + */ + type: "iterate"; + }; + /** + * IterateInvocationOutput + * @description Used to connect iteration outputs. Will be expanded to a specific output. + */ + IterateInvocationOutput: { + /** + * Collection Item + * @description The item being iterated over + */ + item: unknown; + /** + * Index + * @description The index of the item + */ + index: number; + /** + * Total + * @description The total number of items + */ + total: number; + /** + * type + * @default iterate_output + * @constant + */ + type: "iterate_output"; + }; + JsonValue: unknown; + /** + * LaMa Infill + * @description Infills transparent areas of an image using the LaMa model + */ + LaMaInfillInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to process + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * type + * @default infill_lama + * @constant + */ + type: "infill_lama"; + }; + /** + * Latents Collection Primitive + * @description A collection of latents tensor primitive values + */ + LatentsCollectionInvocation: { + /** + * 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; + /** + * Collection + * @description The collection of latents tensors + * @default null + */ + collection?: components["schemas"]["LatentsField"][] | null; + /** + * type + * @default latents_collection + * @constant + */ + type: "latents_collection"; + }; + /** + * LatentsCollectionOutput + * @description Base class for nodes that output a collection of latents tensors + */ + LatentsCollectionOutput: { + /** + * Collection + * @description Latents tensor + */ + collection: components["schemas"]["LatentsField"][]; + /** + * type + * @default latents_collection_output + * @constant + */ + type: "latents_collection_output"; + }; + /** + * LatentsField + * @description A latents tensor primitive field + */ + LatentsField: { + /** + * Latents Name + * @description The name of the latents + */ + latents_name: string; + /** + * Seed + * @description Seed used to generate this latents + * @default null + */ + seed?: number | null; + }; + /** + * Latents Primitive + * @description A latents tensor primitive value + */ + LatentsInvocation: { + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The latents tensor + * @default null + */ + latents?: components["schemas"]["LatentsField"] | null; + /** + * type + * @default latents + * @constant + */ + type: "latents"; + }; + /** + * LatentsMetaOutput + * @description Latents + metadata + */ + LatentsMetaOutput: { + /** @description Metadata Dict */ + metadata: components["schemas"]["MetadataField"]; + /** + * type + * @default latents_meta_output + * @constant + */ + type: "latents_meta_output"; + /** @description Latents tensor */ + latents: components["schemas"]["LatentsField"]; + /** + * Width + * @description Width of output (px) + */ + width: number; + /** + * Height + * @description Height of output (px) + */ + height: number; + }; + /** + * LatentsOutput + * @description Base class for nodes that output a single latents tensor + */ + LatentsOutput: { + /** @description Latents tensor */ + latents: components["schemas"]["LatentsField"]; + /** + * Width + * @description Width of output (px) + */ + width: number; + /** + * Height + * @description Height of output (px) + */ + height: number; + /** + * type + * @default latents_output + * @constant + */ + type: "latents_output"; + }; + /** + * Latents to Image - SD1.5, SDXL + * @description Generates an image from latents. + */ + LatentsToImageInvocation: { + /** + * @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 VAE + * @default null + */ + vae?: components["schemas"]["VAEField"] | null; + /** + * Tiled + * @description Processing using overlapping tiles (reduce memory consumption) + * @default false + */ + tiled?: boolean; + /** + * Tile Size + * @description The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the model will be used. Larger tile sizes generally produce better results at the cost of higher memory usage. + * @default 0 + */ + tile_size?: number; + /** + * Fp32 + * @description Whether or not to use full float32 precision + * @default false + */ + fp32?: boolean; + /** + * type + * @default l2i + * @constant + */ + type: "l2i"; + }; + /** + * Lineart Anime Edge Detection + * @description Geneartes an edge map using the Lineart model. + */ + LineartAnimeEdgeDetectionInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to process + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * type + * @default lineart_anime_edge_detection + * @constant + */ + type: "lineart_anime_edge_detection"; + }; + /** + * Lineart Edge Detection + * @description Generates an edge map using the Lineart model. + */ + LineartEdgeDetectionInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to process + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Coarse + * @description Whether to use coarse mode + * @default false + */ + coarse?: boolean; + /** + * type + * @default lineart_edge_detection + * @constant + */ + type: "lineart_edge_detection"; + }; + /** + * LLaVA OneVision VLLM + * @description Run a LLaVA OneVision VLLM model. + */ + LlavaOnevisionVllmInvocation: { + /** + * 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; + /** + * Images + * @description Input image. + * @default null + */ + images?: (components["schemas"]["ImageField"][] | components["schemas"]["ImageField"]) | null; + /** + * Prompt + * @description Input text prompt. + * @default + */ + prompt?: string; + /** + * LLaVA Model Type + * @description The VLLM model to use + * @default null + */ + vllm_model?: components["schemas"]["ModelIdentifierField"] | null; + /** + * type + * @default llava_onevision_vllm + * @constant + */ + type: "llava_onevision_vllm"; + }; + /** + * LlavaOnevision_Diffusers_Config + * @description Model config for Llava Onevision models. + */ + LlavaOnevision_Diffusers_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; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Submodels + * @description Loadable submodels in this model + */ + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; + /** + * Usage Info + * @description Usage information for this model + */ + usage_info: string | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Type + * @default llava_onevision + * @constant + */ + type: "llava_onevision"; + /** + * Base + * @default any + * @constant + */ + base: "any"; + /** + * Variant + * @default normal + * @constant + */ + variant: "normal"; + }; + /** + * Apply LoRA Collection - SD1.5 + * @description Applies a collection of LoRAs to the provided UNet and CLIP models. + */ + LoRACollectionLoader: { + /** + * 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; + /** + * UNet + * @description UNet (scheduler, LoRAs) + * @default null + */ + unet?: components["schemas"]["UNetField"] | null; + /** + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null + */ + clip?: components["schemas"]["CLIPField"] | null; + /** + * type + * @default lora_collection_loader + * @constant + */ + type: "lora_collection_loader"; + }; + /** LoRAField */ + LoRAField: { + /** @description Info to load lora model */ + lora: components["schemas"]["ModelIdentifierField"]; + /** + * Weight + * @description Weight to apply to lora model + */ + weight: number; + }; + /** + * Apply LoRA - SD1.5 + * @description Apply selected lora to unet and text_encoder. + */ + LoRALoaderInvocation: { + /** + * 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; + /** + * UNet + * @description UNet (scheduler, LoRAs) + * @default null + */ + unet?: components["schemas"]["UNetField"] | null; + /** + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null + */ + clip?: components["schemas"]["CLIPField"] | null; + /** + * type + * @default lora_loader + * @constant + */ + type: "lora_loader"; + }; + /** + * LoRALoaderOutput + * @description Model loader output + */ + LoRALoaderOutput: { + /** + * UNet + * @description UNet (scheduler, LoRAs) + * @default null + */ + unet: components["schemas"]["UNetField"] | null; + /** + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null + */ + clip: components["schemas"]["CLIPField"] | null; + /** + * type + * @default lora_loader_output + * @constant + */ + type: "lora_loader_output"; + }; + /** + * LoRAMetadataField + * @description LoRA Metadata Field + */ + LoRAMetadataField: { + /** @description LoRA model to load */ + model: components["schemas"]["ModelIdentifierField"]; + /** + * Weight + * @description The weight at which the LoRA is applied to each model + */ + weight: number; + }; + /** + * Select LoRA + * @description Selects a LoRA model and weight. + */ + LoRASelectorInvocation: { + /** + * 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; + /** + * type + * @default lora_selector + * @constant + */ + type: "lora_selector"; + }; + /** + * LoRASelectorOutput + * @description Model loader output + */ + LoRASelectorOutput: { + /** + * LoRA + * @description LoRA model and weight + */ + lora: components["schemas"]["LoRAField"]; + /** + * type + * @default lora_selector_output + * @constant + */ + type: "lora_selector_output"; + }; + /** LoRA_Diffusers_FLUX_Config */ + LoRA_Diffusers_FLUX_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; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Submodels + * @description Loadable submodels in this model + */ + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; + /** + * Usage Info + * @description Usage information for this model + */ + usage_info: 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 diffusers + * @constant + */ + format: "diffusers"; + /** + * Base + * @default flux + * @constant + */ + base: "flux"; + }; + /** LoRA_Diffusers_SD1_Config */ + LoRA_Diffusers_SD1_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; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Submodels + * @description Loadable submodels in this model + */ + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; + /** + * Usage Info + * @description Usage information for this model + */ + usage_info: 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 diffusers + * @constant + */ + format: "diffusers"; + /** + * Base + * @default sd-1 + * @constant + */ + base: "sd-1"; + }; + /** LoRA_Diffusers_SD2_Config */ + LoRA_Diffusers_SD2_Config: { /** - * Timestamp - * @description The timestamp of the event + * Key + * @description A unique key for this model. */ - timestamp: number; + key: string; /** - * Queue Id - * @description The ID of the queue + * Hash + * @description The hash of the model file(s). */ - queue_id: string; + hash: string; /** - * Item Id - * @description The ID of the queue item + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - item_id: number; + path: string; /** - * Batch Id - * @description The ID of the queue batch + * File Size + * @description The size of the model in bytes. */ - batch_id: string; + file_size: number; /** - * Origin - * @description The origin of the queue item - * @default null + * Name + * @description Name of the model. */ - origin: string | null; + name: string; /** - * Destination - * @description The destination of the queue item - * @default null + * Description + * @description Model description */ - destination: string | null; + description: string | null; /** - * Session Id - * @description The ID of the session (aka graph execution state) + * Source + * @description The original source of the model (path, URL or repo_id). */ - session_id: string; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Invocation - * @description The ID of the invocation + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | 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"]["CannyEdgeDetectionInvocation"] | 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"]["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"]["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"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | 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"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["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"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | 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"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"]; + source_api_response: string | null; /** - * Invocation Source Id - * @description The ID of the prepared invocation's source node + * Cover Image + * @description Url for image to preview model */ - invocation_source_id: string; + cover_image: string | null; /** - * Message - * @description A message to display + * Submodels + * @description Loadable submodels in this model */ - message: string; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Percentage - * @description The percentage of the progress (omit to indicate indeterminate progress) - * @default null + * Usage Info + * @description Usage information for this model */ - percentage: number | null; + usage_info: string | null; /** - * @description An image representing the current state of the progress - * @default null + * Type + * @default lora + * @constant */ - image: components["schemas"]["ProgressImage"] | null; + 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 diffusers + * @constant + */ + format: "diffusers"; + /** + * Base + * @default sd-2 + * @constant + */ + base: "sd-2"; }; - /** - * InvocationStartedEvent - * @description Event model for invocation_started - */ - InvocationStartedEvent: { + /** LoRA_Diffusers_SDXL_Config */ + LoRA_Diffusers_SDXL_Config: { /** - * Timestamp - * @description The timestamp of the event + * Key + * @description A unique key for this model. */ - timestamp: number; + key: string; /** - * Queue Id - * @description The ID of the queue + * Hash + * @description The hash of the model file(s). */ - queue_id: string; + hash: string; /** - * Item Id - * @description The ID of the queue item + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - item_id: number; + path: string; /** - * Batch Id - * @description The ID of the queue batch + * File Size + * @description The size of the model in bytes. */ - batch_id: string; + file_size: number; /** - * Origin - * @description The origin of the queue item - * @default null + * Name + * @description Name of the model. */ - origin: string | null; + name: string; /** - * Destination - * @description The destination of the queue item - * @default null + * Description + * @description Model description */ - destination: string | null; + description: string | null; /** - * Session Id - * @description The ID of the session (aka graph execution state) + * Source + * @description The original source of the model (path, URL or repo_id). */ - session_id: string; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Invocation - * @description The ID of the invocation + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | 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"]["CannyEdgeDetectionInvocation"] | 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"]["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"]["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"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | 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"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["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"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | 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"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"]; + source_api_response: string | null; /** - * Invocation Source Id - * @description The ID of the prepared invocation's source node + * Cover Image + * @description Url for image to preview model */ - invocation_source_id: string; - }; - /** - * InvokeAIAppConfig - * @description Invoke's global app configuration. - * - * Typically, you won't need to interact with this class directly. Instead, use the `get_config` function from `invokeai.app.services.config` to get a singleton config object. - * - * Attributes: - * host: IP address to bind to. Use `0.0.0.0` to serve to your local network. - * port: Port to bind to. - * allow_origins: Allowed CORS origins. - * allow_credentials: Allow CORS credentials. - * allow_methods: Methods allowed for CORS. - * allow_headers: Headers allowed for CORS. - * ssl_certfile: SSL certificate file for HTTPS. See https://www.uvicorn.org/settings/#https. - * ssl_keyfile: SSL key file for HTTPS. See https://www.uvicorn.org/settings/#https. - * log_tokenization: Enable logging of parsed prompt tokens. - * patchmatch: Enable patchmatch inpaint code. - * models_dir: Path to the models directory. - * convert_cache_dir: Path to the converted models cache directory (DEPRECATED, but do not delete because it is needed for migration from previous versions). - * download_cache_dir: Path to the directory that contains dynamically downloaded models. - * legacy_conf_dir: Path to directory of legacy checkpoint config files. - * db_dir: Path to InvokeAI databases directory. - * outputs_dir: Path to directory for outputs. - * custom_nodes_dir: Path to directory for custom nodes. - * style_presets_dir: Path to directory for style presets. - * workflow_thumbnails_dir: Path to directory for workflow thumbnails. - * log_handlers: Log handler. Valid options are "console", "file=", "syslog=path|address:host:port", "http=". - * log_format: Log format. Use "plain" for text-only, "color" for colorized output, "legacy" for 2.3-style logging and "syslog" for syslog-style.
Valid values: `plain`, `color`, `syslog`, `legacy` - * log_level: Emit logging messages at this level or higher.
Valid values: `debug`, `info`, `warning`, `error`, `critical` - * log_sql: Log SQL queries. `log_level` must be `debug` for this to do anything. Extremely verbose. - * log_level_network: Log level for network-related messages. 'info' and 'debug' are very verbose.
Valid values: `debug`, `info`, `warning`, `error`, `critical` - * use_memory_db: Use in-memory database. Useful for development. - * dev_reload: Automatically reload when Python sources are changed. Does not reload node definitions. - * profile_graphs: Enable graph profiling using `cProfile`. - * profile_prefix: An optional prefix for profile output files. - * profiles_dir: Path to profiles output directory. - * max_cache_ram_gb: The maximum amount of CPU RAM to use for model caching in GB. If unset, the limit will be configured based on the available RAM. In most cases, it is recommended to leave this unset. - * max_cache_vram_gb: The amount of VRAM to use for model caching in GB. If unset, the limit will be configured based on the available VRAM and the device_working_mem_gb. In most cases, it is recommended to leave this unset. - * log_memory_usage: If True, a memory snapshot will be captured before and after every model cache operation, and the result will be logged (at debug level). There is a time cost to capturing the memory snapshots, so it is recommended to only enable this feature if you are actively inspecting the model cache's behaviour. - * device_working_mem_gb: The amount of working memory to keep available on the compute device (in GB). Has no effect if running on CPU. If you are experiencing OOM errors, try increasing this value. - * enable_partial_loading: Enable partial loading of models. This enables models to run with reduced VRAM requirements (at the cost of slower speed) by streaming the model from RAM to VRAM as its used. In some edge cases, partial loading can cause models to run more slowly if they were previously being fully loaded into VRAM. - * keep_ram_copy_of_weights: Whether to keep a full RAM copy of a model's weights when the model is loaded in VRAM. Keeping a RAM copy increases average RAM usage, but speeds up model switching and LoRA patching (assuming there is sufficient RAM). Set this to False if RAM pressure is consistently high. - * ram: DEPRECATED: This setting is no longer used. It has been replaced by `max_cache_ram_gb`, but most users will not need to use this config since automatic cache size limits should work well in most cases. This config setting will be removed once the new model cache behavior is stable. - * vram: DEPRECATED: This setting is no longer used. It has been replaced by `max_cache_vram_gb`, but most users will not need to use this config since automatic cache size limits should work well in most cases. This config setting will be removed once the new model cache behavior is stable. - * lazy_offload: DEPRECATED: This setting is no longer used. Lazy-offloading is enabled by default. This config setting will be removed once the new model cache behavior is stable. - * pytorch_cuda_alloc_conf: Configure the Torch CUDA memory allocator. This will impact peak reserved VRAM usage and performance. Setting to "backend:cudaMallocAsync" works well on many systems. The optimal configuration is highly dependent on the system configuration (device type, VRAM, CUDA driver version, etc.), so must be tuned experimentally. - * device: Preferred execution device. `auto` will choose the device depending on the hardware platform and the installed torch capabilities.
Valid values: `auto`, `cpu`, `cuda`, `mps`, `cuda:N` (where N is a device number) - * precision: Floating point precision. `float16` will consume half the memory of `float32` but produce slightly lower-quality images. The `auto` setting will guess the proper precision based on your video card and operating system.
Valid values: `auto`, `float16`, `bfloat16`, `float32` - * sequential_guidance: Whether to calculate guidance in serial instead of in parallel, lowering memory requirements. - * attention_type: Attention type.
Valid values: `auto`, `normal`, `xformers`, `sliced`, `torch-sdp` - * attention_slice_size: Slice size, valid when attention_type=="sliced".
Valid values: `auto`, `balanced`, `max`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8` - * force_tiled_decode: Whether to enable tiled VAE decode (reduces memory consumption with some performance penalty). - * pil_compress_level: The compress_level setting of PIL.Image.save(), used for PNG encoding. All settings are lossless. 0 = no compression, 1 = fastest with slightly larger filesize, 9 = slowest with smallest filesize. 1 is typically the best setting. - * max_queue_size: Maximum number of items in the session queue. - * clear_queue_on_startup: Empties session queue on startup. - * allow_nodes: List of nodes to allow. Omit to allow all. - * deny_nodes: List of nodes to deny. Omit to deny none. - * node_cache_size: How many cached nodes to keep in memory. - * hashing_algorithm: Model hashing algorthim for model installs. 'blake3_multi' is best for SSDs. 'blake3_single' is best for spinning disk HDDs. 'random' disables hashing, instead assigning a UUID to models. Useful when using a memory db to reduce model installation time, or if you don't care about storing stable hashes for models. Alternatively, any other hashlib algorithm is accepted, though these are not nearly as performant as blake3.
Valid values: `blake3_multi`, `blake3_single`, `random`, `md5`, `sha1`, `sha224`, `sha256`, `sha384`, `sha512`, `blake2b`, `blake2s`, `sha3_224`, `sha3_256`, `sha3_384`, `sha3_512`, `shake_128`, `shake_256` - * remote_api_tokens: List of regular expression and token pairs used when downloading models from URLs. The download URL is tested against the regex, and if it matches, the token is provided in as a Bearer token. - * scan_models_on_startup: Scan the models directory on startup, registering orphaned models. This is typically only used in conjunction with `use_memory_db` for testing purposes. - * unsafe_disable_picklescan: UNSAFE. Disable the picklescan security check during model installation. Recommended only for development and testing purposes. This will allow arbitrary code execution during model installation, so should never be used in production. - * allow_unknown_models: Allow installation of models that we are unable to identify. If enabled, models will be marked as `unknown` in the database, and will not have any metadata associated with them. If disabled, unknown models will be rejected during installation. - */ - InvokeAIAppConfig: { + cover_image: string | null; /** - * Schema Version - * @description Schema version of the config file. This is not a user-configurable setting. - * @default 4.0.2 + * Submodels + * @description Loadable submodels in this model */ - schema_version?: string; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Legacy Models Yaml Path - * @description Path to the legacy models.yaml file. This is not a user-configurable setting. + * Usage Info + * @description Usage information for this model */ - legacy_models_yaml_path?: string | null; + usage_info: string | null; /** - * Host - * @description IP address to bind to. Use `0.0.0.0` to serve to your local network. - * @default 127.0.0.1 + * Type + * @default lora + * @constant */ - host?: string; + type: "lora"; /** - * Port - * @description Port to bind to. - * @default 9090 + * Trigger Phrases + * @description Set of trigger phrases for this model */ - port?: number; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** - * Allow Origins - * @description Allowed CORS origins. - * @default [] + * Format + * @default diffusers + * @constant */ - allow_origins?: string[]; + format: "diffusers"; /** - * Allow Credentials - * @description Allow CORS credentials. - * @default true + * Base + * @default sdxl + * @constant */ - allow_credentials?: boolean; + base: "sdxl"; + }; + /** LoRA_LyCORIS_FLUX_Config */ + LoRA_LyCORIS_FLUX_Config: { /** - * Allow Methods - * @description Methods allowed for CORS. - * @default [ - * "*" - * ] + * Key + * @description A unique key for this model. */ - allow_methods?: string[]; + key: string; /** - * Allow Headers - * @description Headers allowed for CORS. - * @default [ - * "*" - * ] + * Hash + * @description The hash of the model file(s). */ - allow_headers?: string[]; + hash: string; /** - * Ssl Certfile - * @description SSL certificate file for HTTPS. See https://www.uvicorn.org/settings/#https. + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - ssl_certfile?: string | null; + path: string; /** - * Ssl Keyfile - * @description SSL key file for HTTPS. See https://www.uvicorn.org/settings/#https. + * File Size + * @description The size of the model in bytes. */ - ssl_keyfile?: string | null; + file_size: number; /** - * Log Tokenization - * @description Enable logging of parsed prompt tokens. - * @default false + * Name + * @description Name of the model. */ - log_tokenization?: boolean; + name: string; /** - * Patchmatch - * @description Enable patchmatch inpaint code. - * @default true + * Description + * @description Model description */ - patchmatch?: boolean; + description: string | null; /** - * Models Dir - * Format: path - * @description Path to the models directory. - * @default models + * Source + * @description The original source of the model (path, URL or repo_id). */ - models_dir?: string; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Convert Cache Dir - * Format: path - * @description Path to the converted models cache directory (DEPRECATED, but do not delete because it is needed for migration from previous versions). - * @default models/.convert_cache + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - convert_cache_dir?: string; + source_api_response: string | null; /** - * Download Cache Dir - * Format: path - * @description Path to the directory that contains dynamically downloaded models. - * @default models/.download_cache + * Cover Image + * @description Url for image to preview model */ - download_cache_dir?: string; + cover_image: string | null; /** - * Legacy Conf Dir - * Format: path - * @description Path to directory of legacy checkpoint config files. - * @default configs + * Submodels + * @description Loadable submodels in this model */ - legacy_conf_dir?: string; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Db Dir - * Format: path - * @description Path to InvokeAI databases directory. - * @default databases + * Usage Info + * @description Usage information for this model */ - db_dir?: string; + usage_info: string | null; /** - * Outputs Dir - * Format: path - * @description Path to directory for outputs. - * @default outputs + * Type + * @default lora + * @constant */ - outputs_dir?: string; + type: "lora"; /** - * Custom Nodes Dir - * Format: path - * @description Path to directory for custom nodes. - * @default nodes + * Trigger Phrases + * @description Set of trigger phrases for this model */ - custom_nodes_dir?: string; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** - * Style Presets Dir - * Format: path - * @description Path to directory for style presets. - * @default style_presets + * Format + * @default lycoris + * @constant */ - style_presets_dir?: string; + format: "lycoris"; /** - * Workflow Thumbnails Dir - * Format: path - * @description Path to directory for workflow thumbnails. - * @default workflow_thumbnails + * Base + * @default flux + * @constant */ - workflow_thumbnails_dir?: string; + base: "flux"; + }; + /** LoRA_LyCORIS_SD1_Config */ + LoRA_LyCORIS_SD1_Config: { /** - * Log Handlers - * @description Log handler. Valid options are "console", "file=", "syslog=path|address:host:port", "http=". - * @default [ - * "console" - * ] + * Key + * @description A unique key for this model. */ - log_handlers?: string[]; + key: string; /** - * Log Format - * @description Log format. Use "plain" for text-only, "color" for colorized output, "legacy" for 2.3-style logging and "syslog" for syslog-style. - * @default color - * @enum {string} + * Hash + * @description The hash of the model file(s). */ - log_format?: "plain" | "color" | "syslog" | "legacy"; + hash: string; /** - * Log Level - * @description Emit logging messages at this level or higher. - * @default info - * @enum {string} + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - log_level?: "debug" | "info" | "warning" | "error" | "critical"; + path: string; /** - * Log Sql - * @description Log SQL queries. `log_level` must be `debug` for this to do anything. Extremely verbose. - * @default false + * 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; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Submodels + * @description Loadable submodels in this model + */ + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; + /** + * Usage Info + * @description Usage information for this model + */ + usage_info: 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 */ - log_sql?: boolean; + format: "lycoris"; /** - * Log Level Network - * @description Log level for network-related messages. 'info' and 'debug' are very verbose. - * @default warning - * @enum {string} + * Base + * @default sd-1 + * @constant */ - log_level_network?: "debug" | "info" | "warning" | "error" | "critical"; + base: "sd-1"; + }; + /** LoRA_LyCORIS_SD2_Config */ + LoRA_LyCORIS_SD2_Config: { /** - * Use Memory Db - * @description Use in-memory database. Useful for development. - * @default false + * Key + * @description A unique key for this model. */ - use_memory_db?: boolean; + key: string; /** - * Dev Reload - * @description Automatically reload when Python sources are changed. Does not reload node definitions. - * @default false + * Hash + * @description The hash of the model file(s). */ - dev_reload?: boolean; + hash: string; /** - * Profile Graphs - * @description Enable graph profiling using `cProfile`. - * @default false + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - profile_graphs?: boolean; + path: string; /** - * Profile Prefix - * @description An optional prefix for profile output files. + * File Size + * @description The size of the model in bytes. */ - profile_prefix?: string | null; + file_size: number; /** - * Profiles Dir - * Format: path - * @description Path to profiles output directory. - * @default profiles + * Name + * @description Name of the model. */ - profiles_dir?: string; + name: string; /** - * Max Cache Ram Gb - * @description The maximum amount of CPU RAM to use for model caching in GB. If unset, the limit will be configured based on the available RAM. In most cases, it is recommended to leave this unset. + * Description + * @description Model description */ - max_cache_ram_gb?: number | null; + description: string | null; /** - * Max Cache Vram Gb - * @description The amount of VRAM to use for model caching in GB. If unset, the limit will be configured based on the available VRAM and the device_working_mem_gb. In most cases, it is recommended to leave this unset. + * Source + * @description The original source of the model (path, URL or repo_id). */ - max_cache_vram_gb?: number | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Log Memory Usage - * @description If True, a memory snapshot will be captured before and after every model cache operation, and the result will be logged (at debug level). There is a time cost to capturing the memory snapshots, so it is recommended to only enable this feature if you are actively inspecting the model cache's behaviour. - * @default false + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - log_memory_usage?: boolean; + source_api_response: string | null; /** - * Device Working Mem Gb - * @description The amount of working memory to keep available on the compute device (in GB). Has no effect if running on CPU. If you are experiencing OOM errors, try increasing this value. - * @default 3 + * Cover Image + * @description Url for image to preview model */ - device_working_mem_gb?: number; + cover_image: string | null; /** - * Enable Partial Loading - * @description Enable partial loading of models. This enables models to run with reduced VRAM requirements (at the cost of slower speed) by streaming the model from RAM to VRAM as its used. In some edge cases, partial loading can cause models to run more slowly if they were previously being fully loaded into VRAM. - * @default false + * Submodels + * @description Loadable submodels in this model */ - enable_partial_loading?: boolean; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Keep Ram Copy Of Weights - * @description Whether to keep a full RAM copy of a model's weights when the model is loaded in VRAM. Keeping a RAM copy increases average RAM usage, but speeds up model switching and LoRA patching (assuming there is sufficient RAM). Set this to False if RAM pressure is consistently high. - * @default true + * Usage Info + * @description Usage information for this model */ - keep_ram_copy_of_weights?: boolean; + usage_info: string | null; /** - * Ram - * @description DEPRECATED: This setting is no longer used. It has been replaced by `max_cache_ram_gb`, but most users will not need to use this config since automatic cache size limits should work well in most cases. This config setting will be removed once the new model cache behavior is stable. + * Type + * @default lora + * @constant */ - ram?: number | null; + type: "lora"; /** - * Vram - * @description DEPRECATED: This setting is no longer used. It has been replaced by `max_cache_vram_gb`, but most users will not need to use this config since automatic cache size limits should work well in most cases. This config setting will be removed once the new model cache behavior is stable. + * Trigger Phrases + * @description Set of trigger phrases for this model */ - vram?: number | null; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** - * Lazy Offload - * @description DEPRECATED: This setting is no longer used. Lazy-offloading is enabled by default. This config setting will be removed once the new model cache behavior is stable. - * @default true + * Format + * @default lycoris + * @constant */ - lazy_offload?: boolean; + format: "lycoris"; /** - * Pytorch Cuda Alloc Conf - * @description Configure the Torch CUDA memory allocator. This will impact peak reserved VRAM usage and performance. Setting to "backend:cudaMallocAsync" works well on many systems. The optimal configuration is highly dependent on the system configuration (device type, VRAM, CUDA driver version, etc.), so must be tuned experimentally. + * Base + * @default sd-2 + * @constant */ - pytorch_cuda_alloc_conf?: string | null; + base: "sd-2"; + }; + /** LoRA_LyCORIS_SDXL_Config */ + LoRA_LyCORIS_SDXL_Config: { /** - * Device - * @description Preferred execution device. `auto` will choose the device depending on the hardware platform and the installed torch capabilities.
Valid values: `auto`, `cpu`, `cuda`, `mps`, `cuda:N` (where N is a device number) - * @default auto + * Key + * @description A unique key for this model. */ - device?: string; + key: string; /** - * Precision - * @description Floating point precision. `float16` will consume half the memory of `float32` but produce slightly lower-quality images. The `auto` setting will guess the proper precision based on your video card and operating system. - * @default auto - * @enum {string} + * Hash + * @description The hash of the model file(s). */ - precision?: "auto" | "float16" | "bfloat16" | "float32"; + hash: string; /** - * Sequential Guidance - * @description Whether to calculate guidance in serial instead of in parallel, lowering memory requirements. - * @default false + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - sequential_guidance?: boolean; + path: string; /** - * Attention Type - * @description Attention type. - * @default auto - * @enum {string} + * File Size + * @description The size of the model in bytes. */ - attention_type?: "auto" | "normal" | "xformers" | "sliced" | "torch-sdp"; + file_size: number; /** - * Attention Slice Size - * @description Slice size, valid when attention_type=="sliced". - * @default auto - * @enum {unknown} + * Name + * @description Name of the model. */ - attention_slice_size?: "auto" | "balanced" | "max" | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; + name: string; /** - * Force Tiled Decode - * @description Whether to enable tiled VAE decode (reduces memory consumption with some performance penalty). - * @default false + * Description + * @description Model description */ - force_tiled_decode?: boolean; + description: string | null; /** - * Pil Compress Level - * @description The compress_level setting of PIL.Image.save(), used for PNG encoding. All settings are lossless. 0 = no compression, 1 = fastest with slightly larger filesize, 9 = slowest with smallest filesize. 1 is typically the best setting. - * @default 1 + * Source + * @description The original source of the model (path, URL or repo_id). */ - pil_compress_level?: number; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Max Queue Size - * @description Maximum number of items in the session queue. - * @default 10000 + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - max_queue_size?: number; + source_api_response: string | null; /** - * Clear Queue On Startup - * @description Empties session queue on startup. - * @default false + * Cover Image + * @description Url for image to preview model */ - clear_queue_on_startup?: boolean; + cover_image: string | null; /** - * Allow Nodes - * @description List of nodes to allow. Omit to allow all. + * Submodels + * @description Loadable submodels in this model */ - allow_nodes?: string[] | null; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Deny Nodes - * @description List of nodes to deny. Omit to deny none. + * Usage Info + * @description Usage information for this model */ - deny_nodes?: string[] | null; + usage_info: string | null; /** - * Node Cache Size - * @description How many cached nodes to keep in memory. - * @default 512 + * Type + * @default lora + * @constant */ - node_cache_size?: number; + type: "lora"; /** - * Hashing Algorithm - * @description Model hashing algorthim for model installs. 'blake3_multi' is best for SSDs. 'blake3_single' is best for spinning disk HDDs. 'random' disables hashing, instead assigning a UUID to models. Useful when using a memory db to reduce model installation time, or if you don't care about storing stable hashes for models. Alternatively, any other hashlib algorithm is accepted, though these are not nearly as performant as blake3. - * @default blake3_single - * @enum {string} + * Trigger Phrases + * @description Set of trigger phrases for this model */ - hashing_algorithm?: "blake3_multi" | "blake3_single" | "random" | "md5" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512" | "blake2b" | "blake2s" | "sha3_224" | "sha3_256" | "sha3_384" | "sha3_512" | "shake_128" | "shake_256"; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** - * Remote Api Tokens - * @description List of regular expression and token pairs used when downloading models from URLs. The download URL is tested against the regex, and if it matches, the token is provided in as a Bearer token. + * Format + * @default lycoris + * @constant */ - remote_api_tokens?: components["schemas"]["URLRegexTokenPair"][] | null; + format: "lycoris"; /** - * Scan Models On Startup - * @description Scan the models directory on startup, registering orphaned models. This is typically only used in conjunction with `use_memory_db` for testing purposes. - * @default false + * Base + * @default sdxl + * @constant */ - scan_models_on_startup?: boolean; + base: "sdxl"; + }; + /** LoRA_OMI_FLUX_Config */ + LoRA_OMI_FLUX_Config: { /** - * Unsafe Disable Picklescan - * @description UNSAFE. Disable the picklescan security check during model installation. Recommended only for development and testing purposes. This will allow arbitrary code execution during model installation, so should never be used in production. - * @default false + * Key + * @description A unique key for this model. */ - unsafe_disable_picklescan?: boolean; + key: string; /** - * Allow Unknown Models - * @description Allow installation of models that we are unable to identify. If enabled, models will be marked as `unknown` in the database, and will not have any metadata associated with them. If disabled, unknown models will be rejected during installation. - * @default true + * Hash + * @description The hash of the model file(s). */ - allow_unknown_models?: boolean; - }; - /** - * InvokeAIAppConfigWithSetFields - * @description InvokeAI App Config with model fields set - */ - InvokeAIAppConfigWithSetFields: { + hash: string; /** - * Set Fields - * @description The set fields + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - set_fields: string[]; - /** @description The InvokeAI App Config */ - config: components["schemas"]["InvokeAIAppConfig"]; - }; - /** - * Adjust Image Hue Plus - * @description Adjusts the Hue of an image by rotating it in the selected color space. Originally created by @dwringer - */ - InvokeAdjustImageHuePlusInvocation: { + path: string; /** - * @description The board to save the image to - * @default null + * File Size + * @description The size of the model in bytes. */ - board?: components["schemas"]["BoardField"] | null; + file_size: number; /** - * @description Optional metadata to be saved with the image - * @default null + * Name + * @description Name of the model. */ - metadata?: components["schemas"]["MetadataField"] | null; + name: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Description + * @description Model description */ - id: string; + description: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Source + * @description The original source of the model (path, URL or repo_id). */ - is_intermediate?: boolean; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - use_cache?: boolean; + source_api_response: string | null; /** - * @description The image to adjust - * @default null + * Cover Image + * @description Url for image to preview model */ - image?: components["schemas"]["ImageField"] | null; + cover_image: string | null; /** - * Space - * @description Color space in which to rotate hue by polar coords (*: non-invertible) - * @default HSV / HSL / RGB - * @enum {string} + * Submodels + * @description Loadable submodels in this model */ - space?: "HSV / HSL / RGB" | "Okhsl" | "Okhsv" | "*Oklch / Oklab" | "*LCh / CIELab" | "*UPLab (w/CIELab_to_UPLab.icc)"; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Degrees - * @description Degrees by which to rotate image hue - * @default 0 + * Usage Info + * @description Usage information for this model */ - degrees?: number; + usage_info: string | null; /** - * Preserve Lightness - * @description Whether to preserve CIELAB lightness values - * @default false + * Type + * @default lora + * @constant */ - preserve_lightness?: boolean; + type: "lora"; /** - * Ok Adaptive Gamut - * @description Higher preserves chroma at the expense of lightness (Oklab) - * @default 0.05 + * Trigger Phrases + * @description Set of trigger phrases for this model */ - ok_adaptive_gamut?: number; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** - * Ok High Precision - * @description Use more steps in computing gamut (Oklab/Okhsv/Okhsl) - * @default true + * Format + * @default omi + * @constant */ - ok_high_precision?: boolean; + format: "omi"; /** - * type - * @default invokeai_img_hue_adjust_plus + * Base + * @default flux * @constant */ - type: "invokeai_img_hue_adjust_plus"; + base: "flux"; }; - /** - * Equivalent Achromatic Lightness - * @description Calculate Equivalent Achromatic Lightness from image. Originally created by @dwringer - */ - InvokeEquivalentAchromaticLightnessInvocation: { + /** LoRA_OMI_SDXL_Config */ + LoRA_OMI_SDXL_Config: { /** - * @description The board to save the image to - * @default null + * Key + * @description A unique key for this model. */ - board?: components["schemas"]["BoardField"] | null; + key: string; /** - * @description Optional metadata to be saved with the image - * @default null + * Hash + * @description The hash of the model file(s). */ - metadata?: components["schemas"]["MetadataField"] | null; + hash: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - id: string; + path: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * File Size + * @description The size of the model in bytes. */ - is_intermediate?: boolean; + file_size: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Name + * @description Name of the model. */ - use_cache?: boolean; + name: string; /** - * @description Image from which to get channel - * @default null + * Description + * @description Model description */ - image?: components["schemas"]["ImageField"] | null; + description: string | null; /** - * type - * @default invokeai_ealightness - * @constant + * Source + * @description The original source of the model (path, URL or repo_id). */ - type: "invokeai_ealightness"; - }; - /** - * Image Layer Blend - * @description Blend two images together, with optional opacity, mask, and blend modes. Originally created by @dwringer - */ - InvokeImageBlendInvocation: { + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * @description The board to save the image to - * @default null + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - board?: components["schemas"]["BoardField"] | null; + source_api_response: string | null; /** - * @description Optional metadata to be saved with the image - * @default null + * Cover Image + * @description Url for image to preview model */ - metadata?: components["schemas"]["MetadataField"] | null; + cover_image: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Submodels + * @description Loadable submodels in this model */ - id: string; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Usage Info + * @description Usage information for this model */ - is_intermediate?: boolean; + usage_info: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Type + * @default lora + * @constant */ - use_cache?: boolean; + type: "lora"; /** - * @description The top image to blend - * @default null + * Trigger Phrases + * @description Set of trigger phrases for this model */ - layer_upper?: components["schemas"]["ImageField"] | null; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** - * Blend Mode - * @description Available blend modes - * @default Normal - * @enum {string} + * Format + * @default omi + * @constant */ - blend_mode?: "Normal" | "Lighten Only" | "Darken Only" | "Lighten Only (EAL)" | "Darken Only (EAL)" | "Hue" | "Saturation" | "Color" | "Luminosity" | "Linear Dodge (Add)" | "Subtract" | "Multiply" | "Divide" | "Screen" | "Overlay" | "Linear Burn" | "Difference" | "Hard Light" | "Soft Light" | "Vivid Light" | "Linear Light" | "Color Burn" | "Color Dodge"; + format: "omi"; /** - * Opacity - * @description Desired opacity of the upper layer - * @default 1 + * Base + * @default sdxl + * @constant */ - opacity?: number; + base: "sdxl"; + }; + /** + * LocalModelSource + * @description A local file or directory path. + */ + LocalModelSource: { + /** Path */ + path: string; /** - * @description Optional mask, used to restrict areas from blending - * @default null + * Inplace + * @default false */ - mask?: components["schemas"]["ImageField"] | null; + inplace?: boolean | null; /** - * Fit To Width - * @description Scale upper layer to fit base width - * @default false + * @description discriminator enum property added by openapi-typescript + * @enum {string} */ - fit_to_width?: boolean; + type: "local"; + }; + /** + * LogLevel + * @enum {integer} + */ + LogLevel: 0 | 10 | 20 | 30 | 40 | 50; + /** LoraModelDefaultSettings */ + LoraModelDefaultSettings: { + /** + * Weight + * @description Default weight for this model + */ + weight?: number | null; + }; + /** MDControlListOutput */ + MDControlListOutput: { /** - * Fit To Height - * @description Scale upper layer to fit base height - * @default true + * ControlNet-List + * @description ControlNet(s) to apply */ - fit_to_height?: boolean; + control_list: components["schemas"]["ControlField"] | components["schemas"]["ControlField"][] | null; /** - * @description The bottom image to blend - * @default null + * type + * @default md_control_list_output + * @constant */ - layer_base?: components["schemas"]["ImageField"] | null; + type: "md_control_list_output"; + }; + /** MDIPAdapterListOutput */ + MDIPAdapterListOutput: { /** - * Color Space - * @description Available color spaces for blend computations - * @default RGB - * @enum {string} + * IP-Adapter-List + * @description IP-Adapter to apply */ - color_space?: "RGB" | "Linear RGB" | "HSL (RGB)" | "HSV (RGB)" | "Okhsl" | "Okhsv" | "Oklch (Oklab)" | "LCh (CIELab)"; + ip_adapter_list: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; /** - * Adaptive Gamut - * @description Adaptive gamut clipping (0=off). Higher prioritizes chroma over lightness - * @default 0 + * type + * @default md_ip_adapter_list_output + * @constant */ - adaptive_gamut?: number; + type: "md_ip_adapter_list_output"; + }; + /** MDT2IAdapterListOutput */ + MDT2IAdapterListOutput: { /** - * High Precision - * @description Use more steps in computing gamut when possible - * @default true + * T2I Adapter-List + * @description T2I-Adapter(s) to apply */ - high_precision?: boolean; + t2i_adapter_list: components["schemas"]["T2IAdapterField"] | components["schemas"]["T2IAdapterField"][] | null; /** * type - * @default invokeai_img_blend + * @default md_ip_adapters_output * @constant */ - type: "invokeai_img_blend"; + type: "md_ip_adapters_output"; }; /** - * Image Compositor - * @description Removes backdrop from subject image then overlays subject on background image. Originally created by @dwringer + * MLSD Detection + * @description Generates an line segment map using MLSD. */ - InvokeImageCompositorInvocation: { + MLSDDetectionInvocation: { /** * @description The board to save the image to * @default null @@ -13334,137 +16619,82 @@ export type components = { */ use_cache?: boolean; /** - * @description Image of the subject on a plain monochrome background - * @default null - */ - image_subject?: components["schemas"]["ImageField"] | null; - /** - * @description Image of a background scene + * @description The image to process * @default null */ - image_background?: components["schemas"]["ImageField"] | null; - /** - * Chroma Key - * @description Can be empty for corner flood select, or CSS-3 color or tuple - * @default - */ - chroma_key?: string; - /** - * Threshold - * @description Subject isolation flood-fill threshold - * @default 50 - */ - threshold?: number; - /** - * Fill X - * @description Scale base subject image to fit background width - * @default false - */ - fill_x?: boolean; - /** - * Fill Y - * @description Scale base subject image to fit background height - * @default true - */ - fill_y?: boolean; + image?: components["schemas"]["ImageField"] | null; /** - * X Offset - * @description x-offset for the subject - * @default 0 + * Score Threshold + * @description The threshold used to score points when determining line segments + * @default 0.1 */ - x_offset?: number; + score_threshold?: number; /** - * Y Offset - * @description y-offset for the subject - * @default 0 + * Distance Threshold + * @description Threshold for including a line segment - lines shorter than this distance will be discarded + * @default 20 */ - y_offset?: number; + distance_threshold?: number; /** * type - * @default invokeai_img_composite + * @default mlsd_detection * @constant */ - type: "invokeai_img_composite"; + type: "mlsd_detection"; }; - /** - * Image Dilate or Erode - * @description Dilate (expand) or erode (contract) an image. Originally created by @dwringer - */ - InvokeImageDilateOrErodeInvocation: { - /** - * @description Optional metadata to be saved with the image - * @default null - */ - metadata?: components["schemas"]["MetadataField"] | null; + /** MainModelDefaultSettings */ + MainModelDefaultSettings: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Vae + * @description Default VAE for this model (model key) */ - id: string; + vae?: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Vae Precision + * @description Default VAE precision for this model */ - is_intermediate?: boolean; + vae_precision?: ("fp16" | "fp32") | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Scheduler + * @description Default scheduler for this model */ - use_cache?: boolean; + scheduler?: ("ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd") | null; /** - * @description The image from which to create a mask - * @default null + * Steps + * @description Default number of steps for this model */ - image?: components["schemas"]["ImageField"] | null; + steps?: number | null; /** - * Lightness Only - * @description If true, only applies to image lightness (CIELa*b*) - * @default false + * Cfg Scale + * @description Default CFG Scale for this model */ - lightness_only?: boolean; + cfg_scale?: number | null; /** - * Radius W - * @description Width (in pixels) by which to dilate(expand) or erode (contract) the image - * @default 4 + * Cfg Rescale Multiplier + * @description Default CFG Rescale Multiplier for this model */ - radius_w?: number; + cfg_rescale_multiplier?: number | null; /** - * Radius H - * @description Height (in pixels) by which to dilate(expand) or erode (contract) the image - * @default 4 + * Width + * @description Default width for this model */ - radius_h?: number; + width?: number | null; /** - * Mode - * @description How to operate on the image - * @default Dilate - * @enum {string} + * Height + * @description Default height for this model */ - mode?: "Dilate" | "Erode"; + height?: number | null; /** - * type - * @default invokeai_img_dilate_erode - * @constant + * Guidance + * @description Default Guidance for this model */ - type: "invokeai_img_dilate_erode"; + guidance?: number | null; }; /** - * Enhance Image - * @description Applies processing from PIL's ImageEnhance module. Originally created by @dwringer + * Main Model - SD1.5, SD2 + * @description Loads a main model, outputting its submodels. */ - InvokeImageEnhanceInvocation: { - /** - * @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; + MainModelLoaderInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -13483,569 +16713,518 @@ export type components = { */ use_cache?: boolean; /** - * @description The image for which to apply processing + * @description Main model (UNet, VAE, CLIP) to load * @default null */ - image?: components["schemas"]["ImageField"] | null; + model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Invert - * @description Whether to invert the image colors - * @default false + * type + * @default main_model_loader + * @constant */ - invert?: boolean; + type: "main_model_loader"; + }; + /** + * Main_BnBNF4_FLUX_Config + * @description Model config for main checkpoint models. + */ + Main_BnBNF4_FLUX_Config: { /** - * Color - * @description Color enhancement factor - * @default 1 + * Key + * @description A unique key for this model. */ - color?: number; + key: string; /** - * Contrast - * @description Contrast enhancement factor - * @default 1 + * Hash + * @description The hash of the model file(s). */ - contrast?: number; + hash: string; /** - * Brightness - * @description Brightness enhancement factor - * @default 1 + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - brightness?: number; + path: string; /** - * Sharpness - * @description Sharpness enhancement factor - * @default 1 + * File Size + * @description The size of the model in bytes. */ - sharpness?: number; + file_size: number; /** - * type - * @default invokeai_img_enhance - * @constant + * Name + * @description Name of the model. */ - type: "invokeai_img_enhance"; - }; - /** - * Image Value Thresholds - * @description Clip image to pure black/white past specified thresholds. Originally created by @dwringer - */ - InvokeImageValueThresholdsInvocation: { + name: string; /** - * @description The board to save the image to - * @default null + * Description + * @description Model description */ - board?: components["schemas"]["BoardField"] | null; + description: string | null; /** - * @description Optional metadata to be saved with the image - * @default null + * Source + * @description The original source of the model (path, URL or repo_id). */ - metadata?: components["schemas"]["MetadataField"] | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - id: string; + source_api_response: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Cover Image + * @description Url for image to preview model */ - is_intermediate?: boolean; + cover_image: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Submodels + * @description Loadable submodels in this model */ - use_cache?: boolean; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * @description The image from which to create a mask - * @default null + * Usage Info + * @description Usage information for this model */ - image?: components["schemas"]["ImageField"] | null; + usage_info: string | null; /** - * Invert Output - * @description Make light areas dark and vice versa - * @default false + * Type + * @default main + * @constant */ - invert_output?: boolean; + type: "main"; /** - * Renormalize Values - * @description Rescale remaining values from minimum to maximum - * @default false + * Trigger Phrases + * @description Set of trigger phrases for this model */ - renormalize_values?: boolean; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * Lightness Only - * @description If true, only applies to image lightness (CIELa*b*) - * @default false + * Config Path + * @description Path to the config for this model, if any. */ - lightness_only?: boolean; + config_path: string | null; /** - * Threshold Upper - * @description Threshold above which will be set to full value - * @default 0.5 + * Converted At + * @description When this model was last converted to diffusers */ - threshold_upper?: number; + converted_at: number | null; /** - * Threshold Lower - * @description Threshold below which will be set to minimum value - * @default 0.5 + * Base + * @default flux + * @constant */ - threshold_lower?: number; + base: "flux"; /** - * type - * @default invokeai_img_val_thresholds + * Format + * @default bnb_quantized_nf4b * @constant */ - type: "invokeai_img_val_thresholds"; + format: "bnb_quantized_nf4b"; + variant: components["schemas"]["FluxVariantType"]; }; /** - * ItemIdsResult - * @description Response containing ordered item ids with metadata for optimistic updates. + * Main_Checkpoint_FLUX_Config + * @description Model config for main checkpoint models. */ - ItemIdsResult: { + Main_Checkpoint_FLUX_Config: { /** - * Item Ids - * @description Ordered list of item ids + * Key + * @description A unique key for this model. */ - item_ids: number[]; + key: string; /** - * Total Count - * @description Total number of queue items matching the query + * Hash + * @description The hash of the model file(s). */ - total_count: number; - }; - /** - * IterateInvocation - * @description Iterates over a list of items - */ - IterateInvocation: { + hash: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - id: string; + path: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * File Size + * @description The size of the model in bytes. */ - is_intermediate?: boolean; + file_size: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Name + * @description Name of the model. */ - use_cache?: boolean; + name: string; /** - * Collection - * @description The list of items to iterate over - * @default [] + * Description + * @description Model description */ - collection?: unknown[]; + description: string | null; /** - * Index - * @description The index, will be provided on executed iterators - * @default 0 + * Source + * @description The original source of the model (path, URL or repo_id). */ - index?: number; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * type - * @default iterate - * @constant + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - type: "iterate"; - }; - /** - * IterateInvocationOutput - * @description Used to connect iteration outputs. Will be expanded to a specific output. - */ - IterateInvocationOutput: { + source_api_response: string | null; /** - * Collection Item - * @description The item being iterated over + * Cover Image + * @description Url for image to preview model */ - item: unknown; + cover_image: string | null; /** - * Index - * @description The index of the item + * Submodels + * @description Loadable submodels in this model */ - index: number; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Total - * @description The total number of items + * Usage Info + * @description Usage information for this model */ - total: number; + usage_info: string | null; /** - * type - * @default iterate_output + * Type + * @default main * @constant */ - type: "iterate_output"; - }; - JsonValue: unknown; - /** - * LaMa Infill - * @description Infills transparent areas of an image using the LaMa model - */ - LaMaInfillInvocation: { + type: "main"; /** - * @description The board to save the image to - * @default null + * Trigger Phrases + * @description Set of trigger phrases for this model */ - board?: components["schemas"]["BoardField"] | null; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * @description Optional metadata to be saved with the image - * @default null + * Config Path + * @description Path to the config for this model, if any. */ - metadata?: components["schemas"]["MetadataField"] | null; + config_path: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Converted At + * @description When this model was last converted to diffusers */ - id: string; + converted_at: number | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Format + * @default checkpoint + * @constant */ - is_intermediate?: boolean; + format: "checkpoint"; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Base + * @default flux + * @constant */ - use_cache?: boolean; + base: "flux"; + variant: components["schemas"]["FluxVariantType"]; + }; + /** Main_Checkpoint_SD1_Config */ + Main_Checkpoint_SD1_Config: { /** - * @description The image to process - * @default null + * Key + * @description A unique key for this model. */ - image?: components["schemas"]["ImageField"] | null; + key: string; /** - * type - * @default infill_lama - * @constant + * Hash + * @description The hash of the model file(s). */ - type: "infill_lama"; - }; - /** - * Latents Collection Primitive - * @description A collection of latents tensor primitive values - */ - LatentsCollectionInvocation: { + 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; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Description + * @description Model description */ - id: string; + description: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Source + * @description The original source of the model (path, URL or repo_id). */ - is_intermediate?: boolean; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - use_cache?: boolean; + source_api_response: string | null; /** - * Collection - * @description The collection of latents tensors - * @default null + * Cover Image + * @description Url for image to preview model */ - collection?: components["schemas"]["LatentsField"][] | null; + cover_image: string | null; /** - * type - * @default latents_collection - * @constant + * Submodels + * @description Loadable submodels in this model */ - type: "latents_collection"; - }; - /** - * LatentsCollectionOutput - * @description Base class for nodes that output a collection of latents tensors - */ - LatentsCollectionOutput: { + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Collection - * @description Latents tensor + * Usage Info + * @description Usage information for this model */ - collection: components["schemas"]["LatentsField"][]; + usage_info: string | null; /** - * type - * @default latents_collection_output + * Type + * @default main * @constant */ - type: "latents_collection_output"; - }; - /** - * LatentsField - * @description A latents tensor primitive field - */ - LatentsField: { + type: "main"; /** - * Latents Name - * @description The name of the latents + * Trigger Phrases + * @description Set of trigger phrases for this model */ - latents_name: string; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * Seed - * @description Seed used to generate this latents - * @default null + * Config Path + * @description Path to the config for this model, if any. */ - seed?: number | null; - }; - /** - * Latents Primitive - * @description A latents tensor primitive value - */ - LatentsInvocation: { + config_path: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Converted At + * @description When this model was last converted to diffusers */ - id: string; + converted_at: number | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Format + * @default checkpoint + * @constant */ - is_intermediate?: boolean; + format: "checkpoint"; + prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Base + * @default sd-1 + * @constant */ - use_cache?: boolean; + base: "sd-1"; + }; + /** Main_Checkpoint_SD2_Config */ + Main_Checkpoint_SD2_Config: { /** - * @description The latents tensor - * @default null + * Key + * @description A unique key for this model. */ - latents?: components["schemas"]["LatentsField"] | null; + key: string; /** - * type - * @default latents - * @constant + * Hash + * @description The hash of the model file(s). */ - type: "latents"; - }; - /** - * LatentsMetaOutput - * @description Latents + metadata - */ - LatentsMetaOutput: { - /** @description Metadata Dict */ - metadata: components["schemas"]["MetadataField"]; + hash: string; /** - * type - * @default latents_meta_output - * @constant + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - type: "latents_meta_output"; - /** @description Latents tensor */ - latents: components["schemas"]["LatentsField"]; + path: string; /** - * Width - * @description Width of output (px) + * File Size + * @description The size of the model in bytes. */ - width: number; + file_size: number; /** - * Height - * @description Height of output (px) + * Name + * @description Name of the model. */ - height: number; - }; - /** - * LatentsOutput - * @description Base class for nodes that output a single latents tensor - */ - LatentsOutput: { - /** @description Latents tensor */ - latents: components["schemas"]["LatentsField"]; + name: string; /** - * Width - * @description Width of output (px) + * Description + * @description Model description */ - width: number; + description: string | null; /** - * Height - * @description Height of output (px) + * Source + * @description The original source of the model (path, URL or repo_id). */ - height: number; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * type - * @default latents_output - * @constant + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - type: "latents_output"; - }; - /** - * Latents to Image - SD1.5, SDXL - * @description Generates an image from latents. - */ - LatentsToImageInvocation: { + source_api_response: string | null; /** - * @description The board to save the image to - * @default null + * Cover Image + * @description Url for image to preview model */ - board?: components["schemas"]["BoardField"] | null; + cover_image: string | null; /** - * @description Optional metadata to be saved with the image - * @default null + * Submodels + * @description Loadable submodels in this model */ - metadata?: components["schemas"]["MetadataField"] | null; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Usage Info + * @description Usage information for this model */ - id: string; + usage_info: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Type + * @default main + * @constant */ - is_intermediate?: boolean; + type: "main"; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Trigger Phrases + * @description Set of trigger phrases for this model */ - use_cache?: boolean; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * @description Latents tensor - * @default null + * Config Path + * @description Path to the config for this model, if any. */ - latents?: components["schemas"]["LatentsField"] | null; + config_path: string | null; /** - * @description VAE - * @default null + * Converted At + * @description When this model was last converted to diffusers */ - vae?: components["schemas"]["VAEField"] | null; + converted_at: number | null; /** - * Tiled - * @description Processing using overlapping tiles (reduce memory consumption) - * @default false + * Format + * @default checkpoint + * @constant */ - tiled?: boolean; + format: "checkpoint"; + prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; /** - * Tile Size - * @description The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the model will be used. Larger tile sizes generally produce better results at the cost of higher memory usage. - * @default 0 + * Base + * @default sd-2 + * @constant */ - tile_size?: number; + base: "sd-2"; + }; + /** Main_Checkpoint_SDXLRefiner_Config */ + Main_Checkpoint_SDXLRefiner_Config: { /** - * Fp32 - * @description Whether or not to use full float32 precision - * @default false + * Key + * @description A unique key for this model. */ - fp32?: boolean; + key: string; /** - * type - * @default l2i - * @constant + * Hash + * @description The hash of the model file(s). */ - type: "l2i"; - }; - /** - * Lineart Anime Edge Detection - * @description Geneartes an edge map using the Lineart model. - */ - LineartAnimeEdgeDetectionInvocation: { + hash: string; /** - * @description The board to save the image to - * @default null + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - board?: components["schemas"]["BoardField"] | null; + path: string; /** - * @description Optional metadata to be saved with the image - * @default null + * File Size + * @description The size of the model in bytes. */ - metadata?: components["schemas"]["MetadataField"] | null; + file_size: number; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Name + * @description Name of the model. */ - id: string; + name: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Description + * @description Model description */ - is_intermediate?: boolean; + description: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Source + * @description The original source of the model (path, URL or repo_id). */ - use_cache?: boolean; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * @description The image to process - * @default null + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - image?: components["schemas"]["ImageField"] | null; + source_api_response: string | null; /** - * type - * @default lineart_anime_edge_detection - * @constant + * Cover Image + * @description Url for image to preview model */ - type: "lineart_anime_edge_detection"; - }; - /** - * Lineart Edge Detection - * @description Generates an edge map using the Lineart model. - */ - LineartEdgeDetectionInvocation: { + cover_image: string | null; /** - * @description The board to save the image to - * @default null + * Submodels + * @description Loadable submodels in this model */ - board?: components["schemas"]["BoardField"] | null; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * @description Optional metadata to be saved with the image - * @default null + * Usage Info + * @description Usage information for this model */ - metadata?: components["schemas"]["MetadataField"] | null; + usage_info: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Type + * @default main + * @constant */ - id: string; + type: "main"; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Trigger Phrases + * @description Set of trigger phrases for this model */ - is_intermediate?: boolean; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Config Path + * @description Path to the config for this model, if any. */ - use_cache?: boolean; + config_path: string | null; /** - * @description The image to process - * @default null + * Converted At + * @description When this model was last converted to diffusers */ - image?: components["schemas"]["ImageField"] | null; + converted_at: number | null; /** - * Coarse - * @description Whether to use coarse mode - * @default false + * Format + * @default checkpoint + * @constant */ - coarse?: boolean; + format: "checkpoint"; + prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; /** - * type - * @default lineart_edge_detection + * Base + * @default sdxl-refiner * @constant */ - type: "lineart_edge_detection"; + base: "sdxl-refiner"; }; - /** - * LlavaOnevisionConfig - * @description Model config for Llava Onevision models. - */ - LlavaOnevisionConfig: { + /** Main_Checkpoint_SDXL_Config */ + Main_Checkpoint_SDXL_Config: { /** * Key * @description A unique key for this model. @@ -14106,131 +17285,134 @@ export type components = { */ usage_info: string | null; /** - * Format - * @default diffusers + * Type + * @default main * @constant */ - format: "diffusers"; - /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + type: "main"; /** - * Type - * @default llava_onevision - * @constant + * Trigger Phrases + * @description Set of trigger phrases for this model */ - type: "llava_onevision"; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * Base - * @default any + * Config Path + * @description Path to the config for this model, if any. + */ + config_path: string | null; + /** + * Converted At + * @description When this model was last converted to diffusers + */ + converted_at: number | null; + /** + * Format + * @default checkpoint * @constant */ - base: "any"; + format: "checkpoint"; + prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; /** - * Variant - * @default normal + * Base + * @default sdxl * @constant */ - variant: "normal"; + base: "sdxl"; }; - /** - * LLaVA OneVision VLLM - * @description Run a LLaVA OneVision VLLM model. - */ - LlavaOnevisionVllmInvocation: { + /** Main_Diffusers_CogView4_Config */ + Main_Diffusers_CogView4_Config: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Key + * @description A unique key for this model. */ - id: string; + key: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Hash + * @description The hash of the model file(s). */ - is_intermediate?: boolean; + hash: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - use_cache?: boolean; + path: string; /** - * Images - * @description Input image. - * @default null + * File Size + * @description The size of the model in bytes. */ - images?: (components["schemas"]["ImageField"][] | components["schemas"]["ImageField"]) | null; + file_size: number; /** - * Prompt - * @description Input text prompt. - * @default + * Name + * @description Name of the model. */ - prompt?: string; + name: string; /** - * LLaVA Model Type - * @description The VLLM model to use - * @default null + * Description + * @description Model description */ - vllm_model?: components["schemas"]["ModelIdentifierField"] | null; + description: string | null; /** - * type - * @default llava_onevision_vllm - * @constant + * Source + * @description The original source of the model (path, URL or repo_id). */ - type: "llava_onevision_vllm"; - }; - /** - * Apply LoRA Collection - SD1.5 - * @description Applies a collection of LoRAs to the provided UNet and CLIP models. - */ - LoRACollectionLoader: { + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - id: string; + source_api_response: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Cover Image + * @description Url for image to preview model */ - is_intermediate?: boolean; + cover_image: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Submodels + * @description Loadable submodels in this model */ - use_cache?: boolean; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * LoRAs - * @description LoRA models and weights. May be a single LoRA or collection. - * @default null + * Usage Info + * @description Usage information for this model + */ + usage_info: string | null; + /** + * Type + * @default main + * @constant */ - loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null; + type: "main"; /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null + * Trigger Phrases + * @description Set of trigger phrases for this model */ - unet?: components["schemas"]["UNetField"] | null; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * Format + * @default diffusers + * @constant */ - clip?: components["schemas"]["CLIPField"] | null; + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; /** - * type - * @default lora_collection_loader + * Base + * @default cogview4 * @constant */ - type: "lora_collection_loader"; + base: "cogview4"; }; - /** - * LoRADiffusersConfig - * @description Model config for LoRA/Diffusers models. - */ - LoRADiffusersConfig: { + /** Main_Diffusers_SD1_Config */ + Main_Diffusers_SD1_Config: { /** * Key * @description A unique key for this model. @@ -14292,121 +17474,36 @@ export type components = { usage_info: string | null; /** * Type - * @default lora + * @default main * @constant */ - type: "lora"; + 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"]["LoraModelDefaultSettings"] | null; - /** - * Base - * @enum {string} - */ - base: "sd-1" | "sd-2" | "sdxl" | "flux"; + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** * Format * @default diffusers * @constant */ format: "diffusers"; - }; - /** LoRAField */ - LoRAField: { - /** @description Info to load lora model */ - lora: components["schemas"]["ModelIdentifierField"]; - /** - * Weight - * @description Weight to apply to lora model - */ - weight: number; - }; - /** - * Apply LoRA - SD1.5 - * @description Apply selected lora to unet and text_encoder. - */ - LoRALoaderInvocation: { - /** - * 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; - /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null - */ - unet?: components["schemas"]["UNetField"] | null; - /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null - */ - clip?: components["schemas"]["CLIPField"] | null; - /** - * type - * @default lora_loader - * @constant - */ - type: "lora_loader"; - }; - /** - * LoRALoaderOutput - * @description Model loader output - */ - LoRALoaderOutput: { - /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null - */ - unet: components["schemas"]["UNetField"] | null; - /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null - */ - clip: components["schemas"]["CLIPField"] | null; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; /** - * type - * @default lora_loader_output + * Base + * @default sd-1 * @constant */ - type: "lora_loader_output"; + base: "sd-1"; }; - /** - * LoRALyCORISConfig - * @description Model config for LoRA/Lycoris models. - */ - LoRALyCORISConfig: { + /** Main_Diffusers_SD2_Config */ + Main_Diffusers_SD2_Config: { /** * Key * @description A unique key for this model. @@ -14468,44 +17565,36 @@ export type components = { usage_info: string | null; /** * Type - * @default lora + * @default main * @constant */ - type: "lora"; + 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"]["LoraModelDefaultSettings"] | null; - /** - * Base - * @enum {string} - */ - base: "sd-1" | "sd-2" | "sdxl" | "flux"; + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** * Format - * @default lycoris + * @default diffusers * @constant */ - format: "lycoris"; - }; - /** - * LoRAMetadataField - * @description LoRA Metadata Field - */ - LoRAMetadataField: { - /** @description LoRA model to load */ - model: components["schemas"]["ModelIdentifierField"]; + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; /** - * Weight - * @description The weight at which the LoRA is applied to each model + * Base + * @default sd-2 + * @constant */ - weight: number; + base: "sd-2"; }; - /** LoRAOmiConfig */ - LoRAOmiConfig: { + /** Main_Diffusers_SD3_Config */ + Main_Diffusers_SD3_Config: { /** * Key * @description A unique key for this model. @@ -14567,221 +17656,125 @@ export type components = { usage_info: string | null; /** * Type - * @default lora + * @default main * @constant */ - type: "lora"; + 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"]["LoraModelDefaultSettings"] | null; - /** - * Base - * @enum {string} - */ - base: "flux" | "sdxl"; + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** * Format - * @default omi - * @constant - */ - format: "omi"; - }; - /** - * Select LoRA - * @description Selects a LoRA model and weight. - */ - LoRASelectorInvocation: { - /** - * 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; - /** - * type - * @default lora_selector - * @constant - */ - type: "lora_selector"; - }; - /** - * LoRASelectorOutput - * @description Model loader output - */ - LoRASelectorOutput: { - /** - * LoRA - * @description LoRA model and weight - */ - lora: components["schemas"]["LoRAField"]; - /** - * type - * @default lora_selector_output + * @default diffusers * @constant */ - type: "lora_selector_output"; - }; - /** - * LocalModelSource - * @description A local file or directory path. - */ - LocalModelSource: { - /** Path */ - path: string; - /** - * Inplace - * @default false - */ - inplace?: boolean | null; - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - type: "local"; - }; - /** - * LogLevel - * @enum {integer} - */ - LogLevel: 0 | 10 | 20 | 30 | 40 | 50; - /** LoraModelDefaultSettings */ - LoraModelDefaultSettings: { + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; /** - * Weight - * @description Default weight for this model + * Base + * @default sd-3 + * @constant */ - weight?: number | null; + base: "sd-3"; }; - /** MDControlListOutput */ - MDControlListOutput: { + /** Main_Diffusers_SDXLRefiner_Config */ + Main_Diffusers_SDXLRefiner_Config: { /** - * ControlNet-List - * @description ControlNet(s) to apply + * Key + * @description A unique key for this model. */ - control_list: components["schemas"]["ControlField"] | components["schemas"]["ControlField"][] | null; + key: string; /** - * type - * @default md_control_list_output - * @constant + * Hash + * @description The hash of the model file(s). */ - type: "md_control_list_output"; - }; - /** MDIPAdapterListOutput */ - MDIPAdapterListOutput: { + hash: string; /** - * IP-Adapter-List - * @description IP-Adapter to apply + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - ip_adapter_list: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; + path: string; /** - * type - * @default md_ip_adapter_list_output - * @constant + * File Size + * @description The size of the model in bytes. */ - type: "md_ip_adapter_list_output"; - }; - /** MDT2IAdapterListOutput */ - MDT2IAdapterListOutput: { + file_size: number; /** - * T2I Adapter-List - * @description T2I-Adapter(s) to apply + * Name + * @description Name of the model. */ - t2i_adapter_list: components["schemas"]["T2IAdapterField"] | components["schemas"]["T2IAdapterField"][] | null; + name: string; /** - * type - * @default md_ip_adapters_output - * @constant + * Description + * @description Model description */ - type: "md_ip_adapters_output"; - }; - /** - * MLSD Detection - * @description Generates an line segment map using MLSD. - */ - MLSDDetectionInvocation: { + description: string | null; /** - * @description The board to save the image to - * @default null + * Source + * @description The original source of the model (path, URL or repo_id). */ - board?: components["schemas"]["BoardField"] | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * @description Optional metadata to be saved with the image - * @default null + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - metadata?: components["schemas"]["MetadataField"] | null; + source_api_response: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Cover Image + * @description Url for image to preview model */ - id: string; + cover_image: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Submodels + * @description Loadable submodels in this model */ - is_intermediate?: boolean; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Usage Info + * @description Usage information for this model */ - use_cache?: boolean; + usage_info: string | null; /** - * @description The image to process - * @default null + * Type + * @default main + * @constant */ - image?: components["schemas"]["ImageField"] | null; + type: "main"; /** - * Score Threshold - * @description The threshold used to score points when determining line segments - * @default 0.1 + * Trigger Phrases + * @description Set of trigger phrases for this model */ - score_threshold?: number; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * Distance Threshold - * @description Threshold for including a line segment - lines shorter than this distance will be discarded - * @default 20 + * Format + * @default diffusers + * @constant */ - distance_threshold?: number; + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; /** - * type - * @default mlsd_detection + * Base + * @default sdxl-refiner * @constant */ - type: "mlsd_detection"; + base: "sdxl-refiner"; }; - /** - * MainBnbQuantized4bCheckpointConfig - * @description Model config for main checkpoint models. - */ - MainBnbQuantized4bCheckpointConfig: { + /** Main_Diffusers_SDXL_Config */ + Main_Diffusers_SDXL_Config: { /** * Key * @description A unique key for this model. @@ -14854,43 +17847,28 @@ export type components = { trigger_phrases: string[] | null; /** @description Default settings for this model */ default_settings: components["schemas"]["MainModelDefaultSettings"] | null; - /** Variant */ - variant: components["schemas"]["ModelVariantType"] | components["schemas"]["FluxVariantType"]; - /** - * Config Path - * @description Path to the config for this model, if any. - */ - config_path: string | null; - /** - * Converted At - * @description When this model was last converted to diffusers - */ - converted_at: number | null; - /** - * Base - * @default flux - * @constant - */ - base: "flux"; /** * Format - * @default bnb_quantized_nf4b + * @default diffusers * @constant */ - format: "bnb_quantized_nf4b"; - /** @default epsilon */ + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; /** - * Upcast Attention - * @default false + * Base + * @default sdxl + * @constant */ - upcast_attention: boolean; + base: "sdxl"; }; /** - * MainCheckpointConfig + * Main_GGUF_FLUX_Config * @description Model config for main checkpoint models. */ - MainCheckpointConfig: { + Main_GGUF_FLUX_Config: { /** * Key * @description A unique key for this model. @@ -14963,8 +17941,6 @@ export type components = { trigger_phrases: string[] | null; /** @description Default settings for this model */ default_settings: components["schemas"]["MainModelDefaultSettings"] | null; - /** Variant */ - variant: components["schemas"]["ModelVariantType"] | components["schemas"]["FluxVariantType"]; /** * Config Path * @description Path to the config for this model, if any. @@ -14977,278 +17953,375 @@ export type components = { converted_at: number | null; /** * Base - * @enum {string} + * @default flux + * @constant */ - base: "sd-1" | "sd-2" | "sd-3" | "sdxl" | "sdxl-refiner" | "flux" | "cogview4"; + base: "flux"; /** * Format - * @default checkpoint + * @default gguf_quantized * @constant */ - format: "checkpoint"; - /** @default epsilon */ - prediction_type: components["schemas"]["SchedulerPredictionType"]; + format: "gguf_quantized"; + variant: components["schemas"]["FluxVariantType"]; + }; + /** + * Combine Masks + * @description Combine two masks together by multiplying them using `PIL.ImageChops.multiply()`. + */ + MaskCombineInvocation: { /** - * Upcast Attention + * @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 */ - upcast_attention: boolean; + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The first mask to combine + * @default null + */ + mask1?: components["schemas"]["ImageField"] | null; + /** + * @description The second image to combine + * @default null + */ + mask2?: components["schemas"]["ImageField"] | null; + /** + * type + * @default mask_combine + * @constant + */ + type: "mask_combine"; }; /** - * MainDiffusersConfig - * @description Model config for main diffusers models. + * Mask Edge + * @description Applies an edge mask to an image */ - MainDiffusersConfig: { + MaskEdgeInvocation: { /** - * Key - * @description A unique key for this model. + * @description The board to save the image to + * @default null */ - key: string; + board?: components["schemas"]["BoardField"] | null; /** - * Hash - * @description The hash of the model file(s). + * @description Optional metadata to be saved with the image + * @default null */ - hash: string; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Path - * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to apply the mask to + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Edge Size + * @description The size of the edge + * @default null */ - path: string; + edge_size?: number | null; /** - * File Size - * @description The size of the model in bytes. + * Edge Blur + * @description The amount of blur on the edge + * @default null */ - file_size: number; + edge_blur?: number | null; /** - * Name - * @description Name of the model. + * Low Threshold + * @description First threshold for the hysteresis procedure in Canny edge detection + * @default null */ - name: string; + low_threshold?: number | null; /** - * Description - * @description Model description + * High Threshold + * @description Second threshold for the hysteresis procedure in Canny edge detection + * @default null */ - description: string | null; + high_threshold?: number | null; /** - * Source - * @description The original source of the model (path, URL or repo_id). + * type + * @default mask_edge + * @constant */ - source: string; - /** @description The type of source */ - source_type: components["schemas"]["ModelSourceType"]; + type: "mask_edge"; + }; + /** + * Mask from Alpha + * @description Extracts the alpha channel of an image as a mask. + */ + MaskFromAlphaInvocation: { /** - * Source Api Response - * @description The original API response from the source, as stringified JSON. + * @description The board to save the image to + * @default null */ - source_api_response: string | null; + board?: components["schemas"]["BoardField"] | null; /** - * Cover Image - * @description Url for image to preview model + * @description Optional metadata to be saved with the image + * @default null */ - cover_image: string | null; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Submodels - * @description Loadable submodels in this model + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + id: string; /** - * Usage Info - * @description Usage information for this model + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - usage_info: string | null; + is_intermediate?: boolean; /** - * Type - * @default main - * @constant + * Use Cache + * @description Whether or not to use the cache + * @default true */ - type: "main"; + use_cache?: boolean; /** - * Trigger Phrases - * @description Set of trigger phrases for this model + * @description The image to create the mask from + * @default null */ - trigger_phrases: string[] | null; - /** @description Default settings for this model */ - default_settings: components["schemas"]["MainModelDefaultSettings"] | null; - /** Variant */ - variant: components["schemas"]["ModelVariantType"] | components["schemas"]["FluxVariantType"]; + image?: components["schemas"]["ImageField"] | null; /** - * Format - * @default diffusers - * @constant + * Invert + * @description Whether or not to invert the mask + * @default false */ - format: "diffusers"; - /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + invert?: boolean; /** - * Base - * @enum {string} + * type + * @default tomask + * @constant */ - base: "sd-1" | "sd-2" | "sd-3" | "sdxl" | "sdxl-refiner" | "flux" | "cogview4"; + type: "tomask"; }; /** - * MainGGUFCheckpointConfig - * @description Model config for main checkpoint models. + * Mask from Segmented Image + * @description Generate a mask for a particular color in an ID Map */ - MainGGUFCheckpointConfig: { + MaskFromIDInvocation: { /** - * Key - * @description A unique key for this model. + * @description The board to save the image to + * @default null */ - key: string; + board?: components["schemas"]["BoardField"] | null; /** - * Hash - * @description The hash of the model file(s). + * @description Optional metadata to be saved with the image + * @default null */ - hash: string; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Path - * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - path: string; + id: string; /** - * File Size - * @description The size of the model in bytes. + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - file_size: number; + is_intermediate?: boolean; /** - * Name - * @description Name of the model. + * Use Cache + * @description Whether or not to use the cache + * @default true */ - name: string; + use_cache?: boolean; /** - * Description - * @description Model description + * @description The image to create the mask from + * @default null */ - description: string | null; + image?: components["schemas"]["ImageField"] | null; /** - * Source - * @description The original source of the model (path, URL or repo_id). + * @description ID color to mask + * @default null */ - source: string; - /** @description The type of source */ - source_type: components["schemas"]["ModelSourceType"]; + color?: components["schemas"]["ColorField"] | null; /** - * Source Api Response - * @description The original API response from the source, as stringified JSON. + * Threshold + * @description Threshold for color detection + * @default 100 */ - source_api_response: string | null; + threshold?: number; /** - * Cover Image - * @description Url for image to preview model + * Invert + * @description Whether or not to invert the mask + * @default false */ - cover_image: string | null; + invert?: boolean; /** - * Submodels - * @description Loadable submodels in this model + * type + * @default mask_from_id + * @constant */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + type: "mask_from_id"; + }; + /** + * MaskOutput + * @description A torch mask tensor. + */ + MaskOutput: { + /** @description The mask. */ + mask: components["schemas"]["TensorField"]; /** - * Usage Info - * @description Usage information for this model + * Width + * @description The width of the mask in pixels. */ - usage_info: string | null; + width: number; /** - * Type - * @default main + * Height + * @description The height of the mask in pixels. + */ + height: number; + /** + * type + * @default mask_output * @constant */ - type: "main"; + type: "mask_output"; + }; + /** + * Tensor Mask to Image + * @description Convert a mask tensor to an image. + */ + MaskTensorToImageInvocation: { /** - * Trigger Phrases - * @description Set of trigger phrases for this model + * @description The board to save the image to + * @default null */ - trigger_phrases: string[] | null; - /** @description Default settings for this model */ - default_settings: components["schemas"]["MainModelDefaultSettings"] | null; - /** Variant */ - variant: components["schemas"]["ModelVariantType"] | components["schemas"]["FluxVariantType"]; + board?: components["schemas"]["BoardField"] | null; /** - * Config Path - * @description Path to the config for this model, if any. + * @description Optional metadata to be saved with the image + * @default null */ - config_path: string | null; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Converted At - * @description When this model was last converted to diffusers + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - converted_at: number | null; + id: string; /** - * Base - * @default flux - * @constant + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - base: "flux"; + is_intermediate?: boolean; /** - * Format - * @default gguf_quantized - * @constant + * Use Cache + * @description Whether or not to use the cache + * @default true */ - format: "gguf_quantized"; - /** @default epsilon */ - prediction_type: components["schemas"]["SchedulerPredictionType"]; + use_cache?: boolean; /** - * Upcast Attention - * @default false + * @description The mask tensor to convert. + * @default null + */ + mask?: components["schemas"]["TensorField"] | null; + /** + * type + * @default tensor_mask_to_image + * @constant */ - upcast_attention: boolean; + type: "tensor_mask_to_image"; }; - /** MainModelDefaultSettings */ - MainModelDefaultSettings: { + /** + * MediaPipe Face Detection + * @description Detects faces using MediaPipe. + */ + MediaPipeFaceDetectionInvocation: { /** - * Vae - * @description Default VAE for this model (model key) + * @description The board to save the image to + * @default null */ - vae?: string | null; + board?: components["schemas"]["BoardField"] | null; /** - * Vae Precision - * @description Default VAE precision for this model + * @description Optional metadata to be saved with the image + * @default null */ - vae_precision?: ("fp16" | "fp32") | null; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Scheduler - * @description Default scheduler for this model + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - scheduler?: ("ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd") | null; + id: string; /** - * Steps - * @description Default number of steps for this model + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - steps?: number | null; + is_intermediate?: boolean; /** - * Cfg Scale - * @description Default CFG Scale for this model + * Use Cache + * @description Whether or not to use the cache + * @default true */ - cfg_scale?: number | null; + use_cache?: boolean; /** - * Cfg Rescale Multiplier - * @description Default CFG Rescale Multiplier for this model + * @description The image to process + * @default null */ - cfg_rescale_multiplier?: number | null; + image?: components["schemas"]["ImageField"] | null; /** - * Width - * @description Default width for this model + * Max Faces + * @description Maximum number of faces to detect + * @default 1 */ - width?: number | null; + max_faces?: number; /** - * Height - * @description Default height for this model + * Min Confidence + * @description Minimum confidence for face detection + * @default 0.5 */ - height?: number | null; + min_confidence?: number; /** - * Guidance - * @description Default Guidance for this model + * type + * @default mediapipe_face_detection + * @constant */ - guidance?: number | null; + type: "mediapipe_face_detection"; }; /** - * Main Model - SD1.5, SD2 - * @description Loads a main model, outputting its submodels. + * Metadata Merge + * @description Merged a collection of MetadataDict into a single MetadataDict. */ - MainModelLoaderInvocation: { + MergeMetadataInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -15267,22 +18340,23 @@ export type components = { */ use_cache?: boolean; /** - * @description Main model (UNet, VAE, CLIP) to load + * Collection + * @description Collection of Metadata * @default null */ - model?: components["schemas"]["ModelIdentifierField"] | null; + collection?: components["schemas"]["MetadataField"][] | null; /** * type - * @default main_model_loader + * @default merge_metadata * @constant */ - type: "main_model_loader"; + type: "merge_metadata"; }; /** - * Combine Masks - * @description Combine two masks together by multiplying them using `PIL.ImageChops.multiply()`. + * Merge Tiles to Image + * @description Merge multiple tile images into a single image. */ - MaskCombineInvocation: { + MergeTilesToImageInvocation: { /** * @description The board to save the image to * @default null @@ -15311,37 +18385,83 @@ export type components = { */ use_cache?: boolean; /** - * @description The first mask to combine + * Tiles With Images + * @description A list of tile images with tile properties. * @default null */ - mask1?: components["schemas"]["ImageField"] | null; + tiles_with_images?: components["schemas"]["TileWithImage"][] | null; /** - * @description The second image to combine - * @default null + * Blend Mode + * @description blending type Linear or Seam + * @default Seam + * @enum {string} */ - mask2?: components["schemas"]["ImageField"] | null; + blend_mode?: "Linear" | "Seam"; + /** + * Blend Amount + * @description The amount to blend adjacent tiles in pixels. Must be <= the amount of overlap between adjacent tiles. + * @default 32 + */ + blend_amount?: number; /** * type - * @default mask_combine + * @default merge_tiles_to_image * @constant */ - type: "mask_combine"; + type: "merge_tiles_to_image"; }; /** - * Mask Edge - * @description Applies an edge mask to an image + * MetadataField + * @description Pydantic model for metadata with custom root of type dict[str, Any]. + * Metadata is stored without a strict schema. */ - MaskEdgeInvocation: { + MetadataField: Record; + /** + * Metadata Field Extractor + * @description Extracts the text value from an image's metadata given a key. + * Raises an error if the image has no metadata or if the value is not a string (nesting not permitted). + */ + MetadataFieldExtractorInvocation: { /** - * @description The board to save the image to + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to extract metadata from * @default null */ - board?: components["schemas"]["BoardField"] | null; + image?: components["schemas"]["ImageField"] | null; /** - * @description Optional metadata to be saved with the image + * Key + * @description The key in the image's metadata to extract the value from * @default null */ - metadata?: components["schemas"]["MetadataField"] | null; + key?: string | null; + /** + * type + * @default metadata_field_extractor + * @constant + */ + type: "metadata_field_extractor"; + }; + /** + * Metadata From Image + * @description Used to create a core metadata item then Add/Update it to the provided metadata + */ + MetadataFromImageInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -15360,56 +18480,70 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to apply the mask to + * @description The image to process * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Edge Size - * @description The size of the edge - * @default null + * type + * @default metadata_from_image + * @constant */ - edge_size?: number | null; + type: "metadata_from_image"; + }; + /** + * Metadata + * @description Takes a MetadataItem or collection of MetadataItems and outputs a MetadataDict. + */ + MetadataInvocation: { /** - * Edge Blur - * @description The amount of blur on the edge - * @default null + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - edge_blur?: number | null; + id: string; /** - * Low Threshold - * @description First threshold for the hysteresis procedure in Canny edge detection - * @default null + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - low_threshold?: number | null; + is_intermediate?: boolean; /** - * High Threshold - * @description Second threshold for the hysteresis procedure in Canny edge detection + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * Items + * @description A single metadata item or collection of metadata items * @default null */ - high_threshold?: number | null; + items?: components["schemas"]["MetadataItemField"][] | components["schemas"]["MetadataItemField"] | null; /** * type - * @default mask_edge + * @default metadata * @constant */ - type: "mask_edge"; + type: "metadata"; }; - /** - * Mask from Alpha - * @description Extracts the alpha channel of an image as a mask. - */ - MaskFromAlphaInvocation: { + /** MetadataItemField */ + MetadataItemField: { /** - * @description The board to save the image to - * @default null + * Label + * @description Label for this metadata item */ - board?: components["schemas"]["BoardField"] | null; + label: string; /** - * @description Optional metadata to be saved with the image - * @default null + * Value + * @description The value for this metadata item (may be any type) */ - metadata?: components["schemas"]["MetadataField"] | null; + value: unknown; + }; + /** + * Metadata Item + * @description Used to create an arbitrary metadata item. Provide "label" and make a connection to "value" to store that data as the value. + */ + MetadataItemInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -15428,33 +18562,29 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to create the mask from + * Label + * @description Label for this metadata item * @default null */ - image?: components["schemas"]["ImageField"] | null; + label?: string | null; /** - * Invert - * @description Whether or not to invert the mask - * @default false + * Value + * @description The value for this metadata item (may be any type) + * @default null */ - invert?: boolean; + value?: unknown | null; /** * type - * @default tomask + * @default metadata_item * @constant */ - type: "tomask"; + type: "metadata_item"; }; - /** - * Mask from Segmented Image - * @description Generate a mask for a particular color in an ID Map - */ - MaskFromIDInvocation: { - /** - * @description The board to save the image to - * @default null - */ - board?: components["schemas"]["BoardField"] | null; + /** + * Metadata Item Linked + * @description Used to Create/Add/Update a value into a metadata label + */ + MetadataItemLinkedInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -15478,68 +18608,61 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to create the mask from - * @default null + * Label + * @description Label for this metadata item + * @default * CUSTOM LABEL * + * @enum {string} */ - image?: components["schemas"]["ImageField"] | null; + label?: "* CUSTOM LABEL *" | "positive_prompt" | "positive_style_prompt" | "negative_prompt" | "negative_style_prompt" | "width" | "height" | "seed" | "cfg_scale" | "cfg_rescale_multiplier" | "steps" | "scheduler" | "clip_skip" | "model" | "vae" | "seamless_x" | "seamless_y" | "guidance" | "cfg_scale_start_step" | "cfg_scale_end_step"; /** - * @description ID color to mask + * Custom Label + * @description Label for this metadata item * @default null */ - color?: components["schemas"]["ColorField"] | null; - /** - * Threshold - * @description Threshold for color detection - * @default 100 - */ - threshold?: number; + custom_label?: string | null; /** - * Invert - * @description Whether or not to invert the mask - * @default false + * Value + * @description The value for this metadata item (may be any type) + * @default null */ - invert?: boolean; + value?: unknown | null; /** * type - * @default mask_from_id + * @default metadata_item_linked * @constant */ - type: "mask_from_id"; + type: "metadata_item_linked"; }; /** - * MaskOutput - * @description A torch mask tensor. + * MetadataItemOutput + * @description Metadata Item Output */ - MaskOutput: { - /** @description The mask. */ - mask: components["schemas"]["TensorField"]; - /** - * Width - * @description The width of the mask in pixels. - */ - width: number; + MetadataItemOutput: { + /** @description Metadata Item */ + item: components["schemas"]["MetadataItemField"]; /** - * Height - * @description The height of the mask in pixels. + * type + * @default metadata_item_output + * @constant */ - height: number; + type: "metadata_item_output"; + }; + /** MetadataOutput */ + MetadataOutput: { + /** @description Metadata Dict */ + metadata: components["schemas"]["MetadataField"]; /** * type - * @default mask_output + * @default metadata_output * @constant */ - type: "mask_output"; + type: "metadata_output"; }; /** - * Tensor Mask to Image - * @description Convert a mask tensor to an image. + * Metadata To Bool Collection + * @description Extracts a Boolean value Collection of a label from metadata */ - MaskTensorToImageInvocation: { - /** - * @description The board to save the image to - * @default null - */ - board?: components["schemas"]["BoardField"] | null; + MetadataToBoolCollectionInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -15563,27 +18686,36 @@ export type components = { */ use_cache?: boolean; /** - * @description The mask tensor to convert. + * Label + * @description Label for this metadata item + * @default * CUSTOM LABEL * + * @enum {string} + */ + label?: "* CUSTOM LABEL *" | "seamless_x" | "seamless_y"; + /** + * Custom Label + * @description Label for this metadata item * @default null */ - mask?: components["schemas"]["TensorField"] | null; + custom_label?: string | null; + /** + * Default Value + * @description The default bool to use if not found in the metadata + * @default null + */ + default_value?: boolean[] | null; /** * type - * @default tensor_mask_to_image + * @default metadata_to_bool_collection * @constant */ - type: "tensor_mask_to_image"; + type: "metadata_to_bool_collection"; }; /** - * MediaPipe Face Detection - * @description Detects faces using MediaPipe. + * Metadata To Bool + * @description Extracts a Boolean value of a label from metadata */ - MediaPipeFaceDetectionInvocation: { - /** - * @description The board to save the image to - * @default null - */ - board?: components["schemas"]["BoardField"] | null; + MetadataToBoolInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -15607,34 +18739,41 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to process - * @default null + * Label + * @description Label for this metadata item + * @default * CUSTOM LABEL * + * @enum {string} */ - image?: components["schemas"]["ImageField"] | null; + label?: "* CUSTOM LABEL *" | "seamless_x" | "seamless_y"; /** - * Max Faces - * @description Maximum number of faces to detect - * @default 1 + * Custom Label + * @description Label for this metadata item + * @default null */ - max_faces?: number; + custom_label?: string | null; /** - * Min Confidence - * @description Minimum confidence for face detection - * @default 0.5 + * Default Value + * @description The default bool to use if not found in the metadata + * @default null */ - min_confidence?: number; + default_value?: boolean | null; /** * type - * @default mediapipe_face_detection + * @default metadata_to_bool * @constant */ - type: "mediapipe_face_detection"; + type: "metadata_to_bool"; }; /** - * Metadata Merge - * @description Merged a collection of MetadataDict into a single MetadataDict. + * Metadata To ControlNets + * @description Extracts a Controlnets value of a label from metadata */ - MergeMetadataInvocation: { + MetadataToControlnetsInvocation: { + /** + * @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. @@ -15653,28 +18792,22 @@ export type components = { */ use_cache?: boolean; /** - * Collection - * @description Collection of Metadata + * ControlNet-List * @default null */ - collection?: components["schemas"]["MetadataField"][] | null; + control_list?: components["schemas"]["ControlField"] | components["schemas"]["ControlField"][] | null; /** * type - * @default merge_metadata + * @default metadata_to_controlnets * @constant */ - type: "merge_metadata"; + type: "metadata_to_controlnets"; }; /** - * Merge Tiles to Image - * @description Merge multiple tile images into a single image. + * Metadata To Float Collection + * @description Extracts a Float value Collection of a label from metadata */ - MergeTilesToImageInvocation: { - /** - * @description The board to save the image to - * @default null - */ - board?: components["schemas"]["BoardField"] | null; + MetadataToFloatCollectionInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -15698,43 +18831,41 @@ export type components = { */ use_cache?: boolean; /** - * Tiles With Images - * @description A list of tile images with tile properties. - * @default null + * Label + * @description Label for this metadata item + * @default * CUSTOM LABEL * + * @enum {string} */ - tiles_with_images?: components["schemas"]["TileWithImage"][] | null; + label?: "* CUSTOM LABEL *" | "cfg_scale" | "cfg_rescale_multiplier" | "guidance"; /** - * Blend Mode - * @description blending type Linear or Seam - * @default Seam - * @enum {string} + * Custom Label + * @description Label for this metadata item + * @default null */ - blend_mode?: "Linear" | "Seam"; + custom_label?: string | null; /** - * Blend Amount - * @description The amount to blend adjacent tiles in pixels. Must be <= the amount of overlap between adjacent tiles. - * @default 32 + * Default Value + * @description The default float to use if not found in the metadata + * @default null */ - blend_amount?: number; + default_value?: number[] | null; /** * type - * @default merge_tiles_to_image + * @default metadata_to_float_collection * @constant */ - type: "merge_tiles_to_image"; + type: "metadata_to_float_collection"; }; /** - * MetadataField - * @description Pydantic model for metadata with custom root of type dict[str, Any]. - * Metadata is stored without a strict schema. - */ - MetadataField: Record; - /** - * Metadata Field Extractor - * @description Extracts the text value from an image's metadata given a key. - * Raises an error if the image has no metadata or if the value is not a string (nesting not permitted). + * Metadata To Float + * @description Extracts a Float value of a label from metadata */ - MetadataFieldExtractorInvocation: { + MetadataToFloatInvocation: { + /** + * @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. @@ -15753,28 +18884,41 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to extract metadata from + * Label + * @description Label for this metadata item + * @default * CUSTOM LABEL * + * @enum {string} + */ + label?: "* CUSTOM LABEL *" | "cfg_scale" | "cfg_rescale_multiplier" | "guidance"; + /** + * Custom Label + * @description Label for this metadata item * @default null */ - image?: components["schemas"]["ImageField"] | null; + custom_label?: string | null; /** - * Key - * @description The key in the image's metadata to extract the value from + * Default Value + * @description The default float to use if not found in the metadata * @default null */ - key?: string | null; + default_value?: number | null; /** * type - * @default metadata_field_extractor + * @default metadata_to_float * @constant */ - type: "metadata_field_extractor"; + type: "metadata_to_float"; }; /** - * Metadata From Image - * @description Used to create a core metadata item then Add/Update it to the provided metadata + * Metadata To IP-Adapters + * @description Extracts a IP-Adapters value of a label from metadata */ - MetadataFromImageInvocation: { + MetadataToIPAdaptersInvocation: { + /** + * @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. @@ -15793,22 +18937,28 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to process + * IP-Adapter-List + * @description IP-Adapter to apply * @default null */ - image?: components["schemas"]["ImageField"] | null; + ip_adapter_list?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; /** * type - * @default metadata_from_image + * @default metadata_to_ip_adapters * @constant */ - type: "metadata_from_image"; + type: "metadata_to_ip_adapters"; }; /** - * Metadata - * @description Takes a MetadataItem or collection of MetadataItems and outputs a MetadataDict. + * Metadata To Integer Collection + * @description Extracts an integer value Collection of a label from metadata */ - MetadataInvocation: { + MetadataToIntegerCollectionInvocation: { + /** + * @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. @@ -15827,36 +18977,41 @@ export type components = { */ use_cache?: boolean; /** - * Items - * @description A single metadata item or collection of metadata items - * @default null + * Label + * @description Label for this metadata item + * @default * CUSTOM LABEL * + * @enum {string} */ - items?: components["schemas"]["MetadataItemField"][] | components["schemas"]["MetadataItemField"] | null; + label?: "* CUSTOM LABEL *" | "width" | "height" | "seed" | "steps" | "clip_skip" | "cfg_scale_start_step" | "cfg_scale_end_step"; /** - * type - * @default metadata - * @constant + * Custom Label + * @description Label for this metadata item + * @default null */ - type: "metadata"; - }; - /** MetadataItemField */ - MetadataItemField: { + custom_label?: string | null; /** - * Label - * @description Label for this metadata item + * Default Value + * @description The default integer to use if not found in the metadata + * @default null */ - label: string; + default_value?: number[] | null; /** - * Value - * @description The value for this metadata item (may be any type) + * type + * @default metadata_to_integer_collection + * @constant */ - value: unknown; + type: "metadata_to_integer_collection"; }; /** - * Metadata Item - * @description Used to create an arbitrary metadata item. Provide "label" and make a connection to "value" to store that data as the value. + * Metadata To Integer + * @description Extracts an integer value of a label from metadata */ - MetadataItemInvocation: { + MetadataToIntegerInvocation: { + /** + * @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. @@ -15877,27 +19032,34 @@ export type components = { /** * Label * @description Label for this metadata item + * @default * CUSTOM LABEL * + * @enum {string} + */ + label?: "* CUSTOM LABEL *" | "width" | "height" | "seed" | "steps" | "clip_skip" | "cfg_scale_start_step" | "cfg_scale_end_step"; + /** + * Custom Label + * @description Label for this metadata item * @default null */ - label?: string | null; + custom_label?: string | null; /** - * Value - * @description The value for this metadata item (may be any type) + * Default Value + * @description The default integer to use if not found in the metadata * @default null */ - value?: unknown | null; + default_value?: number | null; /** * type - * @default metadata_item + * @default metadata_to_integer * @constant */ - type: "metadata_item"; + type: "metadata_to_integer"; }; /** - * Metadata Item Linked - * @description Used to Create/Add/Update a value into a metadata label + * Metadata To LoRA Collection + * @description Extracts Lora(s) from metadata into a collection */ - MetadataItemLinkedInvocation: { + MetadataToLorasCollectionInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -15920,62 +19082,47 @@ export type components = { * @default true */ use_cache?: boolean; - /** - * Label - * @description Label for this metadata item - * @default * CUSTOM LABEL * - * @enum {string} - */ - label?: "* CUSTOM LABEL *" | "positive_prompt" | "positive_style_prompt" | "negative_prompt" | "negative_style_prompt" | "width" | "height" | "seed" | "cfg_scale" | "cfg_rescale_multiplier" | "steps" | "scheduler" | "clip_skip" | "model" | "vae" | "seamless_x" | "seamless_y" | "guidance" | "cfg_scale_start_step" | "cfg_scale_end_step"; /** * Custom Label * @description Label for this metadata item - * @default null + * @default loras */ - custom_label?: string | null; + custom_label?: string; /** - * Value - * @description The value for this metadata item (may be any type) - * @default null + * LoRAs + * @description LoRA models and weights. May be a single LoRA or collection. + * @default [] */ - value?: unknown | null; + loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null; /** * type - * @default metadata_item_linked + * @default metadata_to_lora_collection * @constant */ - type: "metadata_item_linked"; + type: "metadata_to_lora_collection"; }; /** - * MetadataItemOutput - * @description Metadata Item Output + * MetadataToLorasCollectionOutput + * @description Model loader output */ - MetadataItemOutput: { - /** @description Metadata Item */ - item: components["schemas"]["MetadataItemField"]; + MetadataToLorasCollectionOutput: { /** - * type - * @default metadata_item_output - * @constant + * LoRAs + * @description Collection of LoRA model and weights */ - type: "metadata_item_output"; - }; - /** MetadataOutput */ - MetadataOutput: { - /** @description Metadata Dict */ - metadata: components["schemas"]["MetadataField"]; + lora: components["schemas"]["LoRAField"][]; /** * type - * @default metadata_output + * @default metadata_to_lora_collection_output * @constant */ - type: "metadata_output"; + type: "metadata_to_lora_collection_output"; }; /** - * Metadata To Bool Collection - * @description Extracts a Boolean value Collection of a label from metadata + * Metadata To LoRAs + * @description Extracts a Loras value of a label from metadata */ - MetadataToBoolCollectionInvocation: { + MetadataToLorasInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -15999,36 +19146,29 @@ export type components = { */ use_cache?: boolean; /** - * Label - * @description Label for this metadata item - * @default * CUSTOM LABEL * - * @enum {string} - */ - label?: "* CUSTOM LABEL *" | "seamless_x" | "seamless_y"; - /** - * Custom Label - * @description Label for this metadata item + * UNet + * @description UNet (scheduler, LoRAs) * @default null */ - custom_label?: string | null; + unet?: components["schemas"]["UNetField"] | null; /** - * Default Value - * @description The default bool to use if not found in the metadata + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count * @default null */ - default_value?: boolean[] | null; + clip?: components["schemas"]["CLIPField"] | null; /** * type - * @default metadata_to_bool_collection + * @default metadata_to_loras * @constant */ - type: "metadata_to_bool_collection"; + type: "metadata_to_loras"; }; /** - * Metadata To Bool - * @description Extracts a Boolean value of a label from metadata + * Metadata To Model + * @description Extracts a Model value of a label from metadata */ - MetadataToBoolInvocation: { + MetadataToModelInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -16054,10 +19194,10 @@ export type components = { /** * Label * @description Label for this metadata item - * @default * CUSTOM LABEL * + * @default model * @enum {string} */ - label?: "* CUSTOM LABEL *" | "seamless_x" | "seamless_y"; + label?: "* CUSTOM LABEL *" | "model"; /** * Custom Label * @description Label for this metadata item @@ -16065,62 +19205,59 @@ export type components = { */ custom_label?: string | null; /** - * Default Value - * @description The default bool to use if not found in the metadata + * @description The default model to use if not found in the metadata * @default null */ - default_value?: boolean | null; + default_value?: components["schemas"]["ModelIdentifierField"] | null; /** * type - * @default metadata_to_bool + * @default metadata_to_model * @constant */ - type: "metadata_to_bool"; + type: "metadata_to_model"; }; /** - * Metadata To ControlNets - * @description Extracts a Controlnets value of a label from metadata + * MetadataToModelOutput + * @description String to main model output */ - MetadataToControlnetsInvocation: { + MetadataToModelOutput: { /** - * @description Optional metadata to be saved with the image - * @default null + * Model + * @description Main model (UNet, VAE, CLIP) to load */ - metadata?: components["schemas"]["MetadataField"] | null; + model: components["schemas"]["ModelIdentifierField"]; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Name + * @description Model Name */ - id: string; + name: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * UNet + * @description UNet (scheduler, LoRAs) */ - is_intermediate?: boolean; + unet: components["schemas"]["UNetField"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * VAE + * @description VAE */ - use_cache?: boolean; + vae: components["schemas"]["VAEField"]; /** - * ControlNet-List - * @default null + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count */ - control_list?: components["schemas"]["ControlField"] | components["schemas"]["ControlField"][] | null; + clip: components["schemas"]["CLIPField"]; /** * type - * @default metadata_to_controlnets + * @default metadata_to_model_output * @constant */ - type: "metadata_to_controlnets"; + type: "metadata_to_model_output"; }; /** - * Metadata To Float Collection - * @description Extracts a Float value Collection of a label from metadata + * Metadata To SDXL LoRAs + * @description Extracts a SDXL Loras value of a label from metadata */ - MetadataToFloatCollectionInvocation: { + MetadataToSDXLLorasInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -16144,36 +19281,35 @@ export type components = { */ use_cache?: boolean; /** - * Label - * @description Label for this metadata item - * @default * CUSTOM LABEL * - * @enum {string} + * UNet + * @description UNet (scheduler, LoRAs) + * @default null */ - label?: "* CUSTOM LABEL *" | "cfg_scale" | "cfg_rescale_multiplier" | "guidance"; + unet?: components["schemas"]["UNetField"] | null; /** - * Custom Label - * @description Label for this metadata item + * CLIP 1 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count * @default null */ - custom_label?: string | null; + clip?: components["schemas"]["CLIPField"] | null; /** - * Default Value - * @description The default float to use if not found in the metadata + * CLIP 2 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count * @default null */ - default_value?: number[] | null; + clip2?: components["schemas"]["CLIPField"] | null; /** * type - * @default metadata_to_float_collection + * @default metadata_to_sdlx_loras * @constant */ - type: "metadata_to_float_collection"; + type: "metadata_to_sdlx_loras"; }; /** - * Metadata To Float - * @description Extracts a Float value of a label from metadata + * Metadata To SDXL Model + * @description Extracts a SDXL Model value of a label from metadata */ - MetadataToFloatInvocation: { + MetadataToSDXLModelInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -16199,10 +19335,10 @@ export type components = { /** * Label * @description Label for this metadata item - * @default * CUSTOM LABEL * + * @default model * @enum {string} */ - label?: "* CUSTOM LABEL *" | "cfg_scale" | "cfg_rescale_multiplier" | "guidance"; + label?: "* CUSTOM LABEL *" | "model"; /** * Custom Label * @description Label for this metadata item @@ -16210,63 +19346,64 @@ export type components = { */ custom_label?: string | null; /** - * Default Value - * @description The default float to use if not found in the metadata + * @description The default SDXL Model to use if not found in the metadata * @default null */ - default_value?: number | null; + default_value?: components["schemas"]["ModelIdentifierField"] | null; /** * type - * @default metadata_to_float + * @default metadata_to_sdxl_model * @constant */ - type: "metadata_to_float"; + type: "metadata_to_sdxl_model"; }; /** - * Metadata To IP-Adapters - * @description Extracts a IP-Adapters value of a label from metadata + * MetadataToSDXLModelOutput + * @description String to SDXL main model output */ - MetadataToIPAdaptersInvocation: { + MetadataToSDXLModelOutput: { /** - * @description Optional metadata to be saved with the image - * @default null + * Model + * @description Main model (UNet, VAE, CLIP) to load */ - metadata?: components["schemas"]["MetadataField"] | null; + model: components["schemas"]["ModelIdentifierField"]; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Name + * @description Model Name */ - id: string; + name: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * UNet + * @description UNet (scheduler, LoRAs) */ - is_intermediate?: boolean; + unet: components["schemas"]["UNetField"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * CLIP 1 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count */ - use_cache?: boolean; + clip: components["schemas"]["CLIPField"]; /** - * IP-Adapter-List - * @description IP-Adapter to apply - * @default null + * CLIP 2 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count */ - ip_adapter_list?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; + clip2: components["schemas"]["CLIPField"]; + /** + * VAE + * @description VAE + */ + vae: components["schemas"]["VAEField"]; /** * type - * @default metadata_to_ip_adapters + * @default metadata_to_sdxl_model_output * @constant */ - type: "metadata_to_ip_adapters"; + type: "metadata_to_sdxl_model_output"; }; /** - * Metadata To Integer Collection - * @description Extracts an integer value Collection of a label from metadata + * Metadata To Scheduler + * @description Extracts a Scheduler value of a label from metadata */ - MetadataToIntegerCollectionInvocation: { + MetadataToSchedulerInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -16292,10 +19429,10 @@ export type components = { /** * Label * @description Label for this metadata item - * @default * CUSTOM LABEL * + * @default scheduler * @enum {string} */ - label?: "* CUSTOM LABEL *" | "width" | "height" | "seed" | "steps" | "clip_skip" | "cfg_scale_start_step" | "cfg_scale_end_step"; + label?: "* CUSTOM LABEL *" | "scheduler"; /** * Custom Label * @description Label for this metadata item @@ -16304,22 +19441,23 @@ export type components = { custom_label?: string | null; /** * Default Value - * @description The default integer to use if not found in the metadata - * @default null + * @description The default scheduler to use if not found in the metadata + * @default euler + * @enum {string} */ - default_value?: number[] | null; + default_value?: "ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd"; /** * type - * @default metadata_to_integer_collection + * @default metadata_to_scheduler * @constant */ - type: "metadata_to_integer_collection"; + type: "metadata_to_scheduler"; }; /** - * Metadata To Integer - * @description Extracts an integer value of a label from metadata + * Metadata To String Collection + * @description Extracts a string collection value of a label from metadata */ - MetadataToIntegerInvocation: { + MetadataToStringCollectionInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -16348,7 +19486,7 @@ export type components = { * @default * CUSTOM LABEL * * @enum {string} */ - label?: "* CUSTOM LABEL *" | "width" | "height" | "seed" | "steps" | "clip_skip" | "cfg_scale_start_step" | "cfg_scale_end_step"; + label?: "* CUSTOM LABEL *" | "positive_prompt" | "positive_style_prompt" | "negative_prompt" | "negative_style_prompt"; /** * Custom Label * @description Label for this metadata item @@ -16357,22 +19495,22 @@ export type components = { custom_label?: string | null; /** * Default Value - * @description The default integer to use if not found in the metadata + * @description The default string collection to use if not found in the metadata * @default null */ - default_value?: number | null; + default_value?: string[] | null; /** * type - * @default metadata_to_integer + * @default metadata_to_string_collection * @constant */ - type: "metadata_to_integer"; + type: "metadata_to_string_collection"; }; /** - * Metadata To LoRA Collection - * @description Extracts Lora(s) from metadata into a collection + * Metadata To String + * @description Extracts a string value of a label from metadata */ - MetadataToLorasCollectionInvocation: { + MetadataToStringInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -16396,46 +19534,36 @@ export type components = { */ use_cache?: boolean; /** - * Custom Label + * Label * @description Label for this metadata item - * @default loras - */ - custom_label?: string; - /** - * LoRAs - * @description LoRA models and weights. May be a single LoRA or collection. - * @default [] + * @default * CUSTOM LABEL * + * @enum {string} */ - loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null; + label?: "* CUSTOM LABEL *" | "positive_prompt" | "positive_style_prompt" | "negative_prompt" | "negative_style_prompt"; /** - * type - * @default metadata_to_lora_collection - * @constant + * Custom Label + * @description Label for this metadata item + * @default null */ - type: "metadata_to_lora_collection"; - }; - /** - * MetadataToLorasCollectionOutput - * @description Model loader output - */ - MetadataToLorasCollectionOutput: { + custom_label?: string | null; /** - * LoRAs - * @description Collection of LoRA model and weights + * Default Value + * @description The default string to use if not found in the metadata + * @default null */ - lora: components["schemas"]["LoRAField"][]; + default_value?: string | null; /** * type - * @default metadata_to_lora_collection_output + * @default metadata_to_string * @constant */ - type: "metadata_to_lora_collection_output"; + type: "metadata_to_string"; }; /** - * Metadata To LoRAs - * @description Extracts a Loras value of a label from metadata + * Metadata To T2I-Adapters + * @description Extracts a T2I-Adapters value of a label from metadata */ - MetadataToLorasInvocation: { + MetadataToT2IAdaptersInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -16459,29 +19587,23 @@ export type components = { */ use_cache?: boolean; /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null - */ - unet?: components["schemas"]["UNetField"] | null; - /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * T2I-Adapter + * @description IP-Adapter to apply * @default null */ - clip?: components["schemas"]["CLIPField"] | null; + t2i_adapter_list?: components["schemas"]["T2IAdapterField"] | components["schemas"]["T2IAdapterField"][] | null; /** * type - * @default metadata_to_loras + * @default metadata_to_t2i_adapters * @constant */ - type: "metadata_to_loras"; + type: "metadata_to_t2i_adapters"; }; /** - * Metadata To Model - * @description Extracts a Model value of a label from metadata + * Metadata To VAE + * @description Extracts a VAE value of a label from metadata */ - MetadataToModelInvocation: { + MetadataToVAEInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -16507,10 +19629,10 @@ export type components = { /** * Label * @description Label for this metadata item - * @default model + * @default vae * @enum {string} */ - label?: "* CUSTOM LABEL *" | "model"; + label?: "* CUSTOM LABEL *" | "vae"; /** * Custom Label * @description Label for this metadata item @@ -16518,116 +19640,57 @@ export type components = { */ custom_label?: string | null; /** - * @description The default model to use if not found in the metadata + * @description The default VAE to use if not found in the metadata * @default null */ - default_value?: components["schemas"]["ModelIdentifierField"] | null; - /** - * type - * @default metadata_to_model - * @constant - */ - type: "metadata_to_model"; - }; - /** - * MetadataToModelOutput - * @description String to main model output - */ - MetadataToModelOutput: { - /** - * Model - * @description Main model (UNet, VAE, CLIP) to load - */ - model: components["schemas"]["ModelIdentifierField"]; - /** - * Name - * @description Model Name - */ - name: string; - /** - * UNet - * @description UNet (scheduler, LoRAs) - */ - unet: components["schemas"]["UNetField"]; - /** - * VAE - * @description VAE - */ - vae: components["schemas"]["VAEField"]; - /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - */ - clip: components["schemas"]["CLIPField"]; + default_value?: components["schemas"]["VAEField"] | null; /** * type - * @default metadata_to_model_output + * @default metadata_to_vae * @constant */ - type: "metadata_to_model_output"; + type: "metadata_to_vae"; }; /** - * Metadata To SDXL LoRAs - * @description Extracts a SDXL Loras value of a label from metadata + * ModelFormat + * @description Storage format of model. + * @enum {string} */ - MetadataToSDXLLorasInvocation: { - /** - * @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; + ModelFormat: "omi" | "diffusers" | "checkpoint" | "lycoris" | "onnx" | "olive" | "embedding_file" | "embedding_folder" | "invokeai" | "t5_encoder" | "bnb_quantized_int8b" | "bnb_quantized_nf4b" | "gguf_quantized" | "api" | "unknown"; + /** ModelIdentifierField */ + ModelIdentifierField: { /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null + * Key + * @description The model's unique key */ - unet?: components["schemas"]["UNetField"] | null; + key: string; /** - * CLIP 1 - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * Hash + * @description The model's BLAKE3 hash */ - clip?: components["schemas"]["CLIPField"] | null; + hash: string; /** - * CLIP 2 - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * Name + * @description The model's name */ - clip2?: components["schemas"]["CLIPField"] | null; + name: string; + /** @description The model's base model type */ + base: components["schemas"]["BaseModelType"]; + /** @description The model's type */ + type: components["schemas"]["ModelType"]; /** - * type - * @default metadata_to_sdlx_loras - * @constant + * @description The submodel to load, if this is a main model + * @default null */ - type: "metadata_to_sdlx_loras"; + submodel_type?: components["schemas"]["SubModelType"] | null; }; /** - * Metadata To SDXL Model - * @description Extracts a SDXL Model value of a label from metadata + * Any Model + * @description Selects any model, outputting it its identifier. Be careful with this one! The identifier will be accepted as + * input for any model, even if the model types don't match. If you connect this to a mismatched input, you'll get an + * error. */ - MetadataToSDXLModelInvocation: { - /** - * @description Optional metadata to be saved with the image - * @default null - */ - metadata?: components["schemas"]["MetadataField"] | null; + ModelIdentifierInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -16646,915 +19709,952 @@ export type components = { */ use_cache?: boolean; /** - * Label - * @description Label for this metadata item - * @default model - * @enum {string} + * Model + * @description The model to select + * @default null */ - label?: "* CUSTOM LABEL *" | "model"; + model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Custom Label - * @description Label for this metadata item - * @default null + * type + * @default model_identifier + * @constant */ - custom_label?: string | null; + type: "model_identifier"; + }; + /** + * ModelIdentifierOutput + * @description Model identifier output + */ + ModelIdentifierOutput: { /** - * @description The default SDXL Model to use if not found in the metadata - * @default null + * Model + * @description Model identifier */ - default_value?: components["schemas"]["ModelIdentifierField"] | null; + model: components["schemas"]["ModelIdentifierField"]; /** * type - * @default metadata_to_sdxl_model + * @default model_identifier_output * @constant */ - type: "metadata_to_sdxl_model"; + type: "model_identifier_output"; }; /** - * MetadataToSDXLModelOutput - * @description String to SDXL main model output + * ModelInstallCancelledEvent + * @description Event model for model_install_cancelled */ - MetadataToSDXLModelOutput: { + ModelInstallCancelledEvent: { /** - * Model - * @description Main model (UNet, VAE, CLIP) to load + * Timestamp + * @description The timestamp of the event */ - model: components["schemas"]["ModelIdentifierField"]; + timestamp: number; /** - * Name - * @description Model Name + * Id + * @description The ID of the install job */ - name: string; + id: number; /** - * UNet - * @description UNet (scheduler, LoRAs) + * Source + * @description Source of the model; local path, repo_id or url */ - unet: components["schemas"]["UNetField"]; + source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; + }; + /** + * ModelInstallCompleteEvent + * @description Event model for model_install_complete + */ + ModelInstallCompleteEvent: { /** - * CLIP 1 - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * Timestamp + * @description The timestamp of the event */ - clip: components["schemas"]["CLIPField"]; + timestamp: number; /** - * CLIP 2 - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * Id + * @description The ID of the install job */ - clip2: components["schemas"]["CLIPField"]; + id: number; /** - * VAE - * @description VAE + * Source + * @description Source of the model; local path, repo_id or url */ - vae: components["schemas"]["VAEField"]; + source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; /** - * type - * @default metadata_to_sdxl_model_output - * @constant + * Key + * @description Model config record key */ - type: "metadata_to_sdxl_model_output"; + key: string; + /** + * Total Bytes + * @description Size of the model (may be None for installation of a local path) + */ + total_bytes: number | null; + /** + * 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; }; /** - * Metadata To Scheduler - * @description Extracts a Scheduler value of a label from metadata + * ModelInstallDownloadProgressEvent + * @description Event model for model_install_download_progress */ - MetadataToSchedulerInvocation: { + ModelInstallDownloadProgressEvent: { /** - * @description Optional metadata to be saved with the image - * @default null + * Timestamp + * @description The timestamp of the event */ - metadata?: components["schemas"]["MetadataField"] | null; + timestamp: number; /** * 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 + * @description The ID of the install job */ - is_intermediate?: boolean; + id: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Source + * @description Source of the model; local path, repo_id or url */ - use_cache?: boolean; + source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; /** - * Label - * @description Label for this metadata item - * @default scheduler - * @enum {string} + * Local Path + * @description Where model is downloading to */ - label?: "* CUSTOM LABEL *" | "scheduler"; + local_path: string; /** - * Custom Label - * @description Label for this metadata item - * @default null + * Bytes + * @description Number of bytes downloaded so far */ - custom_label?: string | null; + bytes: number; /** - * Default Value - * @description The default scheduler to use if not found in the metadata - * @default euler - * @enum {string} + * Total Bytes + * @description Total size of download, including all files */ - default_value?: "ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd"; + total_bytes: number; /** - * type - * @default metadata_to_scheduler - * @constant + * Parts + * @description Progress of downloading URLs that comprise the model, if any */ - type: "metadata_to_scheduler"; + parts: { + [key: string]: number | string; + }[]; }; /** - * Metadata To String Collection - * @description Extracts a string collection value of a label from metadata + * ModelInstallDownloadStartedEvent + * @description Event model for model_install_download_started */ - MetadataToStringCollectionInvocation: { + ModelInstallDownloadStartedEvent: { /** - * @description Optional metadata to be saved with the image - * @default null + * Timestamp + * @description The timestamp of the event */ - metadata?: components["schemas"]["MetadataField"] | null; + timestamp: number; /** * 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 + * @description The ID of the install job */ - is_intermediate?: boolean; + id: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Source + * @description Source of the model; local path, repo_id or url */ - use_cache?: boolean; + source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; /** - * Label - * @description Label for this metadata item - * @default * CUSTOM LABEL * - * @enum {string} + * Local Path + * @description Where model is downloading to */ - label?: "* CUSTOM LABEL *" | "positive_prompt" | "positive_style_prompt" | "negative_prompt" | "negative_style_prompt"; + local_path: string; /** - * Custom Label - * @description Label for this metadata item - * @default null + * Bytes + * @description Number of bytes downloaded so far */ - custom_label?: string | null; + bytes: number; /** - * Default Value - * @description The default string collection to use if not found in the metadata - * @default null + * Total Bytes + * @description Total size of download, including all files */ - default_value?: string[] | null; + total_bytes: number; /** - * type - * @default metadata_to_string_collection - * @constant + * Parts + * @description Progress of downloading URLs that comprise the model, if any */ - type: "metadata_to_string_collection"; + parts: { + [key: string]: number | string; + }[]; }; /** - * Metadata To String - * @description Extracts a string value of a label from metadata + * ModelInstallDownloadsCompleteEvent + * @description Emitted once when an install job becomes active. */ - MetadataToStringInvocation: { + ModelInstallDownloadsCompleteEvent: { /** - * @description Optional metadata to be saved with the image - * @default null + * Timestamp + * @description The timestamp of the event */ - metadata?: components["schemas"]["MetadataField"] | null; + timestamp: number; /** * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * @description The ID of the install job */ - id: string; + id: number; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Source + * @description Source of the model; local path, repo_id or url */ - is_intermediate?: boolean; + source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; + }; + /** + * ModelInstallErrorEvent + * @description Event model for model_install_error + */ + ModelInstallErrorEvent: { /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Timestamp + * @description The timestamp of the event */ - use_cache?: boolean; + timestamp: number; /** - * Label - * @description Label for this metadata item - * @default * CUSTOM LABEL * - * @enum {string} + * Id + * @description The ID of the install job */ - label?: "* CUSTOM LABEL *" | "positive_prompt" | "positive_style_prompt" | "negative_prompt" | "negative_style_prompt"; + id: number; /** - * Custom Label - * @description Label for this metadata item - * @default null + * Source + * @description Source of the model; local path, repo_id or url */ - custom_label?: string | null; + source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; /** - * Default Value - * @description The default string to use if not found in the metadata - * @default null + * Error Type + * @description The name of the exception */ - default_value?: string | null; + error_type: string; /** - * type - * @default metadata_to_string - * @constant + * Error + * @description A text description of the exception */ - type: "metadata_to_string"; + error: string; }; /** - * Metadata To T2I-Adapters - * @description Extracts a T2I-Adapters value of a label from metadata + * ModelInstallJob + * @description Object that tracks the current status of an install request. */ - MetadataToT2IAdaptersInvocation: { - /** - * @description Optional metadata to be saved with the image - * @default null - */ - metadata?: components["schemas"]["MetadataField"] | null; + ModelInstallJob: { /** * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * @description Unique ID for this job */ - id: string; + id: number; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * @description Current status of install process + * @default waiting */ - is_intermediate?: boolean; + status?: components["schemas"]["InstallStatus"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Error Reason + * @description Information about why the job failed */ - use_cache?: boolean; + error_reason?: string | null; + /** @description Configuration information (e.g. 'description') to apply to model. */ + config_in?: components["schemas"]["ModelRecordChanges"]; /** - * T2I-Adapter - * @description IP-Adapter to apply - * @default null + * Config Out + * @description After successful installation, this will hold the configuration object. */ - t2i_adapter_list?: components["schemas"]["T2IAdapterField"] | components["schemas"]["T2IAdapterField"][] | 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]) | null; /** - * type - * @default metadata_to_t2i_adapters - * @constant + * Inplace + * @description Leave model in its current location; otherwise install under models directory + * @default false */ - type: "metadata_to_t2i_adapters"; - }; - /** - * Metadata To VAE - * @description Extracts a VAE value of a label from metadata - */ - MetadataToVAEInvocation: { + inplace?: boolean; /** - * @description Optional metadata to be saved with the image - * @default null + * Source + * @description Source (URL, repo_id, or local path) of model */ - metadata?: components["schemas"]["MetadataField"] | null; + source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Local Path + * Format: path + * @description Path to locally-downloaded model; may be the same as the source */ - id: string; + local_path: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Bytes + * @description For a remote model, the number of bytes downloaded so far (may not be available) + * @default 0 */ - is_intermediate?: boolean; + bytes?: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Total Bytes + * @description Total size of the model to be installed + * @default 0 */ - use_cache?: boolean; + total_bytes?: number; /** - * Label - * @description Label for this metadata item - * @default vae - * @enum {string} + * Source Metadata + * @description Metadata provided by the model source */ - label?: "* CUSTOM LABEL *" | "vae"; + source_metadata?: (components["schemas"]["BaseMetadata"] | components["schemas"]["HuggingFaceMetadata"]) | null; /** - * Custom Label - * @description Label for this metadata item - * @default null + * Download Parts + * @description Download jobs contributing to this install */ - custom_label?: string | null; + download_parts?: components["schemas"]["DownloadJob"][]; /** - * @description The default VAE to use if not found in the metadata - * @default null + * Error + * @description On an error condition, this field will contain the text of the exception */ - default_value?: components["schemas"]["VAEField"] | null; + error?: string | null; /** - * type - * @default metadata_to_vae - * @constant + * Error Traceback + * @description On an error condition, this field will contain the exception traceback */ - type: "metadata_to_vae"; + error_traceback?: string | null; }; /** - * ModelFormat - * @description Storage format of model. - * @enum {string} + * ModelInstallStartedEvent + * @description Event model for model_install_started */ - ModelFormat: "omi" | "diffusers" | "checkpoint" | "lycoris" | "onnx" | "olive" | "embedding_file" | "embedding_folder" | "invokeai" | "t5_encoder" | "bnb_quantized_int8b" | "bnb_quantized_nf4b" | "gguf_quantized" | "api" | "unknown"; - /** ModelIdentifierField */ - ModelIdentifierField: { - /** - * Key - * @description The model's unique key - */ - key: string; + ModelInstallStartedEvent: { /** - * Hash - * @description The model's BLAKE3 hash + * Timestamp + * @description The timestamp of the event */ - hash: string; + timestamp: number; /** - * Name - * @description The model's name + * Id + * @description The ID of the install job */ - name: string; - /** @description The model's base model type */ - base: components["schemas"]["BaseModelType"]; - /** @description The model's type */ - type: components["schemas"]["ModelType"]; + id: number; /** - * @description The submodel to load, if this is a main model - * @default null + * Source + * @description Source of the model; local path, repo_id or url */ - submodel_type?: components["schemas"]["SubModelType"] | null; + source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; }; /** - * Any Model - * @description Selects any model, outputting it its identifier. Be careful with this one! The identifier will be accepted as - * input for any model, even if the model types don't match. If you connect this to a mismatched input, you'll get an - * error. + * ModelLoadCompleteEvent + * @description Event model for model_load_complete */ - ModelIdentifierInvocation: { + ModelLoadCompleteEvent: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Timestamp + * @description The timestamp of the event */ - id: string; + timestamp: number; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Config + * @description The model's config */ - is_intermediate?: boolean; + 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * @description The submodel type, if any + * @default null */ - use_cache?: boolean; + submodel_type: components["schemas"]["SubModelType"] | null; + }; + /** + * ModelLoadStartedEvent + * @description Event model for model_load_started + */ + ModelLoadStartedEvent: { /** - * Model - * @description The model to select - * @default null + * Timestamp + * @description The timestamp of the event + */ + timestamp: number; + /** + * Config + * @description The model's config */ - model?: components["schemas"]["ModelIdentifierField"] | null; + 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; /** - * type - * @default model_identifier - * @constant + * @description The submodel type, if any + * @default null */ - type: "model_identifier"; + submodel_type: components["schemas"]["SubModelType"] | null; }; /** - * ModelIdentifierOutput - * @description Model identifier output + * ModelLoaderOutput + * @description Model loader output */ - ModelIdentifierOutput: { + ModelLoaderOutput: { /** - * Model - * @description Model identifier + * VAE + * @description VAE */ - model: components["schemas"]["ModelIdentifierField"]; + vae: components["schemas"]["VAEField"]; /** * type - * @default model_identifier_output + * @default model_loader_output * @constant */ - type: "model_identifier_output"; + type: "model_loader_output"; + /** + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + */ + clip: components["schemas"]["CLIPField"]; + /** + * UNet + * @description UNet (scheduler, LoRAs) + */ + unet: components["schemas"]["UNetField"]; }; /** - * ModelInstallCancelledEvent - * @description Event model for model_install_cancelled + * ModelRecordChanges + * @description A set of changes to apply to a model. */ - ModelInstallCancelledEvent: { + ModelRecordChanges: { /** - * Timestamp - * @description The timestamp of the event + * Source + * @description original source of the model */ - timestamp: number; + source?: string | null; + /** @description type of model source */ + source_type?: components["schemas"]["ModelSourceType"] | null; /** - * Id - * @description The ID of the install job + * Source Api Response + * @description metadata from remote source */ - id: number; + source_api_response?: string | null; /** - * Source - * @description Source of the model; local path, repo_id or url + * Name + * @description Name of the model. */ - source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; - }; - /** - * ModelInstallCompleteEvent - * @description Event model for model_install_complete - */ - ModelInstallCompleteEvent: { + name?: string | null; /** - * Timestamp - * @description The timestamp of the event + * Path + * @description Path to the model. */ - timestamp: number; + path?: string | null; /** - * Id - * @description The ID of the install job + * Description + * @description Model description */ - id: number; + description?: string | null; + /** @description The base model. */ + base?: components["schemas"]["BaseModelType"] | null; + /** @description Type of model */ + type?: components["schemas"]["ModelType"] | null; /** - * Source - * @description Source of the model; local path, repo_id or url + * Key + * @description Database ID for this model */ - source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; + key?: string | null; /** - * Key - * @description Model config record key + * Hash + * @description hash of model file */ - key: string; + hash?: string | null; /** - * Total Bytes - * @description Size of the model (may be None for installation of a local path) + * File Size + * @description Size of model file */ - total_bytes: number | null; + file_size?: number | null; /** - * Config - * @description The installed model's config + * Format + * @description format of model file */ - config: components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"] | components["schemas"]["MainBnbQuantized4bCheckpointConfig"] | components["schemas"]["MainGGUFCheckpointConfig"] | components["schemas"]["VAEDiffusersConfig"] | components["schemas"]["VAECheckpointConfig"] | components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"] | components["schemas"]["LoRALyCORISConfig"] | components["schemas"]["LoRAOmiConfig"] | components["schemas"]["ControlLoRALyCORISConfig"] | components["schemas"]["ControlLoRADiffusersConfig"] | components["schemas"]["LoRADiffusersConfig"] | components["schemas"]["T5EncoderConfig"] | components["schemas"]["T5EncoderBnbQuantizedLlmInt8bConfig"] | components["schemas"]["TextualInversionFileConfig"] | components["schemas"]["TextualInversionFolderConfig"] | components["schemas"]["IPAdapterInvokeAIConfig"] | components["schemas"]["IPAdapterCheckpointConfig"] | components["schemas"]["T2IAdapterConfig"] | components["schemas"]["SpandrelImageToImageConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["CLIPLEmbedDiffusersConfig"] | components["schemas"]["CLIPGEmbedDiffusersConfig"] | components["schemas"]["SigLIPConfig"] | components["schemas"]["FluxReduxConfig"] | components["schemas"]["LlavaOnevisionConfig"] | components["schemas"]["ApiModelConfig"] | components["schemas"]["VideoApiModelConfig"] | components["schemas"]["UnknownModelConfig"]; - }; - /** - * ModelInstallDownloadProgressEvent - * @description Event model for model_install_download_progress - */ - ModelInstallDownloadProgressEvent: { + format?: string | null; /** - * Timestamp - * @description The timestamp of the event + * Trigger Phrases + * @description Set of trigger phrases for this model */ - timestamp: number; + trigger_phrases?: string[] | null; /** - * Id - * @description The ID of the install job + * Default Settings + * @description Default settings for this model */ - id: number; + default_settings?: components["schemas"]["MainModelDefaultSettings"] | components["schemas"]["LoraModelDefaultSettings"] | components["schemas"]["ControlAdapterDefaultSettings"] | null; /** - * Source - * @description Source of the model; local path, repo_id or url + * Variant + * @description The variant of the model. */ - source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; + variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | null; + /** @description The prediction type of the model. */ + prediction_type?: components["schemas"]["SchedulerPredictionType"] | null; /** - * Local Path - * @description Where model is downloading to + * Upcast Attention + * @description Whether to upcast attention. */ - local_path: string; + upcast_attention?: boolean | null; /** - * Bytes - * @description Number of bytes downloaded so far + * Config Path + * @description Path to config file for model */ - bytes: number; + config_path?: string | null; + }; + /** ModelRelationshipBatchRequest */ + ModelRelationshipBatchRequest: { /** - * Total Bytes - * @description Total size of download, including all files + * Model Keys + * @description List of model keys to fetch related models for */ - total_bytes: number; + model_keys: string[]; + }; + /** ModelRelationshipCreateRequest */ + ModelRelationshipCreateRequest: { /** - * Parts - * @description Progress of downloading URLs that comprise the model, if any + * Model Key 1 + * @description The key of the first model in the relationship */ - parts: { - [key: string]: number | string; - }[]; + model_key_1: string; + /** + * Model Key 2 + * @description The key of the second model in the relationship + */ + model_key_2: string; }; /** - * ModelInstallDownloadStartedEvent - * @description Event model for model_install_download_started + * ModelRepoVariant + * @description Various hugging face variants on the diffusers format. + * @enum {string} */ - ModelInstallDownloadStartedEvent: { + ModelRepoVariant: "" | "fp16" | "fp32" | "onnx" | "openvino" | "flax"; + /** + * ModelSourceType + * @description Model source type. + * @enum {string} + */ + ModelSourceType: "path" | "url" | "hf_repo_id"; + /** + * ModelType + * @description Model type. + * @enum {string} + */ + ModelType: "onnx" | "main" | "vae" | "lora" | "control_lora" | "controlnet" | "embedding" | "ip_adapter" | "clip_vision" | "clip_embed" | "t2i_adapter" | "t5_encoder" | "spandrel_image_to_image" | "siglip" | "flux_redux" | "llava_onevision" | "video" | "unknown"; + /** + * ModelVariantType + * @description Variant type. + * @enum {string} + */ + ModelVariantType: "normal" | "inpaint" | "depth"; + /** + * ModelsList + * @description Return list of configs. + */ + 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"])[]; + }; + /** + * Multiply Integers + * @description Multiplies two numbers + */ + MultiplyInvocation: { /** - * Timestamp - * @description The timestamp of the event + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - timestamp: number; + id: string; /** - * Id - * @description The ID of the install job + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - id: number; + is_intermediate?: boolean; /** - * Source - * @description Source of the model; local path, repo_id or url + * Use Cache + * @description Whether or not to use the cache + * @default true */ - source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; + use_cache?: boolean; /** - * Local Path - * @description Where model is downloading to + * A + * @description The first number + * @default 0 */ - local_path: string; + a?: number; /** - * Bytes - * @description Number of bytes downloaded so far + * B + * @description The second number + * @default 0 */ - bytes: number; + b?: number; /** - * Total Bytes - * @description Total size of download, including all files + * type + * @default mul + * @constant + */ + type: "mul"; + }; + /** NodeFieldValue */ + NodeFieldValue: { + /** + * Node Path + * @description The node into which this batch data item will be substituted. + */ + node_path: string; + /** + * Field Name + * @description The field into which this batch data item will be substituted. */ - total_bytes: number; + field_name: string; /** - * Parts - * @description Progress of downloading URLs that comprise the model, if any + * Value + * @description The value to substitute into the node/field. */ - parts: { - [key: string]: number | string; - }[]; + value: string | number | components["schemas"]["ImageField"]; }; /** - * ModelInstallDownloadsCompleteEvent - * @description Emitted once when an install job becomes active. + * Create Latent Noise + * @description Generates latent noise. */ - ModelInstallDownloadsCompleteEvent: { + NoiseInvocation: { /** - * Timestamp - * @description The timestamp of the event + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - timestamp: number; + id: string; /** - * Id - * @description The ID of the install job + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - id: number; + is_intermediate?: boolean; /** - * Source - * @description Source of the model; local path, repo_id or url + * Use Cache + * @description Whether or not to use the cache + * @default true */ - source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; - }; - /** - * ModelInstallErrorEvent - * @description Event model for model_install_error - */ - ModelInstallErrorEvent: { + use_cache?: boolean; /** - * Timestamp - * @description The timestamp of the event + * Seed + * @description Seed for random number generation + * @default 0 */ - timestamp: number; + seed?: number; /** - * Id - * @description The ID of the install job + * Width + * @description Width of output (px) + * @default 512 */ - id: number; + width?: number; /** - * Source - * @description Source of the model; local path, repo_id or url + * Height + * @description Height of output (px) + * @default 512 */ - source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; + height?: number; /** - * Error Type - * @description The name of the exception + * Use Cpu + * @description Use CPU for noise generation (for reproducible results across platforms) + * @default true */ - error_type: string; + use_cpu?: boolean; /** - * Error - * @description A text description of the exception + * type + * @default noise + * @constant */ - error: string; + type: "noise"; }; /** - * ModelInstallJob - * @description Object that tracks the current status of an install request. + * NoiseOutput + * @description Invocation noise output */ - ModelInstallJob: { + NoiseOutput: { + /** @description Noise tensor */ + noise: components["schemas"]["LatentsField"]; /** - * Id - * @description Unique ID for this job + * Width + * @description Width of output (px) */ - id: number; + width: number; /** - * @description Current status of install process - * @default waiting + * Height + * @description Height of output (px) */ - status?: components["schemas"]["InstallStatus"]; + height: number; /** - * Error Reason - * @description Information about why the job failed + * type + * @default noise_output + * @constant */ - error_reason?: string | null; - /** @description Configuration information (e.g. 'description') to apply to model. */ - config_in?: components["schemas"]["ModelRecordChanges"]; + type: "noise_output"; + }; + /** + * Normal Map + * @description Generates a normal map. + */ + NormalMapInvocation: { /** - * Config Out - * @description After successful installation, this will hold the configuration object. + * @description The board to save the image to + * @default null */ - config_out?: (components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"] | components["schemas"]["MainBnbQuantized4bCheckpointConfig"] | components["schemas"]["MainGGUFCheckpointConfig"] | components["schemas"]["VAEDiffusersConfig"] | components["schemas"]["VAECheckpointConfig"] | components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"] | components["schemas"]["LoRALyCORISConfig"] | components["schemas"]["LoRAOmiConfig"] | components["schemas"]["ControlLoRALyCORISConfig"] | components["schemas"]["ControlLoRADiffusersConfig"] | components["schemas"]["LoRADiffusersConfig"] | components["schemas"]["T5EncoderConfig"] | components["schemas"]["T5EncoderBnbQuantizedLlmInt8bConfig"] | components["schemas"]["TextualInversionFileConfig"] | components["schemas"]["TextualInversionFolderConfig"] | components["schemas"]["IPAdapterInvokeAIConfig"] | components["schemas"]["IPAdapterCheckpointConfig"] | components["schemas"]["T2IAdapterConfig"] | components["schemas"]["SpandrelImageToImageConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["CLIPLEmbedDiffusersConfig"] | components["schemas"]["CLIPGEmbedDiffusersConfig"] | components["schemas"]["SigLIPConfig"] | components["schemas"]["FluxReduxConfig"] | components["schemas"]["LlavaOnevisionConfig"] | components["schemas"]["ApiModelConfig"] | components["schemas"]["VideoApiModelConfig"] | components["schemas"]["UnknownModelConfig"]) | null; + board?: components["schemas"]["BoardField"] | null; /** - * Inplace - * @description Leave model in its current location; otherwise install under models directory - * @default false + * @description Optional metadata to be saved with the image + * @default null */ - inplace?: boolean; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Source - * @description Source (URL, repo_id, or local path) of model + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; + id: string; /** - * Local Path - * Format: path - * @description Path to locally-downloaded model; may be the same as the source + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - local_path: string; + is_intermediate?: boolean; /** - * Bytes - * @description For a remote model, the number of bytes downloaded so far (may not be available) - * @default 0 + * Use Cache + * @description Whether or not to use the cache + * @default true */ - bytes?: number; + use_cache?: boolean; /** - * Total Bytes - * @description Total size of the model to be installed - * @default 0 + * @description The image to process + * @default null */ - total_bytes?: number; + image?: components["schemas"]["ImageField"] | null; /** - * Source Metadata - * @description Metadata provided by the model source + * type + * @default normal_map + * @constant */ - source_metadata?: (components["schemas"]["BaseMetadata"] | components["schemas"]["HuggingFaceMetadata"]) | null; + type: "normal_map"; + }; + /** OffsetPaginatedResults[BoardDTO] */ + OffsetPaginatedResults_BoardDTO_: { /** - * Download Parts - * @description Download jobs contributing to this install + * Limit + * @description Limit of items to get */ - download_parts?: components["schemas"]["DownloadJob"][]; + limit: number; /** - * Error - * @description On an error condition, this field will contain the text of the exception + * Offset + * @description Offset from which to retrieve items */ - error?: string | null; + offset: number; /** - * Error Traceback - * @description On an error condition, this field will contain the exception traceback + * Total + * @description Total number of items in result */ - error_traceback?: string | null; + total: number; + /** + * Items + * @description Items + */ + items: components["schemas"]["BoardDTO"][]; }; - /** - * ModelInstallStartedEvent - * @description Event model for model_install_started - */ - ModelInstallStartedEvent: { + /** OffsetPaginatedResults[ImageDTO] */ + OffsetPaginatedResults_ImageDTO_: { /** - * Timestamp - * @description The timestamp of the event + * Limit + * @description Limit of items to get */ - timestamp: number; + limit: number; /** - * Id - * @description The ID of the install job + * Offset + * @description Offset from which to retrieve items */ - id: number; + offset: number; /** - * Source - * @description Source of the model; local path, repo_id or url + * Total + * @description Total number of items in result */ - source: components["schemas"]["LocalModelSource"] | components["schemas"]["HFModelSource"] | components["schemas"]["URLModelSource"]; + total: number; + /** + * Items + * @description Items + */ + items: components["schemas"]["ImageDTO"][]; }; - /** - * ModelLoadCompleteEvent - * @description Event model for model_load_complete - */ - ModelLoadCompleteEvent: { + /** OffsetPaginatedResults[VideoDTO] */ + OffsetPaginatedResults_VideoDTO_: { /** - * Timestamp - * @description The timestamp of the event + * Limit + * @description Limit of items to get */ - timestamp: number; + limit: number; /** - * Config - * @description The model's config + * Offset + * @description Offset from which to retrieve items */ - config: components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"] | components["schemas"]["MainBnbQuantized4bCheckpointConfig"] | components["schemas"]["MainGGUFCheckpointConfig"] | components["schemas"]["VAEDiffusersConfig"] | components["schemas"]["VAECheckpointConfig"] | components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"] | components["schemas"]["LoRALyCORISConfig"] | components["schemas"]["LoRAOmiConfig"] | components["schemas"]["ControlLoRALyCORISConfig"] | components["schemas"]["ControlLoRADiffusersConfig"] | components["schemas"]["LoRADiffusersConfig"] | components["schemas"]["T5EncoderConfig"] | components["schemas"]["T5EncoderBnbQuantizedLlmInt8bConfig"] | components["schemas"]["TextualInversionFileConfig"] | components["schemas"]["TextualInversionFolderConfig"] | components["schemas"]["IPAdapterInvokeAIConfig"] | components["schemas"]["IPAdapterCheckpointConfig"] | components["schemas"]["T2IAdapterConfig"] | components["schemas"]["SpandrelImageToImageConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["CLIPLEmbedDiffusersConfig"] | components["schemas"]["CLIPGEmbedDiffusersConfig"] | components["schemas"]["SigLIPConfig"] | components["schemas"]["FluxReduxConfig"] | components["schemas"]["LlavaOnevisionConfig"] | components["schemas"]["ApiModelConfig"] | components["schemas"]["VideoApiModelConfig"] | components["schemas"]["UnknownModelConfig"]; + offset: number; /** - * @description The submodel type, if any - * @default null + * Total + * @description Total number of items in result */ - submodel_type: components["schemas"]["SubModelType"] | null; - }; - /** - * ModelLoadStartedEvent - * @description Event model for model_load_started - */ - ModelLoadStartedEvent: { + total: number; /** - * Timestamp - * @description The timestamp of the event + * Items + * @description Items */ - timestamp: number; + items: components["schemas"]["VideoDTO"][]; + }; + /** + * OutputFieldJSONSchemaExtra + * @description Extra attributes to be added to input fields and their OpenAPI schema. Used by the workflow editor + * during schema parsing and UI rendering. + */ + OutputFieldJSONSchemaExtra: { + field_kind: components["schemas"]["FieldKind"]; /** - * Config - * @description The model's config + * Ui Hidden + * @default false */ - config: components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"] | components["schemas"]["MainBnbQuantized4bCheckpointConfig"] | components["schemas"]["MainGGUFCheckpointConfig"] | components["schemas"]["VAEDiffusersConfig"] | components["schemas"]["VAECheckpointConfig"] | components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"] | components["schemas"]["LoRALyCORISConfig"] | components["schemas"]["LoRAOmiConfig"] | components["schemas"]["ControlLoRALyCORISConfig"] | components["schemas"]["ControlLoRADiffusersConfig"] | components["schemas"]["LoRADiffusersConfig"] | components["schemas"]["T5EncoderConfig"] | components["schemas"]["T5EncoderBnbQuantizedLlmInt8bConfig"] | components["schemas"]["TextualInversionFileConfig"] | components["schemas"]["TextualInversionFolderConfig"] | components["schemas"]["IPAdapterInvokeAIConfig"] | components["schemas"]["IPAdapterCheckpointConfig"] | components["schemas"]["T2IAdapterConfig"] | components["schemas"]["SpandrelImageToImageConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["CLIPLEmbedDiffusersConfig"] | components["schemas"]["CLIPGEmbedDiffusersConfig"] | components["schemas"]["SigLIPConfig"] | components["schemas"]["FluxReduxConfig"] | components["schemas"]["LlavaOnevisionConfig"] | components["schemas"]["ApiModelConfig"] | components["schemas"]["VideoApiModelConfig"] | components["schemas"]["UnknownModelConfig"]; + ui_hidden: boolean; /** - * @description The submodel type, if any + * Ui Order * @default null */ - submodel_type: components["schemas"]["SubModelType"] | null; + ui_order: number | null; + /** @default null */ + ui_type: components["schemas"]["UIType"] | null; }; - /** - * ModelLoaderOutput - * @description Model loader output - */ - ModelLoaderOutput: { + /** PaginatedResults[WorkflowRecordListItemWithThumbnailDTO] */ + PaginatedResults_WorkflowRecordListItemWithThumbnailDTO_: { /** - * VAE - * @description VAE + * Page + * @description Current Page */ - vae: components["schemas"]["VAEField"]; + page: number; /** - * type - * @default model_loader_output - * @constant + * Pages + * @description Total number of pages */ - type: "model_loader_output"; + pages: number; /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * Per Page + * @description Number of items per page */ - clip: components["schemas"]["CLIPField"]; + per_page: number; /** - * UNet - * @description UNet (scheduler, LoRAs) + * Total + * @description Total number of items in result */ - unet: components["schemas"]["UNetField"]; + total: number; + /** + * Items + * @description Items + */ + items: components["schemas"]["WorkflowRecordListItemWithThumbnailDTO"][]; }; /** - * ModelRecordChanges - * @description A set of changes to apply to a model. + * Pair Tile with Image + * @description Pair an image with its tile properties. */ - ModelRecordChanges: { - /** - * Source - * @description original source of the model - */ - source?: string | null; - /** @description type of model source */ - source_type?: components["schemas"]["ModelSourceType"] | null; + PairTileImageInvocation: { /** - * Source Api Response - * @description metadata from remote source + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - source_api_response?: string | null; + id: string; /** - * Name - * @description Name of the model. + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - name?: string | null; + is_intermediate?: boolean; /** - * Path - * @description Path to the model. + * Use Cache + * @description Whether or not to use the cache + * @default true */ - path?: string | null; + use_cache?: boolean; /** - * Description - * @description Model description + * @description The tile image. + * @default null */ - description?: string | null; - /** @description The base model. */ - base?: components["schemas"]["BaseModelType"] | null; - /** @description Type of model */ - type?: components["schemas"]["ModelType"] | null; + image?: components["schemas"]["ImageField"] | null; /** - * Key - * @description Database ID for this model + * @description The tile properties. + * @default null */ - key?: string | null; + tile?: components["schemas"]["Tile"] | null; /** - * Hash - * @description hash of model file + * type + * @default pair_tile_image + * @constant */ - hash?: string | null; + type: "pair_tile_image"; + }; + /** PairTileImageOutput */ + PairTileImageOutput: { + /** @description A tile description with its corresponding image. */ + tile_with_image: components["schemas"]["TileWithImage"]; /** - * File Size - * @description Size of model file + * type + * @default pair_tile_image_output + * @constant */ - file_size?: number | null; + type: "pair_tile_image_output"; + }; + /** + * Paste Image into Bounding Box + * @description Paste the source image into the target image at the given bounding box. + * + * The source image must be the same size as the bounding box, and the bounding box must fit within the target image. + */ + PasteImageIntoBoundingBoxInvocation: { /** - * Format - * @description format of model file + * @description The board to save the image to + * @default null */ - format?: string | null; + board?: components["schemas"]["BoardField"] | null; /** - * Trigger Phrases - * @description Set of trigger phrases for this model + * @description Optional metadata to be saved with the image + * @default null */ - trigger_phrases?: string[] | null; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Default Settings - * @description Default settings for this model + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - default_settings?: components["schemas"]["MainModelDefaultSettings"] | components["schemas"]["LoraModelDefaultSettings"] | components["schemas"]["ControlAdapterDefaultSettings"] | null; + id: string; /** - * Variant - * @description The variant of the model. + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | null; - /** @description The prediction type of the model. */ - prediction_type?: components["schemas"]["SchedulerPredictionType"] | null; + is_intermediate?: boolean; /** - * Upcast Attention - * @description Whether to upcast attention. + * Use Cache + * @description Whether or not to use the cache + * @default true */ - upcast_attention?: boolean | null; + use_cache?: boolean; /** - * Config Path - * @description Path to config file for model + * @description The image to paste + * @default null */ - config_path?: string | null; - }; - /** ModelRelationshipBatchRequest */ - ModelRelationshipBatchRequest: { + source_image?: components["schemas"]["ImageField"] | null; /** - * Model Keys - * @description List of model keys to fetch related models for + * @description The image to paste into + * @default null */ - model_keys: string[]; - }; - /** ModelRelationshipCreateRequest */ - ModelRelationshipCreateRequest: { + target_image?: components["schemas"]["ImageField"] | null; /** - * Model Key 1 - * @description The key of the first model in the relationship + * @description The bounding box to paste the image into + * @default null */ - model_key_1: string; + bounding_box?: components["schemas"]["BoundingBoxField"] | null; /** - * Model Key 2 - * @description The key of the second model in the relationship + * type + * @default paste_image_into_bounding_box + * @constant */ - model_key_2: string; - }; - /** - * ModelRepoVariant - * @description Various hugging face variants on the diffusers format. - * @enum {string} - */ - ModelRepoVariant: "" | "fp16" | "fp32" | "onnx" | "openvino" | "flax"; - /** - * ModelSourceType - * @description Model source type. - * @enum {string} - */ - ModelSourceType: "path" | "url" | "hf_repo_id"; - /** - * ModelType - * @description Model type. - * @enum {string} - */ - ModelType: "onnx" | "main" | "vae" | "lora" | "control_lora" | "controlnet" | "embedding" | "ip_adapter" | "clip_vision" | "clip_embed" | "t2i_adapter" | "t5_encoder" | "spandrel_image_to_image" | "siglip" | "flux_redux" | "llava_onevision" | "video" | "unknown"; - /** - * ModelVariantType - * @description Variant type. - * @enum {string} - */ - ModelVariantType: "normal" | "inpaint" | "depth"; - /** - * ModelsList - * @description Return list of configs. - */ - ModelsList: { - /** Models */ - models: (components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"] | components["schemas"]["MainBnbQuantized4bCheckpointConfig"] | components["schemas"]["MainGGUFCheckpointConfig"] | components["schemas"]["VAEDiffusersConfig"] | components["schemas"]["VAECheckpointConfig"] | components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"] | components["schemas"]["LoRALyCORISConfig"] | components["schemas"]["LoRAOmiConfig"] | components["schemas"]["ControlLoRALyCORISConfig"] | components["schemas"]["ControlLoRADiffusersConfig"] | components["schemas"]["LoRADiffusersConfig"] | components["schemas"]["T5EncoderConfig"] | components["schemas"]["T5EncoderBnbQuantizedLlmInt8bConfig"] | components["schemas"]["TextualInversionFileConfig"] | components["schemas"]["TextualInversionFolderConfig"] | components["schemas"]["IPAdapterInvokeAIConfig"] | components["schemas"]["IPAdapterCheckpointConfig"] | components["schemas"]["T2IAdapterConfig"] | components["schemas"]["SpandrelImageToImageConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["CLIPLEmbedDiffusersConfig"] | components["schemas"]["CLIPGEmbedDiffusersConfig"] | components["schemas"]["SigLIPConfig"] | components["schemas"]["FluxReduxConfig"] | components["schemas"]["LlavaOnevisionConfig"] | components["schemas"]["ApiModelConfig"] | components["schemas"]["VideoApiModelConfig"] | components["schemas"]["UnknownModelConfig"])[]; + type: "paste_image_into_bounding_box"; }; /** - * Multiply Integers - * @description Multiplies two numbers + * PiDiNet Edge Detection + * @description Generates an edge map using PiDiNet. */ - MultiplyInvocation: { + PiDiNetEdgeDetectionInvocation: { + /** + * @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. @@ -17573,47 +20673,73 @@ export type components = { */ use_cache?: boolean; /** - * A - * @description The first number - * @default 0 + * @description The image to process + * @default null */ - a?: number; + image?: components["schemas"]["ImageField"] | null; /** - * B - * @description The second number - * @default 0 + * Quantize Edges + * @description Whether or not to use safe mode + * @default false + */ + quantize_edges?: boolean; + /** + * Scribble + * @description Whether or not to use scribble mode + * @default false */ - b?: number; + scribble?: boolean; /** * type - * @default mul + * @default pidi_edge_detection * @constant */ - type: "mul"; + type: "pidi_edge_detection"; }; - /** NodeFieldValue */ - NodeFieldValue: { + /** PresetData */ + PresetData: { /** - * Node Path - * @description The node into which this batch data item will be substituted. + * Positive Prompt + * @description Positive prompt */ - node_path: string; + positive_prompt: string; /** - * Field Name - * @description The field into which this batch data item will be substituted. + * Negative Prompt + * @description Negative prompt */ - field_name: string; + negative_prompt: string; + }; + /** + * PresetType + * @enum {string} + */ + PresetType: "user" | "default" | "project"; + /** + * ProgressImage + * @description The progress image sent intermittently during processing + */ + ProgressImage: { /** - * Value - * @description The value to substitute into the node/field. + * Width + * @description The effective width of the image in pixels */ - value: string | number | components["schemas"]["ImageField"]; + width: number; + /** + * Height + * @description The effective height of the image in pixels + */ + height: number; + /** + * Dataurl + * @description The image data as a b64 data URL + */ + dataURL: string; }; /** - * Create Latent Noise - * @description Generates latent noise. + * Prompts from File + * @description Loads prompts from a text file */ - NoiseInvocation: { + PromptsFromFileInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -17632,226 +20758,241 @@ export type components = { */ use_cache?: boolean; /** - * Seed - * @description Seed for random number generation - * @default 0 + * File Path + * @description Path to prompt text file + * @default null */ - seed?: number; + file_path?: string | null; /** - * Width - * @description Width of output (px) - * @default 512 + * Pre Prompt + * @description String to prepend to each prompt + * @default null */ - width?: number; + pre_prompt?: string | null; /** - * Height - * @description Height of output (px) - * @default 512 + * Post Prompt + * @description String to append to each prompt + * @default null */ - height?: number; + post_prompt?: string | null; /** - * Use Cpu - * @description Use CPU for noise generation (for reproducible results across platforms) - * @default true + * Start Line + * @description Line in the file to start start from + * @default 1 */ - use_cpu?: boolean; + start_line?: number; + /** + * Max Prompts + * @description Max lines to read from file (0=all) + * @default 1 + */ + max_prompts?: number; /** * type - * @default noise + * @default prompt_from_file * @constant */ - type: "noise"; + type: "prompt_from_file"; }; /** - * NoiseOutput - * @description Invocation noise output + * PruneResult + * @description Result of pruning the session queue */ - NoiseOutput: { - /** @description Noise tensor */ - noise: components["schemas"]["LatentsField"]; + PruneResult: { /** - * Width - * @description Width of output (px) + * Deleted + * @description Number of queue items deleted */ - width: number; + deleted: number; + }; + /** + * QueueClearedEvent + * @description Event model for queue_cleared + */ + QueueClearedEvent: { /** - * Height - * @description Height of output (px) + * Timestamp + * @description The timestamp of the event */ - height: number; + timestamp: number; /** - * type - * @default noise_output - * @constant + * Queue Id + * @description The ID of the queue */ - type: "noise_output"; + queue_id: string; }; /** - * Normal Map - * @description Generates a normal map. + * QueueItemStatusChangedEvent + * @description Event model for queue_item_status_changed */ - NormalMapInvocation: { + QueueItemStatusChangedEvent: { /** - * @description The board to save the image to - * @default null + * Timestamp + * @description The timestamp of the event */ - board?: components["schemas"]["BoardField"] | null; + timestamp: number; /** - * @description Optional metadata to be saved with the image - * @default null + * Queue Id + * @description The ID of the queue */ - metadata?: components["schemas"]["MetadataField"] | null; + queue_id: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Item Id + * @description The ID of the queue item */ - id: string; + item_id: number; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Batch Id + * @description The ID of the queue batch */ - is_intermediate?: boolean; + batch_id: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Origin + * @description The origin of the queue item + * @default null */ - use_cache?: boolean; + origin: string | null; /** - * @description The image to process + * Destination + * @description The destination of the queue item * @default null */ - image?: components["schemas"]["ImageField"] | null; + destination: string | null; /** - * type - * @default normal_map - * @constant + * Status + * @description The new status of the queue item + * @enum {string} */ - type: "normal_map"; - }; - /** OffsetPaginatedResults[BoardDTO] */ - OffsetPaginatedResults_BoardDTO_: { + status: "pending" | "in_progress" | "completed" | "failed" | "canceled"; /** - * Limit - * @description Limit of items to get + * Error Type + * @description The error type, if any + * @default null */ - limit: number; + error_type: string | null; /** - * Offset - * @description Offset from which to retrieve items + * Error Message + * @description The error message, if any + * @default null */ - offset: number; + error_message: string | null; /** - * Total - * @description Total number of items in result + * Error Traceback + * @description The error traceback, if any + * @default null */ - total: number; + error_traceback: string | null; /** - * Items - * @description Items + * Created At + * @description The timestamp when the queue item was created */ - items: components["schemas"]["BoardDTO"][]; - }; - /** OffsetPaginatedResults[ImageDTO] */ - OffsetPaginatedResults_ImageDTO_: { + created_at: string; /** - * Limit - * @description Limit of items to get + * Updated At + * @description The timestamp when the queue item was last updated */ - limit: number; + updated_at: string; /** - * Offset - * @description Offset from which to retrieve items + * Started At + * @description The timestamp when the queue item was started + * @default null */ - offset: number; + started_at: string | null; /** - * Total - * @description Total number of items in result + * Completed At + * @description The timestamp when the queue item was completed + * @default null */ - total: number; + completed_at: string | null; + /** @description The status of the batch */ + batch_status: components["schemas"]["BatchStatus"]; + /** @description The status of the queue */ + queue_status: components["schemas"]["SessionQueueStatus"]; /** - * Items - * @description Items + * Session Id + * @description The ID of the session (aka graph execution state) */ - items: components["schemas"]["ImageDTO"][]; - }; - /** OffsetPaginatedResults[VideoDTO] */ - OffsetPaginatedResults_VideoDTO_: { + session_id: string; /** - * Limit - * @description Limit of items to get + * Credits + * @description The total credits used for this queue item + * @default null */ - limit: number; + credits: number | null; + }; + /** + * QueueItemsRetriedEvent + * @description Event model for queue_items_retried + */ + QueueItemsRetriedEvent: { /** - * Offset - * @description Offset from which to retrieve items + * Timestamp + * @description The timestamp of the event */ - offset: number; + timestamp: number; /** - * Total - * @description Total number of items in result + * Queue Id + * @description The ID of the queue */ - total: number; + queue_id: string; /** - * Items - * @description Items + * Retried Item Ids + * @description The IDs of the queue items that were retried */ - items: components["schemas"]["VideoDTO"][]; + retried_item_ids: number[]; }; /** - * OutputFieldJSONSchemaExtra - * @description Extra attributes to be added to input fields and their OpenAPI schema. Used by the workflow editor - * during schema parsing and UI rendering. + * Random Float + * @description Outputs a single random float */ - OutputFieldJSONSchemaExtra: { - field_kind: components["schemas"]["FieldKind"]; + RandomFloatInvocation: { /** - * Ui Hidden - * @default false + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - ui_hidden: boolean; + id: string; /** - * Ui Order - * @default null + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - ui_order: number | null; - /** @default null */ - ui_type: components["schemas"]["UIType"] | null; - }; - /** PaginatedResults[WorkflowRecordListItemWithThumbnailDTO] */ - PaginatedResults_WorkflowRecordListItemWithThumbnailDTO_: { + is_intermediate?: boolean; /** - * Page - * @description Current Page + * Use Cache + * @description Whether or not to use the cache + * @default false */ - page: number; + use_cache?: boolean; /** - * Pages - * @description Total number of pages + * Low + * @description The inclusive low value + * @default 0 */ - pages: number; + low?: number; /** - * Per Page - * @description Number of items per page + * High + * @description The exclusive high value + * @default 1 */ - per_page: number; + high?: number; /** - * Total - * @description Total number of items in result + * Decimals + * @description The number of decimal places to round to + * @default 2 */ - total: number; + decimals?: number; /** - * Items - * @description Items + * type + * @default rand_float + * @constant */ - items: components["schemas"]["WorkflowRecordListItemWithThumbnailDTO"][]; + type: "rand_float"; }; /** - * Pair Tile with Image - * @description Pair an image with its tile properties. + * Random Integer + * @description Outputs a single random integer. */ - PairTileImageInvocation: { + RandomIntInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -17866,54 +21007,33 @@ export type components = { /** * Use Cache * @description Whether or not to use the cache - * @default true + * @default false */ use_cache?: boolean; /** - * @description The tile image. - * @default null - */ - image?: components["schemas"]["ImageField"] | null; - /** - * @description The tile properties. - * @default null + * Low + * @description The inclusive low value + * @default 0 */ - tile?: components["schemas"]["Tile"] | null; + low?: number; /** - * type - * @default pair_tile_image - * @constant + * High + * @description The exclusive high value + * @default 2147483647 */ - type: "pair_tile_image"; - }; - /** PairTileImageOutput */ - PairTileImageOutput: { - /** @description A tile description with its corresponding image. */ - tile_with_image: components["schemas"]["TileWithImage"]; + high?: number; /** * type - * @default pair_tile_image_output + * @default rand_int * @constant */ - type: "pair_tile_image_output"; + type: "rand_int"; }; /** - * Paste Image into Bounding Box - * @description Paste the source image into the target image at the given bounding box. - * - * The source image must be the same size as the bounding box, and the bounding box must fit within the target image. + * Random Range + * @description Creates a collection of random numbers */ - PasteImageIntoBoundingBoxInvocation: { - /** - * @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; + RandomRangeInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -17928,46 +21048,45 @@ export type components = { /** * Use Cache * @description Whether or not to use the cache - * @default true + * @default false */ use_cache?: boolean; /** - * @description The image to paste - * @default null + * Low + * @description The inclusive low value + * @default 0 */ - source_image?: components["schemas"]["ImageField"] | null; + low?: number; /** - * @description The image to paste into - * @default null + * High + * @description The exclusive high value + * @default 2147483647 */ - target_image?: components["schemas"]["ImageField"] | null; + high?: number; /** - * @description The bounding box to paste the image into - * @default null + * Size + * @description The number of values to generate + * @default 1 */ - bounding_box?: components["schemas"]["BoundingBoxField"] | null; + size?: number; + /** + * Seed + * @description The seed for the RNG (omit for random) + * @default 0 + */ + seed?: number; /** * type - * @default paste_image_into_bounding_box + * @default random_range * @constant */ - type: "paste_image_into_bounding_box"; + type: "random_range"; }; /** - * PiDiNet Edge Detection - * @description Generates an edge map using PiDiNet. + * Integer Range + * @description Creates a range of numbers from start to stop with step */ - PiDiNetEdgeDetectionInvocation: { - /** - * @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; + RangeInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -17986,73 +21105,87 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to process - * @default null + * Start + * @description The start of the range + * @default 0 */ - image?: components["schemas"]["ImageField"] | null; + start?: number; /** - * Quantize Edges - * @description Whether or not to use safe mode - * @default false + * Stop + * @description The stop of the range + * @default 10 */ - quantize_edges?: boolean; + stop?: number; /** - * Scribble - * @description Whether or not to use scribble mode - * @default false + * Step + * @description The step of the range + * @default 1 */ - scribble?: boolean; + step?: number; /** * type - * @default pidi_edge_detection + * @default range * @constant */ - type: "pidi_edge_detection"; + type: "range"; }; - /** PresetData */ - PresetData: { + /** + * Integer Range of Size + * @description Creates a range from start to start + (size * step) incremented by step + */ + RangeOfSizeInvocation: { /** - * Positive Prompt - * @description Positive prompt + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - positive_prompt: string; + id: string; /** - * Negative Prompt - * @description Negative prompt + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - negative_prompt: string; - }; - /** - * PresetType - * @enum {string} - */ - PresetType: "user" | "default" | "project"; - /** - * ProgressImage - * @description The progress image sent intermittently during processing - */ - ProgressImage: { + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; /** - * Width - * @description The effective width of the image in pixels + * Start + * @description The start of the range + * @default 0 */ - width: number; + start?: number; /** - * Height - * @description The effective height of the image in pixels + * Size + * @description The number of values + * @default 1 */ - height: number; + size?: number; /** - * Dataurl - * @description The image data as a b64 data URL + * Step + * @description The step of the range + * @default 1 */ - dataURL: string; + step?: number; + /** + * type + * @default range_of_size + * @constant + */ + type: "range_of_size"; }; /** - * Prompts from File - * @description Loads prompts from a text file + * Create Rectangle Mask + * @description Create a rectangular mask. */ - PromptsFromFileInvocation: { + RectangleMaskInvocation: { + /** + * @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. @@ -18071,178 +21204,174 @@ export type components = { */ use_cache?: boolean; /** - * File Path - * @description Path to prompt text file + * Width + * @description The width of the entire mask. * @default null */ - file_path?: string | null; + width?: number | null; /** - * Pre Prompt - * @description String to prepend to each prompt + * Height + * @description The height of the entire mask. * @default null */ - pre_prompt?: string | null; + height?: number | null; /** - * Post Prompt - * @description String to append to each prompt + * X Left + * @description The left x-coordinate of the rectangular masked region (inclusive). * @default null */ - post_prompt?: string | null; + x_left?: number | null; /** - * Start Line - * @description Line in the file to start start from - * @default 1 + * Y Top + * @description The top y-coordinate of the rectangular masked region (inclusive). + * @default null */ - start_line?: number; + y_top?: number | null; /** - * Max Prompts - * @description Max lines to read from file (0=all) - * @default 1 + * Rectangle Width + * @description The width of the rectangular masked region. + * @default null */ - max_prompts?: number; + rectangle_width?: number | null; + /** + * Rectangle Height + * @description The height of the rectangular masked region. + * @default null + */ + rectangle_height?: number | null; /** * type - * @default prompt_from_file + * @default rectangle_mask * @constant */ - type: "prompt_from_file"; + type: "rectangle_mask"; }; /** - * PruneResult - * @description Result of pruning the session queue + * RemoteModelFile + * @description Information about a downloadable file that forms part of a model. */ - PruneResult: { + RemoteModelFile: { /** - * Deleted - * @description Number of queue items deleted + * Url + * Format: uri + * @description The url to download this model file */ - deleted: number; - }; - /** - * QueueClearedEvent - * @description Event model for queue_cleared - */ - QueueClearedEvent: { + url: string; /** - * Timestamp - * @description The timestamp of the event + * Path + * Format: path + * @description The path to the file, relative to the model root */ - timestamp: number; + path: string; /** - * Queue Id - * @description The ID of the queue + * Size + * @description The size of this file, in bytes + * @default 0 */ - queue_id: string; - }; - /** - * QueueItemStatusChangedEvent - * @description Event model for queue_item_status_changed - */ - QueueItemStatusChangedEvent: { + size?: number | null; /** - * Timestamp - * @description The timestamp of the event + * Sha256 + * @description SHA256 hash of this model (not always available) */ - timestamp: number; + sha256?: string | null; + }; + /** RemoveImagesFromBoardResult */ + RemoveImagesFromBoardResult: { /** - * Queue Id - * @description The ID of the queue + * Affected Boards + * @description The ids of boards affected by the delete operation */ - queue_id: string; + affected_boards: string[]; /** - * Item Id - * @description The ID of the queue item + * Removed Images + * @description The image names that were removed from their board */ - item_id: number; + removed_images: string[]; + }; + /** RemoveVideosFromBoardResult */ + RemoveVideosFromBoardResult: { /** - * Batch Id - * @description The ID of the queue batch + * Affected Boards + * @description The ids of boards affected by the delete operation */ - batch_id: string; + affected_boards: string[]; /** - * Origin - * @description The origin of the queue item - * @default null + * Removed Videos + * @description The video ids that were removed from their board */ - origin: string | null; + removed_videos: string[]; + }; + /** + * Resize Latents + * @description Resizes latents to explicit width/height (in pixels). Provided dimensions are floor-divided by 8. + */ + ResizeLatentsInvocation: { /** - * Destination - * @description The destination of the queue item - * @default null + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - destination: string | null; + id: string; /** - * Status - * @description The new status of the queue item - * @enum {string} + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - status: "pending" | "in_progress" | "completed" | "failed" | "canceled"; + is_intermediate?: boolean; /** - * Error Type - * @description The error type, if any - * @default null + * Use Cache + * @description Whether or not to use the cache + * @default true */ - error_type: string | null; + use_cache?: boolean; /** - * Error Message - * @description The error message, if any + * @description Latents tensor * @default null */ - error_message: string | null; + latents?: components["schemas"]["LatentsField"] | null; /** - * Error Traceback - * @description The error traceback, if any + * Width + * @description Width of output (px) * @default null */ - error_traceback: string | null; - /** - * Created At - * @description The timestamp when the queue item was created - */ - created_at: string; - /** - * Updated At - * @description The timestamp when the queue item was last updated - */ - updated_at: string; + width?: number | null; /** - * Started At - * @description The timestamp when the queue item was started + * Height + * @description Width of output (px) * @default null */ - started_at: string | null; + height?: number | null; /** - * Completed At - * @description The timestamp when the queue item was completed - * @default null + * Mode + * @description Interpolation mode + * @default bilinear + * @enum {string} */ - completed_at: string | null; - /** @description The status of the batch */ - batch_status: components["schemas"]["BatchStatus"]; - /** @description The status of the queue */ - queue_status: components["schemas"]["SessionQueueStatus"]; + mode?: "nearest" | "linear" | "bilinear" | "bicubic" | "trilinear" | "area" | "nearest-exact"; /** - * Session Id - * @description The ID of the session (aka graph execution state) + * Antialias + * @description Whether or not to apply antialiasing (bilinear or bicubic only) + * @default false */ - session_id: string; + antialias?: boolean; /** - * Credits - * @description The total credits used for this queue item - * @default null + * type + * @default lresize + * @constant */ - credits: number | null; + type: "lresize"; }; /** - * QueueItemsRetriedEvent - * @description Event model for queue_items_retried - */ - QueueItemsRetriedEvent: { - /** - * Timestamp - * @description The timestamp of the event - */ - timestamp: number; + * ResourceOrigin + * @description The origin of a resource (eg image). + * + * - INTERNAL: The resource was created by the application. + * - EXTERNAL: The resource was not created by the application. + * This may be a user-initiated upload, or an internal application upload (eg Canvas init image). + * @enum {string} + */ + ResourceOrigin: "internal" | "external"; + /** RetryItemsResult */ + RetryItemsResult: { /** * Queue Id * @description The ID of the queue @@ -18255,10 +21384,10 @@ export type components = { retried_item_ids: number[]; }; /** - * Random Float - * @description Outputs a single random float + * Round Float + * @description Rounds a float to a specified number of decimal places. */ - RandomFloatInvocation: { + RoundInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -18273,80 +21402,96 @@ export type components = { /** * Use Cache * @description Whether or not to use the cache - * @default false + * @default true */ use_cache?: boolean; /** - * Low - * @description The inclusive low value + * Value + * @description The float value * @default 0 */ - low?: number; - /** - * High - * @description The exclusive high value - * @default 1 - */ - high?: number; + value?: number; /** * Decimals - * @description The number of decimal places to round to - * @default 2 + * @description The number of decimal places + * @default 0 */ decimals?: number; /** * type - * @default rand_float + * @default round_float * @constant */ - type: "rand_float"; + type: "round_float"; }; - /** - * Random Integer - * @description Outputs a single random integer. - */ - RandomIntInvocation: { - /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. - */ - id: string; + /** SAMPoint */ + SAMPoint: { /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * X + * @description The x-coordinate of the point */ - is_intermediate?: boolean; + x: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default false + * Y + * @description The y-coordinate of the point */ - use_cache?: boolean; + y: number; + /** @description The label of the point */ + label: components["schemas"]["SAMPointLabel"]; + }; + /** + * SAMPointLabel + * @enum {integer} + */ + SAMPointLabel: -1 | 0 | 1; + /** SAMPointsField */ + SAMPointsField: { /** - * Low - * @description The inclusive low value - * @default 0 + * Points + * @description The points of the object */ - low?: number; + points: components["schemas"]["SAMPoint"][]; + }; + /** + * SD3ConditioningField + * @description A conditioning tensor primitive value + */ + SD3ConditioningField: { /** - * High - * @description The exclusive high value - * @default 2147483647 + * Conditioning Name + * @description The name of conditioning tensor */ - high?: number; + conditioning_name: string; + }; + /** + * SD3ConditioningOutput + * @description Base class for nodes that output a single SD3 conditioning tensor + */ + SD3ConditioningOutput: { + /** @description Conditioning tensor */ + conditioning: components["schemas"]["SD3ConditioningField"]; /** * type - * @default rand_int + * @default sd3_conditioning_output * @constant */ - type: "rand_int"; + type: "sd3_conditioning_output"; }; /** - * Random Range - * @description Creates a collection of random numbers + * Denoise - SD3 + * @description Run denoising process with a SD3 model. */ - RandomRangeInvocation: { + SD3DenoiseInvocation: { + /** + * @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. @@ -18361,45 +21506,99 @@ export type components = { /** * Use Cache * @description Whether or not to use the cache - * @default false + * @default true */ use_cache?: boolean; /** - * Low - * @description The inclusive low value - * @default 0 + * @description Latents tensor + * @default null */ - low?: number; + latents?: components["schemas"]["LatentsField"] | null; /** - * High - * @description The exclusive high value - * @default 2147483647 + * @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 */ - high?: number; + denoise_mask?: components["schemas"]["DenoiseMaskField"] | null; /** - * Size - * @description The number of values to generate + * 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 */ - size?: number; + denoising_end?: number; + /** + * Transformer + * @description SD3 model (MMDiTX) to load + * @default null + */ + transformer?: components["schemas"]["TransformerField"] | null; + /** + * @description Positive conditioning tensor + * @default null + */ + positive_conditioning?: components["schemas"]["SD3ConditioningField"] | null; + /** + * @description Negative conditioning tensor + * @default null + */ + negative_conditioning?: components["schemas"]["SD3ConditioningField"] | null; + /** + * CFG Scale + * @description Classifier-Free Guidance scale + * @default 3.5 + */ + 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 10 + */ + steps?: number; /** * Seed - * @description The seed for the RNG (omit for random) + * @description Randomness seed for reproducibility. * @default 0 */ seed?: number; /** * type - * @default random_range + * @default sd3_denoise * @constant */ - type: "random_range"; + type: "sd3_denoise"; }; /** - * Integer Range - * @description Creates a range of numbers from start to stop with step + * Image to Latents - SD3 + * @description Generates latents from an image. */ - RangeInvocation: { + SD3ImageToLatentsInvocation: { + /** + * @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. @@ -18418,35 +21617,37 @@ export type components = { */ use_cache?: boolean; /** - * Start - * @description The start of the range - * @default 0 - */ - start?: number; - /** - * Stop - * @description The stop of the range - * @default 10 + * @description The image to encode + * @default null */ - stop?: number; + image?: components["schemas"]["ImageField"] | null; /** - * Step - * @description The step of the range - * @default 1 + * @description VAE + * @default null */ - step?: number; + vae?: components["schemas"]["VAEField"] | null; /** * type - * @default range + * @default sd3_i2l * @constant */ - type: "range"; + type: "sd3_i2l"; }; /** - * Integer Range of Size - * @description Creates a range from start to start + (size * step) incremented by step + * Latents to Image - SD3 + * @description Generates an image from latents. */ - RangeOfSizeInvocation: { + SD3LatentsToImageInvocation: { + /** + * @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. @@ -18460,45 +21661,32 @@ export type components = { is_intermediate?: boolean; /** * Use Cache - * @description Whether or not to use the cache - * @default true - */ - use_cache?: boolean; - /** - * Start - * @description The start of the range - * @default 0 + * @description Whether or not to use the cache + * @default true */ - start?: number; + use_cache?: boolean; /** - * Size - * @description The number of values - * @default 1 + * @description Latents tensor + * @default null */ - size?: number; + latents?: components["schemas"]["LatentsField"] | null; /** - * Step - * @description The step of the range - * @default 1 + * @description VAE + * @default null */ - step?: number; + vae?: components["schemas"]["VAEField"] | null; /** * type - * @default range_of_size + * @default sd3_l2i * @constant */ - type: "range_of_size"; + type: "sd3_l2i"; }; /** - * Create Rectangle Mask - * @description Create a rectangular mask. + * Prompt - SDXL + * @description Parse prompt using compel package to conditioning. */ - RectangleMaskInvocation: { - /** - * @description Optional metadata to be saved with the image - * @default null - */ - metadata?: components["schemas"]["MetadataField"] | null; + SDXLCompelPromptInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -18517,108 +21705,129 @@ export type components = { */ use_cache?: boolean; /** - * Width - * @description The width of the entire mask. - * @default null + * Prompt + * @description Prompt to be parsed by Compel to create a conditioning tensor + * @default */ - width?: number | null; + prompt?: string; /** - * Height - * @description The height of the entire mask. - * @default null + * Style + * @description Prompt to be parsed by Compel to create a conditioning tensor + * @default */ - height?: number | null; + style?: string; /** - * X Left - * @description The left x-coordinate of the rectangular masked region (inclusive). - * @default null + * Original Width + * @default 1024 */ - x_left?: number | null; + original_width?: number; /** - * Y Top - * @description The top y-coordinate of the rectangular masked region (inclusive). + * Original Height + * @default 1024 + */ + original_height?: number; + /** + * Crop Top + * @default 0 + */ + crop_top?: number; + /** + * Crop Left + * @default 0 + */ + crop_left?: number; + /** + * Target Width + * @default 1024 + */ + target_width?: number; + /** + * Target Height + * @default 1024 + */ + target_height?: number; + /** + * CLIP 1 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count * @default null */ - y_top?: number | null; + clip?: components["schemas"]["CLIPField"] | null; /** - * Rectangle Width - * @description The width of the rectangular masked region. + * CLIP 2 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count * @default null */ - rectangle_width?: number | null; + clip2?: components["schemas"]["CLIPField"] | null; /** - * Rectangle Height - * @description The height of the rectangular masked region. + * @description A mask defining the region that this conditioning prompt applies to. * @default null */ - rectangle_height?: number | null; + mask?: components["schemas"]["TensorField"] | null; /** * type - * @default rectangle_mask + * @default sdxl_compel_prompt * @constant */ - type: "rectangle_mask"; + type: "sdxl_compel_prompt"; }; /** - * RemoteModelFile - * @description Information about a downloadable file that forms part of a model. + * Apply LoRA Collection - SDXL + * @description Applies a collection of SDXL LoRAs to the provided UNet and CLIP models. */ - RemoteModelFile: { + SDXLLoRACollectionLoader: { /** - * Url - * Format: uri - * @description The url to download this model file + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - url: string; + id: string; /** - * Path - * Format: path - * @description The path to the file, relative to the model root + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - path: string; + is_intermediate?: boolean; /** - * Size - * @description The size of this file, in bytes - * @default 0 + * Use Cache + * @description Whether or not to use the cache + * @default true */ - size?: number | null; + use_cache?: boolean; /** - * Sha256 - * @description SHA256 hash of this model (not always available) + * LoRAs + * @description LoRA models and weights. May be a single LoRA or collection. + * @default null */ - sha256?: string | null; - }; - /** RemoveImagesFromBoardResult */ - RemoveImagesFromBoardResult: { + loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null; /** - * Affected Boards - * @description The ids of boards affected by the delete operation + * UNet + * @description UNet (scheduler, LoRAs) + * @default null */ - affected_boards: string[]; + unet?: components["schemas"]["UNetField"] | null; /** - * Removed Images - * @description The image names that were removed from their board + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null */ - removed_images: string[]; - }; - /** RemoveVideosFromBoardResult */ - RemoveVideosFromBoardResult: { + clip?: components["schemas"]["CLIPField"] | null; /** - * Affected Boards - * @description The ids of boards affected by the delete operation + * CLIP 2 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null */ - affected_boards: string[]; + clip2?: components["schemas"]["CLIPField"] | null; /** - * Removed Videos - * @description The video ids that were removed from their board + * type + * @default sdxl_lora_collection_loader + * @constant */ - removed_videos: string[]; + type: "sdxl_lora_collection_loader"; }; /** - * Resize Latents - * @description Resizes latents to explicit width/height (in pixels). Provided dimensions are floor-divided by 8. + * Apply LoRA - SDXL + * @description Apply selected lora to unet and text_encoder. */ - ResizeLatentsInvocation: { + SDXLLoRALoaderInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -18637,70 +21846,77 @@ export type components = { */ use_cache?: boolean; /** - * @description Latents tensor + * LoRA + * @description LoRA model to load * @default null */ - latents?: components["schemas"]["LatentsField"] | null; + lora?: components["schemas"]["ModelIdentifierField"] | null; /** - * Width - * @description Width of output (px) - * @default null + * Weight + * @description The weight at which the LoRA is applied to each model + * @default 0.75 */ - width?: number | null; + weight?: number; /** - * Height - * @description Width of output (px) + * UNet + * @description UNet (scheduler, LoRAs) * @default null */ - height?: number | null; + unet?: components["schemas"]["UNetField"] | null; /** - * Mode - * @description Interpolation mode - * @default bilinear - * @enum {string} + * CLIP 1 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null */ - mode?: "nearest" | "linear" | "bilinear" | "bicubic" | "trilinear" | "area" | "nearest-exact"; + clip?: components["schemas"]["CLIPField"] | null; /** - * Antialias - * @description Whether or not to apply antialiasing (bilinear or bicubic only) - * @default false + * CLIP 2 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null */ - antialias?: boolean; + clip2?: components["schemas"]["CLIPField"] | null; /** * type - * @default lresize + * @default sdxl_lora_loader * @constant */ - type: "lresize"; + type: "sdxl_lora_loader"; }; /** - * ResourceOrigin - * @description The origin of a resource (eg image). - * - * - INTERNAL: The resource was created by the application. - * - EXTERNAL: The resource was not created by the application. - * This may be a user-initiated upload, or an internal application upload (eg Canvas init image). - * @enum {string} + * SDXLLoRALoaderOutput + * @description SDXL LoRA Loader Output */ - ResourceOrigin: "internal" | "external"; - /** RetryItemsResult */ - RetryItemsResult: { + SDXLLoRALoaderOutput: { /** - * Queue Id - * @description The ID of the queue + * UNet + * @description UNet (scheduler, LoRAs) + * @default null */ - queue_id: string; + unet: components["schemas"]["UNetField"] | null; /** - * Retried Item Ids - * @description The IDs of the queue items that were retried + * CLIP 1 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null */ - retried_item_ids: number[]; + clip: components["schemas"]["CLIPField"] | null; + /** + * CLIP 2 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null + */ + clip2: components["schemas"]["CLIPField"] | null; + /** + * type + * @default sdxl_lora_loader_output + * @constant + */ + type: "sdxl_lora_loader_output"; }; /** - * Round Float - * @description Rounds a float to a specified number of decimal places. + * Main Model - SDXL + * @description Loads an sdxl base model, outputting its submodels. */ - RoundInvocation: { + SDXLModelLoaderInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -18719,92 +21935,54 @@ export type components = { */ use_cache?: boolean; /** - * Value - * @description The float value - * @default 0 - */ - value?: number; - /** - * Decimals - * @description The number of decimal places - * @default 0 + * @description SDXL Main model (UNet, VAE, CLIP1, CLIP2) to load + * @default null */ - decimals?: number; + model?: components["schemas"]["ModelIdentifierField"] | null; /** * type - * @default round_float + * @default sdxl_model_loader * @constant */ - type: "round_float"; + type: "sdxl_model_loader"; }; - /** SAMPoint */ - SAMPoint: { + /** + * SDXLModelLoaderOutput + * @description SDXL base model loader output + */ + SDXLModelLoaderOutput: { /** - * X - * @description The x-coordinate of the point + * UNet + * @description UNet (scheduler, LoRAs) */ - x: number; + unet: components["schemas"]["UNetField"]; /** - * Y - * @description The y-coordinate of the point + * CLIP 1 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count */ - y: number; - /** @description The label of the point */ - label: components["schemas"]["SAMPointLabel"]; - }; - /** - * SAMPointLabel - * @enum {integer} - */ - SAMPointLabel: -1 | 0 | 1; - /** SAMPointsField */ - SAMPointsField: { + clip: components["schemas"]["CLIPField"]; /** - * Points - * @description The points of the object + * CLIP 2 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count */ - points: components["schemas"]["SAMPoint"][]; - }; - /** - * SD3ConditioningField - * @description A conditioning tensor primitive value - */ - SD3ConditioningField: { + clip2: components["schemas"]["CLIPField"]; /** - * Conditioning Name - * @description The name of conditioning tensor + * VAE + * @description VAE */ - conditioning_name: string; - }; - /** - * SD3ConditioningOutput - * @description Base class for nodes that output a single SD3 conditioning tensor - */ - SD3ConditioningOutput: { - /** @description Conditioning tensor */ - conditioning: components["schemas"]["SD3ConditioningField"]; + vae: components["schemas"]["VAEField"]; /** * type - * @default sd3_conditioning_output + * @default sdxl_model_loader_output * @constant */ - type: "sd3_conditioning_output"; + type: "sdxl_model_loader_output"; }; /** - * Denoise - SD3 - * @description Run denoising process with a SD3 model. + * Prompt - SDXL Refiner + * @description Parse prompt using compel package to conditioning. */ - SD3DenoiseInvocation: { - /** - * @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; + SDXLRefinerCompelPromptInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -18823,85 +22001,120 @@ export type components = { */ use_cache?: boolean; /** - * @description Latents tensor - * @default null + * Style + * @description Prompt to be parsed by Compel to create a conditioning tensor + * @default */ - latents?: components["schemas"]["LatentsField"] | null; + style?: string; /** - * @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 + * Original Width + * @default 1024 */ - denoise_mask?: components["schemas"]["DenoiseMaskField"] | null; + original_width?: number; /** - * Denoising Start - * @description When to start denoising, expressed a percentage of total steps + * Original Height + * @default 1024 + */ + original_height?: number; + /** + * Crop Top * @default 0 */ - denoising_start?: number; + crop_top?: number; /** - * Denoising End - * @description When to stop denoising, expressed a percentage of total steps - * @default 1 + * Crop Left + * @default 0 */ - denoising_end?: number; + crop_left?: number; /** - * Transformer - * @description SD3 model (MMDiTX) to load - * @default null + * Aesthetic Score + * @description The aesthetic score to apply to the conditioning tensor + * @default 6 */ - transformer?: components["schemas"]["TransformerField"] | null; + aesthetic_score?: number; /** - * @description Positive conditioning tensor + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count * @default null */ - positive_conditioning?: components["schemas"]["SD3ConditioningField"] | null; + clip2?: components["schemas"]["CLIPField"] | null; /** - * @description Negative conditioning tensor - * @default null + * type + * @default sdxl_refiner_compel_prompt + * @constant */ - negative_conditioning?: components["schemas"]["SD3ConditioningField"] | null; + type: "sdxl_refiner_compel_prompt"; + }; + /** + * Refiner Model - SDXL + * @description Loads an sdxl refiner model, outputting its submodels. + */ + SDXLRefinerModelLoaderInvocation: { /** - * CFG Scale - * @description Classifier-Free Guidance scale - * @default 3.5 + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - cfg_scale?: number | number[]; + id: string; /** - * Width - * @description Width of the generated image. - * @default 1024 + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - width?: number; + is_intermediate?: boolean; /** - * Height - * @description Height of the generated image. - * @default 1024 + * Use Cache + * @description Whether or not to use the cache + * @default true */ - height?: number; + use_cache?: boolean; /** - * Steps - * @description Number of steps to run - * @default 10 + * @description SDXL Refiner Main Modde (UNet, VAE, CLIP2) to load + * @default null */ - steps?: number; + model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Seed - * @description Randomness seed for reproducibility. - * @default 0 + * type + * @default sdxl_refiner_model_loader + * @constant */ - seed?: number; + type: "sdxl_refiner_model_loader"; + }; + /** + * SDXLRefinerModelLoaderOutput + * @description SDXL refiner model loader output + */ + SDXLRefinerModelLoaderOutput: { + /** + * UNet + * @description UNet (scheduler, LoRAs) + */ + unet: components["schemas"]["UNetField"]; + /** + * CLIP 2 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + */ + clip2: components["schemas"]["CLIPField"]; + /** + * VAE + * @description VAE + */ + vae: components["schemas"]["VAEField"]; /** * type - * @default sd3_denoise + * @default sdxl_refiner_model_loader_output * @constant */ - type: "sd3_denoise"; + type: "sdxl_refiner_model_loader_output"; }; /** - * Image to Latents - SD3 - * @description Generates latents from an image. + * SQLiteDirection + * @enum {string} */ - SD3ImageToLatentsInvocation: { + SQLiteDirection: "ASC" | "DESC"; + /** + * Save Image + * @description Saves an image. Unlike an image primitive, this invocation stores a copy of the image. + */ + SaveImageInvocation: { /** * @description The board to save the image to * @default null @@ -18926,41 +22139,26 @@ export type components = { /** * Use Cache * @description Whether or not to use the cache - * @default true + * @default false */ use_cache?: boolean; /** - * @description The image to encode + * @description The image to process * @default null */ image?: components["schemas"]["ImageField"] | null; - /** - * @description VAE - * @default null - */ - vae?: components["schemas"]["VAEField"] | null; /** * type - * @default sd3_i2l + * @default save_image * @constant */ - type: "sd3_i2l"; + type: "save_image"; }; - /** - * Latents to Image - SD3 - * @description Generates an image from latents. - */ - SD3LatentsToImageInvocation: { - /** - * @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; + /** + * Scale Latents + * @description Scales latents by a given factor. + */ + ScaleLatentsInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -18984,22 +22182,36 @@ export type components = { */ latents?: components["schemas"]["LatentsField"] | null; /** - * @description VAE + * Scale Factor + * @description The factor by which to scale * @default null */ - vae?: components["schemas"]["VAEField"] | null; + scale_factor?: number | null; + /** + * Mode + * @description Interpolation mode + * @default bilinear + * @enum {string} + */ + mode?: "nearest" | "linear" | "bilinear" | "bicubic" | "trilinear" | "area" | "nearest-exact"; + /** + * Antialias + * @description Whether or not to apply antialiasing (bilinear or bicubic only) + * @default false + */ + antialias?: boolean; /** * type - * @default sd3_l2i + * @default lscale * @constant */ - type: "sd3_l2i"; + type: "lscale"; }; /** - * Prompt - SDXL - * @description Parse prompt using compel package to conditioning. + * Scheduler + * @description Selects a scheduler. */ - SDXLCompelPromptInvocation: { + SchedulerInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -19018,76 +22230,45 @@ export type components = { */ use_cache?: boolean; /** - * Prompt - * @description Prompt to be parsed by Compel to create a conditioning tensor - * @default - */ - prompt?: string; - /** - * Style - * @description Prompt to be parsed by Compel to create a conditioning tensor - * @default - */ - style?: string; - /** - * Original Width - * @default 1024 - */ - original_width?: number; - /** - * Original Height - * @default 1024 - */ - original_height?: number; - /** - * Crop Top - * @default 0 - */ - crop_top?: number; - /** - * Crop Left - * @default 0 - */ - crop_left?: number; - /** - * Target Width - * @default 1024 - */ - target_width?: number; - /** - * Target Height - * @default 1024 - */ - target_height?: number; - /** - * CLIP 1 - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * Scheduler + * @description Scheduler to use during inference + * @default euler + * @enum {string} */ - clip?: components["schemas"]["CLIPField"] | null; + scheduler?: "ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd"; /** - * CLIP 2 - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * type + * @default scheduler + * @constant */ - clip2?: components["schemas"]["CLIPField"] | null; + type: "scheduler"; + }; + /** SchedulerOutput */ + SchedulerOutput: { /** - * @description A mask defining the region that this conditioning prompt applies to. - * @default null + * Scheduler + * @description Scheduler to use during inference + * @enum {string} */ - mask?: components["schemas"]["TensorField"] | null; + scheduler: "ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd"; /** * type - * @default sdxl_compel_prompt + * @default scheduler_output * @constant */ - type: "sdxl_compel_prompt"; + type: "scheduler_output"; }; /** - * Apply LoRA Collection - SDXL - * @description Applies a collection of SDXL LoRAs to the provided UNet and CLIP models. + * SchedulerPredictionType + * @description Scheduler prediction type. + * @enum {string} */ - SDXLLoRACollectionLoader: { + SchedulerPredictionType: "epsilon" | "v_prediction" | "sample"; + /** + * Main Model - SD3 + * @description Loads a SD3 base model, outputting its submodels. + */ + Sd3ModelLoaderInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -19105,42 +22286,81 @@ export type components = { * @default true */ use_cache?: boolean; + /** @description SD3 model (MMDiTX) to load */ + model: components["schemas"]["ModelIdentifierField"]; /** - * LoRAs - * @description LoRA models and weights. May be a single LoRA or collection. + * T5 Encoder + * @description T5 tokenizer and text encoder * @default null */ - loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null; + t5_encoder_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * UNet - * @description UNet (scheduler, LoRAs) + * CLIP L Encoder + * @description CLIP Embed loader * @default null */ - unet?: components["schemas"]["UNetField"] | null; + clip_l_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * CLIP G Encoder + * @description CLIP-G Embed loader * @default null */ - clip?: components["schemas"]["CLIPField"] | null; + clip_g_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * CLIP 2 - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * VAE + * @description VAE model to load * @default null */ - clip2?: components["schemas"]["CLIPField"] | null; + vae_model?: components["schemas"]["ModelIdentifierField"] | null; /** * type - * @default sdxl_lora_collection_loader + * @default sd3_model_loader * @constant */ - type: "sdxl_lora_collection_loader"; + type: "sd3_model_loader"; }; /** - * Apply LoRA - SDXL - * @description Apply selected lora to unet and text_encoder. + * Sd3ModelLoaderOutput + * @description SD3 base model loader output. */ - SDXLLoRALoaderInvocation: { + Sd3ModelLoaderOutput: { + /** + * Transformer + * @description Transformer + */ + transformer: components["schemas"]["TransformerField"]; + /** + * CLIP L + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + */ + clip_l: components["schemas"]["CLIPField"]; + /** + * CLIP G + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + */ + clip_g: components["schemas"]["CLIPField"]; + /** + * T5 Encoder + * @description T5 tokenizer and text encoder + */ + t5_encoder: components["schemas"]["T5EncoderField"]; + /** + * VAE + * @description VAE + */ + vae: components["schemas"]["VAEField"]; + /** + * type + * @default sd3_model_loader_output + * @constant + */ + type: "sd3_model_loader_output"; + }; + /** + * Prompt - SD3 + * @description Encodes and preps a prompt for a SD3 image. + */ + Sd3TextEncoderInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -19159,77 +22379,41 @@ export type components = { */ 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; - /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null - */ - unet?: components["schemas"]["UNetField"] | null; - /** - * CLIP 1 + * CLIP L * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count * @default null */ - clip?: components["schemas"]["CLIPField"] | null; + clip_l?: components["schemas"]["CLIPField"] | null; /** - * CLIP 2 + * CLIP G * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count * @default null */ - clip2?: components["schemas"]["CLIPField"] | null; - /** - * type - * @default sdxl_lora_loader - * @constant - */ - type: "sdxl_lora_loader"; - }; - /** - * SDXLLoRALoaderOutput - * @description SDXL LoRA Loader Output - */ - SDXLLoRALoaderOutput: { - /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null - */ - unet: components["schemas"]["UNetField"] | null; + clip_g?: components["schemas"]["CLIPField"] | null; /** - * CLIP 1 - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * T5Encoder + * @description T5 tokenizer and text encoder * @default null */ - clip: components["schemas"]["CLIPField"] | null; + t5_encoder?: components["schemas"]["T5EncoderField"] | null; /** - * CLIP 2 - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * Prompt + * @description Text prompt to encode. * @default null */ - clip2: components["schemas"]["CLIPField"] | null; + prompt?: string | null; /** * type - * @default sdxl_lora_loader_output + * @default sd3_text_encoder * @constant */ - type: "sdxl_lora_loader_output"; + type: "sd3_text_encoder"; }; /** - * Main Model - SDXL - * @description Loads an sdxl base model, outputting its submodels. + * Apply Seamless - SD1.5, SDXL + * @description Applies the seamless transformation to the Model UNet and VAE. */ - SDXLModelLoaderInvocation: { + SeamlessModeInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -19248,54 +22432,65 @@ export type components = { */ use_cache?: boolean; /** - * @description SDXL Main model (UNet, VAE, CLIP1, CLIP2) to load + * UNet + * @description UNet (scheduler, LoRAs) * @default null */ - model?: components["schemas"]["ModelIdentifierField"] | null; + unet?: components["schemas"]["UNetField"] | null; + /** + * VAE + * @description VAE model to load + * @default null + */ + vae?: components["schemas"]["VAEField"] | null; + /** + * Seamless Y + * @description Specify whether Y axis is seamless + * @default true + */ + seamless_y?: boolean; + /** + * Seamless X + * @description Specify whether X axis is seamless + * @default true + */ + seamless_x?: boolean; /** * type - * @default sdxl_model_loader + * @default seamless * @constant */ - type: "sdxl_model_loader"; + type: "seamless"; }; /** - * SDXLModelLoaderOutput - * @description SDXL base model loader output + * SeamlessModeOutput + * @description Modified Seamless Model output */ - SDXLModelLoaderOutput: { + SeamlessModeOutput: { /** * UNet * @description UNet (scheduler, LoRAs) + * @default null */ - unet: components["schemas"]["UNetField"]; - /** - * CLIP 1 - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - */ - clip: components["schemas"]["CLIPField"]; - /** - * CLIP 2 - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - */ - clip2: components["schemas"]["CLIPField"]; + unet: components["schemas"]["UNetField"] | null; /** * VAE * @description VAE + * @default null */ - vae: components["schemas"]["VAEField"]; + vae: components["schemas"]["VAEField"] | null; /** * type - * @default sdxl_model_loader_output + * @default seamless_output * @constant */ - type: "sdxl_model_loader_output"; + type: "seamless_output"; }; /** - * Prompt - SDXL Refiner - * @description Parse prompt using compel package to conditioning. + * Segment Anything + * @description Runs a Segment Anything Model (SAM or SAM2). */ - SDXLRefinerCompelPromptInvocation: { + SegmentAnythingInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -19314,274 +22509,284 @@ export type components = { */ use_cache?: boolean; /** - * Style - * @description Prompt to be parsed by Compel to create a conditioning tensor - * @default - */ - style?: string; - /** - * Original Width - * @default 1024 + * Model + * @description The Segment Anything model to use (SAM or SAM2). + * @default null */ - original_width?: number; + model?: ("segment-anything-base" | "segment-anything-large" | "segment-anything-huge" | "segment-anything-2-tiny" | "segment-anything-2-small" | "segment-anything-2-base" | "segment-anything-2-large") | null; /** - * Original Height - * @default 1024 + * @description The image to segment. + * @default null */ - original_height?: number; + image?: components["schemas"]["ImageField"] | null; /** - * Crop Top - * @default 0 + * Bounding Boxes + * @description The bounding boxes to prompt the model with. + * @default null */ - crop_top?: number; + bounding_boxes?: components["schemas"]["BoundingBoxField"][] | null; /** - * Crop Left - * @default 0 + * Point Lists + * @description The list of point lists to prompt the model with. Each list of points represents a single object. + * @default null */ - crop_left?: number; + point_lists?: components["schemas"]["SAMPointsField"][] | null; /** - * Aesthetic Score - * @description The aesthetic score to apply to the conditioning tensor - * @default 6 + * Apply Polygon Refinement + * @description Whether to apply polygon refinement to the masks. This will smooth the edges of the masks slightly and ensure that each mask consists of a single closed polygon (before merging). + * @default true */ - aesthetic_score?: number; + apply_polygon_refinement?: boolean; /** - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * Mask Filter + * @description The filtering to apply to the detected masks before merging them into a final output. + * @default all + * @enum {string} */ - clip2?: components["schemas"]["CLIPField"] | null; + mask_filter?: "all" | "largest" | "highest_box_score"; /** * type - * @default sdxl_refiner_compel_prompt + * @default segment_anything * @constant */ - type: "sdxl_refiner_compel_prompt"; + type: "segment_anything"; + }; + /** SessionProcessorStatus */ + SessionProcessorStatus: { + /** + * Is Started + * @description Whether the session processor is started + */ + is_started: boolean; + /** + * Is Processing + * @description Whether a session is being processed + */ + is_processing: boolean; }; /** - * Refiner Model - SDXL - * @description Loads an sdxl refiner model, outputting its submodels. + * SessionQueueAndProcessorStatus + * @description The overall status of session queue and processor */ - SDXLRefinerModelLoaderInvocation: { + SessionQueueAndProcessorStatus: { + queue: components["schemas"]["SessionQueueStatus"]; + processor: components["schemas"]["SessionProcessorStatus"]; + }; + /** SessionQueueCountsByDestination */ + SessionQueueCountsByDestination: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Queue Id + * @description The ID of the queue */ - id: string; + queue_id: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Destination + * @description The destination of queue items included in this status */ - is_intermediate?: boolean; + destination: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Pending + * @description Number of queue items with status 'pending' for the destination */ - use_cache?: boolean; + pending: number; /** - * @description SDXL Refiner Main Modde (UNet, VAE, CLIP2) to load - * @default null + * In Progress + * @description Number of queue items with status 'in_progress' for the destination */ - model?: components["schemas"]["ModelIdentifierField"] | null; + in_progress: number; /** - * type - * @default sdxl_refiner_model_loader - * @constant + * Completed + * @description Number of queue items with status 'complete' for the destination */ - type: "sdxl_refiner_model_loader"; + completed: number; + /** + * Failed + * @description Number of queue items with status 'error' for the destination + */ + failed: number; + /** + * Canceled + * @description Number of queue items with status 'canceled' for the destination + */ + canceled: number; + /** + * Total + * @description Total number of queue items for the destination + */ + total: number; }; /** - * SDXLRefinerModelLoaderOutput - * @description SDXL refiner model loader output + * SessionQueueItem + * @description Session queue item without the full graph. Used for serialization. */ - SDXLRefinerModelLoaderOutput: { + SessionQueueItem: { /** - * UNet - * @description UNet (scheduler, LoRAs) + * Item Id + * @description The identifier of the session queue item */ - unet: components["schemas"]["UNetField"]; + item_id: number; /** - * CLIP 2 - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * Status + * @description The status of this queue item + * @default pending + * @enum {string} */ - clip2: components["schemas"]["CLIPField"]; + status: "pending" | "in_progress" | "completed" | "failed" | "canceled"; /** - * VAE - * @description VAE + * Priority + * @description The priority of this queue item + * @default 0 */ - vae: components["schemas"]["VAEField"]; + priority: number; /** - * type - * @default sdxl_refiner_model_loader_output - * @constant + * Batch Id + * @description The ID of the batch associated with this queue item */ - type: "sdxl_refiner_model_loader_output"; - }; - /** - * SQLiteDirection - * @enum {string} - */ - SQLiteDirection: "ASC" | "DESC"; - /** - * Save Image - * @description Saves an image. Unlike an image primitive, this invocation stores a copy of the image. - */ - SaveImageInvocation: { + batch_id: string; /** - * @description The board to save the image to - * @default null + * Origin + * @description The origin of this queue item. This data is used by the frontend to determine how to handle results. */ - board?: components["schemas"]["BoardField"] | null; + origin?: string | null; + /** + * Destination + * @description The origin of this queue item. This data is used by the frontend to determine how to handle results + */ + destination?: string | null; + /** + * Session Id + * @description The ID of the session associated with this queue item. The session doesn't exist in graph_executions until the queue item is executed. + */ + session_id: string; + /** + * Error Type + * @description The error type if this queue item errored + */ + error_type?: string | null; + /** + * Error Message + * @description The error message if this queue item errored + */ + error_message?: string | null; /** - * @description Optional metadata to be saved with the image - * @default null + * Error Traceback + * @description The error traceback if this queue item errored */ - metadata?: components["schemas"]["MetadataField"] | null; + error_traceback?: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Created At + * @description When this queue item was created */ - id: string; + created_at: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Updated At + * @description When this queue item was updated */ - is_intermediate?: boolean; + updated_at: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default false + * Started At + * @description When this queue item was started */ - use_cache?: boolean; + started_at?: string | null; /** - * @description The image to process - * @default null + * Completed At + * @description When this queue item was completed */ - image?: components["schemas"]["ImageField"] | null; + completed_at?: string | null; /** - * type - * @default save_image - * @constant + * Queue Id + * @description The id of the queue with which this item is associated */ - type: "save_image"; - }; - /** - * Scale Latents - * @description Scales latents by a given factor. - */ - ScaleLatentsInvocation: { + queue_id: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Field Values + * @description The field values that were used for this queue item */ - id: string; + field_values?: components["schemas"]["NodeFieldValue"][] | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Retried From Item Id + * @description The item_id of the queue item that this item was retried from */ - is_intermediate?: boolean; + retried_from_item_id?: number | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Is Api Validation Run + * @description Whether this queue item is an API validation run. + * @default false */ - use_cache?: boolean; + is_api_validation_run?: boolean; /** - * @description Latents tensor - * @default null + * Published Workflow Id + * @description The ID of the published workflow associated with this queue item */ - latents?: components["schemas"]["LatentsField"] | null; + published_workflow_id?: string | null; /** - * Scale Factor - * @description The factor by which to scale - * @default null + * Credits + * @description The total credits used for this queue item */ - scale_factor?: number | null; + credits?: number | null; + /** @description The fully-populated session to be executed */ + session: components["schemas"]["GraphExecutionState"]; + /** @description The workflow associated with this queue item */ + workflow?: components["schemas"]["WorkflowWithoutID"] | null; + }; + /** SessionQueueStatus */ + SessionQueueStatus: { /** - * Mode - * @description Interpolation mode - * @default bilinear - * @enum {string} + * Queue Id + * @description The ID of the queue */ - mode?: "nearest" | "linear" | "bilinear" | "bicubic" | "trilinear" | "area" | "nearest-exact"; + queue_id: string; /** - * Antialias - * @description Whether or not to apply antialiasing (bilinear or bicubic only) - * @default false + * Item Id + * @description The current queue item id */ - antialias?: boolean; + item_id: number | null; /** - * type - * @default lscale - * @constant + * Batch Id + * @description The current queue item's batch id */ - type: "lscale"; - }; - /** - * Scheduler - * @description Selects a scheduler. - */ - SchedulerInvocation: { + batch_id: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Session Id + * @description The current queue item's session id */ - id: string; + session_id: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Pending + * @description Number of queue items with status 'pending' */ - is_intermediate?: boolean; + pending: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * In Progress + * @description Number of queue items with status 'in_progress' */ - use_cache?: boolean; + in_progress: number; /** - * Scheduler - * @description Scheduler to use during inference - * @default euler - * @enum {string} + * Completed + * @description Number of queue items with status 'complete' */ - scheduler?: "ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd"; + completed: number; /** - * type - * @default scheduler - * @constant + * Failed + * @description Number of queue items with status 'error' */ - type: "scheduler"; - }; - /** SchedulerOutput */ - SchedulerOutput: { + failed: number; /** - * Scheduler - * @description Scheduler to use during inference - * @enum {string} + * Canceled + * @description Number of queue items with status 'canceled' */ - scheduler: "ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd"; + canceled: number; /** - * type - * @default scheduler_output - * @constant + * Total + * @description Total number of queue items */ - type: "scheduler_output"; + total: number; }; /** - * SchedulerPredictionType - * @description Scheduler prediction type. - * @enum {string} - */ - SchedulerPredictionType: "epsilon" | "v_prediction" | "sample"; - /** - * Main Model - SD3 - * @description Loads a SD3 base model, outputting its submodels. + * Show Image + * @description Displays a provided image using the OS image viewer, and passes it forward in the pipeline. */ - Sd3ModelLoaderInvocation: { + ShowImageInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -19599,134 +22804,118 @@ export type components = { * @default true */ use_cache?: boolean; - /** @description SD3 model (MMDiTX) to load */ - model: components["schemas"]["ModelIdentifierField"]; - /** - * T5 Encoder - * @description T5 tokenizer and text encoder - * @default null - */ - t5_encoder_model?: components["schemas"]["ModelIdentifierField"] | null; - /** - * CLIP L Encoder - * @description CLIP Embed loader - * @default null - */ - clip_l_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * CLIP G Encoder - * @description CLIP-G Embed loader - * @default null - */ - clip_g_model?: components["schemas"]["ModelIdentifierField"] | null; - /** - * VAE - * @description VAE model to load + * @description The image to show * @default null */ - vae_model?: components["schemas"]["ModelIdentifierField"] | null; + image?: components["schemas"]["ImageField"] | null; /** * type - * @default sd3_model_loader + * @default show_image * @constant */ - type: "sd3_model_loader"; + type: "show_image"; }; /** - * Sd3ModelLoaderOutput - * @description SD3 base model loader output. + * SigLIP_Diffusers_Config + * @description Model config for SigLIP. */ - Sd3ModelLoaderOutput: { + SigLIP_Diffusers_Config: { /** - * Transformer - * @description Transformer + * Key + * @description A unique key for this model. */ - transformer: components["schemas"]["TransformerField"]; + key: string; /** - * CLIP L - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * Hash + * @description The hash of the model file(s). */ - clip_l: components["schemas"]["CLIPField"]; + hash: string; /** - * CLIP G - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - clip_g: components["schemas"]["CLIPField"]; + path: string; /** - * T5 Encoder - * @description T5 tokenizer and text encoder + * File Size + * @description The size of the model in bytes. */ - t5_encoder: components["schemas"]["T5EncoderField"]; + file_size: number; /** - * VAE - * @description VAE + * Name + * @description Name of the model. */ - vae: components["schemas"]["VAEField"]; + name: string; /** - * type - * @default sd3_model_loader_output - * @constant + * Description + * @description Model description */ - type: "sd3_model_loader_output"; - }; - /** - * Prompt - SD3 - * @description Encodes and preps a prompt for a SD3 image. - */ - Sd3TextEncoderInvocation: { + description: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Source + * @description The original source of the model (path, URL or repo_id). */ - id: string; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - is_intermediate?: boolean; + source_api_response: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Cover Image + * @description Url for image to preview model */ - use_cache?: boolean; + cover_image: string | null; /** - * CLIP L - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * Submodels + * @description Loadable submodels in this model */ - clip_l?: components["schemas"]["CLIPField"] | null; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * CLIP G - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * Usage Info + * @description Usage information for this model */ - clip_g?: components["schemas"]["CLIPField"] | null; + usage_info: string | null; /** - * T5Encoder - * @description T5 tokenizer and text encoder - * @default null + * Format + * @default diffusers + * @constant */ - t5_encoder?: components["schemas"]["T5EncoderField"] | null; + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; /** - * Prompt - * @description Text prompt to encode. - * @default null + * Type + * @default siglip + * @constant */ - prompt?: string | null; + type: "siglip"; /** - * type - * @default sd3_text_encoder + * Base + * @default any * @constant */ - type: "sd3_text_encoder"; + base: "any"; }; /** - * Apply Seamless - SD1.5, SDXL - * @description Applies the seamless transformation to the Model UNet and VAE. + * Image-to-Image (Autoscale) + * @description Run any spandrel image-to-image model (https://github.com/chaiNNer-org/spandrel) until the target scale is reached. */ - SeamlessModeInvocation: { + SpandrelImageToImageAutoscaleInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -19745,65 +22934,56 @@ export type components = { */ use_cache?: boolean; /** - * UNet - * @description UNet (scheduler, LoRAs) + * @description The input image * @default null */ - unet?: components["schemas"]["UNetField"] | null; + image?: components["schemas"]["ImageField"] | null; /** - * VAE - * @description VAE model to load + * Image-to-Image Model + * @description Image-to-Image model * @default null */ - vae?: components["schemas"]["VAEField"] | null; - /** - * Seamless Y - * @description Specify whether Y axis is seamless - * @default true - */ - seamless_y?: boolean; + image_to_image_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Seamless X - * @description Specify whether X axis is seamless - * @default true + * Tile Size + * @description The tile size for tiled image-to-image. Set to 0 to disable tiling. + * @default 512 */ - seamless_x?: boolean; + tile_size?: number; /** * type - * @default seamless + * @default spandrel_image_to_image_autoscale * @constant */ - type: "seamless"; + type: "spandrel_image_to_image_autoscale"; + /** + * Scale + * @description The final scale of the output image. If the model does not upscale the image, this will be ignored. + * @default 4 + */ + scale?: number; + /** + * Fit To Multiple Of 8 + * @description If true, the output image will be resized to the nearest multiple of 8 in both dimensions. + * @default false + */ + fit_to_multiple_of_8?: boolean; }; /** - * SeamlessModeOutput - * @description Modified Seamless Model output + * Image-to-Image + * @description Run any spandrel image-to-image model (https://github.com/chaiNNer-org/spandrel). */ - SeamlessModeOutput: { + SpandrelImageToImageInvocation: { /** - * UNet - * @description UNet (scheduler, LoRAs) + * @description The board to save the image to * @default null */ - unet: components["schemas"]["UNetField"] | null; + board?: components["schemas"]["BoardField"] | null; /** - * VAE - * @description VAE + * @description Optional metadata to be saved with the image * @default null */ - vae: components["schemas"]["VAEField"] | null; - /** - * type - * @default seamless_output - * @constant - */ - type: "seamless_output"; - }; - /** - * Segment Anything - * @description Runs a Segment Anything Model (SAM or SAM2). - */ - SegmentAnythingInvocation: { + metadata?: components["schemas"]["MetadataField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -19822,284 +23002,269 @@ export type components = { */ use_cache?: boolean; /** - * Model - * @description The Segment Anything model to use (SAM or SAM2). - * @default null - */ - model?: ("segment-anything-base" | "segment-anything-large" | "segment-anything-huge" | "segment-anything-2-tiny" | "segment-anything-2-small" | "segment-anything-2-base" | "segment-anything-2-large") | null; - /** - * @description The image to segment. + * @description The input image * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Bounding Boxes - * @description The bounding boxes to prompt the model with. - * @default null - */ - bounding_boxes?: components["schemas"]["BoundingBoxField"][] | null; - /** - * Point Lists - * @description The list of point lists to prompt the model with. Each list of points represents a single object. + * Image-to-Image Model + * @description Image-to-Image model * @default null */ - point_lists?: components["schemas"]["SAMPointsField"][] | null; - /** - * Apply Polygon Refinement - * @description Whether to apply polygon refinement to the masks. This will smooth the edges of the masks slightly and ensure that each mask consists of a single closed polygon (before merging). - * @default true - */ - apply_polygon_refinement?: boolean; + image_to_image_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Mask Filter - * @description The filtering to apply to the detected masks before merging them into a final output. - * @default all - * @enum {string} + * Tile Size + * @description The tile size for tiled image-to-image. Set to 0 to disable tiling. + * @default 512 */ - mask_filter?: "all" | "largest" | "highest_box_score"; + tile_size?: number; /** * type - * @default segment_anything + * @default spandrel_image_to_image * @constant */ - type: "segment_anything"; - }; - /** SessionProcessorStatus */ - SessionProcessorStatus: { - /** - * Is Started - * @description Whether the session processor is started - */ - is_started: boolean; - /** - * Is Processing - * @description Whether a session is being processed - */ - is_processing: boolean; + type: "spandrel_image_to_image"; }; /** - * SessionQueueAndProcessorStatus - * @description The overall status of session queue and processor + * Spandrel_Checkpoint_Config + * @description Model config for Spandrel Image to Image models. */ - SessionQueueAndProcessorStatus: { - queue: components["schemas"]["SessionQueueStatus"]; - processor: components["schemas"]["SessionProcessorStatus"]; - }; - /** SessionQueueCountsByDestination */ - SessionQueueCountsByDestination: { - /** - * Queue Id - * @description The ID of the queue - */ - queue_id: string; - /** - * Destination - * @description The destination of queue items included in this status - */ - destination: string; - /** - * Pending - * @description Number of queue items with status 'pending' for the destination - */ - pending: number; - /** - * In Progress - * @description Number of queue items with status 'in_progress' for the destination - */ - in_progress: number; - /** - * Completed - * @description Number of queue items with status 'complete' for the destination - */ - completed: number; - /** - * Failed - * @description Number of queue items with status 'error' for the destination - */ - failed: number; - /** - * Canceled - * @description Number of queue items with status 'canceled' for the destination - */ - canceled: number; + Spandrel_Checkpoint_Config: { /** - * Total - * @description Total number of queue items for the destination + * Key + * @description A unique key for this model. */ - total: number; - }; - /** - * SessionQueueItem - * @description Session queue item without the full graph. Used for serialization. - */ - SessionQueueItem: { + key: string; /** - * Item Id - * @description The identifier of the session queue item + * Hash + * @description The hash of the model file(s). */ - item_id: number; + hash: string; /** - * Status - * @description The status of this queue item - * @default pending - * @enum {string} + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - status: "pending" | "in_progress" | "completed" | "failed" | "canceled"; + path: string; /** - * Priority - * @description The priority of this queue item - * @default 0 + * File Size + * @description The size of the model in bytes. */ - priority: number; + file_size: number; /** - * Batch Id - * @description The ID of the batch associated with this queue item + * Name + * @description Name of the model. */ - batch_id: string; + name: string; /** - * Origin - * @description The origin of this queue item. This data is used by the frontend to determine how to handle results. + * Description + * @description Model description */ - origin?: string | null; + description: string | null; /** - * Destination - * @description The origin of this queue item. This data is used by the frontend to determine how to handle results + * Source + * @description The original source of the model (path, URL or repo_id). */ - destination?: string | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Session Id - * @description The ID of the session associated with this queue item. The session doesn't exist in graph_executions until the queue item is executed. + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - session_id: string; + source_api_response: string | null; /** - * Error Type - * @description The error type if this queue item errored + * Cover Image + * @description Url for image to preview model */ - error_type?: string | null; + cover_image: string | null; /** - * Error Message - * @description The error message if this queue item errored + * Submodels + * @description Loadable submodels in this model */ - error_message?: string | null; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Error Traceback - * @description The error traceback if this queue item errored + * Usage Info + * @description Usage information for this model */ - error_traceback?: string | null; + usage_info: string | null; /** - * Created At - * @description When this queue item was created + * Base + * @default any + * @constant */ - created_at: string; + base: "any"; /** - * Updated At - * @description When this queue item was updated + * Type + * @default spandrel_image_to_image + * @constant */ - updated_at: string; + type: "spandrel_image_to_image"; /** - * Started At - * @description When this queue item was started + * Format + * @default checkpoint + * @constant */ - started_at?: string | null; + format: "checkpoint"; + }; + /** StarredImagesResult */ + StarredImagesResult: { /** - * Completed At - * @description When this queue item was completed + * Affected Boards + * @description The ids of boards affected by the delete operation */ - completed_at?: string | null; + affected_boards: string[]; /** - * Queue Id - * @description The id of the queue with which this item is associated + * Starred Images + * @description The names of the images that were starred */ - queue_id: string; + starred_images: string[]; + }; + /** StarredVideosResult */ + StarredVideosResult: { /** - * Field Values - * @description The field values that were used for this queue item + * Affected Boards + * @description The ids of boards affected by the delete operation */ - field_values?: components["schemas"]["NodeFieldValue"][] | null; + affected_boards: string[]; /** - * Retried From Item Id - * @description The item_id of the queue item that this item was retried from + * Starred Videos + * @description The ids of the videos that were starred */ - retried_from_item_id?: number | null; + starred_videos: string[]; + }; + /** StarterModel */ + StarterModel: { + /** Description */ + description: string; + /** Source */ + source: string; + /** Name */ + name: string; + base: components["schemas"]["BaseModelType"]; + type: components["schemas"]["ModelType"]; + format?: components["schemas"]["ModelFormat"] | null; /** - * Is Api Validation Run - * @description Whether this queue item is an API validation run. + * Is Installed * @default false */ - is_api_validation_run?: boolean; + is_installed?: boolean; /** - * Published Workflow Id - * @description The ID of the published workflow associated with this queue item + * Previous Names + * @default [] */ - published_workflow_id?: string | null; + previous_names?: string[]; + /** Dependencies */ + dependencies?: components["schemas"]["StarterModelWithoutDependencies"][] | null; + }; + /** StarterModelBundle */ + StarterModelBundle: { + /** Name */ + name: string; + /** Models */ + models: components["schemas"]["StarterModel"][]; + }; + /** StarterModelResponse */ + StarterModelResponse: { + /** Starter Models */ + starter_models: components["schemas"]["StarterModel"][]; + /** Starter Bundles */ + starter_bundles: { + [key: string]: components["schemas"]["StarterModelBundle"]; + }; + }; + /** StarterModelWithoutDependencies */ + StarterModelWithoutDependencies: { + /** Description */ + description: string; + /** Source */ + source: string; + /** Name */ + name: string; + base: components["schemas"]["BaseModelType"]; + type: components["schemas"]["ModelType"]; + format?: components["schemas"]["ModelFormat"] | null; /** - * Credits - * @description The total credits used for this queue item + * Is Installed + * @default false */ - credits?: number | null; - /** @description The fully-populated session to be executed */ - session: components["schemas"]["GraphExecutionState"]; - /** @description The workflow associated with this queue item */ - workflow?: components["schemas"]["WorkflowWithoutID"] | null; - }; - /** SessionQueueStatus */ - SessionQueueStatus: { + is_installed?: boolean; /** - * Queue Id - * @description The ID of the queue + * Previous Names + * @default [] */ - queue_id: string; + previous_names?: string[]; + }; + /** + * String2Output + * @description Base class for invocations that output two strings + */ + String2Output: { /** - * Item Id - * @description The current queue item id + * String 1 + * @description string 1 */ - item_id: number | null; + string_1: string; /** - * Batch Id - * @description The current queue item's batch id + * String 2 + * @description string 2 */ - batch_id: string | null; + string_2: string; /** - * Session Id - * @description The current queue item's session id + * type + * @default string_2_output + * @constant */ - session_id: string | null; + type: "string_2_output"; + }; + /** + * String Batch + * @description Create a batched generation, where the workflow is executed once for each string in the batch. + */ + StringBatchInvocation: { /** - * Pending - * @description Number of queue items with status 'pending' + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - pending: number; + id: string; /** - * In Progress - * @description Number of queue items with status 'in_progress' + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - in_progress: number; + is_intermediate?: boolean; /** - * Completed - * @description Number of queue items with status 'complete' + * Use Cache + * @description Whether or not to use the cache + * @default true */ - completed: number; + use_cache?: boolean; /** - * Failed - * @description Number of queue items with status 'error' + * Batch Group + * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. + * @default None + * @enum {string} */ - failed: number; + batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; /** - * Canceled - * @description Number of queue items with status 'canceled' + * Strings + * @description The strings to batch over + * @default null */ - canceled: number; + strings?: string[] | null; /** - * Total - * @description Total number of queue items + * type + * @default string_batch + * @constant */ - total: number; + type: "string_batch"; }; /** - * Show Image - * @description Displays a provided image using the OS image viewer, and passes it forward in the pipeline. + * String Collection Primitive + * @description A collection of string primitive values */ - ShowImageInvocation: { + StringCollectionInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -20118,117 +23283,128 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to show - * @default null + * Collection + * @description The collection of string values + * @default [] */ - image?: components["schemas"]["ImageField"] | null; + collection?: string[]; /** * type - * @default show_image + * @default string_collection * @constant */ - type: "show_image"; + type: "string_collection"; }; /** - * SigLIPConfig - * @description Model config for SigLIP. + * StringCollectionOutput + * @description Base class for nodes that output a collection of strings */ - SigLIPConfig: { + StringCollectionOutput: { /** - * Key - * @description A unique key for this model. + * Collection + * @description The output strings */ - key: string; + collection: string[]; /** - * Hash - * @description The hash of the model file(s). + * type + * @default string_collection_output + * @constant */ - hash: string; + type: "string_collection_output"; + }; + /** + * String Generator + * @description Generated a range of strings for use in a batched generation + */ + StringGenerator: { /** - * Path - * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - path: string; + id: string; /** - * File Size - * @description The size of the model in bytes. + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - file_size: number; + is_intermediate?: boolean; /** - * Name - * @description Name of the model. + * Use Cache + * @description Whether or not to use the cache + * @default true */ - name: string; + use_cache?: boolean; /** - * Description - * @description Model description + * Generator Type + * @description The string generator. */ - description: string | null; + generator: components["schemas"]["StringGeneratorField"]; /** - * Source - * @description The original source of the model (path, URL or repo_id). + * type + * @default string_generator + * @constant */ - source: string; - /** @description The type of source */ - source_type: components["schemas"]["ModelSourceType"]; + type: "string_generator"; + }; + /** StringGeneratorField */ + StringGeneratorField: Record; + /** + * StringGeneratorOutput + * @description Base class for nodes that output a collection of strings + */ + StringGeneratorOutput: { /** - * Source Api Response - * @description The original API response from the source, as stringified JSON. + * Strings + * @description The generated strings */ - source_api_response: string | null; + strings: string[]; /** - * Cover Image - * @description Url for image to preview model + * type + * @default string_generator_output + * @constant */ - cover_image: string | null; + type: "string_generator_output"; + }; + /** + * String Primitive + * @description A string primitive value + */ + StringInvocation: { /** - * Submodels - * @description Loadable submodels in this model + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + id: string; /** - * Usage Info - * @description Usage information for this model + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - usage_info: string | null; + is_intermediate?: boolean; /** - * Format - * @default diffusers - * @constant + * Use Cache + * @description Whether or not to use the cache + * @default true */ - format: "diffusers"; - /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + use_cache?: boolean; /** - * Type - * @default siglip - * @constant + * Value + * @description The string value + * @default */ - type: "siglip"; + value?: string; /** - * Base - * @default any + * type + * @default string * @constant */ - base: "any"; + type: "string"; }; /** - * Image-to-Image (Autoscale) - * @description Run any spandrel image-to-image model (https://github.com/chaiNNer-org/spandrel) until the target scale is reached. + * String Join + * @description Joins string left to string right */ - SpandrelImageToImageAutoscaleInvocation: { - /** - * @description The board to save the image to - * @default null - */ - board?: components["schemas"]["BoardField"] | null; - /** - * @description Optional metadata to be saved with the image - * @default null - */ - metadata?: components["schemas"]["MetadataField"] | null; + StringJoinInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -20247,139 +23423,115 @@ export type components = { */ use_cache?: boolean; /** - * @description The input image - * @default null - */ - image?: components["schemas"]["ImageField"] | null; - /** - * Image-to-Image Model - * @description Image-to-Image model - * @default null + * String Left + * @description String Left + * @default */ - image_to_image_model?: components["schemas"]["ModelIdentifierField"] | null; + string_left?: string; /** - * Tile Size - * @description The tile size for tiled image-to-image. Set to 0 to disable tiling. - * @default 512 + * String Right + * @description String Right + * @default */ - tile_size?: number; + string_right?: string; /** * type - * @default spandrel_image_to_image_autoscale + * @default string_join * @constant */ - type: "spandrel_image_to_image_autoscale"; - /** - * Scale - * @description The final scale of the output image. If the model does not upscale the image, this will be ignored. - * @default 4 - */ - scale?: number; - /** - * Fit To Multiple Of 8 - * @description If true, the output image will be resized to the nearest multiple of 8 in both dimensions. - * @default false - */ - fit_to_multiple_of_8?: boolean; + type: "string_join"; }; /** - * SpandrelImageToImageConfig - * @description Model config for Spandrel Image to Image models. + * String Join Three + * @description Joins string left to string middle to string right */ - SpandrelImageToImageConfig: { - /** - * 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; + StringJoinThreeInvocation: { /** - * Description - * @description Model description + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - description: string | null; + id: string; /** - * Source - * @description The original source of the model (path, URL or repo_id). + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - source: string; - /** @description The type of source */ - source_type: components["schemas"]["ModelSourceType"]; + is_intermediate?: boolean; /** - * Source Api Response - * @description The original API response from the source, as stringified JSON. + * Use Cache + * @description Whether or not to use the cache + * @default true */ - source_api_response: string | null; + use_cache?: boolean; /** - * Cover Image - * @description Url for image to preview model + * String Left + * @description String Left + * @default */ - cover_image: string | null; + string_left?: string; /** - * Submodels - * @description Loadable submodels in this model + * String Middle + * @description String Middle + * @default */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + string_middle?: string; /** - * Usage Info - * @description Usage information for this model + * String Right + * @description String Right + * @default */ - usage_info: string | null; + string_right?: string; /** - * Base - * @default any + * type + * @default string_join_three * @constant */ - base: "any"; + type: "string_join_three"; + }; + /** + * StringOutput + * @description Base class for nodes that output a single string + */ + StringOutput: { /** - * Type - * @default spandrel_image_to_image - * @constant + * Value + * @description The output string */ - type: "spandrel_image_to_image"; + value: string; /** - * Format - * @default checkpoint + * type + * @default string_output * @constant */ - format: "checkpoint"; + type: "string_output"; }; /** - * Image-to-Image - * @description Run any spandrel image-to-image model (https://github.com/chaiNNer-org/spandrel). + * StringPosNegOutput + * @description Base class for invocations that output a positive and negative string */ - SpandrelImageToImageInvocation: { + StringPosNegOutput: { /** - * @description The board to save the image to - * @default null + * Positive String + * @description Positive string */ - board?: components["schemas"]["BoardField"] | null; + positive_string: string; /** - * @description Optional metadata to be saved with the image - * @default null + * Negative String + * @description Negative string */ - metadata?: components["schemas"]["MetadataField"] | null; + negative_string: string; + /** + * type + * @default string_pos_neg_output + * @constant + */ + type: "string_pos_neg_output"; + }; + /** + * String Replace + * @description Replaces the search string with the replace string + */ + StringReplaceInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -20398,144 +23550,153 @@ export type components = { */ use_cache?: boolean; /** - * @description The input image - * @default null + * String + * @description String to work on + * @default */ - image?: components["schemas"]["ImageField"] | null; + string?: string; /** - * Image-to-Image Model - * @description Image-to-Image model - * @default null + * Search String + * @description String to search for + * @default */ - image_to_image_model?: components["schemas"]["ModelIdentifierField"] | null; + search_string?: string; /** - * Tile Size - * @description The tile size for tiled image-to-image. Set to 0 to disable tiling. - * @default 512 + * Replace String + * @description String to replace the search + * @default */ - tile_size?: number; + replace_string?: string; + /** + * Use Regex + * @description Use search string as a regex expression (non regex is case insensitive) + * @default false + */ + use_regex?: boolean; /** * type - * @default spandrel_image_to_image + * @default string_replace * @constant */ - type: "spandrel_image_to_image"; + type: "string_replace"; }; - /** StarredImagesResult */ - StarredImagesResult: { + /** + * String Split + * @description Splits string into two strings, based on the first occurance of the delimiter. The delimiter will be removed from the string + */ + StringSplitInvocation: { /** - * Affected Boards - * @description The ids of boards affected by the delete operation + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - affected_boards: string[]; + id: string; /** - * Starred Images - * @description The names of the images that were starred + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - starred_images: string[]; - }; - /** StarredVideosResult */ - StarredVideosResult: { + is_intermediate?: boolean; /** - * Affected Boards - * @description The ids of boards affected by the delete operation + * Use Cache + * @description Whether or not to use the cache + * @default true */ - affected_boards: string[]; + use_cache?: boolean; /** - * Starred Videos - * @description The ids of the videos that were starred + * String + * @description String to split + * @default */ - starred_videos: string[]; - }; - /** StarterModel */ - StarterModel: { - /** Description */ - description: string; - /** Source */ - source: string; - /** Name */ - name: string; - base: components["schemas"]["BaseModelType"]; - type: components["schemas"]["ModelType"]; - format?: components["schemas"]["ModelFormat"] | null; + string?: string; /** - * Is Installed - * @default false + * Delimiter + * @description Delimiter to spilt with. blank will split on the first whitespace + * @default */ - is_installed?: boolean; + delimiter?: string; /** - * Previous Names - * @default [] + * type + * @default string_split + * @constant */ - previous_names?: string[]; - /** Dependencies */ - dependencies?: components["schemas"]["StarterModelWithoutDependencies"][] | null; - }; - /** StarterModelBundle */ - StarterModelBundle: { - /** Name */ - name: string; - /** Models */ - models: components["schemas"]["StarterModel"][]; - }; - /** StarterModelResponse */ - StarterModelResponse: { - /** Starter Models */ - starter_models: components["schemas"]["StarterModel"][]; - /** Starter Bundles */ - starter_bundles: { - [key: string]: components["schemas"]["StarterModelBundle"]; - }; + type: "string_split"; }; - /** StarterModelWithoutDependencies */ - StarterModelWithoutDependencies: { - /** Description */ - description: string; - /** Source */ - source: string; - /** Name */ - name: string; - base: components["schemas"]["BaseModelType"]; - type: components["schemas"]["ModelType"]; - format?: components["schemas"]["ModelFormat"] | null; + /** + * String Split Negative + * @description Splits string into two strings, inside [] goes into negative string everthing else goes into positive string. Each [ and ] character is replaced with a space + */ + StringSplitNegInvocation: { /** - * Is Installed - * @default false + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - is_installed?: boolean; + id: string; /** - * Previous Names - * @default [] + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - previous_names?: string[]; - }; - /** - * String2Output - * @description Base class for invocations that output two strings - */ - String2Output: { + is_intermediate?: boolean; /** - * String 1 - * @description string 1 + * Use Cache + * @description Whether or not to use the cache + * @default true */ - string_1: string; + use_cache?: boolean; /** - * String 2 - * @description string 2 + * String + * @description String to split + * @default */ - string_2: string; + string?: string; /** * type - * @default string_2_output + * @default string_split_neg * @constant */ - type: "string_2_output"; + type: "string_split_neg"; + }; + /** StylePresetRecordWithImage */ + StylePresetRecordWithImage: { + /** + * Name + * @description The name of the style preset. + */ + name: string; + /** @description The preset data */ + preset_data: components["schemas"]["PresetData"]; + /** @description The type of style preset */ + type: components["schemas"]["PresetType"]; + /** + * Id + * @description The style preset ID. + */ + id: string; + /** + * Image + * @description The path for image + */ + image: string | null; }; /** - * String Batch - * @description Create a batched generation, where the workflow is executed once for each string in the batch. + * SubModelType + * @description Submodel type. + * @enum {string} */ - StringBatchInvocation: { + SubModelType: "unet" | "transformer" | "text_encoder" | "text_encoder_2" | "text_encoder_3" | "tokenizer" | "tokenizer_2" | "tokenizer_3" | "vae" | "vae_decoder" | "vae_encoder" | "scheduler" | "safety_checker"; + /** SubmodelDefinition */ + SubmodelDefinition: { + /** Path Or Prefix */ + path_or_prefix: string; + model_type: components["schemas"]["ModelType"]; + /** Variant */ + variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | null; + }; + /** + * Subtract Integers + * @description Subtracts two numbers + */ + SubtractInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -20554,30 +23715,61 @@ export type components = { */ use_cache?: boolean; /** - * Batch Group - * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. - * @default None - * @enum {string} + * A + * @description The first number + * @default 0 */ - batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; + a?: number; /** - * Strings - * @description The strings to batch over - * @default null + * B + * @description The second number + * @default 0 */ - strings?: string[] | null; + b?: number; /** * type - * @default string_batch + * @default sub * @constant */ - type: "string_batch"; + type: "sub"; + }; + /** T2IAdapterField */ + T2IAdapterField: { + /** @description The T2I-Adapter image prompt. */ + image: components["schemas"]["ImageField"]; + /** @description The T2I-Adapter model to use. */ + t2i_adapter_model: components["schemas"]["ModelIdentifierField"]; + /** + * Weight + * @description The weight given to the T2I-Adapter + * @default 1 + */ + weight?: number | number[]; + /** + * Begin Step Percent + * @description When the T2I-Adapter is first applied (% of total steps) + * @default 0 + */ + begin_step_percent?: number; + /** + * End Step Percent + * @description When the T2I-Adapter is last applied (% of total steps) + * @default 1 + */ + end_step_percent?: number; + /** + * Resize Mode + * @description The resize mode to use + * @default just_resize + * @enum {string} + */ + resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; }; /** - * String Collection Primitive - * @description A collection of string primitive values + * T2I-Adapter - SD1.5, SDXL + * @description Collects T2I-Adapter info to pass to other nodes. */ - StringCollectionInvocation: { + T2IAdapterInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -20596,461 +23788,530 @@ export type components = { */ use_cache?: boolean; /** - * Collection - * @description The collection of string values - * @default [] + * @description The IP-Adapter image prompt. + * @default null */ - collection?: string[]; + image?: components["schemas"]["ImageField"] | null; + /** + * T2I-Adapter Model + * @description The T2I-Adapter model. + * @default null + */ + t2i_adapter_model?: components["schemas"]["ModelIdentifierField"] | null; + /** + * Weight + * @description The weight given to the T2I-Adapter + * @default 1 + */ + weight?: number | number[]; + /** + * Begin Step Percent + * @description When the T2I-Adapter is first applied (% of total steps) + * @default 0 + */ + begin_step_percent?: number; + /** + * End Step Percent + * @description When the T2I-Adapter is last applied (% of total steps) + * @default 1 + */ + end_step_percent?: number; + /** + * Resize Mode + * @description The resize mode applied to the T2I-Adapter input image so that it matches the target output size. + * @default just_resize + * @enum {string} + */ + resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; /** * type - * @default string_collection + * @default t2i_adapter * @constant */ - type: "string_collection"; + type: "t2i_adapter"; }; - /** - * StringCollectionOutput - * @description Base class for nodes that output a collection of strings - */ - StringCollectionOutput: { + /** T2IAdapterMetadataField */ + T2IAdapterMetadataField: { + /** @description The control image. */ + image: components["schemas"]["ImageField"]; /** - * Collection - * @description The output strings + * @description The control image, after processing. + * @default null */ - collection: string[]; + processed_image?: components["schemas"]["ImageField"] | null; + /** @description The T2I-Adapter model to use. */ + t2i_adapter_model: components["schemas"]["ModelIdentifierField"]; + /** + * Weight + * @description The weight given to the T2I-Adapter + * @default 1 + */ + weight?: number | number[]; + /** + * Begin Step Percent + * @description When the T2I-Adapter is first applied (% of total steps) + * @default 0 + */ + begin_step_percent?: number; + /** + * End Step Percent + * @description When the T2I-Adapter is last applied (% of total steps) + * @default 1 + */ + end_step_percent?: number; + /** + * Resize Mode + * @description The resize mode to use + * @default just_resize + * @enum {string} + */ + resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; + }; + /** T2IAdapterOutput */ + T2IAdapterOutput: { + /** + * T2I Adapter + * @description T2I-Adapter(s) to apply + */ + t2i_adapter: components["schemas"]["T2IAdapterField"]; + /** + * type + * @default t2i_adapter_output + * @constant + */ + type: "t2i_adapter_output"; + }; + /** T2IAdapter_Diffusers_SD1_Config */ + T2IAdapter_Diffusers_SD1_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; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Submodels + * @description Loadable submodels in this model + */ + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; + /** + * Usage Info + * @description Usage information for this model + */ + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Type + * @default t2i_adapter + * @constant + */ + type: "t2i_adapter"; /** - * type - * @default string_collection_output + * Base + * @default sd-1 * @constant */ - type: "string_collection_output"; + base: "sd-1"; }; - /** - * String Generator - * @description Generated a range of strings for use in a batched generation - */ - StringGenerator: { + /** T2IAdapter_Diffusers_SDXL_Config */ + T2IAdapter_Diffusers_SDXL_Config: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Key + * @description A unique key for this model. */ - id: string; + key: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Hash + * @description The hash of the model file(s). */ - is_intermediate?: boolean; + hash: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - use_cache?: boolean; + path: string; /** - * Generator Type - * @description The string generator. + * File Size + * @description The size of the model in bytes. */ - generator: components["schemas"]["StringGeneratorField"]; + file_size: number; /** - * type - * @default string_generator - * @constant + * Name + * @description Name of the model. */ - type: "string_generator"; - }; - /** StringGeneratorField */ - StringGeneratorField: Record; - /** - * StringGeneratorOutput - * @description Base class for nodes that output a collection of strings - */ - StringGeneratorOutput: { + name: string; /** - * Strings - * @description The generated strings + * Description + * @description Model description */ - strings: string[]; + description: string | null; /** - * type - * @default string_generator_output - * @constant + * Source + * @description The original source of the model (path, URL or repo_id). */ - type: "string_generator_output"; - }; - /** - * String Primitive - * @description A string primitive value - */ - StringInvocation: { + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - id: string; + source_api_response: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Cover Image + * @description Url for image to preview model */ - is_intermediate?: boolean; + cover_image: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Submodels + * @description Loadable submodels in this model */ - use_cache?: boolean; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Value - * @description The string value - * @default + * Usage Info + * @description Usage information for this model */ - value?: string; + usage_info: string | null; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** - * type - * @default string + * Format + * @default diffusers * @constant */ - type: "string"; - }; - /** - * String Join - * @description Joins string left to string right - */ - StringJoinInvocation: { + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Type + * @default t2i_adapter + * @constant */ - id: string; + type: "t2i_adapter"; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Base + * @default sdxl + * @constant */ - is_intermediate?: boolean; + base: "sdxl"; + }; + /** T5EncoderField */ + T5EncoderField: { + /** @description Info to load tokenizer submodel */ + tokenizer: components["schemas"]["ModelIdentifierField"]; + /** @description Info to load text_encoder submodel */ + text_encoder: components["schemas"]["ModelIdentifierField"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Loras + * @description LoRAs to apply on model loading */ - use_cache?: boolean; + loras: components["schemas"]["LoRAField"][]; + }; + /** T5Encoder_BnBLLMint8_Config */ + T5Encoder_BnBLLMint8_Config: { /** - * String Left - * @description String Left - * @default + * Key + * @description A unique key for this model. */ - string_left?: string; + key: string; /** - * String Right - * @description String Right - * @default + * Hash + * @description The hash of the model file(s). */ - string_right?: string; + hash: string; /** - * type - * @default string_join - * @constant + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - type: "string_join"; - }; - /** - * String Join Three - * @description Joins string left to string middle to string right - */ - StringJoinThreeInvocation: { + path: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * File Size + * @description The size of the model in bytes. */ - id: string; + file_size: number; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Name + * @description Name of the model. */ - is_intermediate?: boolean; + name: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Description + * @description Model description */ - use_cache?: boolean; + description: string | null; /** - * String Left - * @description String Left - * @default + * Source + * @description The original source of the model (path, URL or repo_id). */ - string_left?: string; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * String Middle - * @description String Middle - * @default + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - string_middle?: string; + source_api_response: string | null; /** - * String Right - * @description String Right - * @default + * Cover Image + * @description Url for image to preview model */ - string_right?: string; + cover_image: string | null; /** - * type - * @default string_join_three - * @constant + * Submodels + * @description Loadable submodels in this model */ - type: "string_join_three"; - }; - /** - * StringOutput - * @description Base class for nodes that output a single string - */ - StringOutput: { + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Value - * @description The output string + * Usage Info + * @description Usage information for this model */ - value: string; + usage_info: string | null; /** - * type - * @default string_output + * Base + * @default any * @constant */ - type: "string_output"; - }; - /** - * StringPosNegOutput - * @description Base class for invocations that output a positive and negative string - */ - StringPosNegOutput: { - /** - * Positive String - * @description Positive string - */ - positive_string: string; + base: "any"; /** - * Negative String - * @description Negative string + * Type + * @default t5_encoder + * @constant */ - negative_string: string; + type: "t5_encoder"; /** - * type - * @default string_pos_neg_output + * Format + * @default bnb_quantized_int8b * @constant */ - type: "string_pos_neg_output"; + format: "bnb_quantized_int8b"; }; - /** - * String Replace - * @description Replaces the search string with the replace string - */ - StringReplaceInvocation: { + /** T5Encoder_T5Encoder_Config */ + T5Encoder_T5Encoder_Config: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Key + * @description A unique key for this model. */ - id: string; + key: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Hash + * @description The hash of the model file(s). */ - is_intermediate?: boolean; + hash: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - use_cache?: boolean; + path: string; /** - * String - * @description String to work on - * @default + * File Size + * @description The size of the model in bytes. */ - string?: string; + file_size: number; /** - * Search String - * @description String to search for - * @default + * Name + * @description Name of the model. */ - search_string?: string; + name: string; /** - * Replace String - * @description String to replace the search - * @default + * Description + * @description Model description */ - replace_string?: string; + description: string | null; /** - * Use Regex - * @description Use search string as a regex expression (non regex is case insensitive) - * @default false + * Source + * @description The original source of the model (path, URL or repo_id). */ - use_regex?: boolean; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * type - * @default string_replace - * @constant + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - type: "string_replace"; - }; - /** - * String Split - * @description Splits string into two strings, based on the first occurance of the delimiter. The delimiter will be removed from the string - */ - StringSplitInvocation: { + source_api_response: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Cover Image + * @description Url for image to preview model */ - id: string; + cover_image: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Submodels + * @description Loadable submodels in this model */ - is_intermediate?: boolean; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Usage Info + * @description Usage information for this model */ - use_cache?: boolean; + usage_info: string | null; /** - * String - * @description String to split - * @default + * Base + * @default any + * @constant */ - string?: string; + base: "any"; /** - * Delimiter - * @description Delimiter to spilt with. blank will split on the first whitespace - * @default + * Type + * @default t5_encoder + * @constant */ - delimiter?: string; + type: "t5_encoder"; /** - * type - * @default string_split + * Format + * @default t5_encoder * @constant */ - type: "string_split"; + format: "t5_encoder"; }; - /** - * String Split Negative - * @description Splits string into two strings, inside [] goes into negative string everthing else goes into positive string. Each [ and ] character is replaced with a space - */ - StringSplitNegInvocation: { - /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. - */ - id: string; + /** TBLR */ + TBLR: { + /** Top */ + top: number; + /** Bottom */ + bottom: number; + /** Left */ + left: number; + /** Right */ + right: number; + }; + /** TI_File_SD1_Config */ + TI_File_SD1_Config: { /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Key + * @description A unique key for this model. */ - is_intermediate?: boolean; + key: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Hash + * @description The hash of the model file(s). */ - use_cache?: boolean; + hash: string; /** - * String - * @description String to split - * @default + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - string?: string; + path: string; /** - * type - * @default string_split_neg - * @constant + * File Size + * @description The size of the model in bytes. */ - type: "string_split_neg"; - }; - /** StylePresetRecordWithImage */ - StylePresetRecordWithImage: { + file_size: number; /** * Name - * @description The name of the style preset. + * @description Name of the model. */ name: string; - /** @description The preset data */ - preset_data: components["schemas"]["PresetData"]; - /** @description The type of style preset */ - type: components["schemas"]["PresetType"]; /** - * Id - * @description The style preset ID. + * Description + * @description Model description */ - id: string; + description: string | null; /** - * Image - * @description The path for image + * Source + * @description The original source of the model (path, URL or repo_id). */ - image: string | null; - }; - /** - * SubModelType - * @description Submodel type. - * @enum {string} - */ - SubModelType: "unet" | "transformer" | "text_encoder" | "text_encoder_2" | "text_encoder_3" | "tokenizer" | "tokenizer_2" | "tokenizer_3" | "vae" | "vae_decoder" | "vae_encoder" | "scheduler" | "safety_checker"; - /** SubmodelDefinition */ - SubmodelDefinition: { - /** Path Or Prefix */ - path_or_prefix: string; - model_type: components["schemas"]["ModelType"]; - /** Variant */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | null; - }; - /** - * Subtract Integers - * @description Subtracts two numbers - */ - SubtractInvocation: { + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - id: string; + source_api_response: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Cover Image + * @description Url for image to preview model */ - is_intermediate?: boolean; + cover_image: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Submodels + * @description Loadable submodels in this model */ - use_cache?: boolean; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * A - * @description The first number - * @default 0 + * Usage Info + * @description Usage information for this model */ - a?: number; + usage_info: string | null; /** - * B - * @description The second number - * @default 0 + * Type + * @default embedding + * @constant */ - b?: number; + type: "embedding"; /** - * type - * @default sub + * Format + * @default embedding_file + * @constant + */ + format: "embedding_file"; + /** + * Base + * @default sd-1 * @constant */ - type: "sub"; + base: "sd-1"; }; - /** - * T2IAdapterConfig - * @description Model config for T2I. - */ - T2IAdapterConfig: { + /** TI_File_SD2_Config */ + TI_File_SD2_Config: { /** * Key * @description A unique key for this model. @@ -21110,177 +24371,27 @@ export type components = { * @description Usage information for this model */ usage_info: string | null; - default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; - /** - * Format - * @default diffusers - * @constant - */ - format: "diffusers"; - /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; - /** - * Base - * @enum {string} - */ - base: "sd-1" | "sd-2" | "sdxl"; /** * Type - * @default t2i_adapter + * @default embedding * @constant */ - type: "t2i_adapter"; - }; - /** T2IAdapterField */ - T2IAdapterField: { - /** @description The T2I-Adapter image prompt. */ - image: components["schemas"]["ImageField"]; - /** @description The T2I-Adapter model to use. */ - t2i_adapter_model: components["schemas"]["ModelIdentifierField"]; - /** - * Weight - * @description The weight given to the T2I-Adapter - * @default 1 - */ - weight?: number | number[]; - /** - * Begin Step Percent - * @description When the T2I-Adapter is first applied (% of total steps) - * @default 0 - */ - begin_step_percent?: number; - /** - * End Step Percent - * @description When the T2I-Adapter is last applied (% of total steps) - * @default 1 - */ - end_step_percent?: number; - /** - * Resize Mode - * @description The resize mode to use - * @default just_resize - * @enum {string} - */ - resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; - }; - /** - * T2I-Adapter - SD1.5, SDXL - * @description Collects T2I-Adapter info to pass to other nodes. - */ - T2IAdapterInvocation: { - /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. - */ - id: string; - /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false - */ - is_intermediate?: boolean; - /** - * Use Cache - * @description Whether or not to use the cache - * @default true - */ - use_cache?: boolean; - /** - * @description The IP-Adapter image prompt. - * @default null - */ - image?: components["schemas"]["ImageField"] | null; - /** - * T2I-Adapter Model - * @description The T2I-Adapter model. - * @default null - */ - t2i_adapter_model?: components["schemas"]["ModelIdentifierField"] | null; - /** - * Weight - * @description The weight given to the T2I-Adapter - * @default 1 - */ - weight?: number | number[]; - /** - * Begin Step Percent - * @description When the T2I-Adapter is first applied (% of total steps) - * @default 0 - */ - begin_step_percent?: number; - /** - * End Step Percent - * @description When the T2I-Adapter is last applied (% of total steps) - * @default 1 - */ - end_step_percent?: number; - /** - * Resize Mode - * @description The resize mode applied to the T2I-Adapter input image so that it matches the target output size. - * @default just_resize - * @enum {string} - */ - resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; + type: "embedding"; /** - * type - * @default t2i_adapter + * Format + * @default embedding_file * @constant */ - type: "t2i_adapter"; - }; - /** T2IAdapterMetadataField */ - T2IAdapterMetadataField: { - /** @description The control image. */ - image: components["schemas"]["ImageField"]; - /** - * @description The control image, after processing. - * @default null - */ - processed_image?: components["schemas"]["ImageField"] | null; - /** @description The T2I-Adapter model to use. */ - t2i_adapter_model: components["schemas"]["ModelIdentifierField"]; - /** - * Weight - * @description The weight given to the T2I-Adapter - * @default 1 - */ - weight?: number | number[]; - /** - * Begin Step Percent - * @description When the T2I-Adapter is first applied (% of total steps) - * @default 0 - */ - begin_step_percent?: number; - /** - * End Step Percent - * @description When the T2I-Adapter is last applied (% of total steps) - * @default 1 - */ - end_step_percent?: number; - /** - * Resize Mode - * @description The resize mode to use - * @default just_resize - * @enum {string} - */ - resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; - }; - /** T2IAdapterOutput */ - T2IAdapterOutput: { - /** - * T2I Adapter - * @description T2I-Adapter(s) to apply - */ - t2i_adapter: components["schemas"]["T2IAdapterField"]; + format: "embedding_file"; /** - * type - * @default t2i_adapter_output + * Base + * @default sd-2 * @constant */ - type: "t2i_adapter_output"; + base: "sd-2"; }; - /** T5EncoderBnbQuantizedLlmInt8bConfig */ - T5EncoderBnbQuantizedLlmInt8bConfig: { + /** TI_File_SDXL_Config */ + TI_File_SDXL_Config: { /** * Key * @description A unique key for this model. @@ -21341,26 +24452,26 @@ export type components = { */ usage_info: string | null; /** - * Base - * @default any + * Type + * @default embedding * @constant */ - base: "any"; + type: "embedding"; /** - * Type - * @default t5_encoder + * Format + * @default embedding_file * @constant */ - type: "t5_encoder"; + format: "embedding_file"; /** - * Format - * @default bnb_quantized_int8b + * Base + * @default sdxl * @constant */ - format: "bnb_quantized_int8b"; + base: "sdxl"; }; - /** T5EncoderConfig */ - T5EncoderConfig: { + /** TI_Folder_SD1_Config */ + TI_Folder_SD1_Config: { /** * Key * @description A unique key for this model. @@ -21420,64 +24531,27 @@ export type components = { * @description Usage information for this model */ usage_info: string | null; - /** - * Base - * @default any - * @constant - */ - base: "any"; /** * Type - * @default t5_encoder + * @default embedding * @constant */ - type: "t5_encoder"; + type: "embedding"; /** * Format - * @default t5_encoder + * @default embedding_folder * @constant */ - format: "t5_encoder"; - }; - /** T5EncoderField */ - T5EncoderField: { - /** @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"][]; - }; - /** TBLR */ - TBLR: { - /** Top */ - top: number; - /** Bottom */ - bottom: number; - /** Left */ - left: number; - /** Right */ - right: number; - }; - /** - * TensorField - * @description A tensor primitive field. - */ - TensorField: { + format: "embedding_folder"; /** - * Tensor Name - * @description The name of a tensor. + * Base + * @default sd-1 + * @constant */ - tensor_name: string; + base: "sd-1"; }; - /** - * TextualInversionFileConfig - * @description Model config for textual inversion embeddings. - */ - TextualInversionFileConfig: { + /** TI_Folder_SD2_Config */ + TI_Folder_SD2_Config: { /** * Key * @description A unique key for this model. @@ -21537,11 +24611,6 @@ export type components = { * @description Usage information for this model */ usage_info: string | null; - /** - * Base - * @enum {string} - */ - base: "sd-1" | "sd-2" | "sdxl"; /** * Type * @default embedding @@ -21550,16 +24619,19 @@ export type components = { type: "embedding"; /** * Format - * @default embedding_file + * @default embedding_folder * @constant */ - format: "embedding_file"; + format: "embedding_folder"; + /** + * Base + * @default sd-2 + * @constant + */ + base: "sd-2"; }; - /** - * TextualInversionFolderConfig - * @description Model config for textual inversion embeddings. - */ - TextualInversionFolderConfig: { + /** TI_Folder_SDXL_Config */ + TI_Folder_SDXL_Config: { /** * Key * @description A unique key for this model. @@ -21619,11 +24691,6 @@ export type components = { * @description Usage information for this model */ usage_info: string | null; - /** - * Base - * @enum {string} - */ - base: "sd-1" | "sd-2" | "sdxl"; /** * Type * @default embedding @@ -21635,7 +24702,24 @@ export type components = { * @default embedding_folder * @constant */ - format: "embedding_folder"; + format: "embedding_folder"; + /** + * Base + * @default sdxl + * @constant + */ + base: "sdxl"; + }; + /** + * TensorField + * @description A tensor primitive field. + */ + TensorField: { + /** + * Tensor Name + * @description The name of a tensor. + */ + tensor_name: string; }; /** Tile */ Tile: { @@ -22019,8 +25103,11 @@ export type components = { */ token: string; }; - /** UnknownModelConfig */ - UnknownModelConfig: { + /** + * Unknown_Config + * @description Model config for unknown models, used as a fallback when we cannot identify a model. + */ + Unknown_Config: { /** * Key * @description A unique key for this model. @@ -22153,52 +25240,291 @@ export type components = { * @default unsharp_mask * @constant */ - type: "unsharp_mask"; - }; - /** UnstarredImagesResult */ - UnstarredImagesResult: { + type: "unsharp_mask"; + }; + /** UnstarredImagesResult */ + UnstarredImagesResult: { + /** + * Affected Boards + * @description The ids of boards affected by the delete operation + */ + affected_boards: string[]; + /** + * Unstarred Images + * @description The names of the images that were unstarred + */ + unstarred_images: string[]; + }; + /** UnstarredVideosResult */ + UnstarredVideosResult: { + /** + * Affected Boards + * @description The ids of boards affected by the delete operation + */ + affected_boards: string[]; + /** + * Unstarred Videos + * @description The ids of the videos that were unstarred + */ + unstarred_videos: string[]; + }; + /** Upscaler */ + Upscaler: { + /** + * Upscaling Method + * @description Name of upscaling method + */ + upscaling_method: string; + /** + * Upscaling Models + * @description List of upscaling models for this method + */ + upscaling_models: string[]; + }; + /** VAEField */ + VAEField: { + /** @description Info to load vae submodel */ + vae: components["schemas"]["ModelIdentifierField"]; + /** + * Seamless Axes + * @description Axes("x" and "y") to which apply seamless + */ + seamless_axes?: string[]; + }; + /** + * VAE Model - SD1.5, SD2, SDXL, SD3, FLUX + * @description Loads a VAE model, outputting a VaeLoaderOutput + */ + VAELoaderInvocation: { + /** + * 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; + /** + * VAE + * @description VAE model to load + * @default null + */ + vae_model?: components["schemas"]["ModelIdentifierField"] | null; + /** + * type + * @default vae_loader + * @constant + */ + type: "vae_loader"; + }; + /** + * VAEOutput + * @description Base class for invocations that output a VAE field + */ + VAEOutput: { + /** + * VAE + * @description VAE + */ + vae: components["schemas"]["VAEField"]; + /** + * type + * @default vae_output + * @constant + */ + type: "vae_output"; + }; + /** VAE_Checkpoint_FLUX_Config */ + VAE_Checkpoint_FLUX_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; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Submodels + * @description Loadable submodels in this model + */ + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; + /** + * Usage Info + * @description Usage information for this model + */ + usage_info: string | null; + /** + * Config Path + * @description Path to the config for this model, if any. + */ + config_path: string | null; + /** + * Converted At + * @description When this model was last converted to diffusers + */ + converted_at: number | null; + /** + * Type + * @default vae + * @constant + */ + type: "vae"; + /** + * Format + * @default checkpoint + * @constant + */ + format: "checkpoint"; + /** + * Base + * @default flux + * @constant + */ + base: "flux"; + }; + /** VAE_Checkpoint_SD1_Config */ + VAE_Checkpoint_SD1_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; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Submodels + * @description Loadable submodels in this model + */ + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; /** - * Affected Boards - * @description The ids of boards affected by the delete operation + * Usage Info + * @description Usage information for this model */ - affected_boards: string[]; + usage_info: string | null; /** - * Unstarred Images - * @description The names of the images that were unstarred + * Config Path + * @description Path to the config for this model, if any. */ - unstarred_images: string[]; - }; - /** UnstarredVideosResult */ - UnstarredVideosResult: { + config_path: string | null; /** - * Affected Boards - * @description The ids of boards affected by the delete operation + * Converted At + * @description When this model was last converted to diffusers */ - affected_boards: string[]; + converted_at: number | null; /** - * Unstarred Videos - * @description The ids of the videos that were unstarred + * Type + * @default vae + * @constant */ - unstarred_videos: string[]; - }; - /** Upscaler */ - Upscaler: { + type: "vae"; /** - * Upscaling Method - * @description Name of upscaling method + * Format + * @default checkpoint + * @constant */ - upscaling_method: string; + format: "checkpoint"; /** - * Upscaling Models - * @description List of upscaling models for this method + * Base + * @default sd-1 + * @constant */ - upscaling_models: string[]; + base: "sd-1"; }; - /** - * VAECheckpointConfig - * @description Model config for standalone VAE models. - */ - VAECheckpointConfig: { + /** VAE_Checkpoint_SD2_Config */ + VAE_Checkpoint_SD2_Config: { /** * Key * @description A unique key for this model. @@ -22268,11 +25594,6 @@ export type components = { * @description When this model was last converted to diffusers */ converted_at: number | null; - /** - * Base - * @enum {string} - */ - base: "sd-1" | "sd-2" | "sdxl" | "flux"; /** * Type * @default vae @@ -22285,12 +25606,15 @@ export type components = { * @constant */ format: "checkpoint"; + /** + * Base + * @default sd-2 + * @constant + */ + base: "sd-2"; }; - /** - * VAEDiffusersConfig - * @description Model config for standalone VAE models (diffusers version). - */ - VAEDiffusersConfig: { + /** VAE_Checkpoint_SDXL_Config */ + VAE_Checkpoint_SDXL_Config: { /** * Key * @description A unique key for this model. @@ -22351,119 +25675,118 @@ export type components = { */ usage_info: string | null; /** - * Format - * @default diffusers - * @constant + * Config Path + * @description Path to the config for this model, if any. */ - format: "diffusers"; - /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + config_path: string | null; /** - * Base - * @enum {string} + * Converted At + * @description When this model was last converted to diffusers */ - base: "sd-1" | "sdxl"; + converted_at: number | null; /** * Type * @default vae * @constant */ type: "vae"; - }; - /** VAEField */ - VAEField: { - /** @description Info to load vae submodel */ - vae: components["schemas"]["ModelIdentifierField"]; /** - * Seamless Axes - * @description Axes("x" and "y") to which apply seamless + * Format + * @default checkpoint + * @constant */ - seamless_axes?: string[]; + format: "checkpoint"; + /** + * Base + * @default sdxl + * @constant + */ + base: "sdxl"; }; - /** - * VAE Model - SD1.5, SD2, SDXL, SD3, FLUX - * @description Loads a VAE model, outputting a VaeLoaderOutput - */ - VAELoaderInvocation: { + /** VAE_Diffusers_SD1_Config */ + VAE_Diffusers_SD1_Config: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Key + * @description A unique key for this model. */ - id: string; + key: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Hash + * @description The hash of the model file(s). */ - is_intermediate?: boolean; + hash: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - use_cache?: boolean; + path: string; /** - * VAE - * @description VAE model to load - * @default null + * File Size + * @description The size of the model in bytes. */ - vae_model?: components["schemas"]["ModelIdentifierField"] | null; + file_size: number; /** - * type - * @default vae_loader - * @constant + * Name + * @description Name of the model. */ - type: "vae_loader"; - }; - /** - * VAEOutput - * @description Base class for invocations that output a VAE field - */ - VAEOutput: { + name: string; /** - * VAE - * @description VAE + * Description + * @description Model description */ - vae: components["schemas"]["VAEField"]; + description: string | null; /** - * type - * @default vae_output - * @constant + * Source + * @description The original source of the model (path, URL or repo_id). */ - type: "vae_output"; - }; - /** ValidationError */ - ValidationError: { - /** Location */ - loc: (string | number)[]; - /** Message */ - msg: string; - /** Error Type */ - type: string; - }; - /** ValidationRunData */ - ValidationRunData: { + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Workflow Id - * @description The id of the workflow being published. + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - workflow_id: string; + source_api_response: string | null; /** - * Input Fields - * @description The input fields for the published workflow + * Cover Image + * @description Url for image to preview model */ - input_fields: components["schemas"]["FieldIdentifier"][]; + cover_image: string | null; /** - * Output Fields - * @description The output fields for the published workflow + * Submodels + * @description Loadable submodels in this model */ - output_fields: components["schemas"]["FieldIdentifier"][]; + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; + /** + * Usage Info + * @description Usage information for this model + */ + usage_info: string | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; + /** + * Type + * @default vae + * @constant + */ + type: "vae"; + /** + * Base + * @default sd-1 + * @constant + */ + base: "sd-1"; }; - /** - * VideoApiModelConfig - * @description Model config for API-based video models. - */ - VideoApiModelConfig: { + /** VAE_Diffusers_SDXL_Config */ + VAE_Diffusers_SDXL_Config: { /** * Key * @description A unique key for this model. @@ -22523,30 +25846,53 @@ export type components = { * @description Usage information for this model */ usage_info: string | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"] | null; /** * Type - * @default video + * @default vae * @constant */ - type: "video"; + type: "vae"; /** * Base - * @enum {string} + * @default sdxl + * @constant */ - base: "veo3" | "runway"; + base: "sdxl"; + }; + /** ValidationError */ + ValidationError: { + /** Location */ + loc: (string | number)[]; + /** Message */ + msg: string; + /** Error Type */ + type: string; + }; + /** ValidationRunData */ + ValidationRunData: { /** - * Format - * @default api - * @constant + * Workflow Id + * @description The id of the workflow being published. */ - format: "api"; + workflow_id: string; /** - * Trigger Phrases - * @description Set of trigger phrases for this model + * Input Fields + * @description The input fields for the published workflow */ - trigger_phrases: string[] | null; - /** @description Default settings for this model */ - default_settings: components["schemas"]["MainModelDefaultSettings"] | null; + input_fields: components["schemas"]["FieldIdentifier"][]; + /** + * Output Fields + * @description The output fields for the published workflow + */ + output_fields: components["schemas"]["FieldIdentifier"][]; }; /** * VideoDTO @@ -23114,7 +26460,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"] | components["schemas"]["MainBnbQuantized4bCheckpointConfig"] | components["schemas"]["MainGGUFCheckpointConfig"] | components["schemas"]["VAEDiffusersConfig"] | components["schemas"]["VAECheckpointConfig"] | components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"] | components["schemas"]["LoRALyCORISConfig"] | components["schemas"]["LoRAOmiConfig"] | components["schemas"]["ControlLoRALyCORISConfig"] | components["schemas"]["ControlLoRADiffusersConfig"] | components["schemas"]["LoRADiffusersConfig"] | components["schemas"]["T5EncoderConfig"] | components["schemas"]["T5EncoderBnbQuantizedLlmInt8bConfig"] | components["schemas"]["TextualInversionFileConfig"] | components["schemas"]["TextualInversionFolderConfig"] | components["schemas"]["IPAdapterInvokeAIConfig"] | components["schemas"]["IPAdapterCheckpointConfig"] | components["schemas"]["T2IAdapterConfig"] | components["schemas"]["SpandrelImageToImageConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["CLIPLEmbedDiffusersConfig"] | components["schemas"]["CLIPGEmbedDiffusersConfig"] | components["schemas"]["SigLIPConfig"] | components["schemas"]["FluxReduxConfig"] | components["schemas"]["LlavaOnevisionConfig"] | components["schemas"]["ApiModelConfig"] | components["schemas"]["VideoApiModelConfig"] | components["schemas"]["UnknownModelConfig"]; + "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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Validation Error */ @@ -23146,25 +26492,7 @@ export interface operations { [name: string]: unknown; }; content: { - /** @example { - * "path": "string", - * "name": "string", - * "base": "sd-1", - * "type": "main", - * "format": "checkpoint", - * "config_path": "string", - * "key": "string", - * "hash": "string", - * "file_size": 1, - * "description": "string", - * "source": "string", - * "converted_at": 0, - * "variant": "normal", - * "prediction_type": "epsilon", - * "repo_variant": "fp16", - * "upcast_attention": false - * } */ - "application/json": components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"] | components["schemas"]["MainBnbQuantized4bCheckpointConfig"] | components["schemas"]["MainGGUFCheckpointConfig"] | components["schemas"]["VAEDiffusersConfig"] | components["schemas"]["VAECheckpointConfig"] | components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"] | components["schemas"]["LoRALyCORISConfig"] | components["schemas"]["LoRAOmiConfig"] | components["schemas"]["ControlLoRALyCORISConfig"] | components["schemas"]["ControlLoRADiffusersConfig"] | components["schemas"]["LoRADiffusersConfig"] | components["schemas"]["T5EncoderConfig"] | components["schemas"]["T5EncoderBnbQuantizedLlmInt8bConfig"] | components["schemas"]["TextualInversionFileConfig"] | components["schemas"]["TextualInversionFolderConfig"] | components["schemas"]["IPAdapterInvokeAIConfig"] | components["schemas"]["IPAdapterCheckpointConfig"] | components["schemas"]["T2IAdapterConfig"] | components["schemas"]["SpandrelImageToImageConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["CLIPLEmbedDiffusersConfig"] | components["schemas"]["CLIPGEmbedDiffusersConfig"] | components["schemas"]["SigLIPConfig"] | components["schemas"]["FluxReduxConfig"] | components["schemas"]["LlavaOnevisionConfig"] | components["schemas"]["ApiModelConfig"] | components["schemas"]["VideoApiModelConfig"] | components["schemas"]["UnknownModelConfig"]; + "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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ @@ -23251,25 +26579,7 @@ export interface operations { [name: string]: unknown; }; content: { - /** @example { - * "path": "string", - * "name": "string", - * "base": "sd-1", - * "type": "main", - * "format": "checkpoint", - * "config_path": "string", - * "key": "string", - * "hash": "string", - * "file_size": 1, - * "description": "string", - * "source": "string", - * "converted_at": 0, - * "variant": "normal", - * "prediction_type": "epsilon", - * "repo_variant": "fp16", - * "upcast_attention": false - * } */ - "application/json": components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"] | components["schemas"]["MainBnbQuantized4bCheckpointConfig"] | components["schemas"]["MainGGUFCheckpointConfig"] | components["schemas"]["VAEDiffusersConfig"] | components["schemas"]["VAECheckpointConfig"] | components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"] | components["schemas"]["LoRALyCORISConfig"] | components["schemas"]["LoRAOmiConfig"] | components["schemas"]["ControlLoRALyCORISConfig"] | components["schemas"]["ControlLoRADiffusersConfig"] | components["schemas"]["LoRADiffusersConfig"] | components["schemas"]["T5EncoderConfig"] | components["schemas"]["T5EncoderBnbQuantizedLlmInt8bConfig"] | components["schemas"]["TextualInversionFileConfig"] | components["schemas"]["TextualInversionFolderConfig"] | components["schemas"]["IPAdapterInvokeAIConfig"] | components["schemas"]["IPAdapterCheckpointConfig"] | components["schemas"]["T2IAdapterConfig"] | components["schemas"]["SpandrelImageToImageConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["CLIPLEmbedDiffusersConfig"] | components["schemas"]["CLIPGEmbedDiffusersConfig"] | components["schemas"]["SigLIPConfig"] | components["schemas"]["FluxReduxConfig"] | components["schemas"]["LlavaOnevisionConfig"] | components["schemas"]["ApiModelConfig"] | components["schemas"]["VideoApiModelConfig"] | components["schemas"]["UnknownModelConfig"]; + "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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ @@ -23765,25 +27075,7 @@ export interface operations { [name: string]: unknown; }; content: { - /** @example { - * "path": "string", - * "name": "string", - * "base": "sd-1", - * "type": "main", - * "format": "checkpoint", - * "config_path": "string", - * "key": "string", - * "hash": "string", - * "file_size": 1, - * "description": "string", - * "source": "string", - * "converted_at": 0, - * "variant": "normal", - * "prediction_type": "epsilon", - * "repo_variant": "fp16", - * "upcast_attention": false - * } */ - "application/json": components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"] | components["schemas"]["MainBnbQuantized4bCheckpointConfig"] | components["schemas"]["MainGGUFCheckpointConfig"] | components["schemas"]["VAEDiffusersConfig"] | components["schemas"]["VAECheckpointConfig"] | components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"] | components["schemas"]["LoRALyCORISConfig"] | components["schemas"]["LoRAOmiConfig"] | components["schemas"]["ControlLoRALyCORISConfig"] | components["schemas"]["ControlLoRADiffusersConfig"] | components["schemas"]["LoRADiffusersConfig"] | components["schemas"]["T5EncoderConfig"] | components["schemas"]["T5EncoderBnbQuantizedLlmInt8bConfig"] | components["schemas"]["TextualInversionFileConfig"] | components["schemas"]["TextualInversionFolderConfig"] | components["schemas"]["IPAdapterInvokeAIConfig"] | components["schemas"]["IPAdapterCheckpointConfig"] | components["schemas"]["T2IAdapterConfig"] | components["schemas"]["SpandrelImageToImageConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["CLIPLEmbedDiffusersConfig"] | components["schemas"]["CLIPGEmbedDiffusersConfig"] | components["schemas"]["SigLIPConfig"] | components["schemas"]["FluxReduxConfig"] | components["schemas"]["LlavaOnevisionConfig"] | components["schemas"]["ApiModelConfig"] | components["schemas"]["VideoApiModelConfig"] | components["schemas"]["UnknownModelConfig"]; + "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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ @@ -25639,11 +28931,6 @@ export interface operations { [name: string]: unknown; }; content: { - /** @example [ - * "15e9eb28-8cfe-47c9-b610-37907a79fc3c", - * "71272e82-0e5f-46d5-bca9-9a61f4bd8a82", - * "a5d7cd49-1b98-4534-a475-aeee4ccf5fa2" - * ] */ "application/json": string[]; }; }; @@ -25782,15 +29069,6 @@ export interface operations { [name: string]: unknown; }; content: { - /** @example [ - * "ca562b14-995e-4a42-90c1-9528f1a5921d", - * "cc0c2b8a-c62e-41d6-878e-cc74dde5ca8f", - * "18ca7649-6a9e-47d5-bc17-41ab1e8cec81", - * "7c12d1b2-0ef9-4bec-ba55-797b2d8f2ee1", - * "c382eaa3-0e28-4ab0-9446-408667699aeb", - * "71272e82-0e5f-46d5-bca9-9a61f4bd8a82", - * "a5d7cd49-1b98-4534-a475-aeee4ccf5fa2" - * ] */ "application/json": string[]; }; }; From cdc9f02c96a448e0261799bdc7876d6432391387 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 14:47:53 +1100 Subject: [PATCH 49/62] refactor(mm): remove legacy probe, new configs dir structure, update imports --- invokeai/app/api/routers/model_manager.py | 6 +- invokeai/app/invocations/cogview4_denoise.py | 2 +- .../app/invocations/cogview4_model_loader.py | 3 +- .../app/invocations/create_gradient_mask.py | 11 +- invokeai/app/invocations/denoise_latents.py | 2 +- invokeai/app/invocations/flux_ip_adapter.py | 4 +- invokeai/app/invocations/flux_model_loader.py | 4 +- invokeai/app/invocations/flux_redux.py | 4 +- invokeai/app/invocations/flux_text_encoder.py | 2 +- invokeai/app/invocations/flux_vae_encode.py | 2 +- invokeai/app/invocations/image_to_latents.py | 2 +- invokeai/app/invocations/ip_adapter.py | 4 +- invokeai/app/invocations/model.py | 4 +- invokeai/app/invocations/sd3_denoise.py | 2 +- invokeai/app/services/events/events_base.py | 4 +- invokeai/app/services/events/events_common.py | 4 +- .../model_install/model_install_common.py | 2 +- .../model_install/model_install_default.py | 9 +- .../services/model_load/model_load_base.py | 2 +- .../services/model_load/model_load_default.py | 2 +- .../app/services/model_manager/__init__.py | 2 - .../model_records/model_records_base.py | 10 +- .../model_records/model_records_sql.py | 27 +- .../model_relationships_default.py | 2 +- .../app/services/shared/invocation_context.py | 6 +- .../migrations/migration_22.py | 2 +- invokeai/app/util/custom_openapi.py | 2 +- .../backend/flux/flux_state_dict_utils.py | 7 +- invokeai/backend/model_manager/__init__.py | 45 - invokeai/backend/model_manager/config.py | 2584 ----- .../backend/model_manager/configs/base.py | 2 +- .../model_manager/configs/controlnet.py | 39 +- .../backend/model_manager/configs/factory.py | 48 +- .../backend/model_manager/configs/lora.py | 2 +- .../backend/model_manager/configs/main.py | 4 +- .../model_manager/configs/t2i_adapter.py | 2 +- .../backend/model_manager/legacy_probe.py | 1034 -- .../backend/model_manager/load/load_base.py | 2 +- .../model_manager/load/load_default.py | 5 +- .../load/model_loader_registry.py | 6 +- .../load/model_loaders/clip_vision.py | 6 +- .../load/model_loaders/cogview4.py | 7 +- .../load/model_loaders/controlnet.py | 6 +- .../model_manager/load/model_loaders/flux.py | 19 +- .../load/model_loaders/generic_diffusers.py | 11 +- .../load/model_loaders/ip_adapter.py | 2 +- .../load/model_loaders/llava_onevision.py | 4 +- .../model_manager/load/model_loaders/lora.py | 2 +- .../model_manager/load/model_loaders/onnx.py | 2 +- .../load/model_loaders/sig_lip.py | 4 +- .../model_loaders/spandrel_image_to_image.py | 4 +- .../load/model_loaders/stable_diffusion.py | 7 +- .../load/model_loaders/textual_inversion.py | 2 +- .../model_manager/load/model_loaders/vae.py | 3 +- .../util/lora_metadata_extractor.py | 3 +- invokeai/backend/util/test_utils.py | 3 +- .../components/ParamDenoisingStrength.tsx | 7 +- .../controlLayers/store/validators.ts | 2 +- .../subpanels/ModelPanel/ModelView.tsx | 11 +- .../nodes/util/graph/generation/Graph.test.ts | 2 +- .../graph/generation/addControlAdapters.ts | 6 +- .../util/graph/generation/buildFLUXGraph.ts | 2 +- .../nodes/util/graph/graphBuilderUtils.ts | 2 +- .../frontend/web/src/services/api/schema.ts | 9854 ++++++++--------- .../frontend/web/src/services/api/types.ts | 7 +- scripts/classify-model.py | 2 +- tests/test_model_probe.py | 2 +- 67 files changed, 4935 insertions(+), 8953 deletions(-) delete mode 100644 invokeai/backend/model_manager/config.py delete mode 100644 invokeai/backend/model_manager/legacy_probe.py diff --git a/invokeai/app/api/routers/model_manager.py b/invokeai/app/api/routers/model_manager.py index 84db65252e1..7add1d09cf2 100644 --- a/invokeai/app/api/routers/model_manager.py +++ b/invokeai/app/api/routers/model_manager.py @@ -28,9 +28,8 @@ UnknownModelException, ) from invokeai.app.util.suppress_output import SuppressOutput -from invokeai.backend.model_manager import BaseModelType, ModelFormat, ModelType -from invokeai.backend.model_manager.config import ( - AnyModelConfig, +from invokeai.backend.model_manager.configs.factory import AnyModelConfig +from invokeai.backend.model_manager.configs.main import ( Main_Checkpoint_SD1_Config, Main_Checkpoint_SD2_Config, Main_Checkpoint_SDXL_Config, @@ -47,6 +46,7 @@ StarterModelBundle, StarterModelWithoutDependencies, ) +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType model_manager_router = APIRouter(prefix="/v2/models", tags=["model_manager"]) diff --git a/invokeai/app/invocations/cogview4_denoise.py b/invokeai/app/invocations/cogview4_denoise.py index c0b962ba31d..070d8a34783 100644 --- a/invokeai/app/invocations/cogview4_denoise.py +++ b/invokeai/app/invocations/cogview4_denoise.py @@ -22,7 +22,7 @@ from invokeai.app.invocations.primitives import LatentsOutput from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.backend.flux.sampling_utils import clip_timestep_schedule_fractional -from invokeai.backend.model_manager.config import BaseModelType +from invokeai.backend.model_manager.taxonomy import BaseModelType 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 CogView4ConditioningInfo diff --git a/invokeai/app/invocations/cogview4_model_loader.py b/invokeai/app/invocations/cogview4_model_loader.py index 9db4f3c0537..fbafcd345fd 100644 --- a/invokeai/app/invocations/cogview4_model_loader.py +++ b/invokeai/app/invocations/cogview4_model_loader.py @@ -13,8 +13,7 @@ VAEField, ) from invokeai.app.services.shared.invocation_context import InvocationContext -from invokeai.backend.model_manager.config import SubModelType -from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType, SubModelType @invocation_output("cogview4_model_loader_output") diff --git a/invokeai/app/invocations/create_gradient_mask.py b/invokeai/app/invocations/create_gradient_mask.py index f6e046d096e..8a7e7c52317 100644 --- a/invokeai/app/invocations/create_gradient_mask.py +++ b/invokeai/app/invocations/create_gradient_mask.py @@ -20,9 +20,7 @@ from invokeai.app.invocations.image_to_latents import ImageToLatentsInvocation from invokeai.app.invocations.model import UNetField, VAEField from invokeai.app.services.shared.invocation_context import InvocationContext -from invokeai.backend.model_manager import LoadedModel -from invokeai.backend.model_manager.config import Main_Config_Base -from invokeai.backend.model_manager.taxonomy import ModelVariantType +from invokeai.backend.model_manager.taxonomy import FluxVariantType, ModelType, ModelVariantType from invokeai.backend.stable_diffusion.diffusers_pipeline import image_resized_to_grid_as_tensor @@ -182,10 +180,11 @@ def invoke(self, context: InvocationContext) -> GradientMaskOutput: if self.unet is not None and self.vae is not None and self.image is not None: # all three fields must be present at the same time main_model_config = context.models.get_config(self.unet.unet.key) - assert isinstance(main_model_config, Main_Config_Base) - if main_model_config.variant is ModelVariantType.Inpaint: + assert main_model_config.type is ModelType.Main + variant = getattr(main_model_config, "variant", None) + if variant is ModelVariantType.Inpaint or variant is FluxVariantType.DevFill: mask = dilated_mask_tensor - vae_info: LoadedModel = context.models.load(self.vae.vae) + vae_info = context.models.load(self.vae.vae) image = context.images.get_pil(self.image.image_name) image_tensor = image_resized_to_grid_as_tensor(image.convert("RGB")) if image_tensor.dim() == 3: diff --git a/invokeai/app/invocations/denoise_latents.py b/invokeai/app/invocations/denoise_latents.py index 37b385914cc..bb114263e23 100644 --- a/invokeai/app/invocations/denoise_latents.py +++ b/invokeai/app/invocations/denoise_latents.py @@ -39,7 +39,7 @@ from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.app.util.controlnet_utils import prepare_control_image from invokeai.backend.ip_adapter.ip_adapter import IPAdapter -from invokeai.backend.model_manager.config import AnyModelConfig +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelVariantType from invokeai.backend.model_patcher import ModelPatcher from invokeai.backend.patches.layer_patcher import LayerPatcher diff --git a/invokeai/app/invocations/flux_ip_adapter.py b/invokeai/app/invocations/flux_ip_adapter.py index c564023a3a0..4a1997c5122 100644 --- a/invokeai/app/invocations/flux_ip_adapter.py +++ b/invokeai/app/invocations/flux_ip_adapter.py @@ -16,9 +16,7 @@ from invokeai.app.invocations.primitives import ImageField from invokeai.app.invocations.util import validate_begin_end_step, validate_weights from invokeai.app.services.shared.invocation_context import InvocationContext -from invokeai.backend.model_manager.config import ( - IPAdapter_Checkpoint_FLUX_Config, -) +from invokeai.backend.model_manager.configs.ip_adapter import IPAdapter_Checkpoint_FLUX_Config from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType diff --git a/invokeai/app/invocations/flux_model_loader.py b/invokeai/app/invocations/flux_model_loader.py index 2803db48e02..eaac82bafc8 100644 --- a/invokeai/app/invocations/flux_model_loader.py +++ b/invokeai/app/invocations/flux_model_loader.py @@ -14,9 +14,7 @@ preprocess_t5_tokenizer_model_identifier, ) from invokeai.backend.flux.util import get_flux_max_seq_length -from invokeai.backend.model_manager.config import ( - Checkpoint_Config_Base, -) +from invokeai.backend.model_manager.configs.base import Checkpoint_Config_Base from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType, SubModelType diff --git a/invokeai/app/invocations/flux_redux.py b/invokeai/app/invocations/flux_redux.py index 3e34497b105..403d78b0786 100644 --- a/invokeai/app/invocations/flux_redux.py +++ b/invokeai/app/invocations/flux_redux.py @@ -24,9 +24,9 @@ from invokeai.app.services.model_records.model_records_base import ModelRecordChanges from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.backend.flux.redux.flux_redux_model import FluxReduxModel -from invokeai.backend.model_manager import BaseModelType, ModelType -from invokeai.backend.model_manager.config import AnyModelConfig +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.starter_models import siglip +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType from invokeai.backend.sig_lip.sig_lip_pipeline import SigLipPipeline from invokeai.backend.util.devices import TorchDevice diff --git a/invokeai/app/invocations/flux_text_encoder.py b/invokeai/app/invocations/flux_text_encoder.py index 77b6187840c..c395a0bf22d 100644 --- a/invokeai/app/invocations/flux_text_encoder.py +++ b/invokeai/app/invocations/flux_text_encoder.py @@ -17,7 +17,7 @@ from invokeai.app.invocations.primitives import FluxConditioningOutput from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.backend.flux.modules.conditioner import HFEncoder -from invokeai.backend.model_manager import ModelFormat +from invokeai.backend.model_manager.taxonomy import ModelFormat from invokeai.backend.patches.layer_patcher import LayerPatcher from invokeai.backend.patches.lora_conversions.flux_lora_constants import FLUX_LORA_CLIP_PREFIX, FLUX_LORA_T5_PREFIX from invokeai.backend.patches.model_patch_raw import ModelPatchRaw diff --git a/invokeai/app/invocations/flux_vae_encode.py b/invokeai/app/invocations/flux_vae_encode.py index 2932517edcf..4ec0365c2cb 100644 --- a/invokeai/app/invocations/flux_vae_encode.py +++ b/invokeai/app/invocations/flux_vae_encode.py @@ -12,7 +12,7 @@ from invokeai.app.invocations.primitives import LatentsOutput from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.backend.flux.modules.autoencoder import AutoEncoder -from invokeai.backend.model_manager import LoadedModel +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 from invokeai.backend.util.vae_working_memory import estimate_vae_working_memory_flux diff --git a/invokeai/app/invocations/image_to_latents.py b/invokeai/app/invocations/image_to_latents.py index 552f5edb1b2..fde70a34fde 100644 --- a/invokeai/app/invocations/image_to_latents.py +++ b/invokeai/app/invocations/image_to_latents.py @@ -23,7 +23,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.model_manager import LoadedModel +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.stable_diffusion.vae_tiling import patch_vae_tiling_params from invokeai.backend.util.devices import TorchDevice diff --git a/invokeai/app/invocations/ip_adapter.py b/invokeai/app/invocations/ip_adapter.py index 7c3234bdc71..2b2931e78f3 100644 --- a/invokeai/app/invocations/ip_adapter.py +++ b/invokeai/app/invocations/ip_adapter.py @@ -11,8 +11,8 @@ from invokeai.app.invocations.util import validate_begin_end_step, validate_weights from invokeai.app.services.model_records.model_records_base import ModelRecordChanges from invokeai.app.services.shared.invocation_context import InvocationContext -from invokeai.backend.model_manager.config import ( - AnyModelConfig, +from invokeai.backend.model_manager.configs.factory import AnyModelConfig +from invokeai.backend.model_manager.configs.ip_adapter import ( IPAdapter_Checkpoint_Config_Base, IPAdapter_InvokeAI_Config_Base, ) diff --git a/invokeai/app/invocations/model.py b/invokeai/app/invocations/model.py index 327de6ac700..753ae77c559 100644 --- a/invokeai/app/invocations/model.py +++ b/invokeai/app/invocations/model.py @@ -12,9 +12,7 @@ from invokeai.app.invocations.fields import FieldDescriptions, ImageField, Input, InputField, OutputField from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.app.shared.models import FreeUConfig -from invokeai.backend.model_manager.config import ( - AnyModelConfig, -) +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType, SubModelType diff --git a/invokeai/app/invocations/sd3_denoise.py b/invokeai/app/invocations/sd3_denoise.py index f43f26ae0ed..b9d69369b76 100644 --- a/invokeai/app/invocations/sd3_denoise.py +++ b/invokeai/app/invocations/sd3_denoise.py @@ -23,7 +23,7 @@ from invokeai.app.invocations.sd3_text_encoder import SD3_T5_MAX_SEQ_LEN from invokeai.app.services.shared.invocation_context import InvocationContext from invokeai.backend.flux.sampling_utils import clip_timestep_schedule_fractional -from invokeai.backend.model_manager import BaseModelType +from invokeai.backend.model_manager.taxonomy import BaseModelType 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 SD3ConditioningInfo diff --git a/invokeai/app/services/events/events_base.py b/invokeai/app/services/events/events_base.py index fc0f0bb2c69..c70ef3fa16e 100644 --- a/invokeai/app/services/events/events_base.py +++ b/invokeai/app/services/events/events_base.py @@ -44,8 +44,8 @@ SessionQueueItem, SessionQueueStatus, ) - from invokeai.backend.model_manager import SubModelType - from invokeai.backend.model_manager.config import AnyModelConfig + from invokeai.backend.model_manager.configs.factory import AnyModelConfig + from invokeai.backend.model_manager.taxonomy import SubModelType class EventServiceBase: diff --git a/invokeai/app/services/events/events_common.py b/invokeai/app/services/events/events_common.py index 8fbb08015aa..2f995293984 100644 --- a/invokeai/app/services/events/events_common.py +++ b/invokeai/app/services/events/events_common.py @@ -16,8 +16,8 @@ ) from invokeai.app.services.shared.graph import AnyInvocation, AnyInvocationOutput from invokeai.app.util.misc import get_timestamp -from invokeai.backend.model_manager import SubModelType -from invokeai.backend.model_manager.config import AnyModelConfig +from invokeai.backend.model_manager.configs.factory import AnyModelConfig +from invokeai.backend.model_manager.taxonomy import SubModelType if TYPE_CHECKING: from invokeai.app.services.download.download_base import DownloadJob diff --git a/invokeai/app/services/model_install/model_install_common.py b/invokeai/app/services/model_install/model_install_common.py index fea75d73752..67832466f3a 100644 --- a/invokeai/app/services/model_install/model_install_common.py +++ b/invokeai/app/services/model_install/model_install_common.py @@ -10,7 +10,7 @@ from invokeai.app.services.download import DownloadJob, MultiFileDownloadJob from invokeai.app.services.model_records import ModelRecordChanges -from invokeai.backend.model_manager.config import AnyModelConfig +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.metadata import AnyModelRepoMetadata from invokeai.backend.model_manager.taxonomy import ModelRepoVariant, ModelSourceType diff --git a/invokeai/app/services/model_install/model_install_default.py b/invokeai/app/services/model_install/model_install_default.py index 10a954a5636..53bb5cc12df 100644 --- a/invokeai/app/services/model_install/model_install_default.py +++ b/invokeai/app/services/model_install/model_install_default.py @@ -35,10 +35,9 @@ ) from invokeai.app.services.model_records import DuplicateModelException, ModelRecordServiceBase from invokeai.app.services.model_records.model_records_base import ModelRecordChanges -from invokeai.backend.model_manager.config import ( +from invokeai.backend.model_manager.configs.base import Checkpoint_Config_Base +from invokeai.backend.model_manager.configs.factory import ( AnyModelConfig, - Checkpoint_Config_Base, - InvalidModelConfigException, ModelConfigFactory, ) from invokeai.backend.model_manager.metadata import ( @@ -532,7 +531,7 @@ def _set_error(self, install_job: ModelInstallJob, excp: Exception) -> None: x.content_type is not None and "text/html" in x.content_type for x in multifile_download_job.download_parts ): install_job.set_error( - InvalidModelConfigException( + ValueError( f"At least one file in {install_job.local_path} is an HTML page, not a model. This can happen when an access token is required to download." ) ) @@ -602,7 +601,7 @@ def _probe(self, model_path: Path, config: Optional[ModelRecordChanges] = None): return ModelConfigFactory.from_model_on_disk( mod=model_path, - overrides=deepcopy(fields), + override_fields=deepcopy(fields), hash_algo=hash_algo, ) diff --git a/invokeai/app/services/model_load/model_load_base.py b/invokeai/app/services/model_load/model_load_base.py index 8aae80e29da..87a405b4ea4 100644 --- a/invokeai/app/services/model_load/model_load_base.py +++ b/invokeai/app/services/model_load/model_load_base.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import Callable, Optional -from invokeai.backend.model_manager.config import AnyModelConfig +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.load import LoadedModel, LoadedModelWithoutConfig from invokeai.backend.model_manager.load.model_cache.model_cache import ModelCache from invokeai.backend.model_manager.taxonomy import AnyModel, SubModelType diff --git a/invokeai/app/services/model_load/model_load_default.py b/invokeai/app/services/model_load/model_load_default.py index ad4ad97a02c..2e2d2ae219d 100644 --- a/invokeai/app/services/model_load/model_load_default.py +++ b/invokeai/app/services/model_load/model_load_default.py @@ -11,7 +11,7 @@ from invokeai.app.services.config import InvokeAIAppConfig from invokeai.app.services.invoker import Invoker from invokeai.app.services.model_load.model_load_base import ModelLoadServiceBase -from invokeai.backend.model_manager.config import AnyModelConfig +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.load import ( LoadedModel, LoadedModelWithoutConfig, diff --git a/invokeai/app/services/model_manager/__init__.py b/invokeai/app/services/model_manager/__init__.py index aad67ff3527..e703d4f1ffc 100644 --- a/invokeai/app/services/model_manager/__init__.py +++ b/invokeai/app/services/model_manager/__init__.py @@ -1,12 +1,10 @@ """Initialization file for model manager service.""" from invokeai.app.services.model_manager.model_manager_default import ModelManagerService, ModelManagerServiceBase -from invokeai.backend.model_manager import AnyModelConfig from invokeai.backend.model_manager.load import LoadedModel __all__ = [ "ModelManagerServiceBase", "ModelManagerService", - "AnyModelConfig", "LoadedModel", ] diff --git a/invokeai/app/services/model_records/model_records_base.py b/invokeai/app/services/model_records/model_records_base.py index 48f53175364..2d34832dbe0 100644 --- a/invokeai/app/services/model_records/model_records_base.py +++ b/invokeai/app/services/model_records/model_records_base.py @@ -12,12 +12,10 @@ from invokeai.app.services.shared.pagination import PaginatedResults from invokeai.app.util.model_exclude_null import BaseModelExcludeNull -from invokeai.backend.model_manager.config import ( - AnyModelConfig, - ControlAdapterDefaultSettings, - LoraModelDefaultSettings, - MainModelDefaultSettings, -) +from invokeai.backend.model_manager.configs.controlnet import ControlAdapterDefaultSettings +from invokeai.backend.model_manager.configs.factory import AnyModelConfig +from invokeai.backend.model_manager.configs.lora import LoraModelDefaultSettings +from invokeai.backend.model_manager.configs.main import MainModelDefaultSettings from invokeai.backend.model_manager.taxonomy import ( BaseModelType, ClipVariantType, diff --git a/invokeai/app/services/model_records/model_records_sql.py b/invokeai/app/services/model_records/model_records_sql.py index 7fad1761cce..6d9a33ba4a6 100644 --- a/invokeai/app/services/model_records/model_records_sql.py +++ b/invokeai/app/services/model_records/model_records_sql.py @@ -58,10 +58,7 @@ ) from invokeai.app.services.shared.pagination import PaginatedResults from invokeai.app.services.shared.sqlite.sqlite_database import SqliteDatabase -from invokeai.backend.model_manager.config import ( - AnyModelConfig, - ModelConfigFactory, -) +from invokeai.backend.model_manager.configs.factory import AnyModelConfig, ModelConfigFactory from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType @@ -157,7 +154,7 @@ def update_model(self, key: str, changes: ModelRecordChanges) -> AnyModelConfig: record_as_dict[field_name] = getattr(changes, field_name) # 3. create a new model config from the updated dict - record = ModelConfigFactory.make_config(record_as_dict) + record = ModelConfigFactory.from_dict(record_as_dict) # If we get this far, the updated model config is valid, so we can save it to the database. json_serialized = record.model_dump_json() @@ -187,7 +184,7 @@ def get_model(self, key: str) -> AnyModelConfig: with self._db.transaction() as cursor: cursor.execute( """--sql - SELECT config, strftime('%s',updated_at) FROM models + SELECT config FROM models WHERE id=?; """, (key,), @@ -195,14 +192,14 @@ def get_model(self, key: str) -> AnyModelConfig: rows = cursor.fetchone() if not rows: raise UnknownModelException("model not found") - model = ModelConfigFactory.make_config(json.loads(rows[0]), timestamp=rows[1]) + model = ModelConfigFactory.from_dict(json.loads(rows[0])) return model def get_model_by_hash(self, hash: str) -> AnyModelConfig: with self._db.transaction() as cursor: cursor.execute( """--sql - SELECT config, strftime('%s',updated_at) FROM models + SELECT config FROM models WHERE hash=?; """, (hash,), @@ -210,7 +207,7 @@ def get_model_by_hash(self, hash: str) -> AnyModelConfig: rows = cursor.fetchone() if not rows: raise UnknownModelException("model not found") - model = ModelConfigFactory.make_config(json.loads(rows[0]), timestamp=rows[1]) + model = ModelConfigFactory.from_dict(json.loads(rows[0])) return model def exists(self, key: str) -> bool: @@ -278,7 +275,7 @@ def search_by_attr( cursor.execute( f"""--sql - SELECT config, strftime('%s',updated_at) + SELECT config FROM models {where} ORDER BY {ordering[order_by]} -- using ? to bind doesn't work here for some reason; @@ -291,7 +288,7 @@ def search_by_attr( results: list[AnyModelConfig] = [] for row in result: try: - model_config = ModelConfigFactory.make_config(json.loads(row[0]), timestamp=row[1]) + model_config = ModelConfigFactory.from_dict(json.loads(row[0])) except pydantic.ValidationError as e: # We catch this error so that the app can still run if there are invalid model configs in the database. # One reason that an invalid model config might be in the database is if someone had to rollback from a @@ -315,12 +312,12 @@ def search_by_path(self, path: Union[str, Path]) -> List[AnyModelConfig]: with self._db.transaction() as cursor: cursor.execute( """--sql - SELECT config, strftime('%s',updated_at) FROM models + SELECT config FROM models WHERE path=?; """, (str(path),), ) - results = [ModelConfigFactory.make_config(json.loads(x[0]), timestamp=x[1]) for x in cursor.fetchall()] + results = [ModelConfigFactory.from_dict(json.loads(x[0])) for x in cursor.fetchall()] return results def search_by_hash(self, hash: str) -> List[AnyModelConfig]: @@ -328,12 +325,12 @@ def search_by_hash(self, hash: str) -> List[AnyModelConfig]: with self._db.transaction() as cursor: cursor.execute( """--sql - SELECT config, strftime('%s',updated_at) FROM models + SELECT config FROM models WHERE hash=?; """, (hash,), ) - results = [ModelConfigFactory.make_config(json.loads(x[0]), timestamp=x[1]) for x in cursor.fetchall()] + results = [ModelConfigFactory.from_dict(json.loads(x[0])) for x in cursor.fetchall()] return results def list_models( diff --git a/invokeai/app/services/model_relationships/model_relationships_default.py b/invokeai/app/services/model_relationships/model_relationships_default.py index 67fa6c0069d..e4da482ff27 100644 --- a/invokeai/app/services/model_relationships/model_relationships_default.py +++ b/invokeai/app/services/model_relationships/model_relationships_default.py @@ -1,6 +1,6 @@ from invokeai.app.services.invoker import Invoker from invokeai.app.services.model_relationships.model_relationships_base import ModelRelationshipsServiceABC -from invokeai.backend.model_manager.config import AnyModelConfig +from invokeai.backend.model_manager.configs.factory import AnyModelConfig class ModelRelationshipsService(ModelRelationshipsServiceABC): diff --git a/invokeai/app/services/shared/invocation_context.py b/invokeai/app/services/shared/invocation_context.py index 16aacbb9855..97291230e04 100644 --- a/invokeai/app/services/shared/invocation_context.py +++ b/invokeai/app/services/shared/invocation_context.py @@ -19,10 +19,8 @@ from invokeai.app.services.session_processor.session_processor_common import ProgressImage from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection from invokeai.app.util.step_callback import diffusion_step_callback -from invokeai.backend.model_manager.config import ( - AnyModelConfig, - Config_Base, -) +from invokeai.backend.model_manager.configs.base import Config_Base +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.load.load_base import LoadedModel, LoadedModelWithoutConfig from invokeai.backend.model_manager.taxonomy import AnyModel, BaseModelType, ModelFormat, ModelType, SubModelType from invokeai.backend.stable_diffusion.diffusers_pipeline import PipelineIntermediateState diff --git a/invokeai/app/services/shared/sqlite_migrator/migrations/migration_22.py b/invokeai/app/services/shared/sqlite_migrator/migrations/migration_22.py index 08b0e760686..1d9c81529e8 100644 --- a/invokeai/app/services/shared/sqlite_migrator/migrations/migration_22.py +++ b/invokeai/app/services/shared/sqlite_migrator/migrations/migration_22.py @@ -8,7 +8,7 @@ from invokeai.app.services.config import InvokeAIAppConfig from invokeai.app.services.shared.sqlite_migrator.sqlite_migrator_common import Migration -from invokeai.backend.model_manager.config import AnyModelConfigValidator +from invokeai.backend.model_manager.configs.factory import AnyModelConfigValidator class NormalizeResult(NamedTuple): diff --git a/invokeai/app/util/custom_openapi.py b/invokeai/app/util/custom_openapi.py index 2e07622530d..d400e0ff11b 100644 --- a/invokeai/app/util/custom_openapi.py +++ b/invokeai/app/util/custom_openapi.py @@ -12,7 +12,7 @@ from invokeai.app.invocations.model import ModelIdentifierField from invokeai.app.services.events.events_common import EventBase from invokeai.app.services.session_processor.session_processor_common import ProgressImage -from invokeai.backend.model_manager.config import AnyModelConfigValidator +from invokeai.backend.model_manager.configs.factory import AnyModelConfigValidator from invokeai.backend.util.logging import InvokeAILogger logger = InvokeAILogger.get_logger() diff --git a/invokeai/backend/flux/flux_state_dict_utils.py b/invokeai/backend/flux/flux_state_dict_utils.py index 8ffab54c688..c306c88f965 100644 --- a/invokeai/backend/flux/flux_state_dict_utils.py +++ b/invokeai/backend/flux/flux_state_dict_utils.py @@ -1,10 +1,7 @@ -from typing import TYPE_CHECKING +from typing import Any -if TYPE_CHECKING: - from invokeai.backend.model_manager.legacy_probe import CkptType - -def get_flux_in_channels_from_state_dict(state_dict: "CkptType") -> int | None: +def get_flux_in_channels_from_state_dict(state_dict: dict[str | int, Any]) -> int | None: """Gets the in channels from the state dict.""" # "Standard" FLUX models use "img_in.weight", but some community fine tunes use diff --git a/invokeai/backend/model_manager/__init__.py b/invokeai/backend/model_manager/__init__.py index a167687d2e6..e69de29bb2d 100644 --- a/invokeai/backend/model_manager/__init__.py +++ b/invokeai/backend/model_manager/__init__.py @@ -1,45 +0,0 @@ -"""Re-export frequently-used symbols from the Model Manager backend.""" - -from invokeai.backend.model_manager.config import ( - AnyModelConfig, - InvalidModelConfigException, - Config_Base, - ModelConfigFactory, -) -from invokeai.backend.model_manager.legacy_probe import ModelProbe -from invokeai.backend.model_manager.load import LoadedModel -from invokeai.backend.model_manager.search import ModelSearch -from invokeai.backend.model_manager.taxonomy import ( - AnyModel, - AnyVariant, - BaseModelType, - ClipVariantType, - ModelFormat, - ModelRepoVariant, - ModelSourceType, - ModelType, - ModelVariantType, - SchedulerPredictionType, - SubModelType, -) - -__all__ = [ - "AnyModelConfig", - "InvalidModelConfigException", - "LoadedModel", - "ModelConfigFactory", - "ModelProbe", - "ModelSearch", - "Config_Base", - "AnyModel", - "AnyVariant", - "BaseModelType", - "ClipVariantType", - "ModelFormat", - "ModelRepoVariant", - "ModelSourceType", - "ModelType", - "ModelVariantType", - "SchedulerPredictionType", - "SubModelType", -] diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py deleted file mode 100644 index 188ac9ad11f..00000000000 --- a/invokeai/backend/model_manager/config.py +++ /dev/null @@ -1,2584 +0,0 @@ -# Copyright (c) 2023 Lincoln D. Stein and the InvokeAI Development Team -""" -Configuration definitions for image generation models. - -Typical usage: - - from invokeai.backend.model_manager import ModelConfigFactory - raw = dict(path='models/sd-1/main/foo.ckpt', - name='foo', - base='sd-1', - type='main', - config='configs/stable-diffusion/v1-inference.yaml', - variant='normal', - format='checkpoint' - ) - config = ModelConfigFactory.make_config(raw) - print(config.name) - -Validation errors will raise an InvalidModelConfigException error. - -""" - -import json -import logging -import re -import time -from abc import ABC -from enum import Enum -from functools import cache -from inspect import isabstract -from pathlib import Path -from typing import ( - ClassVar, - Literal, - Optional, - Self, - Type, - Union, -) - -import torch -from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag, TypeAdapter, ValidationError -from pydantic_core import CoreSchema, PydanticUndefined, SchemaValidator -from typing_extensions import Annotated, Any, Dict - -from invokeai.app.services.config.config_default import get_config -from invokeai.app.util.misc import uuid_string -from invokeai.backend.flux.controlnet.state_dict_utils import ( - is_state_dict_instantx_controlnet, - is_state_dict_xlabs_controlnet, -) -from invokeai.backend.flux.ip_adapter.state_dict_utils import is_state_dict_xlabs_ip_adapter -from invokeai.backend.flux.redux.flux_redux_state_dict_utils import is_state_dict_likely_flux_redux -from invokeai.backend.model_hash.hash_validator import validate_hash -from invokeai.backend.model_hash.model_hash import HASHING_ALGORITHMS -from invokeai.backend.model_manager.model_on_disk import ModelOnDisk -from invokeai.backend.model_manager.omi import flux_dev_1_lora, stable_diffusion_xl_1_lora -from invokeai.backend.model_manager.taxonomy import ( - AnyVariant, - BaseModelType, - ClipVariantType, - FluxLoRAFormat, - FluxVariantType, - ModelFormat, - ModelRepoVariant, - ModelSourceType, - ModelType, - ModelVariantType, - SchedulerPredictionType, - SubModelType, - variant_type_adapter, -) -from invokeai.backend.model_manager.util.model_util import lora_token_vector_length -from invokeai.backend.patches.lora_conversions.flux_control_lora_utils import is_state_dict_likely_flux_control -from invokeai.backend.quantization.gguf.ggml_tensor import GGMLTensor -from invokeai.backend.spandrel_image_to_image_model import SpandrelImageToImageModel -from invokeai.backend.stable_diffusion.schedulers.schedulers import SCHEDULER_NAME_VALUES - -logger = logging.getLogger(__name__) -app_config = get_config() - - -class InvalidModelConfigException(Exception): - """Exception for when config parser doesn't recognize this combination of model type and format.""" - - pass - - -class NotAMatch(Exception): - """Exception for when a model does not match a config class. - - Args: - config_class: The config class that was being tested. - reason: The reason why the model did not match. - """ - - def __init__( - self, - config_class: type, - reason: str, - ): - super().__init__(f"{config_class.__name__}: {reason}") - - -DEFAULTS_PRECISION = Literal["fp16", "fp32"] - - -class FieldValidator: - """Utility class for validating individual fields of a Pydantic model without instantiating the whole model. - - See: https://github.com/pydantic/pydantic/discussions/7367#discussioncomment-14213144 - """ - - @staticmethod - def find_field_schema(model: type[BaseModel], field_name: str) -> CoreSchema: - """Find the Pydantic core schema for a specific field in a model.""" - schema: CoreSchema = model.__pydantic_core_schema__.copy() - # we shallow copied, be careful not to mutate the original schema! - - assert schema["type"] in ["definitions", "model"] - - # find the field schema - field_schema = schema["schema"] # type: ignore - while "fields" not in field_schema: - field_schema = field_schema["schema"] # type: ignore - - field_schema = field_schema["fields"][field_name]["schema"] # type: ignore - - # if the original schema is a definition schema, replace the model schema with the field schema - if schema["type"] == "definitions": - schema["schema"] = field_schema - return schema - else: - return field_schema - - @cache - @staticmethod - def get_validator(model: type[BaseModel], field_name: str) -> SchemaValidator: - """Get a SchemaValidator for a specific field in a model.""" - return SchemaValidator(FieldValidator.find_field_schema(model, field_name)) - - @staticmethod - def validate_field(model: type[BaseModel], field_name: str, value: Any) -> Any: - """Validate a value for a specific field in a model.""" - return FieldValidator.get_validator(model, field_name).validate_python(value) - - -def has_any_keys(state_dict: dict[str | int, Any], keys: str | set[str]) -> bool: - """Returns true if the state dict has any of the specified keys.""" - _keys = {keys} if isinstance(keys, str) else keys - return any(key in state_dict for key in _keys) - - -def has_any_keys_starting_with(state_dict: dict[str | int, Any], prefixes: str | set[str]) -> bool: - """Returns true if the state dict has any keys starting with any of the specified prefixes.""" - _prefixes = {prefixes} if isinstance(prefixes, str) else prefixes - return any(any(key.startswith(prefix) for prefix in _prefixes) for key in state_dict.keys() if isinstance(key, str)) - - -def has_any_keys_ending_with(state_dict: dict[str | int, Any], suffixes: str | set[str]) -> bool: - """Returns true if the state dict has any keys ending with any of the specified suffixes.""" - _suffixes = {suffixes} if isinstance(suffixes, str) else suffixes - return any(any(key.endswith(suffix) for suffix in _suffixes) for key in state_dict.keys() if isinstance(key, str)) - - -def common_config_paths(path: Path) -> set[Path]: - """Returns common config file paths for models stored in directories.""" - return {path / "config.json", path / "model_index.json"} - - -# These utility functions are tightly coupled to the config classes below in order to make the process of raising -# NotAMatch exceptions as easy and consistent as possible. - - -def _get_config_or_raise( - config_class: type, - config_path: Path | set[Path], -) -> dict[str, Any]: - """Load the config file at the given path, or raise NotAMatch if it cannot be loaded.""" - paths_to_check = config_path if isinstance(config_path, set) else {config_path} - - problems: dict[Path, str] = {} - - for p in paths_to_check: - if not p.exists(): - problems[p] = "file does not exist" - continue - - try: - with open(p, "r") as file: - config = json.load(file) - - return config - except Exception as e: - problems[p] = str(e) - continue - - raise NotAMatch(config_class, f"unable to load config file(s): {problems}") - - -def _get_class_name_from_config( - config_class: type, - config_path: Path | set[Path], -) -> str: - """Load the config file and return the class name. - - Raises: - NotAMatch if the config file is missing or does not contain a valid class name. - """ - - config = _get_config_or_raise(config_class, config_path) - - try: - if "_class_name" in config: - config_class_name = config["_class_name"] - elif "architectures" in config: - config_class_name = config["architectures"][0] - else: - raise ValueError("missing _class_name or architectures field") - except Exception as e: - raise NotAMatch(config_class, f"unable to determine class name from config file: {config_path}") from e - - if not isinstance(config_class_name, str): - raise NotAMatch(config_class, f"_class_name or architectures field is not a string: {config_class_name}") - - return config_class_name - - -def _validate_class_name(config_class: type[BaseModel], config_path: Path | set[Path], expected: set[str]) -> None: - """Check if the class name in the config file matches the expected class names. - - Args: - config_class: The config class that is being tested. - config_path: The path to the config file. - expected: The expected class names.""" - - class_name = _get_class_name_from_config(config_class, config_path) - if class_name not in expected: - raise NotAMatch(config_class, f"invalid class name from config: {class_name}") - - -def _validate_override_fields( - config_class: type[BaseModel], - override_fields: dict[str, Any], -) -> None: - """Check if the provided override fields are valid for the config class. - - Args: - config_class: The config class that is being tested. - override_fields: The override fields provided by the user. - - Raises: - NotAMatch if any override field is invalid for the config. - """ - for field_name, override_value in override_fields.items(): - if field_name not in config_class.model_fields: - raise NotAMatch(config_class, f"unknown override field: {field_name}") - try: - FieldValidator.validate_field(config_class, field_name, override_value) - except ValidationError as e: - raise NotAMatch(config_class, f"invalid override for field '{field_name}': {e}") from e - - -def _validate_is_file( - config_class: type, - mod: ModelOnDisk, -) -> None: - """Raise NotAMatch if the model path is not a file.""" - if not mod.path.is_file(): - raise NotAMatch(config_class, "model path is not a file") - - -def _validate_is_dir( - config_class: type, - mod: ModelOnDisk, -) -> None: - """Raise NotAMatch if the model path is not a directory.""" - if not mod.path.is_dir(): - raise NotAMatch(config_class, "model path is not a directory") - - -class SubmodelDefinition(BaseModel): - path_or_prefix: str - model_type: ModelType - variant: AnyVariant | None = None - - model_config = ConfigDict(protected_namespaces=()) - - -class MainModelDefaultSettings(BaseModel): - vae: str | None = Field(default=None, description="Default VAE for this model (model key)") - vae_precision: DEFAULTS_PRECISION | None = Field(default=None, description="Default VAE precision for this model") - scheduler: SCHEDULER_NAME_VALUES | None = Field(default=None, description="Default scheduler for this model") - steps: int | None = Field(default=None, gt=0, description="Default number of steps for this model") - cfg_scale: float | None = Field(default=None, ge=1, description="Default CFG Scale for this model") - cfg_rescale_multiplier: float | None = Field( - default=None, ge=0, lt=1, description="Default CFG Rescale Multiplier for this model" - ) - width: int | None = Field(default=None, multiple_of=8, ge=64, description="Default width for this model") - height: int | None = Field(default=None, multiple_of=8, ge=64, description="Default height for this model") - guidance: float | None = Field(default=None, ge=1, description="Default Guidance for this model") - - model_config = ConfigDict(extra="forbid") - - -class LoraModelDefaultSettings(BaseModel): - weight: float | None = Field(default=None, ge=-1, le=2, description="Default weight for this model") - model_config = ConfigDict(extra="forbid") - - -class ControlAdapterDefaultSettings(BaseModel): - # This could be narrowed to controlnet processor nodes, but they change. Leaving this a string is safer. - preprocessor: str | None - model_config = ConfigDict(extra="forbid") - - -class LegacyProbeMixin: - """Mixin for classes using the legacy probe for model classification.""" - - pass - - -class Config_Base(ABC, BaseModel): - """ - Abstract base class for model configurations. A model config describes a specific combination of model base, type and - format, along with other metadata about the model. For example, a Stable Diffusion 1.x main model in checkpoint format - would have base=sd-1, type=main, format=checkpoint. - - To create a new config type, inherit from this class and implement its interface: - - Define method 'from_model_on_disk' that returns an instance of the class or raises NotAMatch. This method will be - called during model installation to determine the correct config class for a model. - - Define fields 'type', 'base' and 'format' as pydantic fields. These should be Literals with a single value. A - default must be provided for each of these fields. - - If multiple combinations of base, type and format need to be supported, create a separate subclass for each. - - See MinimalConfigExample in test_model_probe.py for an example implementation. - """ - - key: str = Field( - description="A unique key for this model.", - default_factory=uuid_string, - ) - hash: str = Field( - description="The hash of the model file(s).", - ) - path: str = Field( - description="Path to the model on the filesystem. Relative paths are relative to the Invoke root directory.", - ) - file_size: int = Field( - description="The size of the model in bytes.", - ) - name: str = Field( - description="Name of the model.", - ) - description: str | None = Field( - description="Model description", - default=None, - ) - source: str = Field( - description="The original source of the model (path, URL or repo_id).", - ) - source_type: ModelSourceType = Field( - description="The type of source", - ) - source_api_response: str | None = Field( - description="The original API response from the source, as stringified JSON.", - default=None, - ) - cover_image: str | None = Field( - description="Url for image to preview model", - default=None, - ) - submodels: dict[SubModelType, SubmodelDefinition] | None = Field( - description="Loadable submodels in this model", - default=None, - ) - usage_info: str | None = Field( - default=None, - description="Usage information for this model", - ) - - CONFIG_CLASSES: ClassVar[set[Type["AnyModelConfig"]]] = set() - - model_config = ConfigDict( - validate_assignment=True, - json_schema_serialization_defaults_required=True, - json_schema_mode_override="serialization", - ) - - @classmethod - def __init_subclass__(cls, **kwargs): - super().__init_subclass__(**kwargs) - # Register non-abstract subclasses so we can iterate over them later during model probing. - if not isabstract(cls): - cls.CONFIG_CLASSES.add(cls) - - @classmethod - def __pydantic_init_subclass__(cls, **kwargs): - # Ensure that subclasses define 'base', 'type' and 'format' fields and provide defaults for them. Each subclass - # is expected to represent a single combination of base, type and format. - for name in ("type", "base", "format"): - assert name in cls.model_fields, f"{cls.__name__} must define a '{name}' field" - assert cls.model_fields[name].default is not PydanticUndefined, ( - f"{cls.__name__} must define a default for the '{name}' field" - ) - - @classmethod - def get_tag(cls) -> Tag: - """Constructs a pydantic discriminated union tag for this model config class. When a config is deserialized, - pydantic uses the tag to determine which subclass to instantiate. - - The tag is a dot-separated string of the type, format, base and variant (if applicable). - """ - tag_strings: list[str] = [] - for name in ("type", "format", "base", "variant"): - if field := cls.model_fields.get(name): - if field.default is not PydanticUndefined: - # We expect each of these fields has an Enum for its default; we want the value of the enum. - tag_strings.append(field.default.value) - return Tag(".".join(tag_strings)) - - @staticmethod - def get_model_discriminator_value(v: Any) -> str: - """ - Computes the discriminator value for a model config. - https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions-with-callable-discriminator - """ - if isinstance(v, Config_Base): - # We have an instance of a ModelConfigBase subclass - use its tag directly. - return v.get_tag().tag - if isinstance(v, dict): - # We have a dict - compute the tag from its fields. - tag_strings: list[str] = [] - if type_ := v.get("type"): - if isinstance(type_, Enum): - type_ = type_.value - tag_strings.append(type_) - - if format_ := v.get("format"): - if isinstance(format_, Enum): - format_ = format_.value - tag_strings.append(format_) - - if base_ := v.get("base"): - if isinstance(base_, Enum): - base_ = base_.value - tag_strings.append(base_) - - # Special case: CLIP Embed models also need the variant to distinguish them. - if ( - type_ == ModelType.CLIPEmbed.value - and format_ == ModelFormat.Diffusers.value - and base_ == BaseModelType.Any.value - ): - if variant_value := v.get("variant"): - if isinstance(variant_value, Enum): - variant_value = variant_value.value - tag_strings.append(variant_value) - else: - raise ValueError("CLIP Embed model config dict must include a 'variant' field") - - return ".".join(tag_strings) - else: - raise TypeError("Model config discriminator value must be computed from a dict or ModelConfigBase instance") - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - """Given the model on disk and any overrides, return an instance of this config class. - - Implementations should raise NotAMatch if the model does not match this config class.""" - raise NotImplementedError(f"from_model_on_disk not implemented for {cls.__name__}") - - -class Unknown_Config(Config_Base): - """Model config for unknown models, used as a fallback when we cannot identify a model.""" - - base: Literal[BaseModelType.Unknown] = Field(default=BaseModelType.Unknown) - type: Literal[ModelType.Unknown] = Field(default=ModelType.Unknown) - format: Literal[ModelFormat.Unknown] = Field(default=ModelFormat.Unknown) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - raise NotAMatch(cls, "unknown model config cannot match any model") - - -class Checkpoint_Config_Base(ABC, BaseModel): - """Base class for checkpoint-style models.""" - - config_path: str | None = Field( - description="Path to the config for this model, if any.", - default=None, - ) - converted_at: float | None = Field( - description="When this model was last converted to diffusers", - default_factory=time.time, - ) - - -class Diffusers_Config_Base(ABC, BaseModel): - """Base class for diffusers-style models.""" - - format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - repo_variant: Optional[ModelRepoVariant] = Field(ModelRepoVariant.Default) - - @classmethod - def _get_repo_variant_or_raise(cls, mod: ModelOnDisk) -> ModelRepoVariant: - # get all files ending in .bin or .safetensors - weight_files = list(mod.path.glob("**/*.safetensors")) - weight_files.extend(list(mod.path.glob("**/*.bin"))) - for x in weight_files: - if ".fp16" in x.suffixes: - return ModelRepoVariant.FP16 - if "openvino_model" in x.name: - return ModelRepoVariant.OpenVINO - if "flax_model" in x.name: - return ModelRepoVariant.Flax - if x.suffix == ".onnx": - return ModelRepoVariant.ONNX - return ModelRepoVariant.Default - - -class T5Encoder_T5Encoder_Config(Config_Base): - base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) - type: Literal[ModelType.T5Encoder] = Field(default=ModelType.T5Encoder) - format: Literal[ModelFormat.T5Encoder] = Field(default=ModelFormat.T5Encoder) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - _validate_class_name( - cls, - common_config_paths(mod.path), - { - "T5EncoderModel", - }, - ) - - cls._validate_has_unquantized_config_file(mod) - - return cls(**fields) - - @classmethod - def _validate_has_unquantized_config_file(cls, mod: ModelOnDisk) -> None: - has_unquantized_config = (mod.path / "text_encoder_2" / "model.safetensors.index.json").exists() - - if not has_unquantized_config: - raise NotAMatch(cls, "missing text_encoder_2/model.safetensors.index.json") - - -class T5Encoder_BnBLLMint8_Config(Config_Base): - base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) - type: Literal[ModelType.T5Encoder] = Field(default=ModelType.T5Encoder) - format: Literal[ModelFormat.BnbQuantizedLlmInt8b] = Field(default=ModelFormat.BnbQuantizedLlmInt8b) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - _validate_class_name( - cls, - common_config_paths(mod.path), - { - "T5EncoderModel", - }, - ) - - cls._validate_filename_looks_like_bnb_quantized(mod) - - cls._validate_model_looks_like_bnb_quantized(mod) - - return cls(**fields) - - @classmethod - def _validate_filename_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: - filename_looks_like_bnb = any(x for x in mod.weight_files() if "llm_int8" in x.as_posix()) - if not filename_looks_like_bnb: - raise NotAMatch(cls, "filename does not look like bnb quantized llm_int8") - - @classmethod - def _validate_model_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: - has_scb_key_suffix = has_any_keys_ending_with(mod.load_state_dict(), "SCB") - if not has_scb_key_suffix: - raise NotAMatch(cls, "state dict does not look like bnb quantized llm_int8") - - -class LoRA_Config_Base(ABC, BaseModel): - """Base class for LoRA models.""" - - type: Literal[ModelType.LoRA] = Field(default=ModelType.LoRA) - trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None) - default_settings: Optional[LoraModelDefaultSettings] = Field( - description="Default settings for this model", default=None - ) - - -def _get_flux_lora_format(mod: ModelOnDisk) -> FluxLoRAFormat | None: - # TODO(psyche): Moving this import to the function to avoid circular imports. Refactor later. - from invokeai.backend.patches.lora_conversions.formats import flux_format_from_state_dict - - state_dict = mod.load_state_dict(mod.path) - value = flux_format_from_state_dict(state_dict, mod.metadata()) - return value - - -class LoRA_OMI_Config_Base(LoRA_Config_Base): - format: Literal[ModelFormat.OMI] = Field(default=ModelFormat.OMI) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_looks_like_omi_lora(mod) - - cls._validate_base(mod) - - return cls(**fields) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _validate_looks_like_omi_lora(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model metadata does not look like an OMI LoRA.""" - flux_format = _get_flux_lora_format(mod) - if flux_format in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: - raise NotAMatch(cls, "model looks like ControlLoRA or Diffusers LoRA") - - metadata = mod.metadata() - - metadata_looks_like_omi_lora = ( - bool(metadata.get("modelspec.sai_model_spec")) - and metadata.get("ot_branch") == "omi_format" - and metadata.get("modelspec.architecture", "").split("/")[1].lower() == "lora" - ) - - if not metadata_looks_like_omi_lora: - raise NotAMatch(cls, "metadata does not look like OMI LoRA") - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> Literal[BaseModelType.Flux, BaseModelType.StableDiffusionXL]: - metadata = mod.metadata() - architecture = metadata["modelspec.architecture"] - - if architecture == stable_diffusion_xl_1_lora: - return BaseModelType.StableDiffusionXL - elif architecture == flux_dev_1_lora: - return BaseModelType.Flux - else: - raise NotAMatch(cls, f"unrecognised/unsupported architecture for OMI LoRA: {architecture}") - - -class LoRA_OMI_SDXL_Config(LoRA_OMI_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class LoRA_OMI_FLUX_Config(LoRA_OMI_Config_Base, Config_Base): - base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - - -class LoRA_LyCORIS_Config_Base(LoRA_Config_Base): - """Model config for LoRA/Lycoris models.""" - - type: Literal[ModelType.LoRA] = Field(default=ModelType.LoRA) - format: Literal[ModelFormat.LyCORIS] = Field(default=ModelFormat.LyCORIS) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_looks_like_lora(mod) - - cls._validate_base(mod) - - return cls(**fields) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: - # First rule out ControlLoRA and Diffusers LoRA - flux_format = _get_flux_lora_format(mod) - if flux_format in [FluxLoRAFormat.Control, FluxLoRAFormat.Diffusers]: - raise NotAMatch(cls, "model looks like ControlLoRA or Diffusers LoRA") - - # Note: Existence of these key prefixes/suffixes does not guarantee that this is a LoRA. - # Some main models have these keys, likely due to the creator merging in a LoRA. - has_key_with_lora_prefix = has_any_keys_starting_with( - mod.load_state_dict(), - { - "lora_te_", - "lora_unet_", - "lora_te1_", - "lora_te2_", - "lora_transformer_", - }, - ) - - has_key_with_lora_suffix = has_any_keys_ending_with( - mod.load_state_dict(), - { - "to_k_lora.up.weight", - "to_q_lora.down.weight", - "lora_A.weight", - "lora_B.weight", - }, - ) - - if not has_key_with_lora_prefix and not has_key_with_lora_suffix: - raise NotAMatch(cls, "model does not match LyCORIS LoRA heuristics") - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: - if _get_flux_lora_format(mod): - return BaseModelType.Flux - - state_dict = mod.load_state_dict() - # If we've gotten here, we assume that the model is a Stable Diffusion model - token_vector_length = lora_token_vector_length(state_dict) - if token_vector_length == 768: - return BaseModelType.StableDiffusion1 - elif token_vector_length == 1024: - return BaseModelType.StableDiffusion2 - elif token_vector_length == 1280: - return BaseModelType.StableDiffusionXL # recognizes format at https://civitai.com/models/224641 - elif token_vector_length == 2048: - return BaseModelType.StableDiffusionXL - else: - raise NotAMatch(cls, f"unrecognized token vector length {token_vector_length}") - - -class LoRA_LyCORIS_SD1_Config(LoRA_LyCORIS_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) - - -class LoRA_LyCORIS_SD2_Config(LoRA_LyCORIS_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) - - -class LoRA_LyCORIS_SDXL_Config(LoRA_LyCORIS_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class LoRA_LyCORIS_FLUX_Config(LoRA_LyCORIS_Config_Base, Config_Base): - base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - - -class ControlAdapter_Config_Base(ABC, BaseModel): - default_settings: ControlAdapterDefaultSettings | None = Field(None) - - -class ControlLoRA_LyCORIS_FLUX_Config(ControlAdapter_Config_Base, Config_Base): - """Model config for Control LoRA models.""" - - base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - type: Literal[ModelType.ControlLoRa] = Field(default=ModelType.ControlLoRa) - format: Literal[ModelFormat.LyCORIS] = Field(default=ModelFormat.LyCORIS) - - trigger_phrases: set[str] | None = Field(None) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_looks_like_control_lora(mod) - - return cls(**fields) - - @classmethod - def _validate_looks_like_control_lora(cls, mod: ModelOnDisk) -> None: - state_dict = mod.load_state_dict() - - if not is_state_dict_likely_flux_control(state_dict): - raise NotAMatch(cls, "model state dict does not look like a Flux Control LoRA") - - -class LoRA_Diffusers_Config_Base(LoRA_Config_Base): - """Model config for LoRA/Diffusers models.""" - - # TODO(psyche): Needs base handling. For FLUX, the Diffusers format does not indicate a folder model; it indicates - # the weights format. FLUX Diffusers LoRAs are single files. - - format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_base(mod) - - return cls(**fields) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: - if _get_flux_lora_format(mod): - return BaseModelType.Flux - - # If we've gotten here, we assume that the LoRA is a Stable Diffusion LoRA - path_to_weight_file = cls._get_weight_file_or_raise(mod) - state_dict = mod.load_state_dict(path_to_weight_file) - token_vector_length = lora_token_vector_length(state_dict) - - match token_vector_length: - case 768: - return BaseModelType.StableDiffusion1 - case 1024: - return BaseModelType.StableDiffusion2 - case 1280: - return BaseModelType.StableDiffusionXL # recognizes format at https://civitai.com/models/224641 - case 2048: - return BaseModelType.StableDiffusionXL - case _: - raise NotAMatch(cls, f"unrecognized token vector length {token_vector_length}") - - @classmethod - def _get_weight_file_or_raise(cls, mod: ModelOnDisk) -> Path: - suffixes = ["bin", "safetensors"] - weight_files = [mod.path / f"pytorch_lora_weights.{sfx}" for sfx in suffixes] - for wf in weight_files: - if wf.exists(): - return wf - raise NotAMatch(cls, "missing pytorch_lora_weights.bin or pytorch_lora_weights.safetensors") - - -class LoRA_Diffusers_SD1_Config(LoRA_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) - - -class LoRA_Diffusers_SD2_Config(LoRA_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) - - -class LoRA_Diffusers_SDXL_Config(LoRA_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class LoRA_Diffusers_FLUX_Config(LoRA_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - - -class VAE_Checkpoint_Config_Base(Checkpoint_Config_Base): - """Model config for standalone VAE models.""" - - type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) - format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - - REGEX_TO_BASE: ClassVar[dict[str, BaseModelType]] = { - r"xl": BaseModelType.StableDiffusionXL, - r"sd2": BaseModelType.StableDiffusion2, - r"vae": BaseModelType.StableDiffusion1, - r"FLUX.1-schnell_ae": BaseModelType.Flux, - } - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_looks_like_vae(mod) - - cls._validate_base(mod) - - return cls(**fields) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _validate_looks_like_vae(cls, mod: ModelOnDisk) -> None: - if not has_any_keys_starting_with( - mod.load_state_dict(), - { - "encoder.conv_in", - "decoder.conv_in", - }, - ): - raise NotAMatch(cls, "model does not match Checkpoint VAE heuristics") - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: - # Heuristic: VAEs of all architectures have a similar structure; the best we can do is guess based on name - for regexp, base in cls.REGEX_TO_BASE.items(): - if re.search(regexp, mod.path.name, re.IGNORECASE): - return base - - raise NotAMatch(cls, "cannot determine base type") - - -class VAE_Checkpoint_SD1_Config(VAE_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) - - -class VAE_Checkpoint_SD2_Config(VAE_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) - - -class VAE_Checkpoint_SDXL_Config(VAE_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class VAE_Checkpoint_FLUX_Config(VAE_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - - -class VAE_Diffusers_Config_Base(Diffusers_Config_Base): - """Model config for standalone VAE models (diffusers version).""" - - type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) - format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - _validate_class_name( - cls, - common_config_paths(mod.path), - { - "AutoencoderKL", - "AutoencoderTiny", - }, - ) - - cls._validate_base(mod) - - return cls(**fields) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _config_looks_like_sdxl(cls, config: dict[str, Any]) -> bool: - # Heuristic: These config values that distinguish Stability's SD 1.x VAE from their SDXL VAE. - return config.get("scaling_factor", 0) == 0.13025 and config.get("sample_size") in [512, 1024] - - @classmethod - def _name_looks_like_sdxl(cls, mod: ModelOnDisk) -> bool: - # Heuristic: SD and SDXL VAE are the same shape (3-channel RGB to 4-channel float scaled down - # by a factor of 8), so we can't necessarily tell them apart by config hyperparameters. Best - # we can do is guess based on name. - return bool(re.search(r"xl\b", cls._guess_name(mod), re.IGNORECASE)) - - @classmethod - def _guess_name(cls, mod: ModelOnDisk) -> str: - name = mod.path.name - if name == "vae": - name = mod.path.parent.name - return name - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: - config = _get_config_or_raise(cls, common_config_paths(mod.path)) - if cls._config_looks_like_sdxl(config): - return BaseModelType.StableDiffusionXL - elif cls._name_looks_like_sdxl(mod): - return BaseModelType.StableDiffusionXL - else: - # TODO(psyche): Figure out how to positively identify SD1 here, and raise if we can't. Until then, YOLO. - return BaseModelType.StableDiffusion1 - - -class VAE_Diffusers_SD1_Config(VAE_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) - - -class VAE_Diffusers_SDXL_Config(VAE_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class ControlNet_Diffusers_Config_Base(Diffusers_Config_Base, ControlAdapter_Config_Base): - """Model config for ControlNet models (diffusers version).""" - - type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) - format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - _validate_class_name( - cls, - common_config_paths(mod.path), - { - "ControlNetModel", - "FluxControlNetModel", - }, - ) - - cls._validate_base(mod) - - return cls(**fields) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: - config = _get_config_or_raise(cls, common_config_paths(mod.path)) - - if config.get("_class_name") == "FluxControlNetModel": - return BaseModelType.Flux - - dimension = config.get("cross_attention_dim") - - match dimension: - case 768: - return BaseModelType.StableDiffusion1 - case 1024: - # No obvious way to distinguish between sd2-base and sd2-768, but we don't really differentiate them - # anyway. - return BaseModelType.StableDiffusion2 - case 2048: - return BaseModelType.StableDiffusionXL - case _: - raise NotAMatch(cls, f"unrecognized cross_attention_dim {dimension}") - - -class ControlNet_Diffusers_SD1_Config(ControlNet_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) - - -class ControlNet_Diffusers_SD2_Config(ControlNet_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) - - -class ControlNet_Diffusers_SDXL_Config(ControlNet_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class ControlNet_Diffusers_FLUX_Config(ControlNet_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - - -class ControlNet_Checkpoint_Config_Base(Checkpoint_Config_Base, ControlAdapter_Config_Base): - """Model config for ControlNet models (diffusers version).""" - - type: Literal[ModelType.ControlNet] = Field(default=ModelType.ControlNet) - format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_looks_like_controlnet(mod) - - cls._validate_base(mod) - - return cls(**fields) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _validate_looks_like_controlnet(cls, mod: ModelOnDisk) -> None: - if has_any_keys_starting_with( - mod.load_state_dict(), - { - "controlnet", - "control_model", - "input_blocks", - # XLabs FLUX ControlNet models have keys starting with "controlnet_blocks." - # For example: https://huggingface.co/XLabs-AI/flux-controlnet-collections/blob/86ab1e915a389d5857135c00e0d350e9e38a9048/flux-canny-controlnet_v2.safetensors - # TODO(ryand): This is very fragile. XLabs FLUX ControlNet models also contain keys starting with - # "double_blocks.", which we check for above. But, I'm afraid to modify this logic because it is so - # delicate. - "controlnet_blocks", - }, - ): - raise NotAMatch(cls, "state dict does not look like a ControlNet checkpoint") - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: - state_dict = mod.load_state_dict() - - if is_state_dict_xlabs_controlnet(state_dict) or is_state_dict_instantx_controlnet(state_dict): - # TODO(ryand): Should I distinguish between XLabs, InstantX and other ControlNet models by implementing - # get_format()? - return BaseModelType.Flux - - for key in ( - "control_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight", - "controlnet_mid_block.bias", - "input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight", - "down_blocks.1.attentions.0.transformer_blocks.0.attn2.to_k.weight", - ): - if key not in state_dict: - continue - width = state_dict[key].shape[-1] - match width: - case 768: - return BaseModelType.StableDiffusion1 - case 1024: - return BaseModelType.StableDiffusion2 - case 2048: - return BaseModelType.StableDiffusionXL - case 1280: - return BaseModelType.StableDiffusionXL - case _: - pass - - raise NotAMatch(cls, "unable to determine base type from state dict") - - -class ControlNet_Checkpoint_SD1_Config(ControlNet_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) - - -class ControlNet_Checkpoint_SD2_Config(ControlNet_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) - - -class ControlNet_Checkpoint_SDXL_Config(ControlNet_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class ControlNet_Checkpoint_FLUX_Config(ControlNet_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - - -class TI_Config_Base(ABC, BaseModel): - type: Literal[ModelType.TextualInversion] = Field(default=ModelType.TextualInversion) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk, path: Path | None = None) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod, path) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _file_looks_like_embedding(cls, mod: ModelOnDisk, path: Path | None = None) -> bool: - try: - p = path or mod.path - - if not p.exists(): - return False - - if p.is_dir(): - return False - - if p.name in [f"learned_embeds.{s}" for s in mod.weight_files()]: - return True - - state_dict = mod.load_state_dict(p) - - # Heuristic: textual inversion embeddings have these keys - if any(key in {"string_to_param", "emb_params", "clip_g"} for key in state_dict.keys()): - return True - - # Heuristic: small state dict with all tensor values - if (len(state_dict)) < 10 and all(isinstance(v, torch.Tensor) for v in state_dict.values()): - return True - - return False - except Exception: - return False - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk, path: Path | None = None) -> BaseModelType: - p = path or mod.path - - try: - state_dict = mod.load_state_dict(p) - except Exception as e: - raise NotAMatch(cls, f"unable to load state dict from {p}: {e}") from e - - try: - if "string_to_token" in state_dict: - token_dim = list(state_dict["string_to_param"].values())[0].shape[-1] - elif "emb_params" in state_dict: - token_dim = state_dict["emb_params"].shape[-1] - elif "clip_g" in state_dict: - token_dim = state_dict["clip_g"].shape[-1] - else: - token_dim = list(state_dict.values())[0].shape[0] - except Exception as e: - raise NotAMatch(cls, f"unable to determine token dimension from state dict in {p}: {e}") from e - - match token_dim: - case 768: - return BaseModelType.StableDiffusion1 - case 1024: - return BaseModelType.StableDiffusion2 - case 1280: - return BaseModelType.StableDiffusionXL - case _: - raise NotAMatch(cls, f"unrecognized token dimension {token_dim}") - - -class TI_File_Config_Base(TI_Config_Base): - """Model config for textual inversion embeddings.""" - - format: Literal[ModelFormat.EmbeddingFile] = Field(default=ModelFormat.EmbeddingFile) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - if not cls._file_looks_like_embedding(mod): - raise NotAMatch(cls, "model does not look like a textual inversion embedding file") - - cls._validate_base(mod) - - return cls(**fields) - - -class TI_File_SD1_Config(TI_File_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) - - -class TI_File_SD2_Config(TI_File_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) - - -class TI_File_SDXL_Config(TI_File_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class TI_Folder_Config_Base(TI_Config_Base): - """Model config for textual inversion embeddings.""" - - format: Literal[ModelFormat.EmbeddingFolder] = Field(default=ModelFormat.EmbeddingFolder) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - for p in mod.weight_files(): - if cls._file_looks_like_embedding(mod, p): - cls._validate_base(mod, p) - return cls(**fields) - - raise NotAMatch(cls, "model does not look like a textual inversion embedding folder") - - -class TI_Folder_SD1_Config(TI_Folder_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) - - -class TI_Folder_SD2_Config(TI_Folder_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) - - -class TI_Folder_SDXL_Config(TI_Folder_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class Main_Config_Base(ABC, BaseModel): - type: Literal[ModelType.Main] = Field(default=ModelType.Main) - trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None) - default_settings: Optional[MainModelDefaultSettings] = Field( - description="Default settings for this model", default=None - ) - - -def _has_bnb_nf4_keys(state_dict: dict[str | int, Any]) -> bool: - bnb_nf4_keys = { - "double_blocks.0.img_attn.proj.weight.quant_state.bitsandbytes__nf4", - "model.diffusion_model.double_blocks.0.img_attn.proj.weight.quant_state.bitsandbytes__nf4", - } - return any(key in state_dict for key in bnb_nf4_keys) - - -def _has_ggml_tensors(state_dict: dict[str | int, Any]) -> bool: - return any(isinstance(v, GGMLTensor) for v in state_dict.values()) - - -def _has_main_keys(state_dict: dict[str | int, Any]) -> bool: - for key in state_dict.keys(): - if isinstance(key, int): - continue - elif key.startswith( - ( - "cond_stage_model.", - "first_stage_model.", - "model.diffusion_model.", - # Some FLUX checkpoint files contain transformer keys prefixed with "model.diffusion_model". - # This prefix is typically used to distinguish between multiple models bundled in a single file. - "model.diffusion_model.double_blocks.", - ) - ): - return True - elif key.startswith("double_blocks.") and "ip_adapter" not in key: - # FLUX models in the official BFL format contain keys with the "double_blocks." prefix, but we must be - # careful to avoid false positives on XLabs FLUX IP-Adapter models. - return True - return False - - -class Main_Checkpoint_Config_Base(Checkpoint_Config_Base, Main_Config_Base): - """Model config for main checkpoint models.""" - - format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - - prediction_type: SchedulerPredictionType = Field() - variant: ModelVariantType = Field() - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_looks_like_main_model(mod) - - cls._validate_base(mod) - - prediction_type = fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod) - - variant = fields.get("variant") or cls._get_variant_or_raise(mod) - - return cls(**fields, prediction_type=prediction_type, variant=variant) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: - state_dict = mod.load_state_dict() - - key_name = "model.diffusion_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight" - if key_name in state_dict and state_dict[key_name].shape[-1] == 768: - return BaseModelType.StableDiffusion1 - if key_name in state_dict and state_dict[key_name].shape[-1] == 1024: - return BaseModelType.StableDiffusion2 - - key_name = "model.diffusion_model.input_blocks.4.1.transformer_blocks.0.attn2.to_k.weight" - if key_name in state_dict and state_dict[key_name].shape[-1] == 2048: - return BaseModelType.StableDiffusionXL - elif key_name in state_dict and state_dict[key_name].shape[-1] == 1280: - return BaseModelType.StableDiffusionXLRefiner - - raise NotAMatch(cls, "unable to determine base type from state dict") - - @classmethod - def _get_scheduler_prediction_type_or_raise(cls, mod: ModelOnDisk) -> SchedulerPredictionType: - base = cls.model_fields["base"].default - - if base is BaseModelType.StableDiffusion2: - state_dict = mod.load_state_dict() - key_name = "model.diffusion_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight" - if key_name in state_dict and state_dict[key_name].shape[-1] == 1024: - if "global_step" in state_dict: - if state_dict["global_step"] == 220000: - return SchedulerPredictionType.Epsilon - elif state_dict["global_step"] == 110000: - return SchedulerPredictionType.VPrediction - return SchedulerPredictionType.VPrediction - else: - return SchedulerPredictionType.Epsilon - - @classmethod - def _get_variant_or_raise(cls, mod: ModelOnDisk) -> ModelVariantType: - base = cls.model_fields["base"].default - - state_dict = mod.load_state_dict() - key_name = "model.diffusion_model.input_blocks.0.0.weight" - - if key_name not in state_dict: - raise NotAMatch(cls, "unable to determine model variant from state dict") - - in_channels = state_dict["model.diffusion_model.input_blocks.0.0.weight"].shape[1] - - match in_channels: - case 4: - return ModelVariantType.Normal - case 5: - # Only SD2 has a depth variant - assert base is BaseModelType.StableDiffusion2, f"unexpected unet in_channels 5 for base '{base}'" - return ModelVariantType.Depth - case 9: - return ModelVariantType.Inpaint - case _: - raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels} for base '{base}'") - - @classmethod - def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: - has_main_model_keys = _has_main_keys(mod.load_state_dict()) - if not has_main_model_keys: - raise NotAMatch(cls, "state dict does not look like a main model") - - -class Main_Checkpoint_SD1_Config(Main_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) - - -class Main_Checkpoint_SD2_Config(Main_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) - - -class Main_Checkpoint_SDXL_Config(Main_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class Main_Checkpoint_SDXLRefiner_Config(Main_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXLRefiner] = Field(default=BaseModelType.StableDiffusionXLRefiner) - - -def _get_flux_variant(state_dict: dict[str | int, Any]) -> FluxVariantType | None: - # FLUX Model variant types are distinguished by input channels and the presence of certain keys. - - # Input channels are derived from the shape of either "img_in.weight" or "model.diffusion_model.img_in.weight". - # - # Known models that use the latter key: - # - https://civitai.com/models/885098?modelVersionId=990775 - # - https://civitai.com/models/1018060?modelVersionId=1596255 - # - https://civitai.com/models/978314/ultrareal-fine-tune?modelVersionId=1413133 - # - # Input channels for known FLUX models: - # - Unquantized Dev and Schnell have in_channels=64 - # - BNB-NF4 Dev and Schnell have in_channels=1 - # - FLUX Fill has in_channels=384 - # - Unsure of quantized FLUX Fill models - # - Unsure of GGUF-quantized models - - in_channels = None - for key in {"img_in.weight", "model.diffusion_model.img_in.weight"}: - if key in state_dict: - in_channels = state_dict[key].shape[1] - break - - if in_channels is None: - # TODO(psyche): Should we have a graceful fallback here? Previously we fell back to the "normal" variant, - # but this variant is no longer used for FLUX models. If we get here, but the model is definitely a FLUX - # model, we should figure out a good fallback value. - return None - - # Because FLUX Dev and Schnell models have the same in_channels, we need to check for the presence of - # certain keys to distinguish between them. - is_flux_dev = ( - "guidance_in.out_layer.weight" in state_dict - or "model.diffusion_model.guidance_in.out_layer.weight" in state_dict - ) - - if is_flux_dev and in_channels == 384: - return FluxVariantType.DevFill - elif is_flux_dev: - return FluxVariantType.Dev - else: - # Must be a Schnell model...? - return FluxVariantType.Schnell - - -class Main_Checkpoint_FLUX_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base): - """Model config for main checkpoint models.""" - - format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - - variant: FluxVariantType = Field() - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_looks_like_main_model(mod) - - cls._validate_is_flux(mod) - - cls._validate_does_not_look_like_bnb_quantized(mod) - - cls._validate_does_not_look_like_gguf_quantized(mod) - - variant = fields.get("variant") or cls._get_variant_or_raise(mod) - - return cls(**fields, variant=variant) - - @classmethod - def _validate_is_flux(cls, mod: ModelOnDisk) -> None: - if not has_any_keys( - mod.load_state_dict(), - { - "double_blocks.0.img_attn.norm.key_norm.scale", - "model.diffusion_model.double_blocks.0.img_attn.norm.key_norm.scale", - }, - ): - raise NotAMatch(cls, "state dict does not look like a FLUX checkpoint") - - @classmethod - def _get_variant_or_raise(cls, mod: ModelOnDisk) -> FluxVariantType: - # FLUX Model variant types are distinguished by input channels and the presence of certain keys. - state_dict = mod.load_state_dict() - variant = _get_flux_variant(state_dict) - - if variant is None: - # TODO(psyche): Should we have a graceful fallback here? Previously we fell back to the "normal" variant, - # but this variant is no longer used for FLUX models. If we get here, but the model is definitely a FLUX - # model, we should figure out a good fallback value. - raise NotAMatch(cls, "unable to determine model variant from state dict") - - return variant - - @classmethod - def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: - has_main_model_keys = _has_main_keys(mod.load_state_dict()) - if not has_main_model_keys: - raise NotAMatch(cls, "state dict does not look like a main model") - - @classmethod - def _validate_does_not_look_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: - has_bnb_nf4_keys = _has_bnb_nf4_keys(mod.load_state_dict()) - if has_bnb_nf4_keys: - raise NotAMatch(cls, "state dict looks like bnb quantized nf4") - - @classmethod - def _validate_does_not_look_like_gguf_quantized(cls, mod: ModelOnDisk): - has_ggml_tensors = _has_ggml_tensors(mod.load_state_dict()) - if has_ggml_tensors: - raise NotAMatch(cls, "state dict looks like GGUF quantized") - - -class Main_BnBNF4_FLUX_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base): - """Model config for main checkpoint models.""" - - base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - format: Literal[ModelFormat.BnbQuantizednf4b] = Field(default=ModelFormat.BnbQuantizednf4b) - - variant: FluxVariantType = Field() - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_looks_like_main_model(mod) - - cls._validate_model_looks_like_bnb_quantized(mod) - - variant = fields.get("variant") or cls._get_variant_or_raise(mod) - - return cls(**fields, variant=variant) - - @classmethod - def _get_variant_or_raise(cls, mod: ModelOnDisk) -> FluxVariantType: - # FLUX Model variant types are distinguished by input channels and the presence of certain keys. - state_dict = mod.load_state_dict() - variant = _get_flux_variant(state_dict) - - if variant is None: - # TODO(psyche): Should we have a graceful fallback here? Previously we fell back to the "normal" variant, - # but this variant is no longer used for FLUX models. If we get here, but the model is definitely a FLUX - # model, we should figure out a good fallback value. - raise NotAMatch(cls, "unable to determine model variant from state dict") - - return variant - - @classmethod - def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: - has_main_model_keys = _has_main_keys(mod.load_state_dict()) - if not has_main_model_keys: - raise NotAMatch(cls, "state dict does not look like a main model") - - @classmethod - def _validate_model_looks_like_bnb_quantized(cls, mod: ModelOnDisk) -> None: - has_bnb_nf4_keys = _has_bnb_nf4_keys(mod.load_state_dict()) - if not has_bnb_nf4_keys: - raise NotAMatch(cls, "state dict does not look like bnb quantized nf4") - - -class Main_GGUF_FLUX_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base): - """Model config for main checkpoint models.""" - - base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - format: Literal[ModelFormat.GGUFQuantized] = Field(default=ModelFormat.GGUFQuantized) - - variant: FluxVariantType = Field() - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_looks_like_main_model(mod) - - cls._validate_looks_like_gguf_quantized(mod) - - variant = fields.get("variant") or cls._get_variant_or_raise(mod) - - return cls(**fields, variant=variant) - - @classmethod - def _get_variant_or_raise(cls, mod: ModelOnDisk) -> FluxVariantType: - # FLUX Model variant types are distinguished by input channels and the presence of certain keys. - state_dict = mod.load_state_dict() - variant = _get_flux_variant(state_dict) - - if variant is None: - # TODO(psyche): Should we have a graceful fallback here? Previously we fell back to the "normal" variant, - # but this variant is no longer used for FLUX models. If we get here, but the model is definitely a FLUX - # model, we should figure out a good fallback value. - raise NotAMatch(cls, "unable to determine model variant from state dict") - - return variant - - @classmethod - def _validate_looks_like_main_model(cls, mod: ModelOnDisk) -> None: - has_main_model_keys = _has_main_keys(mod.load_state_dict()) - if not has_main_model_keys: - raise NotAMatch(cls, "state dict does not look like a main model") - - @classmethod - def _validate_looks_like_gguf_quantized(cls, mod: ModelOnDisk) -> None: - has_ggml_tensors = _has_ggml_tensors(mod.load_state_dict()) - if not has_ggml_tensors: - raise NotAMatch(cls, "state dict does not look like GGUF quantized") - - -class Main_Diffusers_Config_Base(Diffusers_Config_Base, Main_Config_Base): - prediction_type: SchedulerPredictionType = Field() - variant: ModelVariantType = Field() - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - _validate_class_name( - cls, - common_config_paths(mod.path), - { - # SD 1.x and 2.x - "StableDiffusionPipeline", - "StableDiffusionInpaintPipeline", - # SDXL - "StableDiffusionXLPipeline", - "StableDiffusionXLInpaintPipeline", - # SDXL Refiner - "StableDiffusionXLImg2ImgPipeline", - # TODO(psyche): Do we actually support LCM models? I don't see using this class anywhere in the codebase. - "LatentConsistencyModelPipeline", - }, - ) - - cls._validate_base(mod) - - variant = fields.get("variant") or cls._get_variant_or_raise(mod) - - prediction_type = fields.get("prediction_type") or cls._get_scheduler_prediction_type_or_raise(mod) - - repo_variant = fields.get("repo_variant") or cls._get_repo_variant_or_raise(mod) - - return cls( - **fields, - variant=variant, - prediction_type=prediction_type, - repo_variant=repo_variant, - ) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: - # Handle pipelines with a UNet (i.e SD 1.x, SD2.x, SDXL). - unet_config_path = mod.path / "unet" / "config.json" - if unet_config_path.exists(): - with open(unet_config_path) as file: - unet_conf = json.load(file) - cross_attention_dim = unet_conf.get("cross_attention_dim") - match cross_attention_dim: - case 768: - return BaseModelType.StableDiffusion1 - case 1024: - return BaseModelType.StableDiffusion2 - case 1280: - return BaseModelType.StableDiffusionXLRefiner - case 2048: - return BaseModelType.StableDiffusionXL - case _: - raise NotAMatch(cls, f"unrecognized cross_attention_dim {cross_attention_dim}") - - raise NotAMatch(cls, "unable to determine base type") - - @classmethod - def _get_scheduler_prediction_type_or_raise(cls, mod: ModelOnDisk) -> SchedulerPredictionType: - scheduler_conf = _get_config_or_raise(cls, mod.path / "scheduler" / "scheduler_config.json") - - # TODO(psyche): Is epsilon the right default or should we raise if it's not present? - prediction_type = scheduler_conf.get("prediction_type", "epsilon") - - match prediction_type: - case "v_prediction": - return SchedulerPredictionType.VPrediction - case "epsilon": - return SchedulerPredictionType.Epsilon - case _: - raise NotAMatch(cls, f"unrecognized scheduler prediction_type {prediction_type}") - - @classmethod - def _get_variant_or_raise(cls, mod: ModelOnDisk) -> ModelVariantType: - base = cls.model_fields["base"].default - unet_config = _get_config_or_raise(cls, mod.path / "unet" / "config.json") - in_channels = unet_config.get("in_channels") - - match in_channels: - case 4: - return ModelVariantType.Normal - case 5: - # Only SD2 has a depth variant - assert base is BaseModelType.StableDiffusion2, f"unexpected unet in_channels 5 for base '{base}'" - return ModelVariantType.Depth - case 9: - return ModelVariantType.Inpaint - case _: - raise NotAMatch(cls, f"unrecognized unet in_channels {in_channels} for base '{base}'") - - -class Main_Diffusers_SD1_Config(Main_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(BaseModelType.StableDiffusion1) - - -class Main_Diffusers_SD2_Config(Main_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion2] = Field(BaseModelType.StableDiffusion2) - - -class Main_Diffusers_SDXL_Config(Main_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(BaseModelType.StableDiffusionXL) - - -class Main_Diffusers_SDXLRefiner_Config(Main_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXLRefiner] = Field(BaseModelType.StableDiffusionXLRefiner) - - -class Main_Diffusers_SD3_Config(Diffusers_Config_Base, Main_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion3] = Field(BaseModelType.StableDiffusion3) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - # This check implies the base type - no further validation needed. - _validate_class_name( - cls, - common_config_paths(mod.path), - { - "StableDiffusion3Pipeline", - "SD3Transformer2DModel", - }, - ) - - submodels = fields.get("submodels") or cls._get_submodels_or_raise(mod) - - repo_variant = fields.get("repo_variant") or cls._get_repo_variant_or_raise(mod) - - return cls( - **fields, - submodels=submodels, - repo_variant=repo_variant, - ) - - @classmethod - def _get_submodels_or_raise(cls, mod: ModelOnDisk) -> dict[SubModelType, SubmodelDefinition]: - # Example: https://huggingface.co/stabilityai/stable-diffusion-3.5-medium/blob/main/model_index.json - config = _get_config_or_raise(cls, common_config_paths(mod.path)) - - submodels: dict[SubModelType, SubmodelDefinition] = {} - - for key, value in config.items(): - # Anything that starts with an underscore is top-level metadata, not a submodel - if key.startswith("_") or not (isinstance(value, list) and len(value) == 2): - continue - # The key is something like "transformer" and is a submodel - it will be in a dir of the same name. - # The value value is something like ["diffusers", "SD3Transformer2DModel"] - _library_name, class_name = value - - match class_name: - case "CLIPTextModelWithProjection": - model_type = ModelType.CLIPEmbed - path_or_prefix = (mod.path / key).resolve().as_posix() - - # We need to read the config to determine the variant of the CLIP model. - clip_embed_config = _get_config_or_raise( - cls, {mod.path / key / "config.json", mod.path / key / "model_index.json"} - ) - variant = _get_clip_variant_type_from_config(clip_embed_config) - submodels[SubModelType(key)] = SubmodelDefinition( - path_or_prefix=path_or_prefix, - model_type=model_type, - variant=variant, - ) - case "SD3Transformer2DModel": - model_type = ModelType.Main - path_or_prefix = (mod.path / key).resolve().as_posix() - variant = None - submodels[SubModelType(key)] = SubmodelDefinition( - path_or_prefix=path_or_prefix, - model_type=model_type, - variant=variant, - ) - case _: - pass - - return submodels - - -class Main_Diffusers_CogView4_Config(Diffusers_Config_Base, Main_Config_Base, Config_Base): - base: Literal[BaseModelType.CogView4] = Field(BaseModelType.CogView4) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - # This check implies the base type - no further validation needed. - _validate_class_name( - cls, - common_config_paths(mod.path), - { - "CogView4Pipeline", - }, - ) - - repo_variant = fields.get("repo_variant") or cls._get_repo_variant_or_raise(mod) - - return cls( - **fields, - repo_variant=repo_variant, - ) - - -class IPAdapter_Config_Base(ABC, BaseModel): - type: Literal[ModelType.IPAdapter] = Field(default=ModelType.IPAdapter) - - -class IPAdapter_InvokeAI_Config_Base(IPAdapter_Config_Base): - """Model config for IP Adapter diffusers format models.""" - - format: Literal[ModelFormat.InvokeAI] = Field(default=ModelFormat.InvokeAI) - - # TODO(ryand): Should we deprecate this field? From what I can tell, it hasn't been probed correctly for a long - # time. Need to go through the history to make sure I'm understanding this fully. - image_encoder_model_id: str = Field() - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_has_weights_file(mod) - - cls._validate_has_image_encoder_metadata_file(mod) - - cls._validate_base(mod) - - return cls(**fields) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _validate_has_weights_file(cls, mod: ModelOnDisk) -> None: - weights_file = mod.path / "ip_adapter.bin" - if not weights_file.exists(): - raise NotAMatch(cls, "missing ip_adapter.bin weights file") - - @classmethod - def _validate_has_image_encoder_metadata_file(cls, mod: ModelOnDisk) -> None: - image_encoder_metadata_file = mod.path / "image_encoder.txt" - if not image_encoder_metadata_file.exists(): - raise NotAMatch(cls, "missing image_encoder.txt metadata file") - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: - state_dict = mod.load_state_dict() - - try: - cross_attention_dim = state_dict["ip_adapter"]["1.to_k_ip.weight"].shape[-1] - except Exception as e: - raise NotAMatch(cls, f"unable to determine cross attention dimension: {e}") from e - - match cross_attention_dim: - case 1280: - return BaseModelType.StableDiffusionXL - case 768: - return BaseModelType.StableDiffusion1 - case 1024: - return BaseModelType.StableDiffusion2 - case _: - raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") - - -class IPAdapter_InvokeAI_SD1_Config(IPAdapter_InvokeAI_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) - - -class IPAdapter_InvokeAI_SD2_Config(IPAdapter_InvokeAI_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) - - -class IPAdapter_InvokeAI_SDXL_Config(IPAdapter_InvokeAI_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class IPAdapter_Checkpoint_Config_Base(IPAdapter_Config_Base): - """Model config for IP Adapter checkpoint format models.""" - - format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_looks_like_ip_adapter(mod) - - cls._validate_base(mod) - - return cls(**fields) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _validate_looks_like_ip_adapter(cls, mod: ModelOnDisk) -> None: - if not has_any_keys_starting_with( - mod.load_state_dict(), - { - "image_proj.", - "ip_adapter.", - # XLabs FLUX IP-Adapter models have keys startinh with "ip_adapter_proj_model.". - "ip_adapter_proj_model.", - }, - ): - raise NotAMatch(cls, "model does not match Checkpoint IP Adapter heuristics") - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: - state_dict = mod.load_state_dict() - - if is_state_dict_xlabs_ip_adapter(state_dict): - return BaseModelType.Flux - - try: - cross_attention_dim = state_dict["ip_adapter.1.to_k_ip.weight"].shape[-1] - except Exception as e: - raise NotAMatch(cls, f"unable to determine cross attention dimension: {e}") from e - - match cross_attention_dim: - case 1280: - return BaseModelType.StableDiffusionXL - case 768: - return BaseModelType.StableDiffusion1 - case 1024: - return BaseModelType.StableDiffusion2 - case _: - raise NotAMatch(cls, f"unrecognized cross attention dimension {cross_attention_dim}") - - -class IPAdapter_Checkpoint_SD1_Config(IPAdapter_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) - - -class IPAdapter_Checkpoint_SD2_Config(IPAdapter_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion2] = Field(default=BaseModelType.StableDiffusion2) - - -class IPAdapter_Checkpoint_SDXL_Config(IPAdapter_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class IPAdapter_Checkpoint_FLUX_Config(IPAdapter_Checkpoint_Config_Base, Config_Base): - base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - - -def _get_clip_variant_type_from_config(config: dict[str, Any]) -> ClipVariantType | None: - try: - hidden_size = config.get("hidden_size") - match hidden_size: - case 1280: - return ClipVariantType.G - case 768: - return ClipVariantType.L - case _: - return None - except Exception: - return None - - -class CLIPEmbed_Diffusers_Config_Base(Diffusers_Config_Base): - base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) - type: Literal[ModelType.CLIPEmbed] = Field(default=ModelType.CLIPEmbed) - format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - _validate_class_name( - cls, - { - mod.path / "config.json", - mod.path / "text_encoder" / "config.json", - }, - { - "CLIPModel", - "CLIPTextModel", - "CLIPTextModelWithProjection", - }, - ) - - cls._validate_variant(mod) - - return cls(**fields) - - @classmethod - def _validate_variant(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model variant does not match this config class.""" - expected_variant = cls.model_fields["variant"].default - config = _get_config_or_raise( - cls, - { - mod.path / "config.json", - mod.path / "text_encoder" / "config.json", - }, - ) - recognized_variant = _get_clip_variant_type_from_config(config) - - if recognized_variant is None: - raise NotAMatch(cls, "unable to determine CLIP variant from config") - - if expected_variant is not recognized_variant: - raise NotAMatch(cls, f"variant is {recognized_variant}, not {expected_variant}") - - -class CLIPEmbed_Diffusers_G_Config(CLIPEmbed_Diffusers_Config_Base, Config_Base): - variant: Literal[ClipVariantType.G] = Field(default=ClipVariantType.G) - - -class CLIPEmbed_Diffusers_L_Config(CLIPEmbed_Diffusers_Config_Base, Config_Base): - variant: Literal[ClipVariantType.L] = Field(default=ClipVariantType.L) - - -class CLIPVision_Diffusers_Config(Diffusers_Config_Base, Config_Base): - """Model config for CLIPVision.""" - - base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) - type: Literal[ModelType.CLIPVision] = Field(default=ModelType.CLIPVision) - format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - _validate_class_name( - cls, - common_config_paths(mod.path), - { - "CLIPVisionModelWithProjection", - }, - ) - - return cls(**fields) - - -class T2IAdapter_Diffusers_Config_Base(Diffusers_Config_Base, ControlAdapter_Config_Base): - """Model config for T2I.""" - - type: Literal[ModelType.T2IAdapter] = Field(default=ModelType.T2IAdapter) - format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - _validate_class_name( - cls, - common_config_paths(mod.path), - { - "T2IAdapter", - }, - ) - - cls._validate_base(mod) - - return cls(**fields) - - @classmethod - def _validate_base(cls, mod: ModelOnDisk) -> None: - """Raise `NotAMatch` if the model base does not match this config class.""" - expected_base = cls.model_fields["base"].default - recognized_base = cls._get_base_or_raise(mod) - if expected_base is not recognized_base: - raise NotAMatch(cls, f"base is {recognized_base}, not {expected_base}") - - @classmethod - def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: - config = _get_config_or_raise(cls, common_config_paths(mod.path)) - - adapter_type = config.get("adapter_type") - - match adapter_type: - case "full_adapter_xl": - return BaseModelType.StableDiffusionXL - case "full_adapter" | "light_adapter": - return BaseModelType.StableDiffusion1 - case _: - raise NotAMatch(cls, f"unrecognized adapter_type '{adapter_type}'") - - -class T2IAdapter_Diffusers_SD1_Config(T2IAdapter_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusion1] = Field(default=BaseModelType.StableDiffusion1) - - -class T2IAdapter_Diffusers_SDXL_Config(T2IAdapter_Diffusers_Config_Base, Config_Base): - base: Literal[BaseModelType.StableDiffusionXL] = Field(default=BaseModelType.StableDiffusionXL) - - -class Spandrel_Checkpoint_Config(Config_Base): - """Model config for Spandrel Image to Image models.""" - - base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) - type: Literal[ModelType.SpandrelImageToImage] = Field(default=ModelType.SpandrelImageToImage) - format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - cls._validate_spandrel_loads_model(mod) - - return cls(**fields) - - @classmethod - def _validate_spandrel_loads_model(cls, mod: ModelOnDisk) -> None: - try: - # It would be nice to avoid having to load the Spandrel model from disk here. A couple of options were - # explored to avoid this: - # 1. Call `SpandrelImageToImageModel.load_from_state_dict(ckpt)`, where `ckpt` is a state_dict on the meta - # device. Unfortunately, some Spandrel models perform operations during initialization that are not - # supported on meta tensors. - # 2. Spandrel has internal logic to determine a model's type from its state_dict before loading the model. - # This logic is not exposed in spandrel's public API. We could copy the logic here, but then we have to - # maintain it, and the risk of false positive detections is higher. - SpandrelImageToImageModel.load_from_file(mod.path) - except Exception as e: - raise NotAMatch(cls, "model does not match SpandrelImageToImage heuristics") from e - - -class SigLIP_Diffusers_Config(Diffusers_Config_Base, Config_Base): - """Model config for SigLIP.""" - - type: Literal[ModelType.SigLIP] = Field(default=ModelType.SigLIP) - format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - _validate_class_name( - cls, - common_config_paths(mod.path), - { - "SiglipModel", - }, - ) - - return cls(**fields) - - -class FLUXRedux_Checkpoint_Config(Config_Base): - """Model config for FLUX Tools Redux model.""" - - type: Literal[ModelType.FluxRedux] = Field(default=ModelType.FluxRedux) - format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_file(cls, mod) - - _validate_override_fields(cls, fields) - - if not is_state_dict_likely_flux_redux(mod.load_state_dict()): - raise NotAMatch(cls, "model does not match FLUX Tools Redux heuristics") - - return cls(**fields) - - -class LlavaOnevision_Diffusers_Config(Diffusers_Config_Base, Config_Base): - """Model config for Llava Onevision models.""" - - type: Literal[ModelType.LlavaOnevision] = Field(default=ModelType.LlavaOnevision) - base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) - variant: Literal[ModelVariantType.Normal] = Field(default=ModelVariantType.Normal) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - _validate_is_dir(cls, mod) - - _validate_override_fields(cls, fields) - - _validate_class_name( - cls, - common_config_paths(mod.path), - { - "LlavaOnevisionForConditionalGeneration", - }, - ) - - return cls(**fields) - - -class ExternalAPI_Config_Base(ABC, BaseModel): - """Model config for API-based models.""" - - format: Literal[ModelFormat.Api] = Field(default=ModelFormat.Api) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - raise NotAMatch(cls, "External API models cannot be built from disk") - - -class ExternalAPI_ChatGPT4o_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): - base: Literal[BaseModelType.ChatGPT4o] = Field(default=BaseModelType.ChatGPT4o) - - -class ExternalAPI_Gemini2_5_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): - base: Literal[BaseModelType.Gemini2_5] = Field(default=BaseModelType.Gemini2_5) - - -class ExternalAPI_Imagen3_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): - base: Literal[BaseModelType.Imagen3] = Field(default=BaseModelType.Imagen3) - - -class ExternalAPI_Imagen4_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): - base: Literal[BaseModelType.Imagen4] = Field(default=BaseModelType.Imagen4) - - -class ExternalAPI_FluxKontext_Config(ExternalAPI_Config_Base, Main_Config_Base, Config_Base): - base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) - - -class VideoConfigBase(ABC, BaseModel): - type: Literal[ModelType.Video] = Field(default=ModelType.Video) - trigger_phrases: set[str] | None = Field(description="Set of trigger phrases for this model", default=None) - default_settings: MainModelDefaultSettings | None = Field( - description="Default settings for this model", default=None - ) - - -class ExternalAPI_Veo3_Config(ExternalAPI_Config_Base, VideoConfigBase, Config_Base): - base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) - - -class ExternalAPI_Runway_Config(ExternalAPI_Config_Base, VideoConfigBase, Config_Base): - base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) - - -# The types are listed explicitly because IDEs/LSPs can't identify the correct types -# when AnyModelConfig is constructed dynamically using ModelConfigBase.all_config_classes -AnyModelConfig = Annotated[ - Union[ - # Main (Pipeline) - diffusers format - Annotated[Main_Diffusers_SD1_Config, Main_Diffusers_SD1_Config.get_tag()], - Annotated[Main_Diffusers_SD2_Config, Main_Diffusers_SD2_Config.get_tag()], - Annotated[Main_Diffusers_SDXL_Config, Main_Diffusers_SDXL_Config.get_tag()], - Annotated[Main_Diffusers_SDXLRefiner_Config, Main_Diffusers_SDXLRefiner_Config.get_tag()], - Annotated[Main_Diffusers_SD3_Config, Main_Diffusers_SD3_Config.get_tag()], - Annotated[Main_Diffusers_CogView4_Config, Main_Diffusers_CogView4_Config.get_tag()], - # Main (Pipeline) - checkpoint format - Annotated[Main_Checkpoint_SD1_Config, Main_Checkpoint_SD1_Config.get_tag()], - Annotated[Main_Checkpoint_SD2_Config, Main_Checkpoint_SD2_Config.get_tag()], - Annotated[Main_Checkpoint_SDXL_Config, Main_Checkpoint_SDXL_Config.get_tag()], - Annotated[Main_Checkpoint_SDXLRefiner_Config, Main_Checkpoint_SDXLRefiner_Config.get_tag()], - Annotated[Main_Checkpoint_FLUX_Config, Main_Checkpoint_FLUX_Config.get_tag()], - # Main (Pipeline) - quantized formats - Annotated[Main_BnBNF4_FLUX_Config, Main_BnBNF4_FLUX_Config.get_tag()], - Annotated[Main_GGUF_FLUX_Config, Main_GGUF_FLUX_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()], - Annotated[VAE_Checkpoint_SDXL_Config, VAE_Checkpoint_SDXL_Config.get_tag()], - Annotated[VAE_Checkpoint_FLUX_Config, VAE_Checkpoint_FLUX_Config.get_tag()], - # VAE - diffusers format - Annotated[VAE_Diffusers_SD1_Config, VAE_Diffusers_SD1_Config.get_tag()], - Annotated[VAE_Diffusers_SDXL_Config, VAE_Diffusers_SDXL_Config.get_tag()], - # ControlNet - checkpoint format - Annotated[ControlNet_Checkpoint_SD1_Config, ControlNet_Checkpoint_SD1_Config.get_tag()], - Annotated[ControlNet_Checkpoint_SD2_Config, ControlNet_Checkpoint_SD2_Config.get_tag()], - Annotated[ControlNet_Checkpoint_SDXL_Config, ControlNet_Checkpoint_SDXL_Config.get_tag()], - Annotated[ControlNet_Checkpoint_FLUX_Config, ControlNet_Checkpoint_FLUX_Config.get_tag()], - # ControlNet - diffusers format - Annotated[ControlNet_Diffusers_SD1_Config, ControlNet_Diffusers_SD1_Config.get_tag()], - Annotated[ControlNet_Diffusers_SD2_Config, ControlNet_Diffusers_SD2_Config.get_tag()], - Annotated[ControlNet_Diffusers_SDXL_Config, ControlNet_Diffusers_SDXL_Config.get_tag()], - Annotated[ControlNet_Diffusers_FLUX_Config, ControlNet_Diffusers_FLUX_Config.get_tag()], - # LoRA - LyCORIS format - Annotated[LoRA_LyCORIS_SD1_Config, LoRA_LyCORIS_SD1_Config.get_tag()], - Annotated[LoRA_LyCORIS_SD2_Config, LoRA_LyCORIS_SD2_Config.get_tag()], - Annotated[LoRA_LyCORIS_SDXL_Config, LoRA_LyCORIS_SDXL_Config.get_tag()], - Annotated[LoRA_LyCORIS_FLUX_Config, LoRA_LyCORIS_FLUX_Config.get_tag()], - # LoRA - OMI format - Annotated[LoRA_OMI_SDXL_Config, LoRA_OMI_SDXL_Config.get_tag()], - Annotated[LoRA_OMI_FLUX_Config, LoRA_OMI_FLUX_Config.get_tag()], - # LoRA - diffusers format - Annotated[LoRA_Diffusers_SD1_Config, LoRA_Diffusers_SD1_Config.get_tag()], - Annotated[LoRA_Diffusers_SD2_Config, LoRA_Diffusers_SD2_Config.get_tag()], - Annotated[LoRA_Diffusers_SDXL_Config, LoRA_Diffusers_SDXL_Config.get_tag()], - Annotated[LoRA_Diffusers_FLUX_Config, LoRA_Diffusers_FLUX_Config.get_tag()], - # ControlLoRA - diffusers format - Annotated[ControlLoRA_LyCORIS_FLUX_Config, ControlLoRA_LyCORIS_FLUX_Config.get_tag()], - # T5 Encoder - all formats - Annotated[T5Encoder_T5Encoder_Config, T5Encoder_T5Encoder_Config.get_tag()], - Annotated[T5Encoder_BnBLLMint8_Config, T5Encoder_BnBLLMint8_Config.get_tag()], - # TI - file format - Annotated[TI_File_SD1_Config, TI_File_SD1_Config.get_tag()], - Annotated[TI_File_SD2_Config, TI_File_SD2_Config.get_tag()], - Annotated[TI_File_SDXL_Config, TI_File_SDXL_Config.get_tag()], - # TI - folder format - Annotated[TI_Folder_SD1_Config, TI_Folder_SD1_Config.get_tag()], - Annotated[TI_Folder_SD2_Config, TI_Folder_SD2_Config.get_tag()], - Annotated[TI_Folder_SDXL_Config, TI_Folder_SDXL_Config.get_tag()], - # IP Adapter - InvokeAI format - Annotated[IPAdapter_InvokeAI_SD1_Config, IPAdapter_InvokeAI_SD1_Config.get_tag()], - Annotated[IPAdapter_InvokeAI_SD2_Config, IPAdapter_InvokeAI_SD2_Config.get_tag()], - Annotated[IPAdapter_InvokeAI_SDXL_Config, IPAdapter_InvokeAI_SDXL_Config.get_tag()], - # IP Adapter - checkpoint format - Annotated[IPAdapter_Checkpoint_SD1_Config, IPAdapter_Checkpoint_SD1_Config.get_tag()], - Annotated[IPAdapter_Checkpoint_SD2_Config, IPAdapter_Checkpoint_SD2_Config.get_tag()], - Annotated[IPAdapter_Checkpoint_SDXL_Config, IPAdapter_Checkpoint_SDXL_Config.get_tag()], - Annotated[IPAdapter_Checkpoint_FLUX_Config, IPAdapter_Checkpoint_FLUX_Config.get_tag()], - # T2I Adapter - diffusers format - Annotated[T2IAdapter_Diffusers_SD1_Config, T2IAdapter_Diffusers_SD1_Config.get_tag()], - Annotated[T2IAdapter_Diffusers_SDXL_Config, T2IAdapter_Diffusers_SDXL_Config.get_tag()], - # Misc models - Annotated[Spandrel_Checkpoint_Config, Spandrel_Checkpoint_Config.get_tag()], - Annotated[CLIPEmbed_Diffusers_G_Config, CLIPEmbed_Diffusers_G_Config.get_tag()], - Annotated[CLIPEmbed_Diffusers_L_Config, CLIPEmbed_Diffusers_L_Config.get_tag()], - Annotated[CLIPVision_Diffusers_Config, CLIPVision_Diffusers_Config.get_tag()], - Annotated[SigLIP_Diffusers_Config, SigLIP_Diffusers_Config.get_tag()], - Annotated[FLUXRedux_Checkpoint_Config, FLUXRedux_Checkpoint_Config.get_tag()], - Annotated[LlavaOnevision_Diffusers_Config, LlavaOnevision_Diffusers_Config.get_tag()], - # API models - Annotated[ExternalAPI_ChatGPT4o_Config, ExternalAPI_ChatGPT4o_Config.get_tag()], - Annotated[ExternalAPI_Gemini2_5_Config, ExternalAPI_Gemini2_5_Config.get_tag()], - Annotated[ExternalAPI_Imagen3_Config, ExternalAPI_Imagen3_Config.get_tag()], - Annotated[ExternalAPI_Imagen4_Config, ExternalAPI_Imagen4_Config.get_tag()], - Annotated[ExternalAPI_FluxKontext_Config, ExternalAPI_FluxKontext_Config.get_tag()], - Annotated[ExternalAPI_Veo3_Config, ExternalAPI_Veo3_Config.get_tag()], - Annotated[ExternalAPI_Runway_Config, ExternalAPI_Runway_Config.get_tag()], - # Unknown model (fallback) - Annotated[Unknown_Config, Unknown_Config.get_tag()], - ], - Discriminator(Config_Base.get_model_discriminator_value), -] - -AnyModelConfigValidator = TypeAdapter[AnyModelConfig](AnyModelConfig) - - -class ModelConfigFactory: - @staticmethod - def make_config(model_data: Dict[str, Any], timestamp: Optional[float] = None) -> AnyModelConfig: - """Return the appropriate config object from raw dict values.""" - model = AnyModelConfigValidator.validate_python(model_data) - if isinstance(model, Checkpoint_Config_Base) and timestamp: - model.converted_at = timestamp - validate_hash(model.hash) - return model - - @staticmethod - def build_common_fields( - mod: ModelOnDisk, - overrides: dict[str, Any] | None = None, - ) -> dict[str, Any]: - """Builds the common fields for all model configs. - - Args: - mod: The model on disk to extract fields from. - overrides: A optional dictionary of fields to override. These fields will take precedence over the values - extracted from the model on disk. - - - Casts string fields to their Enum types. - - Does not validate the fields against the model config schema. - """ - - _overrides: dict[str, Any] = overrides or {} - fields: dict[str, Any] = {} - - if "type" in _overrides: - fields["type"] = ModelType(_overrides["type"]) - - if "format" in _overrides: - fields["format"] = ModelFormat(_overrides["format"]) - - if "base" in _overrides: - fields["base"] = BaseModelType(_overrides["base"]) - - if "source_type" in _overrides: - fields["source_type"] = ModelSourceType(_overrides["source_type"]) - - if "variant" in _overrides: - fields["variant"] = variant_type_adapter.validate_strings(_overrides["variant"]) - - fields["path"] = mod.path.as_posix() - fields["source"] = _overrides.get("source") or fields["path"] - fields["source_type"] = _overrides.get("source_type") or ModelSourceType.Path - fields["name"] = _overrides.get("name") or mod.name - fields["hash"] = _overrides.get("hash") or mod.hash() - fields["key"] = _overrides.get("key") or uuid_string() - fields["description"] = _overrides.get("description") - fields["file_size"] = _overrides.get("file_size") or mod.size() - - return fields - - @staticmethod - def from_model_on_disk( - mod: str | Path | ModelOnDisk, - overrides: dict[str, Any] | None = None, - hash_algo: HASHING_ALGORITHMS = "blake3_single", - ) -> AnyModelConfig: - """ - Returns the best matching ModelConfig instance from a model's file/folder path. - Raises InvalidModelConfigException if no valid configuration is found. - Created to deprecate ModelProbe.probe - """ - if isinstance(mod, Path | str): - mod = ModelOnDisk(Path(mod), hash_algo) - - # We will always need these fields to build any model config. - fields = ModelConfigFactory.build_common_fields(mod, overrides) - - # Store results as a mapping of config class to either an instance of that class or an exception - # that was raised when trying to build it. - results: dict[str, AnyModelConfig | Exception] = {} - - # Try to build an instance of each model config class that uses the classify API. - # Each class will either return an instance of itself or raise NotAMatch if it doesn't match. - # Other exceptions may be raised if something unexpected happens during matching or building. - for config_class in Config_Base.CONFIG_CLASSES: - class_name = config_class.__name__ - try: - instance = config_class.from_model_on_disk(mod, fields) - results[class_name] = instance - except NotAMatch as e: - results[class_name] = e - logger.debug(f"No match for {config_class.__name__} on model {mod.name}") - except ValidationError as e: - # This means the model matched, but we couldn't create the pydantic model instance for the config. - # Maybe invalid overrides were provided? - results[class_name] = e - logger.warning(f"Schema validation error for {config_class.__name__} on model {mod.name}: {e}") - except Exception as e: - results[class_name] = e - logger.warning(f"Unexpected exception while matching {mod.name} to {config_class.__name__}: {e}") - - matches = [r for r in results.values() if isinstance(r, Config_Base)] - - if not matches and app_config.allow_unknown_models: - logger.warning(f"Unable to identify model {mod.name}, falling back to Unknown_Config") - return Unknown_Config(**fields) - - if len(matches) > 1: - # We have multiple matches, in which case at most 1 is correct. We need to pick one. - # - # Known cases: - # - SD main models can look like a LoRA when they have merged in LoRA weights. Prefer the main model. - # - SD main models in diffusers format can look like a CLIP Embed; they have a text_encoder folder with - # a config.json file. Prefer the main model. - - # Sort the matching according to known special cases. - def sort_key(m: AnyModelConfig) -> int: - match m.type: - case ModelType.Main: - return 0 - case ModelType.LoRA: - return 1 - case ModelType.CLIPEmbed: - return 2 - case _: - return 3 - - matches.sort(key=sort_key) - logger.warning( - f"Multiple model config classes matched for model {mod.name}: {[type(m).__name__ for m in matches]}. Using {type(matches[0]).__name__}." - ) - - instance = matches[0] - logger.info(f"Model {mod.name} classified as {type(instance).__name__}") - return instance diff --git a/invokeai/backend/model_manager/configs/base.py b/invokeai/backend/model_manager/configs/base.py index 9e997e4bcd9..e67efd20097 100644 --- a/invokeai/backend/model_manager/configs/base.py +++ b/invokeai/backend/model_manager/configs/base.py @@ -191,8 +191,8 @@ def get_model_discriminator_value(v: Any) -> str: else: raise TypeError("Model config discriminator value must be computed from a dict or ModelConfigBase instance") - @abstractmethod @classmethod + @abstractmethod def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: """Given the model on disk and any override fields, attempt to construct an instance of this config class. diff --git a/invokeai/backend/model_manager/configs/controlnet.py b/invokeai/backend/model_manager/configs/controlnet.py index 7db782a862c..630e81fd243 100644 --- a/invokeai/backend/model_manager/configs/controlnet.py +++ b/invokeai/backend/model_manager/configs/controlnet.py @@ -3,14 +3,13 @@ Self, ) -from pydantic import Field +from pydantic import BaseModel, ConfigDict, Field from typing_extensions import Any from invokeai.backend.flux.controlnet.state_dict_utils import ( is_state_dict_instantx_controlnet, is_state_dict_xlabs_controlnet, ) -from invokeai.backend.model_manager.config import ControlAdapterDefaultSettings from invokeai.backend.model_manager.configs.base import Checkpoint_Config_Base, Config_Base, Diffusers_Config_Base from invokeai.backend.model_manager.configs.identification_utils import ( NotAMatchError, @@ -29,6 +28,42 @@ ModelType, ) +MODEL_NAME_TO_PREPROCESSOR = { + "canny": "canny_image_processor", + "mlsd": "mlsd_image_processor", + "depth": "depth_anything_image_processor", + "bae": "normalbae_image_processor", + "normal": "normalbae_image_processor", + "sketch": "pidi_image_processor", + "scribble": "lineart_image_processor", + "lineart anime": "lineart_anime_image_processor", + "lineart_anime": "lineart_anime_image_processor", + "lineart": "lineart_image_processor", + "soft": "hed_image_processor", + "softedge": "hed_image_processor", + "hed": "hed_image_processor", + "shuffle": "content_shuffle_image_processor", + "pose": "dw_openpose_image_processor", + "mediapipe": "mediapipe_face_processor", + "pidi": "pidi_image_processor", + "zoe": "zoe_depth_image_processor", + "color": "color_map_image_processor", +} + + +class ControlAdapterDefaultSettings(BaseModel): + # This could be narrowed to controlnet processor nodes, but they change. Leaving this a string is safer. + preprocessor: str | None + model_config = ConfigDict(extra="forbid") + + @classmethod + def from_model_name(cls, model_name: str) -> Self: + for k, v in MODEL_NAME_TO_PREPROCESSOR.items(): + model_name_lower = model_name.lower() + if k in model_name_lower: + return cls(preprocessor=v) + return cls(preprocessor=None) + class ControlNet_Diffusers_Config_Base(Diffusers_Config_Base): """Model config for ControlNet models (diffusers version).""" diff --git a/invokeai/backend/model_manager/configs/factory.py b/invokeai/backend/model_manager/configs/factory.py index 27b6f252f1d..6ab16cd5f69 100644 --- a/invokeai/backend/model_manager/configs/factory.py +++ b/invokeai/backend/model_manager/configs/factory.py @@ -14,6 +14,7 @@ from invokeai.backend.model_manager.configs.clip_embed import CLIPEmbed_Diffusers_G_Config, CLIPEmbed_Diffusers_L_Config from invokeai.backend.model_manager.configs.clip_vision import CLIPVision_Diffusers_Config from invokeai.backend.model_manager.configs.controlnet import ( + ControlAdapterDefaultSettings, ControlNet_Checkpoint_FLUX_Config, ControlNet_Checkpoint_SD1_Config, ControlNet_Checkpoint_SD2_Config, @@ -47,6 +48,7 @@ LoRA_LyCORIS_SDXL_Config, LoRA_OMI_FLUX_Config, LoRA_OMI_SDXL_Config, + LoraModelDefaultSettings, ) from invokeai.backend.model_manager.configs.main import ( Main_BnBNF4_FLUX_Config, @@ -67,6 +69,7 @@ Main_ExternalAPI_Imagen3_Config, Main_ExternalAPI_Imagen4_Config, Main_GGUF_FLUX_Config, + MainModelDefaultSettings, Video_ExternalAPI_Runway_Config, Video_ExternalAPI_Veo3_Config, ) @@ -332,9 +335,52 @@ def sort_key(m: AnyModelConfig) -> int: matches.sort(key=sort_key) logger.warning( - f"Multiple model config classes matched for model {mod.name}: {[type(m).__name__ for m in matches]}. Using {type(matches[0]).__name__}." + f"Multiple model config classes matched for model {mod.name}: {[type(m).__name__ for m in matches]}." ) instance = matches[0] logger.info(f"Model {mod.name} classified as {type(instance).__name__}") + + # Now do any post-processing needed for specific model types/bases/etc. + match instance.type: + case ModelType.Main: + match instance.base: + case BaseModelType.StableDiffusion1: + instance.default_settings = MainModelDefaultSettings(width=512, height=512) + case BaseModelType.StableDiffusion2: + instance.default_settings = MainModelDefaultSettings(width=768, height=768) + case BaseModelType.StableDiffusionXL: + instance.default_settings = MainModelDefaultSettings(width=1024, height=1024) + case _: + pass + case ModelType.ControlNet | ModelType.T2IAdapter | ModelType.ControlLoRa: + instance.default_settings = ControlAdapterDefaultSettings.from_model_name(instance.name) + case ModelType.LoRA: + instance.default_settings = LoraModelDefaultSettings() + case _: + pass + return instance + + +MODEL_NAME_TO_PREPROCESSOR = { + "canny": "canny_image_processor", + "mlsd": "mlsd_image_processor", + "depth": "depth_anything_image_processor", + "bae": "normalbae_image_processor", + "normal": "normalbae_image_processor", + "sketch": "pidi_image_processor", + "scribble": "lineart_image_processor", + "lineart anime": "lineart_anime_image_processor", + "lineart_anime": "lineart_anime_image_processor", + "lineart": "lineart_image_processor", + "soft": "hed_image_processor", + "softedge": "hed_image_processor", + "hed": "hed_image_processor", + "shuffle": "content_shuffle_image_processor", + "pose": "dw_openpose_image_processor", + "mediapipe": "mediapipe_face_processor", + "pidi": "pidi_image_processor", + "zoe": "zoe_depth_image_processor", + "color": "color_map_image_processor", +} diff --git a/invokeai/backend/model_manager/configs/lora.py b/invokeai/backend/model_manager/configs/lora.py index 512137b4c3c..24e10c035a9 100644 --- a/invokeai/backend/model_manager/configs/lora.py +++ b/invokeai/backend/model_manager/configs/lora.py @@ -9,10 +9,10 @@ from pydantic import BaseModel, ConfigDict, Field from typing_extensions import Any -from invokeai.backend.model_manager.config import ControlAdapterDefaultSettings from invokeai.backend.model_manager.configs.base import ( Config_Base, ) +from invokeai.backend.model_manager.configs.controlnet import ControlAdapterDefaultSettings from invokeai.backend.model_manager.configs.identification_utils import ( NotAMatchError, raise_for_override_fields, diff --git a/invokeai/backend/model_manager/configs/main.py b/invokeai/backend/model_manager/configs/main.py index ef1ab1fe77f..26f6b5b60e0 100644 --- a/invokeai/backend/model_manager/configs/main.py +++ b/invokeai/backend/model_manager/configs/main.py @@ -685,8 +685,8 @@ class Video_Config_Base(ABC, BaseModel): class Video_ExternalAPI_Veo3_Config(ExternalAPI_Config_Base, Video_Config_Base, Config_Base): - base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) + base: Literal[BaseModelType.Veo3] = Field(default=BaseModelType.Veo3) class Video_ExternalAPI_Runway_Config(ExternalAPI_Config_Base, Video_Config_Base, Config_Base): - base: Literal[BaseModelType.FluxKontext] = Field(default=BaseModelType.FluxKontext) + base: Literal[BaseModelType.Runway] = Field(default=BaseModelType.Runway) diff --git a/invokeai/backend/model_manager/configs/t2i_adapter.py b/invokeai/backend/model_manager/configs/t2i_adapter.py index 865c4dc7638..a1da40e9b4b 100644 --- a/invokeai/backend/model_manager/configs/t2i_adapter.py +++ b/invokeai/backend/model_manager/configs/t2i_adapter.py @@ -6,8 +6,8 @@ from pydantic import Field from typing_extensions import Any -from invokeai.backend.model_manager.config import ControlAdapterDefaultSettings from invokeai.backend.model_manager.configs.base import Config_Base, Diffusers_Config_Base +from invokeai.backend.model_manager.configs.controlnet import ControlAdapterDefaultSettings from invokeai.backend.model_manager.configs.identification_utils import ( NotAMatchError, common_config_paths, diff --git a/invokeai/backend/model_manager/legacy_probe.py b/invokeai/backend/model_manager/legacy_probe.py deleted file mode 100644 index 85a39fd25ef..00000000000 --- a/invokeai/backend/model_manager/legacy_probe.py +++ /dev/null @@ -1,1034 +0,0 @@ -import json -from pathlib import Path -from typing import Any, Callable, Dict, Literal, Optional, Union - -import picklescan.scanner as pscan -import safetensors.torch -import torch - -import invokeai.backend.util.logging as logger -from invokeai.app.services.config.config_default import get_config -from invokeai.app.util.misc import uuid_string -from invokeai.backend.flux.controlnet.state_dict_utils import ( - is_state_dict_instantx_controlnet, - is_state_dict_xlabs_controlnet, -) -from invokeai.backend.flux.flux_state_dict_utils import get_flux_in_channels_from_state_dict -from invokeai.backend.flux.ip_adapter.state_dict_utils import is_state_dict_xlabs_ip_adapter -from invokeai.backend.flux.redux.flux_redux_state_dict_utils import is_state_dict_likely_flux_redux -from invokeai.backend.model_hash.model_hash import HASHING_ALGORITHMS, ModelHash -from invokeai.backend.model_manager.config import ( - AnyModelConfig, - ControlAdapterDefaultSettings, - InvalidModelConfigException, - LoraModelDefaultSettings, - MainModelDefaultSettings, - ModelConfigFactory, - SubmodelDefinition, -) -from invokeai.backend.model_manager.load.model_loaders.generic_diffusers import ConfigLoader -from invokeai.backend.model_manager.model_on_disk import ModelOnDisk -from invokeai.backend.model_manager.taxonomy import ( - AnyVariant, - BaseModelType, - FluxVariantType, - ModelFormat, - ModelRepoVariant, - ModelSourceType, - ModelType, - ModelVariantType, - SchedulerPredictionType, - SubModelType, -) -from invokeai.backend.model_manager.util.model_util import ( - get_clip_variant_type, - lora_token_vector_length, - read_checkpoint_meta, -) -from invokeai.backend.patches.lora_conversions.flux_control_lora_utils import is_state_dict_likely_flux_control -from invokeai.backend.patches.lora_conversions.flux_diffusers_lora_conversion_utils import ( - is_state_dict_likely_in_flux_diffusers_format, -) -from invokeai.backend.patches.lora_conversions.flux_kohya_lora_conversion_utils import ( - is_state_dict_likely_in_flux_kohya_format, -) -from invokeai.backend.patches.lora_conversions.flux_onetrainer_lora_conversion_utils import ( - is_state_dict_likely_in_flux_onetrainer_format, -) -from invokeai.backend.quantization.gguf.ggml_tensor import GGMLTensor -from invokeai.backend.quantization.gguf.loaders import gguf_sd_loader -from invokeai.backend.util.silence_warnings import SilenceWarnings - -CkptType = Dict[str | int, Any] - - -LEGACY_CONFIGS: Dict[BaseModelType, Dict[ModelVariantType, Union[str, Dict[SchedulerPredictionType, str]]]] = { - BaseModelType.StableDiffusion1: { - ModelVariantType.Normal: { - SchedulerPredictionType.Epsilon: "v1-inference.yaml", - SchedulerPredictionType.VPrediction: "v1-inference-v.yaml", - }, - ModelVariantType.Inpaint: "v1-inpainting-inference.yaml", - }, - BaseModelType.StableDiffusion2: { - ModelVariantType.Normal: { - SchedulerPredictionType.Epsilon: "v2-inference.yaml", - SchedulerPredictionType.VPrediction: "v2-inference-v.yaml", - }, - ModelVariantType.Inpaint: { - SchedulerPredictionType.Epsilon: "v2-inpainting-inference.yaml", - SchedulerPredictionType.VPrediction: "v2-inpainting-inference-v.yaml", - }, - ModelVariantType.Depth: "v2-midas-inference.yaml", - }, - BaseModelType.StableDiffusionXL: { - ModelVariantType.Normal: "sd_xl_base.yaml", - ModelVariantType.Inpaint: "sd_xl_inpaint.yaml", - }, - BaseModelType.StableDiffusionXLRefiner: { - ModelVariantType.Normal: "sd_xl_refiner.yaml", - }, -} - - -class ProbeBase(object): - """Base class for probes.""" - - def __init__(self, model_path: Path): - self.model_path = model_path - - def get_base_type(self) -> BaseModelType: - """Get model base type.""" - raise NotImplementedError - - def get_format(self) -> ModelFormat: - """Get model file format.""" - raise NotImplementedError - - def get_variant_type(self) -> AnyVariant | None: - """Get model variant type.""" - return None - - def get_scheduler_prediction_type(self) -> Optional[SchedulerPredictionType]: - """Get model scheduler prediction type.""" - return None - - def get_image_encoder_model_id(self) -> Optional[str]: - """Get image encoder (IP adapters only).""" - return None - - -class ModelProbe(object): - PROBES: Dict[str, Dict[ModelType, type[ProbeBase]]] = { - "diffusers": {}, - "checkpoint": {}, - "onnx": {}, - } - - CLASS2TYPE = { - "FluxPipeline": ModelType.Main, - "StableDiffusionPipeline": ModelType.Main, - "StableDiffusionInpaintPipeline": ModelType.Main, - "StableDiffusionXLPipeline": ModelType.Main, - "StableDiffusionXLImg2ImgPipeline": ModelType.Main, - "StableDiffusionXLInpaintPipeline": ModelType.Main, - "StableDiffusion3Pipeline": ModelType.Main, - "LatentConsistencyModelPipeline": ModelType.Main, - "AutoencoderKL": ModelType.VAE, - "AutoencoderTiny": ModelType.VAE, - "ControlNetModel": ModelType.ControlNet, - "CLIPVisionModelWithProjection": ModelType.CLIPVision, - "T2IAdapter": ModelType.T2IAdapter, - "CLIPModel": ModelType.CLIPEmbed, - "CLIPTextModel": ModelType.CLIPEmbed, - "T5EncoderModel": ModelType.T5Encoder, - "FluxControlNetModel": ModelType.ControlNet, - "SD3Transformer2DModel": ModelType.Main, - "CLIPTextModelWithProjection": ModelType.CLIPEmbed, - "SiglipModel": ModelType.SigLIP, - "LlavaOnevisionForConditionalGeneration": ModelType.LlavaOnevision, - "CogView4Pipeline": ModelType.Main, - } - - TYPE2VARIANT: Dict[ModelType, Callable[[str], Optional[AnyVariant]]] = {ModelType.CLIPEmbed: get_clip_variant_type} - - @classmethod - def register_probe( - cls, format: Literal["diffusers", "checkpoint", "onnx"], model_type: ModelType, probe_class: type[ProbeBase] - ) -> None: - cls.PROBES[format][model_type] = probe_class - - @classmethod - def probe( - cls, model_path: Path, fields: Optional[Dict[str, Any]] = None, hash_algo: HASHING_ALGORITHMS = "blake3_single" - ) -> AnyModelConfig: - """ - Probe the model at model_path and return its configuration record. - - :param model_path: Path to the model file (checkpoint) or directory (diffusers). - :param fields: An optional dictionary that can be used to override probed - fields. Typically used for fields that don't probe well, such as prediction_type. - - Returns: The appropriate model configuration derived from ModelConfigBase. - """ - if fields is None: - fields = {} - - model_path = model_path.resolve() - - format_type = ModelFormat.Diffusers if model_path.is_dir() else ModelFormat.Checkpoint - model_info = None - model_type = ModelType(fields["type"]) if "type" in fields and fields["type"] else None - if not model_type: - if format_type is ModelFormat.Diffusers: - model_type = cls.get_model_type_from_folder(model_path) - else: - model_type = cls.get_model_type_from_checkpoint(model_path) - format_type = ModelFormat.ONNX if model_type == ModelType.ONNX else format_type - - probe_class = cls.PROBES[format_type].get(model_type) - if not probe_class: - raise InvalidModelConfigException(f"Unhandled combination of {format_type} and {model_type}") - - probe = probe_class(model_path) - - fields["source_type"] = fields.get("source_type") or ModelSourceType.Path - fields["source"] = fields.get("source") or model_path.as_posix() - fields["key"] = fields.get("key", uuid_string()) - fields["path"] = model_path.as_posix() - fields["type"] = fields.get("type") or model_type - fields["base"] = fields.get("base") or probe.get_base_type() - variant_func = cls.TYPE2VARIANT.get(fields["type"], None) - fields["variant"] = ( - fields.get("variant") or (variant_func and variant_func(model_path.as_posix())) or probe.get_variant_type() - ) - fields["prediction_type"] = fields.get("prediction_type") or probe.get_scheduler_prediction_type() - fields["image_encoder_model_id"] = fields.get("image_encoder_model_id") or probe.get_image_encoder_model_id() - fields["name"] = fields.get("name") or cls.get_model_name(model_path) - fields["description"] = ( - fields.get("description") or f"{fields['base'].value} {model_type.value} model {fields['name']}" - ) - fields["format"] = ModelFormat(fields.get("format")) if "format" in fields else probe.get_format() - fields["hash"] = fields.get("hash") or ModelHash(algorithm=hash_algo).hash(model_path) - fields["file_size"] = fields.get("file_size") or ModelOnDisk(model_path).size() - - fields["default_settings"] = fields.get("default_settings") - - if not fields["default_settings"]: - if fields["type"] in {ModelType.ControlNet, ModelType.T2IAdapter, ModelType.ControlLoRa}: - fields["default_settings"] = get_default_settings_control_adapters(fields["name"]) - if fields["type"] in {ModelType.LoRA}: - fields["default_settings"] = get_default_settings_lora() - elif fields["type"] is ModelType.Main: - fields["default_settings"] = get_default_settings_main(fields["base"]) - - if format_type == ModelFormat.Diffusers and isinstance(probe, FolderProbeBase): - fields["repo_variant"] = fields.get("repo_variant") or probe.get_repo_variant() - - # additional fields needed for main and controlnet models - if fields["type"] in [ModelType.Main, ModelType.ControlNet, ModelType.VAE] and fields["format"] in [ - ModelFormat.Checkpoint, - ModelFormat.BnbQuantizednf4b, - ModelFormat.GGUFQuantized, - ]: - ckpt_config_path = cls._get_checkpoint_config_path( - model_path, - model_type=fields["type"], - base_type=fields["base"], - variant_type=fields["variant"], - prediction_type=fields["prediction_type"], - ) - fields["config_path"] = str(ckpt_config_path) - - # additional fields needed for main non-checkpoint models - elif fields["type"] == ModelType.Main and fields["format"] in [ - ModelFormat.ONNX, - ModelFormat.Olive, - ModelFormat.Diffusers, - ]: - fields["upcast_attention"] = fields.get("upcast_attention") or ( - fields["base"] == BaseModelType.StableDiffusion2 - and fields["prediction_type"] == SchedulerPredictionType.VPrediction - ) - - get_submodels = getattr(probe, "get_submodels", None) - if fields["base"] == BaseModelType.StableDiffusion3 and callable(get_submodels): - fields["submodels"] = get_submodels() - - model_info = ModelConfigFactory.make_config(fields) - return model_info - - @classmethod - def get_model_name(cls, model_path: Path) -> str: - if model_path.suffix in {".safetensors", ".bin", ".pt", ".ckpt"}: - return model_path.stem - else: - return model_path.name - - @classmethod - def get_model_type_from_checkpoint(cls, model_path: Path, checkpoint: Optional[CkptType] = None) -> ModelType: - if model_path.suffix not in (".bin", ".pt", ".ckpt", ".safetensors", ".pth", ".gguf"): - raise InvalidModelConfigException(f"{model_path}: unrecognized suffix") - - if model_path.name == "learned_embeds.bin": - return ModelType.TextualInversion - - ckpt = checkpoint if checkpoint else read_checkpoint_meta(model_path, scan=True) - ckpt = ckpt.get("state_dict", ckpt) - - if isinstance(ckpt, dict) and is_state_dict_likely_flux_control(ckpt): - return ModelType.ControlLoRa - - if isinstance(ckpt, dict) and is_state_dict_likely_flux_redux(ckpt): - return ModelType.FluxRedux - - for key in [str(k) for k in ckpt.keys()]: - if key.startswith( - ( - "cond_stage_model.", - "first_stage_model.", - "model.diffusion_model.", - # Some FLUX checkpoint files contain transformer keys prefixed with "model.diffusion_model". - # This prefix is typically used to distinguish between multiple models bundled in a single file. - "model.diffusion_model.double_blocks.", - ) - ): - # Keys starting with double_blocks are associated with Flux models - return ModelType.Main - # FLUX models in the official BFL format contain keys with the "double_blocks." prefix, but we must be - # careful to avoid false positives on XLabs FLUX IP-Adapter models. - elif key.startswith("double_blocks.") and "ip_adapter" not in key: - return ModelType.Main - elif key.startswith(("encoder.conv_in", "decoder.conv_in")): - return ModelType.VAE - elif key.startswith(("lora_te_", "lora_unet_", "lora_te1_", "lora_te2_", "lora_transformer_")): - return ModelType.LoRA - # "lora_A.weight" and "lora_B.weight" are associated with models in PEFT format. We don't support all PEFT - # LoRA models, but as of the time of writing, we support Diffusers FLUX PEFT LoRA models. - elif key.endswith(("to_k_lora.up.weight", "to_q_lora.down.weight", "lora_A.weight", "lora_B.weight")): - return ModelType.LoRA - elif key.startswith( - ( - "controlnet", - "control_model", - "input_blocks", - # XLabs FLUX ControlNet models have keys starting with "controlnet_blocks." - # For example: https://huggingface.co/XLabs-AI/flux-controlnet-collections/blob/86ab1e915a389d5857135c00e0d350e9e38a9048/flux-canny-controlnet_v2.safetensors - # TODO(ryand): This is very fragile. XLabs FLUX ControlNet models also contain keys starting with - # "double_blocks.", which we check for above. But, I'm afraid to modify this logic because it is so - # delicate. - "controlnet_blocks", - ) - ): - return ModelType.ControlNet - elif key.startswith( - ( - "image_proj.", - "ip_adapter.", - # XLabs FLUX IP-Adapter models have keys startinh with "ip_adapter_proj_model.". - "ip_adapter_proj_model.", - ) - ): - return ModelType.IPAdapter - elif key in {"emb_params", "string_to_param"}: - return ModelType.TextualInversion - - # diffusers-ti - if len(ckpt) < 10 and all(isinstance(v, torch.Tensor) for v in ckpt.values()): - return ModelType.TextualInversion - - raise InvalidModelConfigException(f"Unable to determine model type for {model_path}") - - @classmethod - def get_model_type_from_folder(cls, folder_path: Path) -> ModelType: - """Get the model type of a hugging-face style folder.""" - class_name = None - error_hint = None - for suffix in ["bin", "safetensors"]: - if (folder_path / f"learned_embeds.{suffix}").exists(): - return ModelType.TextualInversion - if (folder_path / f"pytorch_lora_weights.{suffix}").exists(): - return ModelType.LoRA - if (folder_path / "unet/model.onnx").exists(): - return ModelType.ONNX - if (folder_path / "image_encoder.txt").exists(): - return ModelType.IPAdapter - - config_path = None - for p in [ - folder_path / "model_index.json", # pipeline - folder_path / "config.json", # most diffusers - folder_path / "text_encoder_2" / "config.json", # T5 text encoder - folder_path / "text_encoder" / "config.json", # T5 CLIP - ]: - if p.exists(): - config_path = p - break - - if config_path: - with open(config_path, "r") as file: - conf = json.load(file) - if "_class_name" in conf: - class_name = conf["_class_name"] - elif "architectures" in conf: - class_name = conf["architectures"][0] - else: - class_name = None - else: - error_hint = f"No model_index.json or config.json found in {folder_path}." - - if class_name and (type := cls.CLASS2TYPE.get(class_name)): - return type - else: - error_hint = f"class {class_name} is not one of the supported classes [{', '.join(cls.CLASS2TYPE.keys())}]" - - # give up - raise InvalidModelConfigException( - f"Unable to determine model type for {folder_path}" + (f"; {error_hint}" if error_hint else "") - ) - - @classmethod - def _get_checkpoint_config_path( - cls, - model_path: Path, - model_type: ModelType, - base_type: BaseModelType, - variant_type: ModelVariantType, - prediction_type: SchedulerPredictionType, - ) -> Path: - # look for a YAML file adjacent to the model file first - possible_conf = model_path.with_suffix(".yaml") - if possible_conf.exists(): - return possible_conf.absolute() - - if model_type is ModelType.Main: - if base_type == BaseModelType.Flux: - # TODO: Decide between dev/schnell - checkpoint = ModelProbe._scan_and_load_checkpoint(model_path) - state_dict = checkpoint.get("state_dict") or checkpoint - - # HACK: For FLUX, config_file is used as a key into invokeai.backend.flux.util.params during model - # loading. When FLUX support was first added, it was decided that this was the easiest way to support - # the various FLUX formats rather than adding new model types/formats. Be careful when modifying this in - # the future. - if ( - "guidance_in.out_layer.weight" in state_dict - or "model.diffusion_model.guidance_in.out_layer.weight" in state_dict - ): - if variant_type == ModelVariantType.Normal: - config_file = "flux-dev" - elif variant_type == ModelVariantType.Inpaint: - config_file = "flux-dev-fill" - else: - raise ValueError(f"Unexpected FLUX variant type: {variant_type}") - else: - config_file = "flux-schnell" - else: - config_file = LEGACY_CONFIGS[base_type][variant_type] - if isinstance(config_file, dict): # need another tier for sd-2.x models - config_file = config_file[prediction_type] - config_file = f"stable-diffusion/{config_file}" - elif model_type is ModelType.ControlNet: - config_file = ( - "controlnet/cldm_v15.yaml" - if base_type is BaseModelType.StableDiffusion1 - else "controlnet/cldm_v21.yaml" - ) - elif model_type is ModelType.VAE: - config_file = ( - # For flux, this is a key in invokeai.backend.flux.util.ae_params - # Due to model type and format being the descriminator for model configs this - # is used rather than attempting to support flux with separate model types and format - # If changed in the future, please fix me - "flux" - if base_type is BaseModelType.Flux - else "stable-diffusion/v1-inference.yaml" - if base_type is BaseModelType.StableDiffusion1 - else "stable-diffusion/sd_xl_base.yaml" - if base_type is BaseModelType.StableDiffusionXL - else "stable-diffusion/v2-inference.yaml" - ) - else: - raise InvalidModelConfigException( - f"{model_path}: Unrecognized combination of model_type={model_type}, base_type={base_type}" - ) - return Path(config_file) - - @classmethod - def _scan_and_load_checkpoint(cls, model_path: Path) -> CkptType: - with SilenceWarnings(): - if model_path.suffix.endswith((".ckpt", ".pt", ".pth", ".bin")): - cls._scan_model(model_path.name, model_path) - model = torch.load(model_path, map_location="cpu") - assert isinstance(model, dict) - return model - elif model_path.suffix.endswith(".gguf"): - return gguf_sd_loader(model_path, compute_dtype=torch.float32) - else: - return safetensors.torch.load_file(model_path) - - @classmethod - def _scan_model(cls, model_name: str, checkpoint: Path) -> None: - """ - Apply picklescanner to the indicated checkpoint and issue a warning - and option to exit if an infected file is identified. - """ - # scan model - scan_result = pscan.scan_file_path(checkpoint) - if scan_result.infected_files != 0: - if get_config().unsafe_disable_picklescan: - logger.warning( - f"The model {model_name} is potentially infected by malware, but picklescan is disabled. " - "Proceeding with caution." - ) - else: - raise RuntimeError(f"The model {model_name} is potentially infected by malware. Aborting import.") - if scan_result.scan_err: - if get_config().unsafe_disable_picklescan: - logger.warning( - f"Error scanning the model at {model_name} for malware, but picklescan is disabled. " - "Proceeding with caution." - ) - else: - raise RuntimeError(f"Error scanning the model at {model_name} for malware. Aborting import.") - - -# Probing utilities -MODEL_NAME_TO_PREPROCESSOR = { - "canny": "canny_image_processor", - "mlsd": "mlsd_image_processor", - "depth": "depth_anything_image_processor", - "bae": "normalbae_image_processor", - "normal": "normalbae_image_processor", - "sketch": "pidi_image_processor", - "scribble": "lineart_image_processor", - "lineart anime": "lineart_anime_image_processor", - "lineart_anime": "lineart_anime_image_processor", - "lineart": "lineart_image_processor", - "soft": "hed_image_processor", - "softedge": "hed_image_processor", - "hed": "hed_image_processor", - "shuffle": "content_shuffle_image_processor", - "pose": "dw_openpose_image_processor", - "mediapipe": "mediapipe_face_processor", - "pidi": "pidi_image_processor", - "zoe": "zoe_depth_image_processor", - "color": "color_map_image_processor", -} - - -def get_default_settings_control_adapters(model_name: str) -> Optional[ControlAdapterDefaultSettings]: - for k, v in MODEL_NAME_TO_PREPROCESSOR.items(): - model_name_lower = model_name.lower() - if k in model_name_lower: - return ControlAdapterDefaultSettings(preprocessor=v) - return None - - -def get_default_settings_lora() -> LoraModelDefaultSettings: - return LoraModelDefaultSettings() - - -def get_default_settings_main(model_base: BaseModelType) -> Optional[MainModelDefaultSettings]: - if model_base is BaseModelType.StableDiffusion1 or model_base is BaseModelType.StableDiffusion2: - return MainModelDefaultSettings(width=512, height=512) - elif model_base is BaseModelType.StableDiffusionXL: - return MainModelDefaultSettings(width=1024, height=1024) - # We don't provide defaults for BaseModelType.StableDiffusionXLRefiner, as they are not standalone models. - return None - - -# ##################################################3 -# Checkpoint probing -# ##################################################3 - - -class CheckpointProbeBase(ProbeBase): - def __init__(self, model_path: Path): - super().__init__(model_path) - self.checkpoint = ModelProbe._scan_and_load_checkpoint(model_path) - - def get_format(self) -> ModelFormat: - state_dict = self.checkpoint.get("state_dict") or self.checkpoint - if ( - "double_blocks.0.img_attn.proj.weight.quant_state.bitsandbytes__nf4" in state_dict - or "model.diffusion_model.double_blocks.0.img_attn.proj.weight.quant_state.bitsandbytes__nf4" in state_dict - ): - return ModelFormat.BnbQuantizednf4b - elif any(isinstance(v, GGMLTensor) for v in state_dict.values()): - return ModelFormat.GGUFQuantized - return ModelFormat("checkpoint") - - def get_variant_type(self) -> AnyVariant: - model_type = ModelProbe.get_model_type_from_checkpoint(self.model_path, self.checkpoint) - base_type = self.get_base_type() - if model_type != ModelType.Main: - return ModelVariantType.Normal - state_dict = self.checkpoint.get("state_dict") or self.checkpoint - - if base_type == BaseModelType.Flux: - in_channels = get_flux_in_channels_from_state_dict(state_dict) - - if in_channels is None: - # If we cannot find the in_channels, we assume that this is a normal variant. Log a warning. - logger.warning( - f"{self.model_path} does not have img_in.weight or model.diffusion_model.img_in.weight key. Assuming normal variant." - ) - return ModelVariantType.Normal - - is_flux_dev = ( - "guidance_in.out_layer.weight" in state_dict - or "model.diffusion_model.guidance_in.out_layer.weight" in state_dict - ) - - # FLUX Model variant types are distinguished by input channels: - # - Unquantized Dev and Schnell have in_channels=64 - # - BNB-NF4 Dev and Schnell have in_channels=1 - # - FLUX Fill has in_channels=384 - # - Unsure of quantized FLUX Fill models - # - Unsure of GGUF-quantized models - if is_flux_dev and in_channels == 384: - # This is a FLUX Fill model. FLUX Fill needs special handling throughout the application. The variant - # type is used to determine whether to use the fill model or the base model. - return FluxVariantType.DevFill - elif is_flux_dev: - # Fall back on "normal" variant type for all other FLUX models. - return FluxVariantType.Dev - else: - return FluxVariantType.Schnell - - in_channels = state_dict["model.diffusion_model.input_blocks.0.0.weight"].shape[1] - if in_channels == 9: - return ModelVariantType.Inpaint - elif in_channels == 5: - return ModelVariantType.Depth - elif in_channels == 4: - return ModelVariantType.Normal - else: - raise InvalidModelConfigException( - f"Cannot determine variant type (in_channels={in_channels}) at {self.model_path}" - ) - - -class PipelineCheckpointProbe(CheckpointProbeBase): - def get_base_type(self) -> BaseModelType: - checkpoint = self.checkpoint - state_dict = self.checkpoint.get("state_dict") or checkpoint - if ( - "double_blocks.0.img_attn.norm.key_norm.scale" in state_dict - or "model.diffusion_model.double_blocks.0.img_attn.norm.key_norm.scale" in state_dict - ): - return BaseModelType.Flux - key_name = "model.diffusion_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight" - if key_name in state_dict and state_dict[key_name].shape[-1] == 768: - return BaseModelType.StableDiffusion1 - if key_name in state_dict and state_dict[key_name].shape[-1] == 1024: - return BaseModelType.StableDiffusion2 - key_name = "model.diffusion_model.input_blocks.4.1.transformer_blocks.0.attn2.to_k.weight" - if key_name in state_dict and state_dict[key_name].shape[-1] == 2048: - return BaseModelType.StableDiffusionXL - elif key_name in state_dict and state_dict[key_name].shape[-1] == 1280: - return BaseModelType.StableDiffusionXLRefiner - else: - raise InvalidModelConfigException("Cannot determine base type") - - def get_scheduler_prediction_type(self) -> SchedulerPredictionType: - """Return model prediction type.""" - type = self.get_base_type() - if type == BaseModelType.StableDiffusion2: - checkpoint = self.checkpoint - state_dict = self.checkpoint.get("state_dict") or checkpoint - key_name = "model.diffusion_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight" - if key_name in state_dict and state_dict[key_name].shape[-1] == 1024: - if "global_step" in checkpoint: - if checkpoint["global_step"] == 220000: - return SchedulerPredictionType.Epsilon - elif checkpoint["global_step"] == 110000: - return SchedulerPredictionType.VPrediction - return SchedulerPredictionType.VPrediction # a guess for sd2 ckpts - - elif type == BaseModelType.StableDiffusion1: - return SchedulerPredictionType.Epsilon # a reasonable guess for sd1 ckpts - else: - return SchedulerPredictionType.Epsilon - - -class LoRACheckpointProbe(CheckpointProbeBase): - """Class for LoRA checkpoints.""" - - def get_format(self) -> ModelFormat: - if is_state_dict_likely_in_flux_diffusers_format(self.checkpoint): - # TODO(ryand): This is an unusual case. In other places throughout the codebase, we treat - # ModelFormat.Diffusers as meaning that the model is in a directory. In this case, the model is a single - # file, but the weight keys are in the diffusers format. - return ModelFormat.Diffusers - return ModelFormat.LyCORIS - - def get_base_type(self) -> BaseModelType: - if ( - is_state_dict_likely_in_flux_kohya_format(self.checkpoint) - or is_state_dict_likely_in_flux_onetrainer_format(self.checkpoint) - or is_state_dict_likely_in_flux_diffusers_format(self.checkpoint) - or is_state_dict_likely_flux_control(self.checkpoint) - ): - return BaseModelType.Flux - - # If we've gotten here, we assume that the model is a Stable Diffusion model. - token_vector_length = lora_token_vector_length(self.checkpoint) - if token_vector_length == 768: - return BaseModelType.StableDiffusion1 - elif token_vector_length == 1024: - return BaseModelType.StableDiffusion2 - elif token_vector_length == 1280: - return BaseModelType.StableDiffusionXL # recognizes format at https://civitai.com/models/224641 - elif token_vector_length == 2048: - return BaseModelType.StableDiffusionXL - else: - raise InvalidModelConfigException(f"Unknown LoRA type: {self.model_path}") - - -class ControlNetCheckpointProbe(CheckpointProbeBase): - """Class for probing controlnets.""" - - def get_base_type(self) -> BaseModelType: - checkpoint = self.checkpoint - if is_state_dict_xlabs_controlnet(checkpoint) or is_state_dict_instantx_controlnet(checkpoint): - # TODO(ryand): Should I distinguish between XLabs, InstantX and other ControlNet models by implementing - # get_format()? - return BaseModelType.Flux - - for key_name in ( - "control_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight", - "controlnet_mid_block.bias", - "input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight", - "down_blocks.1.attentions.0.transformer_blocks.0.attn2.to_k.weight", - ): - if key_name not in checkpoint: - continue - width = checkpoint[key_name].shape[-1] - if width == 768: - return BaseModelType.StableDiffusion1 - elif width == 1024: - return BaseModelType.StableDiffusion2 - elif width == 2048: - return BaseModelType.StableDiffusionXL - elif width == 1280: - return BaseModelType.StableDiffusionXL - raise InvalidModelConfigException(f"{self.model_path}: Unable to determine base type") - - -class IPAdapterCheckpointProbe(CheckpointProbeBase): - """Class for probing IP Adapters""" - - def get_base_type(self) -> BaseModelType: - checkpoint = self.checkpoint - - if is_state_dict_xlabs_ip_adapter(checkpoint): - return BaseModelType.Flux - - for key in checkpoint.keys(): - if not key.startswith(("image_proj.", "ip_adapter.")): - continue - cross_attention_dim = checkpoint["ip_adapter.1.to_k_ip.weight"].shape[-1] - if cross_attention_dim == 768: - return BaseModelType.StableDiffusion1 - elif cross_attention_dim == 1024: - return BaseModelType.StableDiffusion2 - elif cross_attention_dim == 2048: - return BaseModelType.StableDiffusionXL - else: - raise InvalidModelConfigException( - f"IP-Adapter had unexpected cross-attention dimension: {cross_attention_dim}." - ) - raise InvalidModelConfigException(f"{self.model_path}: Unable to determine base type") - - -class CLIPVisionCheckpointProbe(CheckpointProbeBase): - def get_base_type(self) -> BaseModelType: - raise NotImplementedError() - - -class T2IAdapterCheckpointProbe(CheckpointProbeBase): - def get_base_type(self) -> BaseModelType: - raise NotImplementedError() - - -class SpandrelImageToImageCheckpointProbe(CheckpointProbeBase): - def get_base_type(self) -> BaseModelType: - return BaseModelType.Any - - -class SigLIPCheckpointProbe(CheckpointProbeBase): - def get_base_type(self) -> BaseModelType: - raise NotImplementedError() - - -class FluxReduxCheckpointProbe(CheckpointProbeBase): - def get_base_type(self) -> BaseModelType: - return BaseModelType.Flux - - -class LlavaOnevisionCheckpointProbe(CheckpointProbeBase): - def get_base_type(self) -> BaseModelType: - raise NotImplementedError() - - -######################################################## -# classes for probing folders -####################################################### -class FolderProbeBase(ProbeBase): - def get_variant_type(self) -> ModelVariantType: - return ModelVariantType.Normal - - def get_format(self) -> ModelFormat: - return ModelFormat("diffusers") - - def get_repo_variant(self) -> ModelRepoVariant: - # get all files ending in .bin or .safetensors - weight_files = list(self.model_path.glob("**/*.safetensors")) - weight_files.extend(list(self.model_path.glob("**/*.bin"))) - for x in weight_files: - if ".fp16" in x.suffixes: - return ModelRepoVariant.FP16 - if "openvino_model" in x.name: - return ModelRepoVariant.OpenVINO - if "flax_model" in x.name: - return ModelRepoVariant.Flax - if x.suffix == ".onnx": - return ModelRepoVariant.ONNX - return ModelRepoVariant.Default - - -class PipelineFolderProbe(FolderProbeBase): - def get_base_type(self) -> BaseModelType: - # Handle pipelines with a UNet (i.e SD 1.x, SD2, SDXL). - config_path = self.model_path / "unet" / "config.json" - if config_path.exists(): - with open(config_path) as file: - unet_conf = json.load(file) - if unet_conf["cross_attention_dim"] == 768: - return BaseModelType.StableDiffusion1 - elif unet_conf["cross_attention_dim"] == 1024: - return BaseModelType.StableDiffusion2 - elif unet_conf["cross_attention_dim"] == 1280: - return BaseModelType.StableDiffusionXLRefiner - elif unet_conf["cross_attention_dim"] == 2048: - return BaseModelType.StableDiffusionXL - else: - raise InvalidModelConfigException(f"Unknown base model for {self.model_path}") - - # Handle pipelines with a transformer (i.e. SD3). - config_path = self.model_path / "transformer" / "config.json" - if config_path.exists(): - with open(config_path) as file: - transformer_conf = json.load(file) - if transformer_conf["_class_name"] == "SD3Transformer2DModel": - return BaseModelType.StableDiffusion3 - elif transformer_conf["_class_name"] == "CogView4Transformer2DModel": - return BaseModelType.CogView4 - else: - raise InvalidModelConfigException(f"Unknown base model for {self.model_path}") - - raise InvalidModelConfigException(f"Unknown base model for {self.model_path}") - - def get_scheduler_prediction_type(self) -> SchedulerPredictionType: - with open(self.model_path / "scheduler" / "scheduler_config.json", "r") as file: - scheduler_conf = json.load(file) - if scheduler_conf.get("prediction_type", "epsilon") == "v_prediction": - return SchedulerPredictionType.VPrediction - elif scheduler_conf.get("prediction_type", "epsilon") == "epsilon": - return SchedulerPredictionType.Epsilon - else: - raise InvalidModelConfigException("Unknown scheduler prediction type: {scheduler_conf['prediction_type']}") - - def get_submodels(self) -> Dict[SubModelType, SubmodelDefinition]: - config = ConfigLoader.load_config(self.model_path, config_name="model_index.json") - submodels: Dict[SubModelType, SubmodelDefinition] = {} - for key, value in config.items(): - if key.startswith("_") or not (isinstance(value, list) and len(value) == 2): - continue - model_loader = str(value[1]) - if model_type := ModelProbe.CLASS2TYPE.get(model_loader): - variant_func = ModelProbe.TYPE2VARIANT.get(model_type, None) - submodels[SubModelType(key)] = SubmodelDefinition( - path_or_prefix=(self.model_path / key).resolve().as_posix(), - model_type=model_type, - variant=variant_func and variant_func((self.model_path / key).as_posix()), - ) - - return submodels - - def get_variant_type(self) -> ModelVariantType: - # This only works for pipelines! Any kind of - # exception results in our returning the - # "normal" variant type - try: - config_file = self.model_path / "unet" / "config.json" - with open(config_file, "r") as file: - conf = json.load(file) - - in_channels = conf["in_channels"] - if in_channels == 9: - return ModelVariantType.Inpaint - elif in_channels == 5: - return ModelVariantType.Depth - elif in_channels == 4: - return ModelVariantType.Normal - except Exception: - pass - return ModelVariantType.Normal - - -class ONNXFolderProbe(PipelineFolderProbe): - def get_base_type(self) -> BaseModelType: - # Due to the way the installer is set up, the configuration file for safetensors - # will come along for the ride if both the onnx and safetensors forms - # share the same directory. We take advantage of this here. - if (self.model_path / "unet" / "config.json").exists(): - return super().get_base_type() - else: - logger.warning('Base type probing is not implemented for ONNX models. Assuming "sd-1"') - return BaseModelType.StableDiffusion1 - - def get_format(self) -> ModelFormat: - return ModelFormat("onnx") - - def get_variant_type(self) -> ModelVariantType: - return ModelVariantType.Normal - - -class ControlNetFolderProbe(FolderProbeBase): - def get_base_type(self) -> BaseModelType: - config_file = self.model_path / "config.json" - if not config_file.exists(): - raise InvalidModelConfigException(f"Cannot determine base type for {self.model_path}") - with open(config_file, "r") as file: - config = json.load(file) - - if config.get("_class_name", None) == "FluxControlNetModel": - return BaseModelType.Flux - - # no obvious way to distinguish between sd2-base and sd2-768 - dimension = config["cross_attention_dim"] - if dimension == 768: - return BaseModelType.StableDiffusion1 - if dimension == 1024: - return BaseModelType.StableDiffusion2 - if dimension == 2048: - return BaseModelType.StableDiffusionXL - raise InvalidModelConfigException(f"Unable to determine model base for {self.model_path}") - - -class LoRAFolderProbe(FolderProbeBase): - def get_base_type(self) -> BaseModelType: - model_file = None - for suffix in ["safetensors", "bin"]: - base_file = self.model_path / f"pytorch_lora_weights.{suffix}" - if base_file.exists(): - model_file = base_file - break - if not model_file: - raise InvalidModelConfigException("Unknown LoRA format encountered") - return LoRACheckpointProbe(model_file).get_base_type() - - -class IPAdapterFolderProbe(FolderProbeBase): - def get_format(self) -> ModelFormat: - return ModelFormat.InvokeAI - - def get_base_type(self) -> BaseModelType: - model_file = self.model_path / "ip_adapter.bin" - if not model_file.exists(): - raise InvalidModelConfigException("Unknown IP-Adapter model format.") - - state_dict = torch.load(model_file, map_location="cpu") - cross_attention_dim = state_dict["ip_adapter"]["1.to_k_ip.weight"].shape[-1] - if cross_attention_dim == 768: - return BaseModelType.StableDiffusion1 - elif cross_attention_dim == 1024: - return BaseModelType.StableDiffusion2 - elif cross_attention_dim == 2048: - return BaseModelType.StableDiffusionXL - else: - raise InvalidModelConfigException( - f"IP-Adapter had unexpected cross-attention dimension: {cross_attention_dim}." - ) - - def get_image_encoder_model_id(self) -> Optional[str]: - encoder_id_path = self.model_path / "image_encoder.txt" - if not encoder_id_path.exists(): - return None - with open(encoder_id_path, "r") as f: - image_encoder_model = f.readline().strip() - return image_encoder_model - - -class CLIPVisionFolderProbe(FolderProbeBase): - def get_base_type(self) -> BaseModelType: - return BaseModelType.Any - - -class SpandrelImageToImageFolderProbe(FolderProbeBase): - def get_base_type(self) -> BaseModelType: - raise NotImplementedError() - - -class SigLIPFolderProbe(FolderProbeBase): - def get_base_type(self) -> BaseModelType: - return BaseModelType.Any - - -class FluxReduxFolderProbe(FolderProbeBase): - def get_base_type(self) -> BaseModelType: - raise NotImplementedError() - - -class LlaveOnevisionFolderProbe(FolderProbeBase): - def get_base_type(self) -> BaseModelType: - return BaseModelType.Any - - -class T2IAdapterFolderProbe(FolderProbeBase): - def get_base_type(self) -> BaseModelType: - config_file = self.model_path / "config.json" - if not config_file.exists(): - raise InvalidModelConfigException(f"Cannot determine base type for {self.model_path}") - with open(config_file, "r") as file: - config = json.load(file) - - adapter_type = config.get("adapter_type", None) - if adapter_type == "full_adapter_xl": - return BaseModelType.StableDiffusionXL - elif adapter_type == "full_adapter" or "light_adapter": - # I haven't seen any T2I adapter models for SD2, so assume that this is an SD1 adapter. - return BaseModelType.StableDiffusion1 - else: - raise InvalidModelConfigException( - f"Unable to determine base model for '{self.model_path}' (adapter_type = {adapter_type})." - ) - - -# Register probe classes -ModelProbe.register_probe("diffusers", ModelType.Main, PipelineFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.LoRA, LoRAFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.ControlLoRa, LoRAFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.ControlNet, ControlNetFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.IPAdapter, IPAdapterFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.CLIPVision, CLIPVisionFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.T2IAdapter, T2IAdapterFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.SigLIP, SigLIPFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.FluxRedux, FluxReduxFolderProbe) -ModelProbe.register_probe("diffusers", ModelType.LlavaOnevision, LlaveOnevisionFolderProbe) - -ModelProbe.register_probe("checkpoint", ModelType.Main, PipelineCheckpointProbe) -ModelProbe.register_probe("checkpoint", ModelType.LoRA, LoRACheckpointProbe) -ModelProbe.register_probe("checkpoint", ModelType.ControlLoRa, LoRACheckpointProbe) -ModelProbe.register_probe("checkpoint", ModelType.ControlNet, ControlNetCheckpointProbe) -ModelProbe.register_probe("checkpoint", ModelType.IPAdapter, IPAdapterCheckpointProbe) -ModelProbe.register_probe("checkpoint", ModelType.CLIPVision, CLIPVisionCheckpointProbe) -ModelProbe.register_probe("checkpoint", ModelType.T2IAdapter, T2IAdapterCheckpointProbe) -ModelProbe.register_probe("checkpoint", ModelType.SigLIP, SigLIPCheckpointProbe) -ModelProbe.register_probe("checkpoint", ModelType.FluxRedux, FluxReduxCheckpointProbe) -ModelProbe.register_probe("checkpoint", ModelType.LlavaOnevision, LlavaOnevisionCheckpointProbe) - -ModelProbe.register_probe("onnx", ModelType.ONNX, ONNXFolderProbe) diff --git a/invokeai/backend/model_manager/load/load_base.py b/invokeai/backend/model_manager/load/load_base.py index 75191517c76..c4d71bfe314 100644 --- a/invokeai/backend/model_manager/load/load_base.py +++ b/invokeai/backend/model_manager/load/load_base.py @@ -12,7 +12,7 @@ import torch from invokeai.app.services.config import InvokeAIAppConfig -from invokeai.backend.model_manager.config import AnyModelConfig +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.load.model_cache.cache_record import CacheRecord from invokeai.backend.model_manager.load.model_cache.model_cache import ModelCache from invokeai.backend.model_manager.taxonomy import AnyModel, SubModelType diff --git a/invokeai/backend/model_manager/load/load_default.py b/invokeai/backend/model_manager/load/load_default.py index 139a7d2940b..3fb7a574f31 100644 --- a/invokeai/backend/model_manager/load/load_default.py +++ b/invokeai/backend/model_manager/load/load_default.py @@ -6,7 +6,8 @@ from typing import Optional from invokeai.app.services.config import InvokeAIAppConfig -from invokeai.backend.model_manager.config import AnyModelConfig, Diffusers_Config_Base, InvalidModelConfigException +from invokeai.backend.model_manager.configs.base import Diffusers_Config_Base +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.load.load_base import LoadedModel, ModelLoaderBase from invokeai.backend.model_manager.load.model_cache.cache_record import CacheRecord from invokeai.backend.model_manager.load.model_cache.model_cache import ModelCache, get_model_cache_key @@ -50,7 +51,7 @@ def load_model(self, model_config: AnyModelConfig, submodel_type: Optional[SubMo model_path = self._get_model_path(model_config) if not model_path.exists(): - raise InvalidModelConfigException(f"Files for model '{model_config.name}' not found at {model_path}") + raise FileNotFoundError(f"Files for model '{model_config.name}' not found at {model_path}") with skip_torch_weight_init(): cache_record = self._load_and_cache(model_config, submodel_type) diff --git a/invokeai/backend/model_manager/load/model_loader_registry.py b/invokeai/backend/model_manager/load/model_loader_registry.py index 9b242fe1673..ca9ea56edbe 100644 --- a/invokeai/backend/model_manager/load/model_loader_registry.py +++ b/invokeai/backend/model_manager/load/model_loader_registry.py @@ -18,10 +18,8 @@ from abc import ABC, abstractmethod from typing import Callable, Dict, Optional, Tuple, Type, TypeVar -from invokeai.backend.model_manager.config import ( - AnyModelConfig, - Config_Base, -) +from invokeai.backend.model_manager.configs.base import Config_Base +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.load import ModelLoaderBase from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType, SubModelType diff --git a/invokeai/backend/model_manager/load/model_loaders/clip_vision.py b/invokeai/backend/model_manager/load/model_loaders/clip_vision.py index 9065e51fbfb..0150e24248f 100644 --- a/invokeai/backend/model_manager/load/model_loaders/clip_vision.py +++ b/invokeai/backend/model_manager/load/model_loaders/clip_vision.py @@ -3,10 +3,8 @@ from transformers import CLIPVisionModelWithProjection -from invokeai.backend.model_manager.config import ( - AnyModelConfig, - Diffusers_Config_Base, -) +from invokeai.backend.model_manager.configs.base import Diffusers_Config_Base +from invokeai.backend.model_manager.configs.factory import AnyModelConfig 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.taxonomy import AnyModel, BaseModelType, ModelFormat, ModelType, SubModelType diff --git a/invokeai/backend/model_manager/load/model_loaders/cogview4.py b/invokeai/backend/model_manager/load/model_loaders/cogview4.py index a1a9269edbe..782ff38450c 100644 --- a/invokeai/backend/model_manager/load/model_loaders/cogview4.py +++ b/invokeai/backend/model_manager/load/model_loaders/cogview4.py @@ -3,11 +3,8 @@ import torch -from invokeai.backend.model_manager.config import ( - AnyModelConfig, - Checkpoint_Config_Base, - Diffusers_Config_Base, -) +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.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 ( diff --git a/invokeai/backend/model_manager/load/model_loaders/controlnet.py b/invokeai/backend/model_manager/load/model_loaders/controlnet.py index 62a8ed4f65e..8fd1796b8f5 100644 --- a/invokeai/backend/model_manager/load/model_loaders/controlnet.py +++ b/invokeai/backend/model_manager/load/model_loaders/controlnet.py @@ -5,10 +5,8 @@ from diffusers import ControlNetModel -from invokeai.backend.model_manager.config import ( - AnyModelConfig, - ControlNet_Checkpoint_Config_Base, -) +from invokeai.backend.model_manager.configs.controlnet import ControlNet_Checkpoint_Config_Base +from invokeai.backend.model_manager.configs.factory import AnyModelConfig 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 ( diff --git a/invokeai/backend/model_manager/load/model_loaders/flux.py b/invokeai/backend/model_manager/load/model_loaders/flux.py index 07967c7c565..e44ddec382c 100644 --- a/invokeai/backend/model_manager/load/model_loaders/flux.py +++ b/invokeai/backend/model_manager/load/model_loaders/flux.py @@ -34,21 +34,22 @@ from invokeai.backend.flux.modules.autoencoder import AutoEncoder from invokeai.backend.flux.redux.flux_redux_model import FluxReduxModel from invokeai.backend.flux.util import get_flux_ae_params, get_flux_transformers_params -from invokeai.backend.model_manager.config import ( - AnyModelConfig, - Checkpoint_Config_Base, - CLIPEmbed_Diffusers_Config_Base, +from invokeai.backend.model_manager.configs.base import Checkpoint_Config_Base +from invokeai.backend.model_manager.configs.clip_embed import CLIPEmbed_Diffusers_Config_Base +from invokeai.backend.model_manager.configs.controlnet import ( ControlNet_Checkpoint_Config_Base, ControlNet_Diffusers_Config_Base, - FLUXRedux_Checkpoint_Config, - IPAdapter_Checkpoint_Config_Base, +) +from invokeai.backend.model_manager.configs.factory import AnyModelConfig +from invokeai.backend.model_manager.configs.flux_redux import FLUXRedux_Checkpoint_Config +from invokeai.backend.model_manager.configs.ip_adapter import IPAdapter_Checkpoint_Config_Base +from invokeai.backend.model_manager.configs.main import ( Main_BnBNF4_FLUX_Config, Main_Checkpoint_FLUX_Config, Main_GGUF_FLUX_Config, - T5Encoder_BnBLLMint8_Config, - T5Encoder_T5Encoder_Config, - VAE_Checkpoint_Config_Base, ) +from invokeai.backend.model_manager.configs.t5_encoder import T5Encoder_BnBLLMint8_Config, T5Encoder_T5Encoder_Config +from invokeai.backend.model_manager.configs.vae import VAE_Checkpoint_Config_Base 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.taxonomy import ( diff --git a/invokeai/backend/model_manager/load/model_loaders/generic_diffusers.py b/invokeai/backend/model_manager/load/model_loaders/generic_diffusers.py index 407a116b681..b888c69edf9 100644 --- a/invokeai/backend/model_manager/load/model_loaders/generic_diffusers.py +++ b/invokeai/backend/model_manager/load/model_loaders/generic_diffusers.py @@ -8,7 +8,8 @@ from diffusers.configuration_utils import ConfigMixin from diffusers.models.modeling_utils import ModelMixin -from invokeai.backend.model_manager.config import AnyModelConfig, Diffusers_Config_Base, InvalidModelConfigException +from invokeai.backend.model_manager.configs.base import Diffusers_Config_Base +from invokeai.backend.model_manager.configs.factory import AnyModelConfig 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.taxonomy import ( @@ -56,9 +57,7 @@ def get_hf_load_class(self, model_path: Path, submodel_type: Optional[SubModelTy module, class_name = config[submodel_type.value] result = self._hf_definition_to_type(module=module, class_name=class_name) except KeyError as e: - raise InvalidModelConfigException( - f'The "{submodel_type}" submodel is not available for this model.' - ) from e + raise ValueError(f'The "{submodel_type}" submodel is not available for this model.') from e else: try: config = self._load_diffusers_config(model_path, config_name="config.json") @@ -67,9 +66,9 @@ def get_hf_load_class(self, model_path: Path, submodel_type: Optional[SubModelTy elif class_name := config.get("architectures"): result = self._hf_definition_to_type(module="transformers", class_name=class_name[0]) else: - raise InvalidModelConfigException("Unable to decipher Load Class based on given config.json") + raise RuntimeError("Unable to decipher Load Class based on given config.json") except KeyError as e: - raise InvalidModelConfigException("An expected config.json file is missing from this model.") from e + raise ValueError("An expected config.json file is missing from this model.") from e assert result is not None return result diff --git a/invokeai/backend/model_manager/load/model_loaders/ip_adapter.py b/invokeai/backend/model_manager/load/model_loaders/ip_adapter.py index d103bc5dbcb..d133a36498c 100644 --- a/invokeai/backend/model_manager/load/model_loaders/ip_adapter.py +++ b/invokeai/backend/model_manager/load/model_loaders/ip_adapter.py @@ -7,7 +7,7 @@ import torch from invokeai.backend.ip_adapter.ip_adapter import build_ip_adapter -from invokeai.backend.model_manager.config import AnyModelConfig +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.load import ModelLoader, ModelLoaderRegistry from invokeai.backend.model_manager.taxonomy import AnyModel, BaseModelType, ModelFormat, ModelType, SubModelType from invokeai.backend.raw_model import RawModel diff --git a/invokeai/backend/model_manager/load/model_loaders/llava_onevision.py b/invokeai/backend/model_manager/load/model_loaders/llava_onevision.py index b508137f814..e459bbf2bb1 100644 --- a/invokeai/backend/model_manager/load/model_loaders/llava_onevision.py +++ b/invokeai/backend/model_manager/load/model_loaders/llava_onevision.py @@ -3,9 +3,7 @@ from transformers import LlavaOnevisionForConditionalGeneration -from invokeai.backend.model_manager.config import ( - AnyModelConfig, -) +from invokeai.backend.model_manager.configs.factory import AnyModelConfig 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.taxonomy import AnyModel, BaseModelType, ModelFormat, ModelType, SubModelType diff --git a/invokeai/backend/model_manager/load/model_loaders/lora.py b/invokeai/backend/model_manager/load/model_loaders/lora.py index 98f54224fad..29fb815d541 100644 --- a/invokeai/backend/model_manager/load/model_loaders/lora.py +++ b/invokeai/backend/model_manager/load/model_loaders/lora.py @@ -9,7 +9,7 @@ from safetensors.torch import load_file from invokeai.app.services.config import InvokeAIAppConfig -from invokeai.backend.model_manager.config import AnyModelConfig +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.load.load_default import ModelLoader from invokeai.backend.model_manager.load.model_cache.model_cache import ModelCache from invokeai.backend.model_manager.load.model_loader_registry import ModelLoaderRegistry diff --git a/invokeai/backend/model_manager/load/model_loaders/onnx.py b/invokeai/backend/model_manager/load/model_loaders/onnx.py index 3078d622b4e..a565bb11d05 100644 --- a/invokeai/backend/model_manager/load/model_loaders/onnx.py +++ b/invokeai/backend/model_manager/load/model_loaders/onnx.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import Optional -from invokeai.backend.model_manager.config import AnyModelConfig +from invokeai.backend.model_manager.configs.factory import AnyModelConfig 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 ( diff --git a/invokeai/backend/model_manager/load/model_loaders/sig_lip.py b/invokeai/backend/model_manager/load/model_loaders/sig_lip.py index bdf38887a3a..16b8e6c88da 100644 --- a/invokeai/backend/model_manager/load/model_loaders/sig_lip.py +++ b/invokeai/backend/model_manager/load/model_loaders/sig_lip.py @@ -3,9 +3,7 @@ from transformers import SiglipVisionModel -from invokeai.backend.model_manager.config import ( - AnyModelConfig, -) +from invokeai.backend.model_manager.configs.factory import AnyModelConfig 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.taxonomy import AnyModel, BaseModelType, ModelFormat, ModelType, SubModelType diff --git a/invokeai/backend/model_manager/load/model_loaders/spandrel_image_to_image.py b/invokeai/backend/model_manager/load/model_loaders/spandrel_image_to_image.py index 44cb0277fc4..e6d8f429904 100644 --- a/invokeai/backend/model_manager/load/model_loaders/spandrel_image_to_image.py +++ b/invokeai/backend/model_manager/load/model_loaders/spandrel_image_to_image.py @@ -3,9 +3,7 @@ import torch -from invokeai.backend.model_manager.config import ( - AnyModelConfig, -) +from invokeai.backend.model_manager.configs.factory import AnyModelConfig 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.taxonomy import AnyModel, BaseModelType, ModelFormat, ModelType, SubModelType diff --git a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py index 647ad4dbf43..d0cc5893796 100644 --- a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py +++ b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py @@ -11,10 +11,9 @@ StableDiffusionXLInpaintPipeline, ) -from invokeai.backend.model_manager.config import ( - AnyModelConfig, - Checkpoint_Config_Base, - Diffusers_Config_Base, +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_SD1_Config, Main_Checkpoint_SD2_Config, Main_Checkpoint_SDXL_Config, diff --git a/invokeai/backend/model_manager/load/model_loaders/textual_inversion.py b/invokeai/backend/model_manager/load/model_loaders/textual_inversion.py index 60ae4ea08b7..2d0411a8df2 100644 --- a/invokeai/backend/model_manager/load/model_loaders/textual_inversion.py +++ b/invokeai/backend/model_manager/load/model_loaders/textual_inversion.py @@ -4,7 +4,7 @@ from pathlib import Path from typing import Optional -from invokeai.backend.model_manager.config import AnyModelConfig +from invokeai.backend.model_manager.configs.factory import AnyModelConfig 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.taxonomy import ( diff --git a/invokeai/backend/model_manager/load/model_loaders/vae.py b/invokeai/backend/model_manager/load/model_loaders/vae.py index 12789e58c2f..e91903ccdad 100644 --- a/invokeai/backend/model_manager/load/model_loaders/vae.py +++ b/invokeai/backend/model_manager/load/model_loaders/vae.py @@ -5,7 +5,8 @@ from diffusers.models.autoencoders.autoencoder_kl import AutoencoderKL -from invokeai.backend.model_manager.config import AnyModelConfig, VAE_Checkpoint_Config_Base +from invokeai.backend.model_manager.configs.factory import AnyModelConfig +from invokeai.backend.model_manager.configs.vae import VAE_Checkpoint_Config_Base 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 ( diff --git a/invokeai/backend/model_manager/util/lora_metadata_extractor.py b/invokeai/backend/model_manager/util/lora_metadata_extractor.py index 842e78a7880..12b10739354 100644 --- a/invokeai/backend/model_manager/util/lora_metadata_extractor.py +++ b/invokeai/backend/model_manager/util/lora_metadata_extractor.py @@ -8,7 +8,8 @@ from PIL import Image from invokeai.app.util.thumbnails import make_thumbnail -from invokeai.backend.model_manager.config import AnyModelConfig, ModelType +from invokeai.backend.model_manager.configs.factory import AnyModelConfig +from invokeai.backend.model_manager.taxonomy import ModelType logger = logging.getLogger(__name__) diff --git a/invokeai/backend/util/test_utils.py b/invokeai/backend/util/test_utils.py index add394e71be..e4208dc848f 100644 --- a/invokeai/backend/util/test_utils.py +++ b/invokeai/backend/util/test_utils.py @@ -7,7 +7,8 @@ from invokeai.app.services.model_manager import ModelManagerServiceBase from invokeai.app.services.model_records import UnknownModelException -from invokeai.backend.model_manager import BaseModelType, LoadedModel, ModelType, SubModelType +from invokeai.backend.model_manager.load.load_base import LoadedModel +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType, SubModelType @pytest.fixture(scope="session") diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ParamDenoisingStrength.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ParamDenoisingStrength.tsx index bf4464bd5bd..49a289b875c 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ParamDenoisingStrength.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ParamDenoisingStrength.tsx @@ -17,6 +17,7 @@ import { selectImg2imgStrengthConfig } from 'features/system/store/configSlice'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelectedModelConfig } from 'services/api/hooks/useSelectedModelConfig'; +import { isFluxFillMainModelModelConfig } from 'services/api/types'; const selectHasRasterLayersWithContent = createSelector( selectActiveRasterLayerEntities, @@ -46,11 +47,7 @@ export const ParamDenoisingStrength = memo(() => { // Denoising strength does nothing if there are no raster layers w/ content return true; } - if ( - selectedModelConfig?.type === 'main' && - selectedModelConfig?.base === 'flux' && - selectedModelConfig.variant === 'inpaint' - ) { + if (selectedModelConfig && isFluxFillMainModelModelConfig(selectedModelConfig)) { // Denoising strength is ignored by FLUX Fill, which is indicated by the variant being 'inpaint' return true; } diff --git a/invokeai/frontend/web/src/features/controlLayers/store/validators.ts b/invokeai/frontend/web/src/features/controlLayers/store/validators.ts index 03ef5404a6d..197a3d6e3e3 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/validators.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/validators.ts @@ -154,7 +154,7 @@ export const getControlLayerWarnings = ( warnings.push(WARNINGS.CONTROL_ADAPTER_INCOMPATIBLE_BASE_MODEL); } else if ( model.base === 'flux' && - model.variant === 'inpaint' && + model.variant === 'dev_fill' && entity.controlAdapter.model.type === 'control_lora' ) { // FLUX inpaint variants are FLUX Fill models - not compatible w/ Control LoRA diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx index 75b3ba4bc40..538ebf597e0 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx @@ -56,18 +56,17 @@ export const ModelView = memo(({ modelConfig }: Props) => { - {modelConfig.type === 'main' && ( + {modelConfig.type === 'main' && 'variant' in modelConfig && ( )} {modelConfig.type === 'main' && modelConfig.format === 'diffusers' && modelConfig.repo_variant && ( )} {modelConfig.type === 'main' && modelConfig.format === 'checkpoint' && ( - <> - - - - + + )} + {modelConfig.type === 'main' && modelConfig.format === 'checkpoint' && 'prediction_type' in modelConfig && ( + )} {modelConfig.type === 'ip_adapter' && modelConfig.format === 'invokeai' && ( diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts index 5a766e8d399..42d66f0fc81 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts @@ -660,6 +660,7 @@ describe('Graph', () => { cover_image: null, type: 'main', trigger_phrases: null, + prediction_type: 'epsilon', default_settings: { vae: null, vae_precision: null, @@ -673,7 +674,6 @@ describe('Graph', () => { variant: 'inpaint', format: 'diffusers', repo_variant: 'fp16', - submodels: null, usage_info: null, }); expect(field).toEqual({ diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlAdapters.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlAdapters.ts index 5fcd13ba4fd..0f7163c44c2 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlAdapters.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlAdapters.ts @@ -5,7 +5,7 @@ import type { CanvasControlLayerState, Rect } from 'features/controlLayers/store import { getControlLayerWarnings } from 'features/controlLayers/store/validators'; import type { Graph } from 'features/nodes/util/graph/generation/Graph'; import { serializeError } from 'serialize-error'; -import type { ImageDTO, Invocation, MainModelConfig } from 'services/api/types'; +import type { FLUXModelConfig, ImageDTO, Invocation, MainModelConfig } from 'services/api/types'; import { assert } from 'tsafe'; const log = logger('system'); @@ -113,7 +113,7 @@ type AddControlLoRAArg = { entities: CanvasControlLayerState[]; g: Graph; rect: Rect; - model: MainModelConfig; + model: FLUXModelConfig; denoise: Invocation<'flux_denoise'>; }; @@ -129,7 +129,7 @@ export const addControlLoRA = async ({ manager, entities, g, rect, model, denois return; } - assert(model.variant !== 'inpaint', 'FLUX Control LoRA is not compatible with FLUX Fill.'); + assert(model.variant !== 'dev_fill', 'FLUX Control LoRA is not compatible with FLUX Fill.'); assert(validControlLayers.length <= 1, 'Cannot add more than one FLUX control LoRA.'); const getImageDTOResult = await withResultAsync(() => { diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts index b47244e5fc2..a827fff1069 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts @@ -49,7 +49,7 @@ export const buildFLUXGraph = async (arg: GraphBuilderArg): Promise; + /** + * FloatGeneratorOutput + * @description Base class for nodes that output a collection of floats + */ + FloatGeneratorOutput: { /** - * T5 Encoder - * @description T5 tokenizer and text encoder - * @default null + * Floats + * @description The generated floats */ - t5_encoder?: components["schemas"]["T5EncoderField"] | null; + floats: number[]; /** * type - * @default flux_lora_collection_loader + * @default float_generator_output * @constant */ - type: "flux_lora_collection_loader"; + type: "float_generator_output"; }; /** - * FLUXRedux_Checkpoint_Config - * @description Model config for FLUX Tools Redux model. + * Float Primitive + * @description A float primitive value */ - FLUXRedux_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; - /** - * Cover Image - * @description Url for image to preview model - */ - cover_image: string | null; + FloatInvocation: { /** - * Submodels - * @description Loadable submodels in this model + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + id: string; /** - * Usage Info - * @description Usage information for this model + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - usage_info: string | null; + is_intermediate?: boolean; /** - * Type - * @default flux_redux - * @constant + * Use Cache + * @description Whether or not to use the cache + * @default true */ - type: "flux_redux"; + use_cache?: boolean; /** - * Format - * @default checkpoint - * @constant + * Value + * @description The float value + * @default 0 */ - format: "checkpoint"; + value?: number; /** - * Base - * @default flux + * type + * @default float * @constant */ - base: "flux"; + type: "float"; }; /** - * FaceIdentifier - * @description Outputs an image with detected face IDs printed on each face. For use with other FaceTools. + * Float Range + * @description Creates a range */ - FaceIdentifierInvocation: { - /** - * @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; + FloatLinearRangeInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -8206,39 +8055,35 @@ export type components = { */ use_cache?: boolean; /** - * @description Image to face detect - * @default null + * Start + * @description The first value of the range + * @default 5 */ - image?: components["schemas"]["ImageField"] | null; + start?: number; /** - * Minimum Confidence - * @description Minimum confidence for face detection (lower if detection is failing) - * @default 0.5 + * Stop + * @description The last value of the range + * @default 10 */ - minimum_confidence?: number; + stop?: number; /** - * Chunk - * @description Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image. - * @default false + * Steps + * @description number of values to interpolate over (including start and stop) + * @default 30 */ - chunk?: boolean; + steps?: number; /** * type - * @default face_identifier + * @default float_range * @constant */ - type: "face_identifier"; + type: "float_range"; }; /** - * FaceMask - * @description Face mask creation using mediapipe face detection + * Float Math + * @description Performs floating point math. */ - FaceMaskInvocation: { - /** - * @description Optional metadata to be saved with the image - * @default null - */ - metadata?: components["schemas"]["MetadataField"] | null; + FloatMathInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -8257,89 +8102,53 @@ export type components = { */ use_cache?: boolean; /** - * @description Image to face detect - * @default null - */ - image?: components["schemas"]["ImageField"] | null; - /** - * Face Ids - * @description Comma-separated list of face ids to mask eg '0,2,7'. Numbered from 0. Leave empty to mask all. Find face IDs with FaceIdentifier node. - * @default - */ - face_ids?: string; - /** - * Minimum Confidence - * @description Minimum confidence for face detection (lower if detection is failing) - * @default 0.5 - */ - minimum_confidence?: number; - /** - * X Offset - * @description Offset for the X-axis of the face mask - * @default 0 - */ - x_offset?: number; - /** - * Y Offset - * @description Offset for the Y-axis of the face mask - * @default 0 + * Operation + * @description The operation to perform + * @default ADD + * @enum {string} */ - y_offset?: number; + operation?: "ADD" | "SUB" | "MUL" | "DIV" | "EXP" | "ABS" | "SQRT" | "MIN" | "MAX"; /** - * Chunk - * @description Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image. - * @default false + * A + * @description The first number + * @default 1 */ - chunk?: boolean; + a?: number; /** - * Invert Mask - * @description Toggle to invert the mask - * @default false + * B + * @description The second number + * @default 1 */ - invert_mask?: boolean; + b?: number; /** * type - * @default face_mask_detection + * @default float_math * @constant */ - type: "face_mask_detection"; + type: "float_math"; }; /** - * FaceMaskOutput - * @description Base class for FaceMask output + * FloatOutput + * @description Base class for nodes that output a single float */ - FaceMaskOutput: { - /** @description The output image */ - image: components["schemas"]["ImageField"]; - /** - * Width - * @description The width of the image in pixels - */ - width: number; + FloatOutput: { /** - * Height - * @description The height of the image in pixels + * Value + * @description The output float */ - height: number; + value: number; /** * type - * @default face_mask_output + * @default float_output * @constant */ - type: "face_mask_output"; - /** @description The output mask */ - mask: components["schemas"]["ImageField"]; + type: "float_output"; }; /** - * FaceOff - * @description Bound, extract, and mask a face from an image using MediaPipe detection + * Float To Integer + * @description Rounds a float number to (a multiple of) an integer. */ - FaceOffInvocation: { - /** - * @description Optional metadata to be saved with the image - * @default null - */ - metadata?: components["schemas"]["MetadataField"] | null; + FloatToIntegerInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -8358,136 +8167,83 @@ export type components = { */ use_cache?: boolean; /** - * @description Image for face detection - * @default null - */ - image?: components["schemas"]["ImageField"] | null; - /** - * Face Id - * @description The face ID to process, numbered from 0. Multiple faces not supported. Find a face's ID with FaceIdentifier node. - * @default 0 - */ - face_id?: number; - /** - * Minimum Confidence - * @description Minimum confidence for face detection (lower if detection is failing) - * @default 0.5 - */ - minimum_confidence?: number; - /** - * X Offset - * @description X-axis offset of the mask - * @default 0 - */ - x_offset?: number; - /** - * Y Offset - * @description Y-axis offset of the mask + * Value + * @description The value to round * @default 0 */ - y_offset?: number; + value?: number; /** - * Padding - * @description All-axis padding around the mask in pixels - * @default 0 + * Multiple of + * @description The multiple to round to + * @default 1 */ - padding?: number; + multiple?: number; /** - * Chunk - * @description Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image. - * @default false + * Method + * @description The method to use for rounding + * @default Nearest + * @enum {string} */ - chunk?: boolean; + method?: "Nearest" | "Floor" | "Ceiling" | "Truncate"; /** * type - * @default face_off + * @default float_to_int * @constant */ - type: "face_off"; + type: "float_to_int"; }; /** - * FaceOffOutput - * @description Base class for FaceOff Output + * FluxConditioningCollectionOutput + * @description Base class for nodes that output a collection of conditioning tensors */ - FaceOffOutput: { - /** @description The output image */ - image: components["schemas"]["ImageField"]; - /** - * Width - * @description The width of the image in pixels - */ - width: number; + FluxConditioningCollectionOutput: { /** - * Height - * @description The height of the image in pixels + * Collection + * @description The output conditioning tensors */ - height: number; + collection: components["schemas"]["FluxConditioningField"][]; /** * type - * @default face_off_output + * @default flux_conditioning_collection_output * @constant */ - type: "face_off_output"; - /** @description The output mask */ - mask: components["schemas"]["ImageField"]; - /** - * X - * @description The x coordinate of the bounding box's left side - */ - x: number; - /** - * Y - * @description The y coordinate of the bounding box's top side - */ - y: number; + type: "flux_conditioning_collection_output"; }; - /** FieldIdentifier */ - FieldIdentifier: { - /** - * Kind - * @description The kind of field - * @enum {string} - */ - kind: "input" | "output"; - /** - * Node Id - * @description The ID of the node - */ - node_id: string; + /** + * FluxConditioningField + * @description A conditioning tensor primitive value + */ + FluxConditioningField: { /** - * Field Name - * @description The name of the field + * Conditioning Name + * @description The name of conditioning tensor */ - field_name: string; + conditioning_name: string; /** - * User Label - * @description The user label of the field, if any + * @description The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True. + * @default null */ - user_label: string | null; + mask?: components["schemas"]["TensorField"] | null; }; /** - * FieldKind - * @description The kind of field. - * - `Input`: An input field on a node. - * - `Output`: An output field on a node. - * - `Internal`: A field which is treated as an input, but cannot be used in node definitions. Metadata is - * one example. It is provided to nodes via the WithMetadata class, and we want to reserve the field name - * "metadata" for this on all nodes. `FieldKind` is used to short-circuit the field name validation logic, - * allowing "metadata" for that field. - * - `NodeAttribute`: The field is a node attribute. These are fields which are not inputs or outputs, - * but which are used to store information about the node. For example, the `id` and `type` fields are node - * attributes. - * - * The presence of this in `json_schema_extra["field_kind"]` is used when initializing node schemas on app - * startup, and when generating the OpenAPI schema for the workflow editor. - * @enum {string} + * FluxConditioningOutput + * @description Base class for nodes that output a single conditioning tensor */ - FieldKind: "input" | "output" | "internal" | "node_attribute"; + FluxConditioningOutput: { + /** @description Conditioning tensor */ + conditioning: components["schemas"]["FluxConditioningField"]; + /** + * type + * @default flux_conditioning_output + * @constant + */ + type: "flux_conditioning_output"; + }; /** - * Float Batch - * @description Create a batched generation, where the workflow is executed once for each float in the batch. + * Control LoRA - FLUX + * @description LoRA model and Image to use with FLUX transformer generation. */ - FloatBatchInvocation: { + FluxControlLoRALoaderInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -8506,82 +8262,90 @@ export type components = { */ use_cache?: boolean; /** - * Batch Group - * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. - * @default None - * @enum {string} + * Control LoRA + * @description Control LoRA model to load + * @default null */ - batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; + lora?: components["schemas"]["ModelIdentifierField"] | null; /** - * Floats - * @description The floats to batch over + * @description The image to encode. * @default null */ - floats?: number[] | null; + image?: components["schemas"]["ImageField"] | null; + /** + * Weight + * @description The weight of the LoRA. + * @default 1 + */ + weight?: number; /** * type - * @default float_batch + * @default flux_control_lora_loader * @constant */ - type: "float_batch"; + type: "flux_control_lora_loader"; }; /** - * Float Collection Primitive - * @description A collection of float primitive values + * FluxControlLoRALoaderOutput + * @description Flux Control LoRA Loader Output */ - FloatCollectionInvocation: { + FluxControlLoRALoaderOutput: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Flux Control LoRA + * @description Control LoRAs to apply on model loading + * @default null */ - id: string; + control_lora: components["schemas"]["ControlLoRAField"]; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * type + * @default flux_control_lora_loader_output + * @constant */ - is_intermediate?: boolean; + type: "flux_control_lora_loader_output"; + }; + /** FluxControlNetField */ + FluxControlNetField: { + /** @description The control image */ + image: components["schemas"]["ImageField"]; + /** @description The ControlNet model to use */ + control_model: components["schemas"]["ModelIdentifierField"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Control Weight + * @description The weight given to the ControlNet + * @default 1 */ - use_cache?: boolean; + control_weight?: number | number[]; /** - * Collection - * @description The collection of float values - * @default [] + * Begin Step Percent + * @description When the ControlNet is first applied (% of total steps) + * @default 0 */ - collection?: number[]; + begin_step_percent?: number; /** - * type - * @default float_collection - * @constant + * End Step Percent + * @description When the ControlNet is last applied (% of total steps) + * @default 1 */ - type: "float_collection"; - }; - /** - * FloatCollectionOutput - * @description Base class for nodes that output a collection of floats - */ - FloatCollectionOutput: { + end_step_percent?: number; /** - * Collection - * @description The float collection + * Resize Mode + * @description The resize mode to use + * @default just_resize + * @enum {string} */ - collection: number[]; + resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; /** - * type - * @default float_collection_output - * @constant + * Instantx Control Mode + * @description The control mode for InstantX ControlNet union models. Ignored for other ControlNet models. The standard mapping is: canny (0), tile (1), depth (2), blur (3), pose (4), gray (5), low quality (6). Negative values will be treated as 'None'. + * @default -1 */ - type: "float_collection_output"; + instantx_control_mode?: number | null; }; /** - * Float Generator - * @description Generated a range of floats for use in a batched generation + * FLUX ControlNet + * @description Collect FLUX ControlNet info to pass to other nodes. */ - FloatGenerator: { + FluxControlNetInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -8600,41 +8364,72 @@ export type components = { */ use_cache?: boolean; /** - * Generator Type - * @description The float generator. + * @description The control image + * @default null */ - generator: components["schemas"]["FloatGeneratorField"]; + image?: components["schemas"]["ImageField"] | null; + /** + * @description ControlNet model to load + * @default null + */ + control_model?: components["schemas"]["ModelIdentifierField"] | null; + /** + * Control Weight + * @description The weight given to the ControlNet + * @default 1 + */ + control_weight?: number | number[]; + /** + * Begin Step Percent + * @description When the ControlNet is first applied (% of total steps) + * @default 0 + */ + begin_step_percent?: number; + /** + * End Step Percent + * @description When the ControlNet is last applied (% of total steps) + * @default 1 + */ + end_step_percent?: number; + /** + * Resize Mode + * @description The resize mode used + * @default just_resize + * @enum {string} + */ + resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; + /** + * Instantx Control Mode + * @description The control mode for InstantX ControlNet union models. Ignored for other ControlNet models. The standard mapping is: canny (0), tile (1), depth (2), blur (3), pose (4), gray (5), low quality (6). Negative values will be treated as 'None'. + * @default -1 + */ + instantx_control_mode?: number | null; /** * type - * @default float_generator + * @default flux_controlnet * @constant */ - type: "float_generator"; + type: "flux_controlnet"; }; - /** FloatGeneratorField */ - FloatGeneratorField: Record; /** - * FloatGeneratorOutput - * @description Base class for nodes that output a collection of floats + * FluxControlNetOutput + * @description FLUX ControlNet info */ - FloatGeneratorOutput: { - /** - * Floats - * @description The generated floats - */ - floats: number[]; + FluxControlNetOutput: { + /** @description ControlNet(s) to apply */ + control: components["schemas"]["FluxControlNetField"]; /** * type - * @default float_generator_output + * @default flux_controlnet_output * @constant */ - type: "float_generator_output"; + type: "flux_controlnet_output"; }; /** - * Float Primitive - * @description A float primitive value + * FLUX Denoise + * @description Run denoising process with a FLUX transformer model. */ - FloatInvocation: { + FluxDenoiseInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -8653,70 +8448,156 @@ export type components = { */ use_cache?: boolean; /** - * Value - * @description The float value - * @default 0 + * @description Latents tensor + * @default null */ - value?: number; + latents?: components["schemas"]["LatentsField"] | null; /** - * type - * @default float - * @constant + * @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 */ - type: "float"; - }; - /** - * Float Range - * @description Creates a range - */ - FloatLinearRangeInvocation: { + denoise_mask?: components["schemas"]["DenoiseMaskField"] | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Denoising Start + * @description When to start denoising, expressed a percentage of total steps + * @default 0 */ - id: string; + denoising_start?: number; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Denoising End + * @description When to stop denoising, expressed a percentage of total steps + * @default 1 */ - is_intermediate?: boolean; + denoising_end?: number; /** - * Use Cache - * @description Whether or not to use the cache + * Add Noise + * @description Add noise based on denoising start. * @default true */ - use_cache?: boolean; + add_noise?: boolean; /** - * Start - * @description The first value of the range - * @default 5 + * Transformer + * @description Flux model (Transformer) to load + * @default null */ - start?: number; + transformer?: components["schemas"]["TransformerField"] | null; /** - * Stop - * @description The last value of the range - * @default 10 + * Control LoRA + * @description Control LoRA model to load + * @default null */ - stop?: number; + control_lora?: components["schemas"]["ControlLoRAField"] | null; /** - * Steps - * @description number of values to interpolate over (including start and stop) - * @default 30 + * Positive Text Conditioning + * @description Positive conditioning tensor + * @default null */ - steps?: number; + positive_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; + /** + * Negative Text Conditioning + * @description Negative conditioning tensor. Can be None if cfg_scale is 1.0. + * @default null + */ + negative_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; + /** + * Redux Conditioning + * @description FLUX Redux conditioning tensor. + * @default null + */ + redux_conditioning?: components["schemas"]["FluxReduxConditioningField"] | components["schemas"]["FluxReduxConditioningField"][] | null; + /** + * @description FLUX Fill conditioning. + * @default null + */ + fill_conditioning?: components["schemas"]["FluxFillConditioningField"] | null; + /** + * CFG Scale + * @description Classifier-Free Guidance scale + * @default 1 + */ + cfg_scale?: number | number[]; + /** + * CFG Scale Start Step + * @description Index of the first step to apply cfg_scale. Negative indices count backwards from the the last step (e.g. a value of -1 refers to the final step). + * @default 0 + */ + cfg_scale_start_step?: number; + /** + * CFG Scale End Step + * @description Index of the last step to apply cfg_scale. Negative indices count backwards from the last step (e.g. a value of -1 refers to the final step). + * @default -1 + */ + cfg_scale_end_step?: number; + /** + * Width + * @description Width of the generated image. + * @default 1024 + */ + width?: number; + /** + * Height + * @description Height of the generated image. + * @default 1024 + */ + height?: number; + /** + * Num Steps + * @description Number of diffusion steps. Recommended values are schnell: 4, dev: 50. + * @default 4 + */ + num_steps?: number; + /** + * Guidance + * @description The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell. + * @default 4 + */ + guidance?: number; + /** + * Seed + * @description Randomness seed for reproducibility. + * @default 0 + */ + seed?: number; + /** + * Control + * @description ControlNet models. + * @default null + */ + control?: components["schemas"]["FluxControlNetField"] | components["schemas"]["FluxControlNetField"][] | null; + /** + * @description VAE + * @default null + */ + controlnet_vae?: components["schemas"]["VAEField"] | null; + /** + * IP-Adapter + * @description IP-Adapter to apply + * @default null + */ + ip_adapter?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; + /** + * Kontext Conditioning + * @description FLUX Kontext conditioning (reference image). + * @default null + */ + kontext_conditioning?: components["schemas"]["FluxKontextConditioningField"] | components["schemas"]["FluxKontextConditioningField"][] | null; /** * type - * @default float_range + * @default flux_denoise * @constant */ - type: "float_range"; + type: "flux_denoise"; }; /** - * Float Math - * @description Performs floating point math. + * FLUX Denoise + Metadata + * @description Run denoising process with a FLUX transformer model + metadata. */ - FloatMathInvocation: { + FluxDenoiseLatentsMetaInvocation: { + /** + * @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. @@ -8735,148 +8616,161 @@ export type components = { */ use_cache?: boolean; /** - * Operation - * @description The operation to perform - * @default ADD - * @enum {string} + * @description Latents tensor + * @default null */ - operation?: "ADD" | "SUB" | "MUL" | "DIV" | "EXP" | "ABS" | "SQRT" | "MIN" | "MAX"; + latents?: components["schemas"]["LatentsField"] | null; /** - * A - * @description The first number - * @default 1 + * @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 */ - a?: number; + denoise_mask?: components["schemas"]["DenoiseMaskField"] | null; /** - * B - * @description The second number - * @default 1 + * Denoising Start + * @description When to start denoising, expressed a percentage of total steps + * @default 0 */ - b?: number; + denoising_start?: number; /** - * type - * @default float_math - * @constant + * Denoising End + * @description When to stop denoising, expressed a percentage of total steps + * @default 1 */ - type: "float_math"; - }; - /** - * FloatOutput - * @description Base class for nodes that output a single float - */ - FloatOutput: { + denoising_end?: number; /** - * Value - * @description The output float + * Add Noise + * @description Add noise based on denoising start. + * @default true */ - value: number; + add_noise?: boolean; /** - * type - * @default float_output - * @constant + * Transformer + * @description Flux model (Transformer) to load + * @default null */ - type: "float_output"; - }; - /** - * Float To Integer - * @description Rounds a float number to (a multiple of) an integer. - */ - FloatToIntegerInvocation: { + transformer?: components["schemas"]["TransformerField"] | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Control LoRA + * @description Control LoRA model to load + * @default null */ - id: string; + control_lora?: components["schemas"]["ControlLoRAField"] | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Positive Text Conditioning + * @description Positive conditioning tensor + * @default null */ - is_intermediate?: boolean; + positive_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Negative Text Conditioning + * @description Negative conditioning tensor. Can be None if cfg_scale is 1.0. + * @default null */ - use_cache?: boolean; + negative_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; /** - * Value - * @description The value to round - * @default 0 + * Redux Conditioning + * @description FLUX Redux conditioning tensor. + * @default null */ - value?: number; + redux_conditioning?: components["schemas"]["FluxReduxConditioningField"] | components["schemas"]["FluxReduxConditioningField"][] | null; /** - * Multiple of - * @description The multiple to round to + * @description FLUX Fill conditioning. + * @default null + */ + fill_conditioning?: components["schemas"]["FluxFillConditioningField"] | null; + /** + * CFG Scale + * @description Classifier-Free Guidance scale * @default 1 */ - multiple?: number; + cfg_scale?: number | number[]; /** - * Method - * @description The method to use for rounding - * @default Nearest - * @enum {string} + * CFG Scale Start Step + * @description Index of the first step to apply cfg_scale. Negative indices count backwards from the the last step (e.g. a value of -1 refers to the final step). + * @default 0 */ - method?: "Nearest" | "Floor" | "Ceiling" | "Truncate"; + cfg_scale_start_step?: number; /** - * type - * @default float_to_int - * @constant + * CFG Scale End Step + * @description Index of the last step to apply cfg_scale. Negative indices count backwards from the last step (e.g. a value of -1 refers to the final step). + * @default -1 */ - type: "float_to_int"; - }; - /** - * FluxConditioningCollectionOutput - * @description Base class for nodes that output a collection of conditioning tensors - */ - FluxConditioningCollectionOutput: { + cfg_scale_end_step?: number; /** - * Collection - * @description The output conditioning tensors + * Width + * @description Width of the generated image. + * @default 1024 */ - collection: components["schemas"]["FluxConditioningField"][]; + width?: number; /** - * type - * @default flux_conditioning_collection_output - * @constant + * Height + * @description Height of the generated image. + * @default 1024 */ - type: "flux_conditioning_collection_output"; - }; - /** - * FluxConditioningField - * @description A conditioning tensor primitive value - */ - FluxConditioningField: { + height?: number; /** - * Conditioning Name - * @description The name of conditioning tensor + * Num Steps + * @description Number of diffusion steps. Recommended values are schnell: 4, dev: 50. + * @default 4 */ - conditioning_name: string; + num_steps?: number; /** - * @description The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True. + * Guidance + * @description The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell. + * @default 4 + */ + guidance?: number; + /** + * Seed + * @description Randomness seed for reproducibility. + * @default 0 + */ + seed?: number; + /** + * Control + * @description ControlNet models. * @default null */ - mask?: components["schemas"]["TensorField"] | null; - }; - /** - * FluxConditioningOutput - * @description Base class for nodes that output a single conditioning tensor - */ - FluxConditioningOutput: { - /** @description Conditioning tensor */ - conditioning: components["schemas"]["FluxConditioningField"]; + control?: components["schemas"]["FluxControlNetField"] | components["schemas"]["FluxControlNetField"][] | null; + /** + * @description VAE + * @default null + */ + controlnet_vae?: components["schemas"]["VAEField"] | null; + /** + * IP-Adapter + * @description IP-Adapter to apply + * @default null + */ + ip_adapter?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; + /** + * Kontext Conditioning + * @description FLUX Kontext conditioning (reference image). + * @default null + */ + kontext_conditioning?: components["schemas"]["FluxKontextConditioningField"] | components["schemas"]["FluxKontextConditioningField"][] | null; /** * type - * @default flux_conditioning_output + * @default flux_denoise_meta * @constant */ - type: "flux_conditioning_output"; + type: "flux_denoise_meta"; }; /** - * Control LoRA - FLUX - * @description LoRA model and Image to use with FLUX transformer generation. + * FluxFillConditioningField + * @description A FLUX Fill conditioning field. */ - FluxControlLoRALoaderInvocation: { + FluxFillConditioningField: { + /** @description The FLUX Fill reference image. */ + image: components["schemas"]["ImageField"]; + /** @description The FLUX Fill inpaint mask. */ + mask: components["schemas"]["TensorField"]; + }; + /** + * FLUX Fill Conditioning + * @description Prepare the FLUX Fill conditioning data. + */ + FluxFillInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -8895,90 +8789,120 @@ export type components = { */ use_cache?: boolean; /** - * Control LoRA - * @description Control LoRA model to load - * @default null - */ - lora?: components["schemas"]["ModelIdentifierField"] | null; - /** - * @description The image to encode. + * @description The FLUX Fill reference image. * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Weight - * @description The weight of the LoRA. - * @default 1 + * @description The bool inpainting mask. Excluded regions should be set to False, included regions should be set to True. + * @default null */ - weight?: number; + mask?: components["schemas"]["TensorField"] | null; /** * type - * @default flux_control_lora_loader + * @default flux_fill * @constant */ - type: "flux_control_lora_loader"; + type: "flux_fill"; }; /** - * FluxControlLoRALoaderOutput - * @description Flux Control LoRA Loader Output + * FluxFillOutput + * @description The conditioning output of a FLUX Fill invocation. */ - FluxControlLoRALoaderOutput: { + FluxFillOutput: { /** - * Flux Control LoRA - * @description Control LoRAs to apply on model loading - * @default null + * Conditioning + * @description FLUX Redux conditioning tensor */ - control_lora: components["schemas"]["ControlLoRAField"]; + fill_cond: components["schemas"]["FluxFillConditioningField"]; /** * type - * @default flux_control_lora_loader_output + * @default flux_fill_output * @constant */ - type: "flux_control_lora_loader_output"; + type: "flux_fill_output"; }; - /** FluxControlNetField */ - FluxControlNetField: { - /** @description The control image */ - image: components["schemas"]["ImageField"]; - /** @description The ControlNet model to use */ - control_model: components["schemas"]["ModelIdentifierField"]; + /** + * FLUX IP-Adapter + * @description Collects FLUX IP-Adapter info to pass to other nodes. + */ + FluxIPAdapterInvocation: { /** - * Control Weight - * @description The weight given to the ControlNet - * @default 1 + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - control_weight?: number | number[]; + id: string; /** - * Begin Step Percent - * @description When the ControlNet is first applied (% of total steps) - * @default 0 + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - begin_step_percent?: number; + is_intermediate?: boolean; /** - * End Step Percent - * @description When the ControlNet is last applied (% of total steps) + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The IP-Adapter image prompt(s). + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * IP-Adapter Model + * @description The IP-Adapter model. + * @default null + */ + ip_adapter_model?: components["schemas"]["ModelIdentifierField"] | null; + /** + * Clip Vision Model + * @description CLIP Vision model to use. + * @default ViT-L + * @constant + */ + clip_vision_model?: "ViT-L"; + /** + * Weight + * @description The weight given to the IP-Adapter * @default 1 */ - end_step_percent?: number; + weight?: number | number[]; /** - * Resize Mode - * @description The resize mode to use - * @default just_resize - * @enum {string} + * Begin Step Percent + * @description When the IP-Adapter is first applied (% of total steps) + * @default 0 */ - resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; + begin_step_percent?: number; /** - * Instantx Control Mode - * @description The control mode for InstantX ControlNet union models. Ignored for other ControlNet models. The standard mapping is: canny (0), tile (1), depth (2), blur (3), pose (4), gray (5), low quality (6). Negative values will be treated as 'None'. - * @default -1 + * End Step Percent + * @description When the IP-Adapter is last applied (% of total steps) + * @default 1 */ - instantx_control_mode?: number | null; + end_step_percent?: number; + /** + * type + * @default flux_ip_adapter + * @constant + */ + type: "flux_ip_adapter"; }; /** - * FLUX ControlNet - * @description Collect FLUX ControlNet info to pass to other nodes. + * FLUX Kontext Image Prep + * @description Prepares an image or images for use with FLUX Kontext. The first/single image is resized to the nearest + * preferred Kontext resolution. All other images are concatenated horizontally, maintaining their aspect ratio. */ - FluxControlNetInvocation: { + FluxKontextConcatenateImagesInvocation: { + /** + * @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. @@ -8997,72 +8921,88 @@ export type components = { */ use_cache?: boolean; /** - * @description The control image + * Images + * @description The images to concatenate * @default null */ - image?: components["schemas"]["ImageField"] | null; + images?: components["schemas"]["ImageField"][] | null; /** - * @description ControlNet model to load - * @default null + * Use Preferred Resolution + * @description Use FLUX preferred resolutions for the first image + * @default true */ - control_model?: components["schemas"]["ModelIdentifierField"] | null; + use_preferred_resolution?: boolean; /** - * Control Weight - * @description The weight given to the ControlNet - * @default 1 + * type + * @default flux_kontext_image_prep + * @constant */ - control_weight?: number | number[]; + type: "flux_kontext_image_prep"; + }; + /** + * FluxKontextConditioningField + * @description A conditioning field for FLUX Kontext (reference image). + */ + FluxKontextConditioningField: { + /** @description The Kontext reference image. */ + image: components["schemas"]["ImageField"]; + }; + /** + * Kontext Conditioning - FLUX + * @description Prepares a reference image for FLUX Kontext conditioning. + */ + FluxKontextInvocation: { /** - * Begin Step Percent - * @description When the ControlNet is first applied (% of total steps) - * @default 0 + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - begin_step_percent?: number; + id: string; /** - * End Step Percent - * @description When the ControlNet is last applied (% of total steps) - * @default 1 + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - end_step_percent?: number; + is_intermediate?: boolean; /** - * Resize Mode - * @description The resize mode used - * @default just_resize - * @enum {string} + * Use Cache + * @description Whether or not to use the cache + * @default true */ - resize_mode?: "just_resize" | "crop_resize" | "fill_resize" | "just_resize_simple"; + use_cache?: boolean; /** - * Instantx Control Mode - * @description The control mode for InstantX ControlNet union models. Ignored for other ControlNet models. The standard mapping is: canny (0), tile (1), depth (2), blur (3), pose (4), gray (5), low quality (6). Negative values will be treated as 'None'. - * @default -1 + * @description The Kontext reference image. + * @default null */ - instantx_control_mode?: number | null; + image?: components["schemas"]["ImageField"] | null; /** * type - * @default flux_controlnet + * @default flux_kontext * @constant */ - type: "flux_controlnet"; + type: "flux_kontext"; }; /** - * FluxControlNetOutput - * @description FLUX ControlNet info + * FluxKontextOutput + * @description The conditioning output of a FLUX Kontext invocation. */ - FluxControlNetOutput: { - /** @description ControlNet(s) to apply */ - control: components["schemas"]["FluxControlNetField"]; + FluxKontextOutput: { + /** + * Kontext Conditioning + * @description FLUX Kontext conditioning (reference image) + */ + kontext_cond: components["schemas"]["FluxKontextConditioningField"]; /** * type - * @default flux_controlnet_output + * @default flux_kontext_output * @constant */ - type: "flux_controlnet_output"; + type: "flux_kontext_output"; }; /** - * FLUX Denoise - * @description Run denoising process with a FLUX transformer model. + * Apply LoRA - FLUX + * @description Apply a LoRA model to a FLUX transformer and/or text encoder. */ - FluxDenoiseInvocation: { + FluxLoRALoaderInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9081,156 +9021,175 @@ export type components = { */ 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. + * LoRA + * @description LoRA model to load * @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; + lora?: components["schemas"]["ModelIdentifierField"] | null; /** - * Add Noise - * @description Add noise based on denoising start. - * @default true + * Weight + * @description The weight at which the LoRA is applied to each model + * @default 0.75 */ - add_noise?: boolean; + weight?: number; /** - * Transformer - * @description Flux model (Transformer) to load + * FLUX Transformer + * @description Transformer * @default null */ transformer?: components["schemas"]["TransformerField"] | null; /** - * Control LoRA - * @description Control LoRA model to load + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count * @default null */ - control_lora?: components["schemas"]["ControlLoRAField"] | null; + clip?: components["schemas"]["CLIPField"] | null; /** - * Positive Text Conditioning - * @description Positive conditioning tensor + * T5 Encoder + * @description T5 tokenizer and text encoder * @default null */ - positive_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; + t5_encoder?: components["schemas"]["T5EncoderField"] | null; /** - * Negative Text Conditioning - * @description Negative conditioning tensor. Can be None if cfg_scale is 1.0. + * type + * @default flux_lora_loader + * @constant + */ + type: "flux_lora_loader"; + }; + /** + * FluxLoRALoaderOutput + * @description FLUX LoRA Loader Output + */ + FluxLoRALoaderOutput: { + /** + * FLUX Transformer + * @description Transformer * @default null */ - negative_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; + transformer: components["schemas"]["TransformerField"] | null; /** - * Redux Conditioning - * @description FLUX Redux conditioning tensor. + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count * @default null */ - redux_conditioning?: components["schemas"]["FluxReduxConditioningField"] | components["schemas"]["FluxReduxConditioningField"][] | null; + clip: components["schemas"]["CLIPField"] | null; /** - * @description FLUX Fill conditioning. + * T5 Encoder + * @description T5 tokenizer and text encoder * @default null */ - fill_conditioning?: components["schemas"]["FluxFillConditioningField"] | null; + t5_encoder: components["schemas"]["T5EncoderField"] | null; /** - * CFG Scale - * @description Classifier-Free Guidance scale - * @default 1 + * type + * @default flux_lora_loader_output + * @constant */ - cfg_scale?: number | number[]; + type: "flux_lora_loader_output"; + }; + /** + * Main Model - FLUX + * @description Loads a flux base model, outputting its submodels. + */ + FluxModelLoaderInvocation: { /** - * CFG Scale Start Step - * @description Index of the first step to apply cfg_scale. Negative indices count backwards from the the last step (e.g. a value of -1 refers to the final step). - * @default 0 + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - cfg_scale_start_step?: number; + id: string; /** - * CFG Scale End Step - * @description Index of the last step to apply cfg_scale. Negative indices count backwards from the last step (e.g. a value of -1 refers to the final step). - * @default -1 + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - cfg_scale_end_step?: number; + is_intermediate?: boolean; /** - * Width - * @description Width of the generated image. - * @default 1024 + * Use Cache + * @description Whether or not to use the cache + * @default true */ - width?: number; + use_cache?: boolean; + /** @description Flux model (Transformer) to load */ + model: components["schemas"]["ModelIdentifierField"]; /** - * Height - * @description Height of the generated image. - * @default 1024 + * T5 Encoder + * @description T5 tokenizer and text encoder */ - height?: number; + t5_encoder_model: components["schemas"]["ModelIdentifierField"]; /** - * Num Steps - * @description Number of diffusion steps. Recommended values are schnell: 4, dev: 50. - * @default 4 + * CLIP Embed + * @description CLIP Embed loader */ - num_steps?: number; + clip_embed_model: components["schemas"]["ModelIdentifierField"]; /** - * Guidance - * @description The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell. - * @default 4 + * VAE + * @description VAE model to load + * @default null */ - guidance?: number; + vae_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Seed - * @description Randomness seed for reproducibility. - * @default 0 + * type + * @default flux_model_loader + * @constant */ - seed?: number; + type: "flux_model_loader"; + }; + /** + * FluxModelLoaderOutput + * @description Flux base model loader output + */ + FluxModelLoaderOutput: { /** - * Control - * @description ControlNet models. - * @default null + * Transformer + * @description Transformer */ - control?: components["schemas"]["FluxControlNetField"] | components["schemas"]["FluxControlNetField"][] | null; + transformer: components["schemas"]["TransformerField"]; /** - * @description VAE - * @default null + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count */ - controlnet_vae?: components["schemas"]["VAEField"] | null; + clip: components["schemas"]["CLIPField"]; /** - * IP-Adapter - * @description IP-Adapter to apply - * @default null + * T5 Encoder + * @description T5 tokenizer and text encoder */ - ip_adapter?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; + t5_encoder: components["schemas"]["T5EncoderField"]; /** - * Kontext Conditioning - * @description FLUX Kontext conditioning (reference image). - * @default null + * VAE + * @description VAE */ - kontext_conditioning?: components["schemas"]["FluxKontextConditioningField"] | components["schemas"]["FluxKontextConditioningField"][] | null; + vae: components["schemas"]["VAEField"]; + /** + * Max Seq Length + * @description The max sequence length to used for the T5 encoder. (256 for schnell transformer, 512 for dev transformer) + * @enum {integer} + */ + max_seq_len: 256 | 512; /** * type - * @default flux_denoise + * @default flux_model_loader_output * @constant */ - type: "flux_denoise"; + type: "flux_model_loader_output"; }; /** - * FLUX Denoise + Metadata - * @description Run denoising process with a FLUX transformer model + metadata. + * FluxReduxConditioningField + * @description A FLUX Redux conditioning tensor primitive value */ - FluxDenoiseLatentsMetaInvocation: { + FluxReduxConditioningField: { + /** @description The Redux image conditioning tensor. */ + conditioning: components["schemas"]["TensorField"]; /** - * @description Optional metadata to be saved with the image + * @description The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True. * @default null */ - metadata?: components["schemas"]["MetadataField"] | null; + mask?: components["schemas"]["TensorField"] | null; + }; + /** + * FLUX Redux + * @description Runs a FLUX Redux model to generate a conditioning tensor. + */ + FluxReduxInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9249,161 +9208,137 @@ export type components = { */ use_cache?: boolean; /** - * @description Latents tensor + * @description The FLUX Redux image prompt. * @default null */ - latents?: components["schemas"]["LatentsField"] | null; + image?: components["schemas"]["ImageField"] | 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. + * @description The bool mask associated with this FLUX Redux image prompt. Excluded regions should be set to False, included regions should be set to True. * @default null */ - denoise_mask?: components["schemas"]["DenoiseMaskField"] | null; + mask?: components["schemas"]["TensorField"] | null; /** - * Denoising Start - * @description When to start denoising, expressed a percentage of total steps - * @default 0 + * FLUX Redux Model + * @description The FLUX Redux model to use. + * @default null */ - denoising_start?: number; + redux_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Denoising End - * @description When to stop denoising, expressed a percentage of total steps + * Downsampling Factor + * @description Redux Downsampling Factor (1-9) * @default 1 */ - denoising_end?: number; - /** - * Add Noise - * @description Add noise based on denoising start. - * @default true - */ - add_noise?: boolean; + downsampling_factor?: number; /** - * Transformer - * @description Flux model (Transformer) to load - * @default null + * Downsampling Function + * @description Redux Downsampling Function + * @default area + * @enum {string} */ - transformer?: components["schemas"]["TransformerField"] | null; + downsampling_function?: "nearest" | "bilinear" | "bicubic" | "area" | "nearest-exact"; /** - * Control LoRA - * @description Control LoRA model to load - * @default null + * Weight + * @description Redux weight (0.0-1.0) + * @default 1 */ - control_lora?: components["schemas"]["ControlLoRAField"] | null; + weight?: number; /** - * Positive Text Conditioning - * @description Positive conditioning tensor - * @default null + * type + * @default flux_redux + * @constant */ - positive_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; + type: "flux_redux"; + }; + /** + * FluxReduxOutput + * @description The conditioning output of a FLUX Redux invocation. + */ + FluxReduxOutput: { /** - * Negative Text Conditioning - * @description Negative conditioning tensor. Can be None if cfg_scale is 1.0. - * @default null + * Conditioning + * @description FLUX Redux conditioning tensor */ - negative_text_conditioning?: components["schemas"]["FluxConditioningField"] | components["schemas"]["FluxConditioningField"][] | null; + redux_cond: components["schemas"]["FluxReduxConditioningField"]; /** - * Redux Conditioning - * @description FLUX Redux conditioning tensor. - * @default null + * type + * @default flux_redux_output + * @constant */ - redux_conditioning?: components["schemas"]["FluxReduxConditioningField"] | components["schemas"]["FluxReduxConditioningField"][] | null; + type: "flux_redux_output"; + }; + /** + * Prompt - FLUX + * @description Encodes and preps a prompt for a flux image. + */ + FluxTextEncoderInvocation: { /** - * @description FLUX Fill conditioning. - * @default null + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - fill_conditioning?: components["schemas"]["FluxFillConditioningField"] | null; + id: string; /** - * CFG Scale - * @description Classifier-Free Guidance scale - * @default 1 + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - cfg_scale?: number | number[]; + is_intermediate?: boolean; /** - * CFG Scale Start Step - * @description Index of the first step to apply cfg_scale. Negative indices count backwards from the the last step (e.g. a value of -1 refers to the final step). - * @default 0 + * Use Cache + * @description Whether or not to use the cache + * @default true */ - cfg_scale_start_step?: number; + use_cache?: boolean; /** - * CFG Scale End Step - * @description Index of the last step to apply cfg_scale. Negative indices count backwards from the last step (e.g. a value of -1 refers to the final step). - * @default -1 + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null */ - cfg_scale_end_step?: number; + clip?: components["schemas"]["CLIPField"] | null; /** - * Width - * @description Width of the generated image. - * @default 1024 + * T5Encoder + * @description T5 tokenizer and text encoder + * @default null */ - width?: number; + t5_encoder?: components["schemas"]["T5EncoderField"] | null; /** - * Height - * @description Height of the generated image. - * @default 1024 + * T5 Max Seq Len + * @description Max sequence length for the T5 encoder. Expected to be 256 for FLUX schnell models and 512 for FLUX dev models. + * @default null */ - height?: number; + t5_max_seq_len?: (256 | 512) | null; /** - * Num Steps - * @description Number of diffusion steps. Recommended values are schnell: 4, dev: 50. - * @default 4 + * Prompt + * @description Text prompt to encode. + * @default null */ - num_steps?: number; + prompt?: string | null; /** - * Guidance - * @description The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell. - * @default 4 + * @description A mask defining the region that this conditioning prompt applies to. + * @default null */ - guidance?: number; + mask?: components["schemas"]["TensorField"] | null; /** - * Seed - * @description Randomness seed for reproducibility. - * @default 0 + * type + * @default flux_text_encoder + * @constant */ - seed?: number; - /** - * Control - * @description ControlNet models. - * @default null - */ - control?: components["schemas"]["FluxControlNetField"] | components["schemas"]["FluxControlNetField"][] | null; - /** - * @description VAE - * @default null - */ - controlnet_vae?: components["schemas"]["VAEField"] | null; + type: "flux_text_encoder"; + }; + /** + * Latents to Image - FLUX + * @description Generates an image from latents. + */ + FluxVaeDecodeInvocation: { /** - * IP-Adapter - * @description IP-Adapter to apply + * @description The board to save the image to * @default null */ - ip_adapter?: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; + board?: components["schemas"]["BoardField"] | null; /** - * Kontext Conditioning - * @description FLUX Kontext conditioning (reference image). + * @description Optional metadata to be saved with the image * @default null */ - kontext_conditioning?: components["schemas"]["FluxKontextConditioningField"] | components["schemas"]["FluxKontextConditioningField"][] | null; - /** - * type - * @default flux_denoise_meta - * @constant - */ - type: "flux_denoise_meta"; - }; - /** - * FluxFillConditioningField - * @description A FLUX Fill conditioning field. - */ - FluxFillConditioningField: { - /** @description The FLUX Fill reference image. */ - image: components["schemas"]["ImageField"]; - /** @description The FLUX Fill inpaint mask. */ - mask: components["schemas"]["TensorField"]; - }; - /** - * FLUX Fill Conditioning - * @description Prepare the FLUX Fill conditioning data. - */ - FluxFillInvocation: { + metadata?: components["schemas"]["MetadataField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9422,44 +9357,27 @@ export type components = { */ use_cache?: boolean; /** - * @description The FLUX Fill reference image. + * @description Latents tensor * @default null */ - image?: components["schemas"]["ImageField"] | null; + latents?: components["schemas"]["LatentsField"] | null; /** - * @description The bool inpainting mask. Excluded regions should be set to False, included regions should be set to True. + * @description VAE * @default null */ - mask?: components["schemas"]["TensorField"] | null; - /** - * type - * @default flux_fill - * @constant - */ - type: "flux_fill"; - }; - /** - * FluxFillOutput - * @description The conditioning output of a FLUX Fill invocation. - */ - FluxFillOutput: { - /** - * Conditioning - * @description FLUX Redux conditioning tensor - */ - fill_cond: components["schemas"]["FluxFillConditioningField"]; + vae?: components["schemas"]["VAEField"] | null; /** * type - * @default flux_fill_output + * @default flux_vae_decode * @constant */ - type: "flux_fill_output"; + type: "flux_vae_decode"; }; /** - * FLUX IP-Adapter - * @description Collects FLUX IP-Adapter info to pass to other nodes. + * Image to Latents - FLUX + * @description Encodes an image into latents. */ - FluxIPAdapterInvocation: { + FluxVaeEncodeInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9478,64 +9396,77 @@ export type components = { */ use_cache?: boolean; /** - * @description The IP-Adapter image prompt(s). + * @description The image to encode. * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * IP-Adapter Model - * @description The IP-Adapter model. + * @description VAE * @default null */ - ip_adapter_model?: components["schemas"]["ModelIdentifierField"] | null; + vae?: components["schemas"]["VAEField"] | null; /** - * Clip Vision Model - * @description CLIP Vision model to use. - * @default ViT-L + * type + * @default flux_vae_encode * @constant */ - clip_vision_model?: "ViT-L"; + type: "flux_vae_encode"; + }; + /** + * FluxVariantType + * @enum {string} + */ + FluxVariantType: "schnell" | "dev" | "dev_fill"; + /** FoundModel */ + FoundModel: { /** - * Weight - * @description The weight given to the IP-Adapter - * @default 1 + * Path + * @description Path to the model */ - weight?: number | number[]; + path: string; /** - * Begin Step Percent - * @description When the IP-Adapter is first applied (% of total steps) - * @default 0 + * Is Installed + * @description Whether or not the model is already installed */ - begin_step_percent?: number; + is_installed: boolean; + }; + /** + * FreeUConfig + * @description Configuration for the FreeU hyperparameters. + * - https://huggingface.co/docs/diffusers/main/en/using-diffusers/freeu + * - https://github.com/ChenyangSi/FreeU + */ + FreeUConfig: { /** - * End Step Percent - * @description When the IP-Adapter is last applied (% of total steps) - * @default 1 + * S1 + * @description Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. */ - end_step_percent?: number; + s1: number; /** - * type - * @default flux_ip_adapter - * @constant + * S2 + * @description Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. */ - type: "flux_ip_adapter"; - }; - /** - * FLUX Kontext Image Prep - * @description Prepares an image or images for use with FLUX Kontext. The first/single image is resized to the nearest - * preferred Kontext resolution. All other images are concatenated horizontally, maintaining their aspect ratio. - */ - FluxKontextConcatenateImagesInvocation: { + s2: number; /** - * @description The board to save the image to - * @default null + * B1 + * @description Scaling factor for stage 1 to amplify the contributions of backbone features. */ - board?: components["schemas"]["BoardField"] | null; + b1: number; /** - * @description Optional metadata to be saved with the image - * @default null + * B2 + * @description Scaling factor for stage 2 to amplify the contributions of backbone features. */ - metadata?: components["schemas"]["MetadataField"] | null; + b2: number; + }; + /** + * Apply FreeU - SD1.5, SDXL + * @description Applies FreeU to the UNet. Suggested values (b1/b2/s1/s2): + * + * SD1.5: 1.2/1.4/0.9/0.2, + * SD2: 1.1/1.2/0.9/0.2, + * SDXL: 1.1/1.2/0.6/0.4, + */ + FreeUInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9554,37 +9485,47 @@ export type components = { */ use_cache?: boolean; /** - * Images - * @description The images to concatenate + * UNet + * @description UNet (scheduler, LoRAs) * @default null */ - images?: components["schemas"]["ImageField"][] | null; + unet?: components["schemas"]["UNetField"] | null; /** - * Use Preferred Resolution - * @description Use FLUX preferred resolutions for the first image - * @default true + * B1 + * @description Scaling factor for stage 1 to amplify the contributions of backbone features. + * @default 1.2 */ - use_preferred_resolution?: boolean; + b1?: number; + /** + * B2 + * @description Scaling factor for stage 2 to amplify the contributions of backbone features. + * @default 1.4 + */ + b2?: number; + /** + * S1 + * @description Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. + * @default 0.9 + */ + s1?: number; + /** + * S2 + * @description Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. + * @default 0.2 + */ + s2?: number; /** * type - * @default flux_kontext_image_prep + * @default freeu * @constant */ - type: "flux_kontext_image_prep"; - }; - /** - * FluxKontextConditioningField - * @description A conditioning field for FLUX Kontext (reference image). - */ - FluxKontextConditioningField: { - /** @description The Kontext reference image. */ - image: components["schemas"]["ImageField"]; + type: "freeu"; }; /** - * Kontext Conditioning - FLUX - * @description Prepares a reference image for FLUX Kontext conditioning. + * Get Image Mask Bounding Box + * @description Gets the bounding box of the given mask image. */ - FluxKontextInvocation: { + GetMaskBoundingBoxInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9603,128 +9544,134 @@ export type components = { */ use_cache?: boolean; /** - * @description The Kontext reference image. + * @description The mask to crop. * @default null */ - image?: components["schemas"]["ImageField"] | null; + mask?: components["schemas"]["ImageField"] | null; /** - * type - * @default flux_kontext - * @constant + * Margin + * @description Margin to add to the bounding box. + * @default 0 */ - type: "flux_kontext"; - }; - /** - * FluxKontextOutput - * @description The conditioning output of a FLUX Kontext invocation. - */ - FluxKontextOutput: { + margin?: number; /** - * Kontext Conditioning - * @description FLUX Kontext conditioning (reference image) + * @description Color of the mask in the image. + * @default { + * "r": 255, + * "g": 255, + * "b": 255, + * "a": 255 + * } */ - kontext_cond: components["schemas"]["FluxKontextConditioningField"]; + mask_color?: components["schemas"]["ColorField"]; /** * type - * @default flux_kontext_output + * @default get_image_mask_bounding_box * @constant */ - type: "flux_kontext_output"; + type: "get_image_mask_bounding_box"; + }; + /** GlmEncoderField */ + GlmEncoderField: { + /** @description Info to load tokenizer submodel */ + tokenizer: components["schemas"]["ModelIdentifierField"]; + /** @description Info to load text_encoder submodel */ + text_encoder: components["schemas"]["ModelIdentifierField"]; }; /** - * Apply LoRA - FLUX - * @description Apply a LoRA model to a FLUX transformer and/or text encoder. + * GradientMaskOutput + * @description Outputs a denoise mask and an image representing the total gradient of the mask. */ - FluxLoRALoaderInvocation: { - /** - * 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; + GradientMaskOutput: { + /** @description Mask for denoise model run. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved. */ + denoise_mask: components["schemas"]["DenoiseMaskField"]; + /** @description Image representing the total gradient area of the mask. For paste-back purposes. */ + expanded_mask_area: components["schemas"]["ImageField"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * type + * @default gradient_mask_output + * @constant */ - use_cache?: boolean; + type: "gradient_mask_output"; + }; + /** Graph */ + Graph: { /** - * LoRA - * @description LoRA model to load - * @default null + * Id + * @description The id of this graph */ - lora?: components["schemas"]["ModelIdentifierField"] | null; + id?: string; /** - * Weight - * @description The weight at which the LoRA is applied to each model - * @default 0.75 + * Nodes + * @description The nodes in this graph */ - weight?: number; + nodes?: { + [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | 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"]["CannyEdgeDetectionInvocation"] | 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"]["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"]["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"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | 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"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["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"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | 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"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"]; + }; /** - * FLUX Transformer - * @description Transformer - * @default null + * Edges + * @description The connections between nodes and their fields in this graph */ - transformer?: components["schemas"]["TransformerField"] | null; + edges?: components["schemas"]["Edge"][]; + }; + /** + * GraphExecutionState + * @description Tracks the state of a graph execution + */ + GraphExecutionState: { /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * Id + * @description The id of the execution state */ - clip?: components["schemas"]["CLIPField"] | null; + id: string; + /** @description The graph being executed */ + graph: components["schemas"]["Graph"]; + /** @description The expanded graph of activated and executed nodes */ + execution_graph: components["schemas"]["Graph"]; /** - * T5 Encoder - * @description T5 tokenizer and text encoder - * @default null + * Executed + * @description The set of node ids that have been executed */ - t5_encoder?: components["schemas"]["T5EncoderField"] | null; + executed: string[]; /** - * type - * @default flux_lora_loader - * @constant + * Executed History + * @description The list of node ids that have been executed, in order of execution */ - type: "flux_lora_loader"; - }; - /** - * FluxLoRALoaderOutput - * @description FLUX LoRA Loader Output - */ - FluxLoRALoaderOutput: { + executed_history: string[]; /** - * FLUX Transformer - * @description Transformer - * @default null + * Results + * @description The results of node executions */ - transformer: components["schemas"]["TransformerField"] | null; + results: { + [key: string]: 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"]["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"]["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"]["PairTileImageOutput"] | 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"]["VideoOutput"]; + }; /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * Errors + * @description Errors raised when executing nodes */ - clip: components["schemas"]["CLIPField"] | null; + errors: { + [key: string]: string; + }; /** - * T5 Encoder - * @description T5 tokenizer and text encoder - * @default null + * Prepared Source Mapping + * @description The map of prepared nodes to original graph nodes */ - t5_encoder: components["schemas"]["T5EncoderField"] | null; + prepared_source_mapping: { + [key: string]: string; + }; /** - * type - * @default flux_lora_loader_output - * @constant + * Source Prepared Mapping + * @description The map of original graph nodes to prepared nodes */ - type: "flux_lora_loader_output"; + source_prepared_mapping: { + [key: string]: string[]; + }; }; /** - * Main Model - FLUX - * @description Loads a flux base model, outputting its submodels. + * Grounding DINO (Text Prompt Object Detection) + * @description Runs a Grounding DINO model. Performs zero-shot bounding-box object detection from a text prompt. */ - FluxModelLoaderInvocation: { + GroundingDinoInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9742,87 +9689,51 @@ export type components = { * @default true */ use_cache?: boolean; - /** @description Flux model (Transformer) to load */ - model: components["schemas"]["ModelIdentifierField"]; - /** - * T5 Encoder - * @description T5 tokenizer and text encoder - */ - t5_encoder_model: components["schemas"]["ModelIdentifierField"]; - /** - * CLIP Embed - * @description CLIP Embed loader - */ - clip_embed_model: components["schemas"]["ModelIdentifierField"]; /** - * VAE - * @description VAE model to load + * Model + * @description The Grounding DINO model to use. * @default null */ - vae_model?: components["schemas"]["ModelIdentifierField"] | null; - /** - * type - * @default flux_model_loader - * @constant - */ - type: "flux_model_loader"; - }; - /** - * FluxModelLoaderOutput - * @description Flux base model loader output - */ - FluxModelLoaderOutput: { - /** - * Transformer - * @description Transformer - */ - transformer: components["schemas"]["TransformerField"]; - /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - */ - clip: components["schemas"]["CLIPField"]; + model?: ("grounding-dino-tiny" | "grounding-dino-base") | null; /** - * T5 Encoder - * @description T5 tokenizer and text encoder + * Prompt + * @description The prompt describing the object to segment. + * @default null */ - t5_encoder: components["schemas"]["T5EncoderField"]; + prompt?: string | null; /** - * VAE - * @description VAE + * @description The image to segment. + * @default null */ - vae: components["schemas"]["VAEField"]; + image?: components["schemas"]["ImageField"] | null; /** - * Max Seq Length - * @description The max sequence length to used for the T5 encoder. (256 for schnell transformer, 512 for dev transformer) - * @enum {integer} + * Detection Threshold + * @description The detection threshold for the Grounding DINO model. All detected bounding boxes with scores above this threshold will be returned. + * @default 0.3 */ - max_seq_len: 256 | 512; + detection_threshold?: number; /** * type - * @default flux_model_loader_output + * @default grounding_dino * @constant */ - type: "flux_model_loader_output"; + type: "grounding_dino"; }; /** - * FluxReduxConditioningField - * @description A FLUX Redux conditioning tensor primitive value + * HED Edge Detection + * @description Geneartes an edge map using the HED (softedge) model. */ - FluxReduxConditioningField: { - /** @description The Redux image conditioning tensor. */ - conditioning: components["schemas"]["TensorField"]; + HEDEdgeDetectionInvocation: { /** - * @description The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True. + * @description The board to save the image to * @default null */ - mask?: components["schemas"]["TensorField"] | null; - }; - /** - * FLUX Redux - * @description Runs a FLUX Redux model to generate a conditioning tensor. - */ - FluxReduxInvocation: { + 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. @@ -9841,69 +9752,59 @@ export type components = { */ use_cache?: boolean; /** - * @description The FLUX Redux image prompt. + * @description The image to process * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * @description The bool mask associated with this FLUX Redux image prompt. Excluded regions should be set to False, included regions should be set to True. - * @default null - */ - mask?: components["schemas"]["TensorField"] | null; - /** - * FLUX Redux Model - * @description The FLUX Redux model to use. - * @default null + * Scribble + * @description Whether or not to use scribble mode + * @default false */ - redux_model?: components["schemas"]["ModelIdentifierField"] | null; + scribble?: boolean; /** - * Downsampling Factor - * @description Redux Downsampling Factor (1-9) - * @default 1 + * type + * @default hed_edge_detection + * @constant */ - downsampling_factor?: number; - /** - * Downsampling Function - * @description Redux Downsampling Function - * @default area - * @enum {string} - */ - downsampling_function?: "nearest" | "bilinear" | "bicubic" | "area" | "nearest-exact"; - /** - * Weight - * @description Redux weight (0.0-1.0) - * @default 1 - */ - weight?: number; - /** - * type - * @default flux_redux - * @constant - */ - type: "flux_redux"; + type: "hed_edge_detection"; }; /** - * FluxReduxOutput - * @description The conditioning output of a FLUX Redux invocation. + * HFModelSource + * @description A HuggingFace repo_id with optional variant, sub-folder and access token. + * Note that the variant option, if not provided to the constructor, will default to fp16, which is + * what people (almost) always want. */ - FluxReduxOutput: { - /** - * Conditioning - * @description FLUX Redux conditioning tensor - */ - redux_cond: components["schemas"]["FluxReduxConditioningField"]; + HFModelSource: { + /** Repo Id */ + repo_id: string; + /** @default fp16 */ + variant?: components["schemas"]["ModelRepoVariant"] | null; + /** Subfolder */ + subfolder?: string | null; + /** Access Token */ + access_token?: string | null; /** - * type - * @default flux_redux_output - * @constant + * @description discriminator enum property added by openapi-typescript + * @enum {string} */ - type: "flux_redux_output"; + type: "hf"; }; /** - * Prompt - FLUX - * @description Encodes and preps a prompt for a flux image. + * HFTokenStatus + * @enum {string} */ - FluxTextEncoderInvocation: { + HFTokenStatus: "valid" | "invalid" | "unknown"; + /** HTTPValidationError */ + HTTPValidationError: { + /** Detail */ + detail?: components["schemas"]["ValidationError"][]; + }; + /** + * Heuristic Resize + * @description Resize an image using a heuristic method. Preserves edge maps. + */ + HeuristicResizeInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -9922,95 +9823,136 @@ export type components = { */ use_cache?: boolean; /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null - */ - clip?: components["schemas"]["CLIPField"] | null; - /** - * T5Encoder - * @description T5 tokenizer and text encoder - * @default null - */ - t5_encoder?: components["schemas"]["T5EncoderField"] | null; - /** - * T5 Max Seq Len - * @description Max sequence length for the T5 encoder. Expected to be 256 for FLUX schnell models and 512 for FLUX dev models. + * @description The image to resize * @default null */ - t5_max_seq_len?: (256 | 512) | null; + image?: components["schemas"]["ImageField"] | null; /** - * Prompt - * @description Text prompt to encode. - * @default null + * Width + * @description The width to resize to (px) + * @default 512 */ - prompt?: string | null; + width?: number; /** - * @description A mask defining the region that this conditioning prompt applies to. - * @default null + * Height + * @description The height to resize to (px) + * @default 512 */ - mask?: components["schemas"]["TensorField"] | null; + height?: number; /** * type - * @default flux_text_encoder + * @default heuristic_resize * @constant */ - type: "flux_text_encoder"; + type: "heuristic_resize"; }; /** - * Latents to Image - FLUX - * @description Generates an image from latents. + * HuggingFaceMetadata + * @description Extended metadata fields provided by HuggingFace. */ - FluxVaeDecodeInvocation: { + HuggingFaceMetadata: { /** - * @description The board to save the image to - * @default null + * Name + * @description model's name */ - board?: components["schemas"]["BoardField"] | null; + name: string; /** - * @description Optional metadata to be saved with the image - * @default null + * Files + * @description model files and their sizes */ - metadata?: components["schemas"]["MetadataField"] | null; + files?: components["schemas"]["RemoteModelFile"][]; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "huggingface"; /** * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * @description The HF model id */ id: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. + * Api Response + * @description Response from the HF API as stringified JSON + */ + api_response?: string | null; + /** + * Is Diffusers + * @description Whether the metadata is for a Diffusers format model * @default false */ - is_intermediate?: boolean; + is_diffusers?: boolean; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Ckpt Urls + * @description URLs for all checkpoint format models in the metadata */ - use_cache?: boolean; + ckpt_urls?: string[] | null; + }; + /** HuggingFaceModels */ + HuggingFaceModels: { /** - * @description Latents tensor - * @default null + * Urls + * @description URLs for all checkpoint format models in the metadata */ - latents?: components["schemas"]["LatentsField"] | null; + urls: string[] | null; /** - * @description VAE - * @default null + * Is Diffusers + * @description Whether the metadata is for a Diffusers format model */ - vae?: components["schemas"]["VAEField"] | null; + is_diffusers: boolean; + }; + /** IPAdapterField */ + IPAdapterField: { /** - * type - * @default flux_vae_decode - * @constant + * Image + * @description The IP-Adapter image prompt(s). */ - type: "flux_vae_decode"; + image: components["schemas"]["ImageField"] | components["schemas"]["ImageField"][]; + /** @description The IP-Adapter model to use. */ + ip_adapter_model: components["schemas"]["ModelIdentifierField"]; + /** @description The name of the CLIP image encoder model. */ + image_encoder_model: components["schemas"]["ModelIdentifierField"]; + /** + * Weight + * @description The weight given to the IP-Adapter. + * @default 1 + */ + weight?: number | number[]; + /** + * Target Blocks + * @description The IP Adapter blocks to apply + * @default [] + */ + target_blocks?: string[]; + /** + * Method + * @description Weight apply method + * @default full + */ + method?: string; + /** + * Begin Step Percent + * @description When the IP-Adapter is first applied (% of total steps) + * @default 0 + */ + begin_step_percent?: number; + /** + * End Step Percent + * @description When the IP-Adapter is last applied (% of total steps) + * @default 1 + */ + end_step_percent?: number; + /** + * @description The bool mask associated with this IP-Adapter. Excluded regions should be set to False, included regions should be set to True. + * @default null + */ + mask?: components["schemas"]["TensorField"] | null; }; /** - * Image to Latents - FLUX - * @description Encodes an image into latents. + * IP-Adapter - SD1.5, SDXL + * @description Collects IP-Adapter info to pass to other nodes. */ - FluxVaeEncodeInvocation: { + IPAdapterInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -10029,689 +9971,406 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to encode. + * Image + * @description The IP-Adapter image prompt(s). * @default null */ - image?: components["schemas"]["ImageField"] | null; + image?: components["schemas"]["ImageField"] | components["schemas"]["ImageField"][] | null; /** - * @description VAE + * IP-Adapter Model + * @description The IP-Adapter model. * @default null */ - vae?: components["schemas"]["VAEField"] | null; + ip_adapter_model?: components["schemas"]["ModelIdentifierField"] | null; /** - * type - * @default flux_vae_encode - * @constant + * Clip Vision Model + * @description CLIP Vision model to use. Overrides model settings. Mandatory for checkpoint models. + * @default ViT-H + * @enum {string} */ - type: "flux_vae_encode"; - }; - /** - * FluxVariantType - * @enum {string} - */ - FluxVariantType: "schnell" | "dev" | "dev_fill"; - /** FoundModel */ - FoundModel: { + clip_vision_model?: "ViT-H" | "ViT-G" | "ViT-L"; /** - * Path - * @description Path to the model + * Weight + * @description The weight given to the IP-Adapter + * @default 1 */ - path: string; + weight?: number | number[]; /** - * Is Installed - * @description Whether or not the model is already installed + * Method + * @description The method to apply the IP-Adapter + * @default full + * @enum {string} */ - is_installed: boolean; - }; - /** - * FreeUConfig - * @description Configuration for the FreeU hyperparameters. - * - https://huggingface.co/docs/diffusers/main/en/using-diffusers/freeu - * - https://github.com/ChenyangSi/FreeU - */ - FreeUConfig: { + method?: "full" | "style" | "composition" | "style_strong" | "style_precise"; /** - * S1 - * @description Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. + * Begin Step Percent + * @description When the IP-Adapter is first applied (% of total steps) + * @default 0 */ - s1: number; + begin_step_percent?: number; /** - * S2 - * @description Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. + * End Step Percent + * @description When the IP-Adapter is last applied (% of total steps) + * @default 1 */ - s2: number; + end_step_percent?: number; /** - * B1 - * @description Scaling factor for stage 1 to amplify the contributions of backbone features. + * @description A mask defining the region that this IP-Adapter applies to. + * @default null */ - b1: number; + mask?: components["schemas"]["TensorField"] | null; /** - * B2 - * @description Scaling factor for stage 2 to amplify the contributions of backbone features. + * type + * @default ip_adapter + * @constant */ - b2: number; + type: "ip_adapter"; }; /** - * Apply FreeU - SD1.5, SDXL - * @description Applies FreeU to the UNet. Suggested values (b1/b2/s1/s2): - * - * SD1.5: 1.2/1.4/0.9/0.2, - * SD2: 1.1/1.2/0.9/0.2, - * SDXL: 1.1/1.2/0.6/0.4, + * IPAdapterMetadataField + * @description IP Adapter Field, minus the CLIP Vision Encoder model */ - FreeUInvocation: { + IPAdapterMetadataField: { + /** @description The IP-Adapter image prompt. */ + image: components["schemas"]["ImageField"]; + /** @description The IP-Adapter model. */ + ip_adapter_model: components["schemas"]["ModelIdentifierField"]; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Clip Vision Model + * @description The CLIP Vision model + * @enum {string} */ - id: string; + clip_vision_model: "ViT-L" | "ViT-H" | "ViT-G"; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Method + * @description Method to apply IP Weights with + * @enum {string} */ - is_intermediate?: boolean; + method: "full" | "style" | "composition" | "style_strong" | "style_precise"; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Weight + * @description The weight given to the IP-Adapter */ - use_cache?: boolean; + weight: number | number[]; /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null + * Begin Step Percent + * @description When the IP-Adapter is first applied (% of total steps) */ - unet?: components["schemas"]["UNetField"] | null; + begin_step_percent: number; /** - * B1 - * @description Scaling factor for stage 1 to amplify the contributions of backbone features. - * @default 1.2 + * End Step Percent + * @description When the IP-Adapter is last applied (% of total steps) */ - b1?: number; + end_step_percent: number; + }; + /** IPAdapterOutput */ + IPAdapterOutput: { /** - * B2 - * @description Scaling factor for stage 2 to amplify the contributions of backbone features. - * @default 1.4 + * IP-Adapter + * @description IP-Adapter to apply */ - b2?: number; + ip_adapter: components["schemas"]["IPAdapterField"]; /** - * S1 - * @description Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. - * @default 0.9 + * type + * @default ip_adapter_output + * @constant */ - s1?: number; + type: "ip_adapter_output"; + }; + /** IPAdapter_Checkpoint_FLUX_Config */ + IPAdapter_Checkpoint_FLUX_Config: { /** - * S2 - * @description Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process. - * @default 0.2 + * Key + * @description A unique key for this model. */ - s2?: number; + key: string; /** - * type - * @default freeu - * @constant + * Hash + * @description The hash of the model file(s). */ - type: "freeu"; - }; - /** - * Get Image Mask Bounding Box - * @description Gets the bounding box of the given mask image. - */ - GetMaskBoundingBoxInvocation: { + hash: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - id: string; + path: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * File Size + * @description The size of the model in bytes. */ - is_intermediate?: boolean; + file_size: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Name + * @description Name of the model. */ - use_cache?: boolean; + name: string; /** - * @description The mask to crop. - * @default null + * Description + * @description Model description */ - mask?: components["schemas"]["ImageField"] | null; + description: string | null; /** - * Margin - * @description Margin to add to the bounding box. - * @default 0 + * Source + * @description The original source of the model (path, URL or repo_id). */ - margin?: number; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * @description Color of the mask in the image. - * @default { - * "r": 255, - * "g": 255, - * "b": 255, - * "a": 255 - * } + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - mask_color?: components["schemas"]["ColorField"]; + source_api_response: string | null; /** - * type - * @default get_image_mask_bounding_box - * @constant + * Cover Image + * @description Url for image to preview model */ - type: "get_image_mask_bounding_box"; - }; - /** GlmEncoderField */ - GlmEncoderField: { - /** @description Info to load tokenizer submodel */ - tokenizer: components["schemas"]["ModelIdentifierField"]; - /** @description Info to load text_encoder submodel */ - text_encoder: components["schemas"]["ModelIdentifierField"]; - }; - /** - * GradientMaskOutput - * @description Outputs a denoise mask and an image representing the total gradient of the mask. - */ - GradientMaskOutput: { - /** @description Mask for denoise model run. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved. */ - denoise_mask: components["schemas"]["DenoiseMaskField"]; - /** @description Image representing the total gradient area of the mask. For paste-back purposes. */ - expanded_mask_area: components["schemas"]["ImageField"]; + cover_image: string | null; /** - * type - * @default gradient_mask_output - * @constant + * Usage Info + * @description Usage information for this model */ - type: "gradient_mask_output"; - }; - /** Graph */ - Graph: { + usage_info: string | null; /** - * Id - * @description The id of this graph + * Type + * @default ip_adapter + * @constant */ - id?: string; + type: "ip_adapter"; /** - * Nodes - * @description The nodes in this graph + * Format + * @default checkpoint + * @constant */ - nodes?: { - [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | 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"]["CannyEdgeDetectionInvocation"] | 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"]["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"]["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"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | 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"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["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"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | 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"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"]; - }; + format: "checkpoint"; /** - * Edges - * @description The connections between nodes and their fields in this graph + * Base + * @default flux + * @constant */ - edges?: components["schemas"]["Edge"][]; + base: "flux"; }; - /** - * GraphExecutionState - * @description Tracks the state of a graph execution - */ - GraphExecutionState: { + /** IPAdapter_Checkpoint_SD1_Config */ + IPAdapter_Checkpoint_SD1_Config: { /** - * Id - * @description The id of the execution state + * Key + * @description A unique key for this model. */ - id: string; - /** @description The graph being executed */ - graph: components["schemas"]["Graph"]; - /** @description The expanded graph of activated and executed nodes */ - execution_graph: components["schemas"]["Graph"]; + key: string; /** - * Executed - * @description The set of node ids that have been executed + * Hash + * @description The hash of the model file(s). */ - executed: string[]; + hash: string; /** - * Executed History - * @description The list of node ids that have been executed, in order of execution + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - executed_history: string[]; + path: string; /** - * Results - * @description The results of node executions + * File Size + * @description The size of the model in bytes. */ - results: { - [key: string]: 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"]["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"]["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"]["PairTileImageOutput"] | 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"]["VideoOutput"]; - }; - /** - * Errors - * @description Errors raised when executing nodes - */ - errors: { - [key: string]: string; - }; - /** - * Prepared Source Mapping - * @description The map of prepared nodes to original graph nodes - */ - prepared_source_mapping: { - [key: string]: string; - }; + file_size: number; /** - * Source Prepared Mapping - * @description The map of original graph nodes to prepared nodes + * Name + * @description Name of the model. */ - source_prepared_mapping: { - [key: string]: string[]; - }; - }; - /** - * Grounding DINO (Text Prompt Object Detection) - * @description Runs a Grounding DINO model. Performs zero-shot bounding-box object detection from a text prompt. - */ - GroundingDinoInvocation: { + name: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Description + * @description Model description */ - id: string; + description: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Source + * @description The original source of the model (path, URL or repo_id). */ - is_intermediate?: boolean; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - use_cache?: boolean; + source_api_response: string | null; /** - * Model - * @description The Grounding DINO model to use. - * @default null + * Cover Image + * @description Url for image to preview model */ - model?: ("grounding-dino-tiny" | "grounding-dino-base") | null; + cover_image: string | null; /** - * Prompt - * @description The prompt describing the object to segment. - * @default null + * Usage Info + * @description Usage information for this model */ - prompt?: string | null; + usage_info: string | null; /** - * @description The image to segment. - * @default null + * Type + * @default ip_adapter + * @constant */ - image?: components["schemas"]["ImageField"] | null; + type: "ip_adapter"; /** - * Detection Threshold - * @description The detection threshold for the Grounding DINO model. All detected bounding boxes with scores above this threshold will be returned. - * @default 0.3 + * Format + * @default checkpoint + * @constant */ - detection_threshold?: number; + format: "checkpoint"; /** - * type - * @default grounding_dino + * Base + * @default sd-1 * @constant */ - type: "grounding_dino"; + base: "sd-1"; }; - /** - * HED Edge Detection - * @description Geneartes an edge map using the HED (softedge) model. - */ - HEDEdgeDetectionInvocation: { + /** IPAdapter_Checkpoint_SD2_Config */ + IPAdapter_Checkpoint_SD2_Config: { /** - * @description The board to save the image to - * @default null + * Key + * @description A unique key for this model. */ - board?: components["schemas"]["BoardField"] | null; + key: string; /** - * @description Optional metadata to be saved with the image - * @default null + * Hash + * @description The hash of the model file(s). */ - metadata?: components["schemas"]["MetadataField"] | null; + hash: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - id: string; + path: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * File Size + * @description The size of the model in bytes. */ - is_intermediate?: boolean; + file_size: number; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Name + * @description Name of the model. */ - use_cache?: boolean; + name: string; /** - * @description The image to process - * @default null + * Description + * @description Model description */ - image?: components["schemas"]["ImageField"] | null; + description: string | null; /** - * Scribble - * @description Whether or not to use scribble mode - * @default false + * Source + * @description The original source of the model (path, URL or repo_id). */ - scribble?: boolean; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * type - * @default hed_edge_detection - * @constant + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - type: "hed_edge_detection"; - }; - /** - * HFModelSource - * @description A HuggingFace repo_id with optional variant, sub-folder and access token. - * Note that the variant option, if not provided to the constructor, will default to fp16, which is - * what people (almost) always want. - */ - HFModelSource: { - /** Repo Id */ - repo_id: string; - /** @default fp16 */ - variant?: components["schemas"]["ModelRepoVariant"] | null; - /** Subfolder */ - subfolder?: string | null; - /** Access Token */ - access_token?: string | null; + source_api_response: string | null; /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} + * Cover Image + * @description Url for image to preview model */ - type: "hf"; - }; - /** - * HFTokenStatus - * @enum {string} - */ - HFTokenStatus: "valid" | "invalid" | "unknown"; - /** HTTPValidationError */ - HTTPValidationError: { - /** Detail */ - detail?: components["schemas"]["ValidationError"][]; - }; - /** - * Heuristic Resize - * @description Resize an image using a heuristic method. Preserves edge maps. - */ - HeuristicResizeInvocation: { + cover_image: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Usage Info + * @description Usage information for this model */ - id: string; + usage_info: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Type + * @default ip_adapter + * @constant */ - is_intermediate?: boolean; + type: "ip_adapter"; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Format + * @default checkpoint + * @constant */ - use_cache?: boolean; + format: "checkpoint"; /** - * @description The image to resize - * @default null + * Base + * @default sd-2 + * @constant */ - image?: components["schemas"]["ImageField"] | null; + base: "sd-2"; + }; + /** IPAdapter_Checkpoint_SDXL_Config */ + IPAdapter_Checkpoint_SDXL_Config: { /** - * Width - * @description The width to resize to (px) - * @default 512 + * Key + * @description A unique key for this model. */ - width?: number; + key: string; /** - * Height - * @description The height to resize to (px) - * @default 512 + * Hash + * @description The hash of the model file(s). */ - height?: number; + hash: string; /** - * type - * @default heuristic_resize - * @constant + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - type: "heuristic_resize"; - }; - /** - * HuggingFaceMetadata - * @description Extended metadata fields provided by HuggingFace. - */ - HuggingFaceMetadata: { + path: string; + /** + * File Size + * @description The size of the model in bytes. + */ + file_size: number; /** * Name - * @description model's name + * @description Name of the model. */ name: string; /** - * Files - * @description model files and their sizes - */ - files?: components["schemas"]["RemoteModelFile"][]; - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - type: "huggingface"; - /** - * Id - * @description The HF model id - */ - id: string; - /** - * Api Response - * @description Response from the HF API as stringified JSON - */ - api_response?: string | null; - /** - * Is Diffusers - * @description Whether the metadata is for a Diffusers format model - * @default false - */ - is_diffusers?: boolean; - /** - * Ckpt Urls - * @description URLs for all checkpoint format models in the metadata - */ - ckpt_urls?: string[] | null; - }; - /** HuggingFaceModels */ - HuggingFaceModels: { - /** - * Urls - * @description URLs for all checkpoint format models in the metadata - */ - urls: string[] | null; - /** - * Is Diffusers - * @description Whether the metadata is for a Diffusers format model - */ - is_diffusers: boolean; - }; - /** IPAdapterField */ - IPAdapterField: { - /** - * Image - * @description The IP-Adapter image prompt(s). - */ - image: components["schemas"]["ImageField"] | components["schemas"]["ImageField"][]; - /** @description The IP-Adapter model to use. */ - ip_adapter_model: components["schemas"]["ModelIdentifierField"]; - /** @description The name of the CLIP image encoder model. */ - image_encoder_model: components["schemas"]["ModelIdentifierField"]; - /** - * Weight - * @description The weight given to the IP-Adapter. - * @default 1 - */ - weight?: number | number[]; - /** - * Target Blocks - * @description The IP Adapter blocks to apply - * @default [] - */ - target_blocks?: string[]; - /** - * Method - * @description Weight apply method - * @default full - */ - method?: string; - /** - * Begin Step Percent - * @description When the IP-Adapter is first applied (% of total steps) - * @default 0 - */ - begin_step_percent?: number; - /** - * End Step Percent - * @description When the IP-Adapter is last applied (% of total steps) - * @default 1 - */ - end_step_percent?: number; - /** - * @description The bool mask associated with this IP-Adapter. Excluded regions should be set to False, included regions should be set to True. - * @default null - */ - mask?: components["schemas"]["TensorField"] | null; - }; - /** - * IP-Adapter - SD1.5, SDXL - * @description Collects IP-Adapter info to pass to other nodes. - */ - IPAdapterInvocation: { - /** - * 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; - /** - * Image - * @description The IP-Adapter image prompt(s). - * @default null - */ - image?: components["schemas"]["ImageField"] | components["schemas"]["ImageField"][] | null; - /** - * IP-Adapter Model - * @description The IP-Adapter model. - * @default null - */ - ip_adapter_model?: components["schemas"]["ModelIdentifierField"] | null; - /** - * Clip Vision Model - * @description CLIP Vision model to use. Overrides model settings. Mandatory for checkpoint models. - * @default ViT-H - * @enum {string} - */ - clip_vision_model?: "ViT-H" | "ViT-G" | "ViT-L"; - /** - * Weight - * @description The weight given to the IP-Adapter - * @default 1 + * Description + * @description Model description */ - weight?: number | number[]; + description: string | null; /** - * Method - * @description The method to apply the IP-Adapter - * @default full - * @enum {string} + * Source + * @description The original source of the model (path, URL or repo_id). */ - method?: "full" | "style" | "composition" | "style_strong" | "style_precise"; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Begin Step Percent - * @description When the IP-Adapter is first applied (% of total steps) - * @default 0 + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - begin_step_percent?: number; + source_api_response: string | null; /** - * End Step Percent - * @description When the IP-Adapter is last applied (% of total steps) - * @default 1 + * Cover Image + * @description Url for image to preview model */ - end_step_percent?: number; + cover_image: string | null; /** - * @description A mask defining the region that this IP-Adapter applies to. - * @default null + * Usage Info + * @description Usage information for this model */ - mask?: components["schemas"]["TensorField"] | null; + usage_info: string | null; /** - * type + * Type * @default ip_adapter * @constant */ type: "ip_adapter"; - }; - /** - * IPAdapterMetadataField - * @description IP Adapter Field, minus the CLIP Vision Encoder model - */ - IPAdapterMetadataField: { - /** @description The IP-Adapter image prompt. */ - image: components["schemas"]["ImageField"]; - /** @description The IP-Adapter model. */ - ip_adapter_model: components["schemas"]["ModelIdentifierField"]; - /** - * Clip Vision Model - * @description The CLIP Vision model - * @enum {string} - */ - clip_vision_model: "ViT-L" | "ViT-H" | "ViT-G"; - /** - * Method - * @description Method to apply IP Weights with - * @enum {string} - */ - method: "full" | "style" | "composition" | "style_strong" | "style_precise"; /** - * Weight - * @description The weight given to the IP-Adapter - */ - weight: number | number[]; - /** - * Begin Step Percent - * @description When the IP-Adapter is first applied (% of total steps) - */ - begin_step_percent: number; - /** - * End Step Percent - * @description When the IP-Adapter is last applied (% of total steps) - */ - end_step_percent: number; - }; - /** IPAdapterOutput */ - IPAdapterOutput: { - /** - * IP-Adapter - * @description IP-Adapter to apply + * Format + * @default checkpoint + * @constant */ - ip_adapter: components["schemas"]["IPAdapterField"]; + format: "checkpoint"; /** - * type - * @default ip_adapter_output + * Base + * @default sdxl * @constant */ - type: "ip_adapter_output"; + base: "sdxl"; }; - /** IPAdapter_Checkpoint_FLUX_Config */ - IPAdapter_Checkpoint_FLUX_Config: { + /** IPAdapter_InvokeAI_SD1_Config */ + IPAdapter_InvokeAI_SD1_Config: { /** * Key * @description A unique key for this model. @@ -10759,13 +10418,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -10779,19 +10431,21 @@ export type components = { type: "ip_adapter"; /** * Format - * @default checkpoint + * @default invokeai * @constant */ - format: "checkpoint"; + format: "invokeai"; + /** Image Encoder Model Id */ + image_encoder_model_id: string; /** * Base - * @default flux + * @default sd-1 * @constant */ - base: "flux"; + base: "sd-1"; }; - /** IPAdapter_Checkpoint_SD1_Config */ - IPAdapter_Checkpoint_SD1_Config: { + /** IPAdapter_InvokeAI_SD2_Config */ + IPAdapter_InvokeAI_SD2_Config: { /** * Key * @description A unique key for this model. @@ -10839,13 +10493,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -10859,19 +10506,21 @@ export type components = { type: "ip_adapter"; /** * Format - * @default checkpoint + * @default invokeai * @constant */ - format: "checkpoint"; + format: "invokeai"; + /** Image Encoder Model Id */ + image_encoder_model_id: string; /** * Base - * @default sd-1 + * @default sd-2 * @constant */ - base: "sd-1"; + base: "sd-2"; }; - /** IPAdapter_Checkpoint_SD2_Config */ - IPAdapter_Checkpoint_SD2_Config: { + /** IPAdapter_InvokeAI_SDXL_Config */ + IPAdapter_InvokeAI_SDXL_Config: { /** * Key * @description A unique key for this model. @@ -10919,13 +10568,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -10939,348 +10581,378 @@ export type components = { type: "ip_adapter"; /** * Format - * @default checkpoint + * @default invokeai * @constant */ - format: "checkpoint"; + format: "invokeai"; + /** Image Encoder Model Id */ + image_encoder_model_id: string; /** * Base - * @default sd-2 + * @default sdxl * @constant */ - base: "sd-2"; + base: "sdxl"; }; - /** IPAdapter_Checkpoint_SDXL_Config */ - IPAdapter_Checkpoint_SDXL_Config: { - /** - * Key - * @description A unique key for this model. - */ - key: string; - /** - * Hash - * @description The hash of the model file(s). - */ - hash: string; + /** + * Ideal Size - SD1.5, SDXL + * @description Calculates the ideal size for generation to avoid duplication + */ + IdealSizeInvocation: { /** - * Path - * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - path: string; + id: string; /** - * File Size - * @description The size of the model in bytes. + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - file_size: number; + is_intermediate?: boolean; /** - * Name - * @description Name of the model. + * Use Cache + * @description Whether or not to use the cache + * @default true */ - name: string; + use_cache?: boolean; /** - * Description - * @description Model description + * Width + * @description Final image width + * @default 1024 */ - description: string | null; + width?: number; /** - * Source - * @description The original source of the model (path, URL or repo_id). + * Height + * @description Final image height + * @default 576 */ - source: string; - /** @description The type of source */ - source_type: components["schemas"]["ModelSourceType"]; + height?: number; /** - * Source Api Response - * @description The original API response from the source, as stringified JSON. + * @description UNet (scheduler, LoRAs) + * @default null */ - source_api_response: string | null; + unet?: components["schemas"]["UNetField"] | null; /** - * Cover Image - * @description Url for image to preview model + * Multiplier + * @description Amount to multiply the model's dimensions by when calculating the ideal size (may result in initial generation artifacts if too large) + * @default 1 */ - cover_image: string | null; + multiplier?: number; /** - * Submodels - * @description Loadable submodels in this model + * type + * @default ideal_size + * @constant */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + type: "ideal_size"; + }; + /** + * IdealSizeOutput + * @description Base class for invocations that output an image + */ + IdealSizeOutput: { /** - * Usage Info - * @description Usage information for this model + * Width + * @description The ideal width of the image (in pixels) */ - usage_info: string | null; + width: number; /** - * Type - * @default ip_adapter - * @constant + * Height + * @description The ideal height of the image (in pixels) */ - type: "ip_adapter"; + height: number; /** - * Format - * @default checkpoint + * type + * @default ideal_size_output * @constant */ - format: "checkpoint"; + type: "ideal_size_output"; + }; + /** + * Image Batch + * @description Create a batched generation, where the workflow is executed once for each image in the batch. + */ + ImageBatchInvocation: { /** - * Base - * @default sdxl - * @constant + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - base: "sdxl"; - }; - /** IPAdapter_InvokeAI_SD1_Config */ - IPAdapter_InvokeAI_SD1_Config: { + id: string; /** - * Key - * @description A unique key for this model. + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - key: string; + is_intermediate?: boolean; /** - * Hash - * @description The hash of the model file(s). + * Use Cache + * @description Whether or not to use the cache + * @default true */ - hash: string; + use_cache?: boolean; /** - * Path - * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + * Batch Group + * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. + * @default None + * @enum {string} */ - path: string; + batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; /** - * File Size - * @description The size of the model in bytes. + * Images + * @description The images to batch over + * @default null */ - file_size: number; + images?: components["schemas"]["ImageField"][] | null; /** - * Name - * @description Name of the model. + * type + * @default image_batch + * @constant */ - name: string; + type: "image_batch"; + }; + /** + * Blur Image + * @description Blurs an image + */ + ImageBlurInvocation: { /** - * Description - * @description Model description + * @description The board to save the image to + * @default null */ - description: string | null; + board?: components["schemas"]["BoardField"] | null; /** - * Source - * @description The original source of the model (path, URL or repo_id). + * @description Optional metadata to be saved with the image + * @default null */ - source: string; - /** @description The type of source */ - source_type: components["schemas"]["ModelSourceType"]; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Source Api Response - * @description The original API response from the source, as stringified JSON. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - source_api_response: string | null; + id: string; /** - * Cover Image - * @description Url for image to preview model + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - cover_image: string | null; + is_intermediate?: boolean; /** - * Submodels - * @description Loadable submodels in this model + * Use Cache + * @description Whether or not to use the cache + * @default true */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + use_cache?: boolean; /** - * Usage Info - * @description Usage information for this model + * @description The image to blur + * @default null */ - usage_info: string | null; + image?: components["schemas"]["ImageField"] | null; /** - * Type - * @default ip_adapter - * @constant + * Radius + * @description The blur radius + * @default 8 */ - type: "ip_adapter"; + radius?: number; /** - * Format - * @default invokeai - * @constant + * Blur Type + * @description The type of blur + * @default gaussian + * @enum {string} */ - format: "invokeai"; - /** Image Encoder Model Id */ - image_encoder_model_id: string; + blur_type?: "gaussian" | "box"; /** - * Base - * @default sd-1 + * type + * @default img_blur * @constant */ - base: "sd-1"; + type: "img_blur"; }; - /** IPAdapter_InvokeAI_SD2_Config */ - IPAdapter_InvokeAI_SD2_Config: { - /** - * Key - * @description A unique key for this model. - */ - key: string; + /** + * ImageCategory + * @description The category of an image. + * + * - GENERAL: The image is an output, init image, or otherwise an image without a specialized purpose. + * - MASK: The image is a mask image. + * - CONTROL: The image is a ControlNet control image. + * - USER: The image is a user-provide image. + * - OTHER: The image is some other type of image with a specialized purpose. To be used by external nodes. + * @enum {string} + */ + ImageCategory: "general" | "mask" | "control" | "user" | "other"; + /** + * Extract Image Channel + * @description Gets a channel from an image. + */ + ImageChannelInvocation: { /** - * Hash - * @description The hash of the model file(s). + * @description The board to save the image to + * @default null */ - hash: string; + board?: components["schemas"]["BoardField"] | null; /** - * Path - * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + * @description Optional metadata to be saved with the image + * @default null */ - path: string; + metadata?: components["schemas"]["MetadataField"] | null; /** - * File Size - * @description The size of the model in bytes. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - file_size: number; + id: string; /** - * Name - * @description Name of the model. + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - name: string; + is_intermediate?: boolean; /** - * Description - * @description Model description + * Use Cache + * @description Whether or not to use the cache + * @default true */ - description: string | null; + use_cache?: boolean; /** - * Source - * @description The original source of the model (path, URL or repo_id). + * @description The image to get the channel from + * @default null */ - source: string; - /** @description The type of source */ - source_type: components["schemas"]["ModelSourceType"]; + image?: components["schemas"]["ImageField"] | null; /** - * Source Api Response - * @description The original API response from the source, as stringified JSON. + * Channel + * @description The channel to get + * @default A + * @enum {string} */ - source_api_response: string | null; + channel?: "A" | "R" | "G" | "B"; /** - * Cover Image - * @description Url for image to preview model + * type + * @default img_chan + * @constant */ - cover_image: string | null; + type: "img_chan"; + }; + /** + * Multiply Image Channel + * @description Scale a specific color channel of an image. + */ + ImageChannelMultiplyInvocation: { /** - * Submodels - * @description Loadable submodels in this model + * @description The board to save the image to + * @default null */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + board?: components["schemas"]["BoardField"] | null; /** - * Usage Info - * @description Usage information for this model + * @description Optional metadata to be saved with the image + * @default null */ - usage_info: string | null; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Type - * @default ip_adapter - * @constant + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - type: "ip_adapter"; + id: string; /** - * Format - * @default invokeai - * @constant + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - format: "invokeai"; - /** Image Encoder Model Id */ - image_encoder_model_id: string; + is_intermediate?: boolean; /** - * Base - * @default sd-2 - * @constant + * Use Cache + * @description Whether or not to use the cache + * @default true */ - base: "sd-2"; - }; - /** IPAdapter_InvokeAI_SDXL_Config */ - IPAdapter_InvokeAI_SDXL_Config: { + use_cache?: boolean; /** - * Key - * @description A unique key for this model. + * @description The image to adjust + * @default null */ - key: string; + image?: components["schemas"]["ImageField"] | null; /** - * Hash - * @description The hash of the model file(s). + * Channel + * @description Which channel to adjust + * @default null */ - hash: string; + channel?: ("Red (RGBA)" | "Green (RGBA)" | "Blue (RGBA)" | "Alpha (RGBA)" | "Cyan (CMYK)" | "Magenta (CMYK)" | "Yellow (CMYK)" | "Black (CMYK)" | "Hue (HSV)" | "Saturation (HSV)" | "Value (HSV)" | "Luminosity (LAB)" | "A (LAB)" | "B (LAB)" | "Y (YCbCr)" | "Cb (YCbCr)" | "Cr (YCbCr)") | null; /** - * Path - * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + * Scale + * @description The amount to scale the channel by. + * @default 1 */ - path: string; + scale?: number; /** - * File Size - * @description The size of the model in bytes. + * Invert Channel + * @description Invert the channel after scaling + * @default false */ - file_size: number; + invert_channel?: boolean; /** - * Name - * @description Name of the model. + * type + * @default img_channel_multiply + * @constant */ - name: string; + type: "img_channel_multiply"; + }; + /** + * Offset Image Channel + * @description Add or subtract a value from a specific color channel of an image. + */ + ImageChannelOffsetInvocation: { /** - * Description - * @description Model description + * @description The board to save the image to + * @default null */ - description: string | null; + board?: components["schemas"]["BoardField"] | null; /** - * Source - * @description The original source of the model (path, URL or repo_id). + * @description Optional metadata to be saved with the image + * @default null */ - source: string; - /** @description The type of source */ - source_type: components["schemas"]["ModelSourceType"]; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Source Api Response - * @description The original API response from the source, as stringified JSON. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - source_api_response: string | null; + id: string; /** - * Cover Image - * @description Url for image to preview model + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - cover_image: string | null; + is_intermediate?: boolean; /** - * Submodels - * @description Loadable submodels in this model + * Use Cache + * @description Whether or not to use the cache + * @default true */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + use_cache?: boolean; /** - * Usage Info - * @description Usage information for this model + * @description The image to adjust + * @default null */ - usage_info: string | null; + image?: components["schemas"]["ImageField"] | null; /** - * Type - * @default ip_adapter - * @constant + * Channel + * @description Which channel to adjust + * @default null */ - type: "ip_adapter"; + channel?: ("Red (RGBA)" | "Green (RGBA)" | "Blue (RGBA)" | "Alpha (RGBA)" | "Cyan (CMYK)" | "Magenta (CMYK)" | "Yellow (CMYK)" | "Black (CMYK)" | "Hue (HSV)" | "Saturation (HSV)" | "Value (HSV)" | "Luminosity (LAB)" | "A (LAB)" | "B (LAB)" | "Y (YCbCr)" | "Cb (YCbCr)" | "Cr (YCbCr)") | null; /** - * Format - * @default invokeai - * @constant + * Offset + * @description The amount to adjust the channel by + * @default 0 */ - format: "invokeai"; - /** Image Encoder Model Id */ - image_encoder_model_id: string; + offset?: number; /** - * Base - * @default sdxl + * type + * @default img_channel_offset * @constant */ - base: "sdxl"; + type: "img_channel_offset"; }; /** - * Ideal Size - SD1.5, SDXL - * @description Calculates the ideal size for generation to avoid duplication + * Image Collection Primitive + * @description A collection of image primitive values */ - IdealSizeInvocation: { + ImageCollectionInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -11299,62 +10971,50 @@ export type components = { */ use_cache?: boolean; /** - * Width - * @description Final image width - * @default 1024 - */ - width?: number; - /** - * Height - * @description Final image height - * @default 576 - */ - height?: number; - /** - * @description UNet (scheduler, LoRAs) + * Collection + * @description The collection of image values * @default null */ - unet?: components["schemas"]["UNetField"] | null; - /** - * Multiplier - * @description Amount to multiply the model's dimensions by when calculating the ideal size (may result in initial generation artifacts if too large) - * @default 1 - */ - multiplier?: number; + collection?: components["schemas"]["ImageField"][] | null; /** * type - * @default ideal_size + * @default image_collection * @constant */ - type: "ideal_size"; + type: "image_collection"; }; /** - * IdealSizeOutput - * @description Base class for invocations that output an image + * ImageCollectionOutput + * @description Base class for nodes that output a collection of images */ - IdealSizeOutput: { - /** - * Width - * @description The ideal width of the image (in pixels) - */ - width: number; + ImageCollectionOutput: { /** - * Height - * @description The ideal height of the image (in pixels) + * Collection + * @description The output images */ - height: number; + collection: components["schemas"]["ImageField"][]; /** * type - * @default ideal_size_output + * @default image_collection_output * @constant */ - type: "ideal_size_output"; + type: "image_collection_output"; }; /** - * Image Batch - * @description Create a batched generation, where the workflow is executed once for each image in the batch. + * Convert Image Mode + * @description Converts an image to a different mode. */ - ImageBatchInvocation: { + ImageConvertInvocation: { + /** + * @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. @@ -11373,30 +11033,29 @@ export type components = { */ use_cache?: boolean; /** - * Batch Group - * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. - * @default None - * @enum {string} + * @description The image to convert + * @default null */ - batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; + image?: components["schemas"]["ImageField"] | null; /** - * Images - * @description The images to batch over - * @default null + * Mode + * @description The mode to convert to + * @default L + * @enum {string} */ - images?: components["schemas"]["ImageField"][] | null; + mode?: "L" | "RGB" | "RGBA" | "CMYK" | "YCbCr" | "LAB" | "HSV" | "I" | "F"; /** * type - * @default image_batch + * @default img_conv * @constant */ - type: "image_batch"; + type: "img_conv"; }; /** - * Blur Image - * @description Blurs an image + * Crop Image + * @description Crops an image to a specified box. The box can be outside of the image. */ - ImageBlurInvocation: { + ImageCropInvocation: { /** * @description The board to save the image to * @default null @@ -11425,108 +11084,137 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to blur + * @description The image to crop * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Radius - * @description The blur radius - * @default 8 + * X + * @description The left x coordinate of the crop rectangle + * @default 0 */ - radius?: number; + x?: number; /** - * Blur Type - * @description The type of blur - * @default gaussian - * @enum {string} + * Y + * @description The top y coordinate of the crop rectangle + * @default 0 */ - blur_type?: "gaussian" | "box"; + y?: number; + /** + * Width + * @description The width of the crop rectangle + * @default 512 + */ + width?: number; + /** + * Height + * @description The height of the crop rectangle + * @default 512 + */ + height?: number; /** * type - * @default img_blur + * @default img_crop * @constant */ - type: "img_blur"; + type: "img_crop"; }; /** - * ImageCategory - * @description The category of an image. - * - * - GENERAL: The image is an output, init image, or otherwise an image without a specialized purpose. - * - MASK: The image is a mask image. - * - CONTROL: The image is a ControlNet control image. - * - USER: The image is a user-provide image. - * - OTHER: The image is some other type of image with a specialized purpose. To be used by external nodes. - * @enum {string} - */ - ImageCategory: "general" | "mask" | "control" | "user" | "other"; - /** - * Extract Image Channel - * @description Gets a channel from an image. + * ImageDTO + * @description Deserialized image record, enriched for the frontend. */ - ImageChannelInvocation: { + ImageDTO: { /** - * @description The board to save the image to - * @default null + * Image Name + * @description The unique name of the image. */ - board?: components["schemas"]["BoardField"] | null; + image_name: string; /** - * @description Optional metadata to be saved with the image - * @default null + * Image Url + * @description The URL of the image. */ - metadata?: components["schemas"]["MetadataField"] | null; + image_url: string; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Thumbnail Url + * @description The URL of the image's thumbnail. */ - id: string; + thumbnail_url: string; + /** @description The type of the image. */ + image_origin: components["schemas"]["ResourceOrigin"]; + /** @description The category of the image. */ + image_category: components["schemas"]["ImageCategory"]; + /** + * Width + * @description The width of the image in px. + */ + width: number; + /** + * Height + * @description The height of the image in px. + */ + height: number; + /** + * Created At + * @description The created timestamp of the image. + */ + created_at: string; + /** + * Updated At + * @description The updated timestamp of the image. + */ + updated_at: string; + /** + * Deleted At + * @description The deleted timestamp of the image. + */ + deleted_at?: string | null; /** * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * @description Whether this is an intermediate image. */ - is_intermediate?: boolean; + is_intermediate: boolean; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Session Id + * @description The session ID that generated this image, if it is a generated image. */ - use_cache?: boolean; + session_id?: string | null; /** - * @description The image to get the channel from - * @default null + * Node Id + * @description The node ID that generated this image, if it is a generated image. */ - image?: components["schemas"]["ImageField"] | null; + node_id?: string | null; /** - * Channel - * @description The channel to get - * @default A - * @enum {string} + * Starred + * @description Whether this image is starred. */ - channel?: "A" | "R" | "G" | "B"; + starred: boolean; /** - * type - * @default img_chan - * @constant + * Has Workflow + * @description Whether this image has a workflow. */ - type: "img_chan"; + has_workflow: boolean; + /** + * Board Id + * @description The id of the board the image belongs to, if one exists. + */ + board_id?: string | null; }; /** - * Multiply Image Channel - * @description Scale a specific color channel of an image. + * ImageField + * @description An image primitive field */ - ImageChannelMultiplyInvocation: { - /** - * @description The board to save the image to - * @default null - */ - board?: components["schemas"]["BoardField"] | null; + ImageField: { /** - * @description Optional metadata to be saved with the image - * @default null + * Image Name + * @description The name of the image */ - metadata?: components["schemas"]["MetadataField"] | null; + image_name: string; + }; + /** + * Image Generator + * @description Generated a collection of images for use in a batched generation + */ + ImageGenerator: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -11545,40 +11233,41 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to adjust - * @default null + * Generator Type + * @description The image generator. */ - image?: components["schemas"]["ImageField"] | null; + generator: components["schemas"]["ImageGeneratorField"]; /** - * Channel - * @description Which channel to adjust - * @default null + * type + * @default image_generator + * @constant */ - channel?: ("Red (RGBA)" | "Green (RGBA)" | "Blue (RGBA)" | "Alpha (RGBA)" | "Cyan (CMYK)" | "Magenta (CMYK)" | "Yellow (CMYK)" | "Black (CMYK)" | "Hue (HSV)" | "Saturation (HSV)" | "Value (HSV)" | "Luminosity (LAB)" | "A (LAB)" | "B (LAB)" | "Y (YCbCr)" | "Cb (YCbCr)" | "Cr (YCbCr)") | null; + type: "image_generator"; + }; + /** ImageGeneratorField */ + ImageGeneratorField: Record; + /** + * ImageGeneratorOutput + * @description Base class for nodes that output a collection of boards + */ + ImageGeneratorOutput: { /** - * Scale - * @description The amount to scale the channel by. - * @default 1 - */ - scale?: number; - /** - * Invert Channel - * @description Invert the channel after scaling - * @default false + * Images + * @description The generated images */ - invert_channel?: boolean; + images: components["schemas"]["ImageField"][]; /** * type - * @default img_channel_multiply + * @default image_generator_output * @constant */ - type: "img_channel_multiply"; + type: "image_generator_output"; }; /** - * Offset Image Channel - * @description Add or subtract a value from a specific color channel of an image. + * Adjust Image Hue + * @description Adjusts the Hue of an image. */ - ImageChannelOffsetInvocation: { + ImageHueAdjustmentInvocation: { /** * @description The board to save the image to * @default null @@ -11612,29 +11301,33 @@ export type components = { */ image?: components["schemas"]["ImageField"] | null; /** - * Channel - * @description Which channel to adjust - * @default null - */ - channel?: ("Red (RGBA)" | "Green (RGBA)" | "Blue (RGBA)" | "Alpha (RGBA)" | "Cyan (CMYK)" | "Magenta (CMYK)" | "Yellow (CMYK)" | "Black (CMYK)" | "Hue (HSV)" | "Saturation (HSV)" | "Value (HSV)" | "Luminosity (LAB)" | "A (LAB)" | "B (LAB)" | "Y (YCbCr)" | "Cb (YCbCr)" | "Cr (YCbCr)") | null; - /** - * Offset - * @description The amount to adjust the channel by + * Hue + * @description The degrees by which to rotate the hue, 0-360 * @default 0 */ - offset?: number; + hue?: number; /** * type - * @default img_channel_offset + * @default img_hue_adjust * @constant */ - type: "img_channel_offset"; + type: "img_hue_adjust"; }; /** - * Image Collection Primitive - * @description A collection of image primitive values + * Inverse Lerp Image + * @description Inverse linear interpolation of all pixels of an image */ - ImageCollectionInvocation: { + ImageInverseLerpInvocation: { + /** + * @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. @@ -11653,40 +11346,68 @@ export type components = { */ use_cache?: boolean; /** - * Collection - * @description The collection of image values + * @description The image to lerp * @default null */ - collection?: components["schemas"]["ImageField"][] | null; + image?: components["schemas"]["ImageField"] | null; + /** + * Min + * @description The minimum input value + * @default 0 + */ + min?: number; + /** + * Max + * @description The maximum input value + * @default 255 + */ + max?: number; /** * type - * @default image_collection + * @default img_ilerp * @constant */ - type: "image_collection"; + type: "img_ilerp"; }; /** - * ImageCollectionOutput - * @description Base class for nodes that output a collection of images + * Image Primitive + * @description An image primitive value */ - ImageCollectionOutput: { + ImageInvocation: { /** - * Collection - * @description The output images + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - collection: components["schemas"]["ImageField"][]; + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to load + * @default null + */ + image?: components["schemas"]["ImageField"] | null; /** * type - * @default image_collection_output + * @default image * @constant */ - type: "image_collection_output"; + type: "image"; }; /** - * Convert Image Mode - * @description Converts an image to a different mode. + * Lerp Image + * @description Linear interpolation of all pixels of an image */ - ImageConvertInvocation: { + ImageLerpInvocation: { /** * @description The board to save the image to * @default null @@ -11715,34 +11436,34 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to convert + * @description The image to lerp * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Mode - * @description The mode to convert to - * @default L - * @enum {string} + * Min + * @description The minimum output value + * @default 0 */ - mode?: "L" | "RGB" | "RGBA" | "CMYK" | "YCbCr" | "LAB" | "HSV" | "I" | "F"; + min?: number; + /** + * Max + * @description The maximum output value + * @default 255 + */ + max?: number; /** * type - * @default img_conv + * @default img_lerp * @constant */ - type: "img_conv"; + type: "img_lerp"; }; /** - * Crop Image - * @description Crops an image to a specified box. The box can be outside of the image. + * Image Mask to Tensor + * @description Convert a mask image to a tensor. Converts the image to grayscale and uses thresholding at the specified value. */ - ImageCropInvocation: { - /** - * @description The board to save the image to - * @default null - */ - board?: components["schemas"]["BoardField"] | null; + ImageMaskToTensorInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -11766,137 +11487,93 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to crop + * @description The mask image to convert. * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * X - * @description The left x coordinate of the crop rectangle - * @default 0 - */ - x?: number; - /** - * Y - * @description The top y coordinate of the crop rectangle - * @default 0 - */ - y?: number; - /** - * Width - * @description The width of the crop rectangle - * @default 512 + * Cutoff + * @description Cutoff (<) + * @default 128 */ - width?: number; + cutoff?: number; /** - * Height - * @description The height of the crop rectangle - * @default 512 + * Invert + * @description Whether to invert the mask. + * @default false */ - height?: number; + invert?: boolean; /** * type - * @default img_crop + * @default image_mask_to_tensor * @constant */ - type: "img_crop"; + type: "image_mask_to_tensor"; }; /** - * ImageDTO - * @description Deserialized image record, enriched for the frontend. + * Multiply Images + * @description Multiplies two images together using `PIL.ImageChops.multiply()`. */ - ImageDTO: { + ImageMultiplyInvocation: { /** - * Image Name - * @description The unique name of the image. + * @description The board to save the image to + * @default null */ - image_name: string; + board?: components["schemas"]["BoardField"] | null; /** - * Image Url - * @description The URL of the image. + * @description Optional metadata to be saved with the image + * @default null */ - image_url: string; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Thumbnail Url - * @description The URL of the image's thumbnail. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - thumbnail_url: string; - /** @description The type of the image. */ - image_origin: components["schemas"]["ResourceOrigin"]; - /** @description The category of the image. */ - image_category: components["schemas"]["ImageCategory"]; + id: string; /** - * Width - * @description The width of the image in px. + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - width: number; + is_intermediate?: boolean; /** - * Height - * @description The height of the image in px. + * Use Cache + * @description Whether or not to use the cache + * @default true */ - height: number; + use_cache?: boolean; /** - * Created At - * @description The created timestamp of the image. + * @description The first image to multiply + * @default null */ - created_at: string; + image1?: components["schemas"]["ImageField"] | null; /** - * Updated At - * @description The updated timestamp of the image. + * @description The second image to multiply + * @default null */ - updated_at: string; + image2?: components["schemas"]["ImageField"] | null; /** - * Deleted At - * @description The deleted timestamp of the image. + * type + * @default img_mul + * @constant */ - deleted_at?: string | null; + type: "img_mul"; + }; + /** + * Blur NSFW Image + * @description Add blur to NSFW-flagged images + */ + ImageNSFWBlurInvocation: { /** - * Is Intermediate - * @description Whether this is an intermediate image. + * @description The board to save the image to + * @default null */ - is_intermediate: boolean; + board?: components["schemas"]["BoardField"] | null; /** - * Session Id - * @description The session ID that generated this image, if it is a generated image. - */ - session_id?: string | null; - /** - * Node Id - * @description The node ID that generated this image, if it is a generated image. - */ - node_id?: string | null; - /** - * Starred - * @description Whether this image is starred. - */ - starred: boolean; - /** - * Has Workflow - * @description Whether this image has a workflow. - */ - has_workflow: boolean; - /** - * Board Id - * @description The id of the board the image belongs to, if one exists. - */ - board_id?: string | null; - }; - /** - * ImageField - * @description An image primitive field - */ - ImageField: { - /** - * Image Name - * @description The name of the image + * @description Optional metadata to be saved with the image + * @default null */ - image_name: string; - }; - /** - * Image Generator - * @description Generated a collection of images for use in a batched generation - */ - ImageGenerator: { + metadata?: components["schemas"]["MetadataField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -11915,41 +11592,43 @@ export type components = { */ use_cache?: boolean; /** - * Generator Type - * @description The image generator. + * @description The image to check + * @default null */ - generator: components["schemas"]["ImageGeneratorField"]; + image?: components["schemas"]["ImageField"] | null; /** * type - * @default image_generator + * @default img_nsfw * @constant */ - type: "image_generator"; + type: "img_nsfw"; }; - /** ImageGeneratorField */ - ImageGeneratorField: Record; /** - * ImageGeneratorOutput - * @description Base class for nodes that output a collection of boards + * ImageNamesResult + * @description Response containing ordered image names with metadata for optimistic updates. */ - ImageGeneratorOutput: { + ImageNamesResult: { /** - * Images - * @description The generated images + * Image Names + * @description Ordered list of image names */ - images: components["schemas"]["ImageField"][]; + image_names: string[]; /** - * type - * @default image_generator_output - * @constant + * Starred Count + * @description Number of starred images (when starred_first=True) */ - type: "image_generator_output"; + starred_count: number; + /** + * Total Count + * @description Total number of images matching the query + */ + total_count: number; }; /** - * Adjust Image Hue - * @description Adjusts the Hue of an image. + * Add Image Noise + * @description Add noise to an image */ - ImageHueAdjustmentInvocation: { + ImageNoiseInvocation: { /** * @description The board to save the image to * @default null @@ -11978,84 +11657,112 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to adjust + * @description The image to add noise to * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Hue - * @description The degrees by which to rotate the hue, 0-360 + * @description Optional mask determining where to apply noise (black=noise, white=no noise) + * @default null + */ + mask?: components["schemas"]["ImageField"] | null; + /** + * Seed + * @description Seed for random number generation * @default 0 */ - hue?: number; + seed?: number; + /** + * Noise Type + * @description The type of noise to add + * @default gaussian + * @enum {string} + */ + noise_type?: "gaussian" | "salt_and_pepper"; + /** + * Amount + * @description The amount of noise to add + * @default 0.1 + */ + amount?: number; + /** + * Noise Color + * @description Whether to add colored noise + * @default true + */ + noise_color?: boolean; + /** + * Size + * @description The size of the noise points + * @default 1 + */ + size?: number; /** * type - * @default img_hue_adjust + * @default img_noise * @constant */ - type: "img_hue_adjust"; + type: "img_noise"; }; /** - * Inverse Lerp Image - * @description Inverse linear interpolation of all pixels of an image + * ImageOutput + * @description Base class for nodes that output a single image */ - ImageInverseLerpInvocation: { - /** - * @description The board to save the image to - * @default null - */ - board?: components["schemas"]["BoardField"] | null; + ImageOutput: { + /** @description The output image */ + image: components["schemas"]["ImageField"]; /** - * @description Optional metadata to be saved with the image - * @default null + * Width + * @description The width of the image in pixels */ - metadata?: components["schemas"]["MetadataField"] | null; + width: number; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Height + * @description The height of the image in pixels */ - id: string; + height: number; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * type + * @default image_output + * @constant */ - is_intermediate?: boolean; + type: "image_output"; + }; + /** ImagePanelCoordinateOutput */ + ImagePanelCoordinateOutput: { /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * X Left + * @description The left x-coordinate of the panel. */ - use_cache?: boolean; + x_left: number; /** - * @description The image to lerp - * @default null + * Y Top + * @description The top y-coordinate of the panel. */ - image?: components["schemas"]["ImageField"] | null; + y_top: number; /** - * Min - * @description The minimum input value - * @default 0 + * Width + * @description The width of the panel. */ - min?: number; + width: number; /** - * Max - * @description The maximum input value - * @default 255 + * Height + * @description The height of the panel. */ - max?: number; + height: number; /** * type - * @default img_ilerp + * @default image_panel_coordinate_output * @constant */ - type: "img_ilerp"; + type: "image_panel_coordinate_output"; }; /** - * Image Primitive - * @description An image primitive value + * Image Panel Layout + * @description Get the coordinates of a single panel in a grid. (If the full image shape cannot be divided evenly into panels, + * then the grid may not cover the entire image.) */ - ImageInvocation: { + ImagePanelLayoutInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -12074,22 +11781,53 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to load + * Width + * @description The width of the entire grid. * @default null */ - image?: components["schemas"]["ImageField"] | null; + width?: number | null; + /** + * Height + * @description The height of the entire grid. + * @default null + */ + height?: number | null; + /** + * Num Cols + * @description The number of columns in the grid. + * @default 1 + */ + num_cols?: number; + /** + * Num Rows + * @description The number of rows in the grid. + * @default 1 + */ + num_rows?: number; + /** + * Panel Col Idx + * @description The column index of the panel to be processed. + * @default 0 + */ + panel_col_idx?: number; + /** + * Panel Row Idx + * @description The row index of the panel to be processed. + * @default 0 + */ + panel_row_idx?: number; /** * type - * @default image + * @default image_panel_layout * @constant */ - type: "image"; + type: "image_panel_layout"; }; /** - * Lerp Image - * @description Linear interpolation of all pixels of an image + * Paste Image + * @description Pastes an image into another image. */ - ImageLerpInvocation: { + ImagePasteInvocation: { /** * @description The board to save the image to * @default null @@ -12118,34 +11856,86 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to lerp + * @description The base image * @default null */ - image?: components["schemas"]["ImageField"] | null; + base_image?: components["schemas"]["ImageField"] | null; /** - * Min - * @description The minimum output value - * @default 0 + * @description The image to paste + * @default null */ - min?: number; + image?: components["schemas"]["ImageField"] | null; /** - * Max - * @description The maximum output value - * @default 255 + * @description The mask to use when pasting + * @default null */ - max?: number; + mask?: components["schemas"]["ImageField"] | null; + /** + * X + * @description The left x coordinate at which to paste the image + * @default 0 + */ + x?: number; + /** + * Y + * @description The top y coordinate at which to paste the image + * @default 0 + */ + y?: number; + /** + * Crop + * @description Crop to base image dimensions + * @default false + */ + crop?: boolean; /** * type - * @default img_lerp + * @default img_paste * @constant */ - type: "img_lerp"; + type: "img_paste"; }; /** - * Image Mask to Tensor - * @description Convert a mask image to a tensor. Converts the image to grayscale and uses thresholding at the specified value. + * ImageRecordChanges + * @description A set of changes to apply to an image record. + * + * Only limited changes are valid: + * - `image_category`: change the category of an image + * - `session_id`: change the session associated with an image + * - `is_intermediate`: change the image's `is_intermediate` flag + * - `starred`: change whether the image is starred */ - ImageMaskToTensorInvocation: { + ImageRecordChanges: { + /** @description The image's new category. */ + image_category?: components["schemas"]["ImageCategory"] | null; + /** + * Session Id + * @description The image's new session ID. + */ + session_id?: string | null; + /** + * Is Intermediate + * @description The image's new `is_intermediate` flag. + */ + is_intermediate?: boolean | null; + /** + * Starred + * @description The image's new `starred` state + */ + starred?: boolean | null; + } & { + [key: string]: unknown; + }; + /** + * Resize Image + * @description Resizes an image to specific dimensions + */ + ImageResizeInvocation: { + /** + * @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 @@ -12169,34 +11959,41 @@ export type components = { */ use_cache?: boolean; /** - * @description The mask image to convert. + * @description The image to resize * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Cutoff - * @description Cutoff (<) - * @default 128 + * Width + * @description The width to resize to (px) + * @default 512 */ - cutoff?: number; + width?: number; /** - * Invert - * @description Whether to invert the mask. - * @default false + * Height + * @description The height to resize to (px) + * @default 512 */ - invert?: boolean; + height?: number; + /** + * Resample Mode + * @description The resampling mode + * @default bicubic + * @enum {string} + */ + resample_mode?: "nearest" | "box" | "bilinear" | "hamming" | "bicubic" | "lanczos"; /** * type - * @default image_mask_to_tensor + * @default img_resize * @constant */ - type: "image_mask_to_tensor"; + type: "img_resize"; }; /** - * Multiply Images - * @description Multiplies two images together using `PIL.ImageChops.multiply()`. + * Scale Image + * @description Scales an image by a factor */ - ImageMultiplyInvocation: { + ImageScaleInvocation: { /** * @description The board to save the image to * @default null @@ -12225,37 +12022,35 @@ export type components = { */ use_cache?: boolean; /** - * @description The first image to multiply + * @description The image to scale * @default null */ - image1?: components["schemas"]["ImageField"] | null; + image?: components["schemas"]["ImageField"] | null; /** - * @description The second image to multiply - * @default null + * Scale Factor + * @description The factor by which to scale the image + * @default 2 */ - image2?: components["schemas"]["ImageField"] | null; + scale_factor?: number; + /** + * Resample Mode + * @description The resampling mode + * @default bicubic + * @enum {string} + */ + resample_mode?: "nearest" | "box" | "bilinear" | "hamming" | "bicubic" | "lanczos"; /** * type - * @default img_mul + * @default img_scale * @constant */ - type: "img_mul"; + type: "img_scale"; }; /** - * Blur NSFW Image - * @description Add blur to NSFW-flagged images + * Image to Latents - SD1.5, SDXL + * @description Encodes an image into latents. */ - ImageNSFWBlurInvocation: { - /** - * @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; + ImageToLatentsInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -12274,43 +12069,76 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to check + * @description The image to encode * @default null */ image?: components["schemas"]["ImageField"] | null; + /** + * @description VAE + * @default null + */ + vae?: components["schemas"]["VAEField"] | null; + /** + * Tiled + * @description Processing using overlapping tiles (reduce memory consumption) + * @default false + */ + tiled?: boolean; + /** + * Tile Size + * @description The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the model will be used. Larger tile sizes generally produce better results at the cost of higher memory usage. + * @default 0 + */ + tile_size?: number; + /** + * Fp32 + * @description Whether or not to use full float32 precision + * @default false + */ + fp32?: boolean; /** * type - * @default img_nsfw + * @default i2l * @constant */ - type: "img_nsfw"; + type: "i2l"; + }; + /** ImageUploadEntry */ + ImageUploadEntry: { + /** @description The image DTO */ + image_dto: components["schemas"]["ImageDTO"]; + /** + * Presigned Url + * @description The URL to get the presigned URL for the image upload + */ + presigned_url: string; }; /** - * ImageNamesResult - * @description Response containing ordered image names with metadata for optimistic updates. + * ImageUrlsDTO + * @description The URLs for an image and its thumbnail. */ - ImageNamesResult: { + ImageUrlsDTO: { /** - * Image Names - * @description Ordered list of image names + * Image Name + * @description The unique name of the image. */ - image_names: string[]; + image_name: string; /** - * Starred Count - * @description Number of starred images (when starred_first=True) + * Image Url + * @description The URL of the image. */ - starred_count: number; + image_url: string; /** - * Total Count - * @description Total number of images matching the query + * Thumbnail Url + * @description The URL of the image's thumbnail. */ - total_count: number; + thumbnail_url: string; }; /** - * Add Image Noise - * @description Add noise to an image + * Add Invisible Watermark + * @description Add an invisible watermark to an image */ - ImageNoiseInvocation: { + ImageWatermarkInvocation: { /** * @description The board to save the image to * @default null @@ -12339,177 +12167,41 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to add noise to + * @description The image to check * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * @description Optional mask determining where to apply noise (black=noise, white=no noise) - * @default null + * Text + * @description Watermark text + * @default InvokeAI */ - mask?: components["schemas"]["ImageField"] | null; + text?: string; /** - * Seed - * @description Seed for random number generation - * @default 0 + * type + * @default img_watermark + * @constant */ - seed?: number; + type: "img_watermark"; + }; + /** ImagesDownloaded */ + ImagesDownloaded: { /** - * Noise Type - * @description The type of noise to add - * @default gaussian - * @enum {string} + * Response + * @description The message to display to the user when images begin downloading */ - noise_type?: "gaussian" | "salt_and_pepper"; + response?: string | null; /** - * Amount - * @description The amount of noise to add - * @default 0.1 + * Bulk Download Item Name + * @description The name of the bulk download item for which events will be emitted */ - amount?: number; - /** - * Noise Color - * @description Whether to add colored noise - * @default true - */ - noise_color?: boolean; - /** - * Size - * @description The size of the noise points - * @default 1 - */ - size?: number; - /** - * type - * @default img_noise - * @constant - */ - type: "img_noise"; - }; - /** - * ImageOutput - * @description Base class for nodes that output a single image - */ - ImageOutput: { - /** @description The output image */ - image: components["schemas"]["ImageField"]; - /** - * Width - * @description The width of the image in pixels - */ - width: number; - /** - * Height - * @description The height of the image in pixels - */ - height: number; - /** - * type - * @default image_output - * @constant - */ - type: "image_output"; - }; - /** ImagePanelCoordinateOutput */ - ImagePanelCoordinateOutput: { - /** - * X Left - * @description The left x-coordinate of the panel. - */ - x_left: number; - /** - * Y Top - * @description The top y-coordinate of the panel. - */ - y_top: number; - /** - * Width - * @description The width of the panel. - */ - width: number; - /** - * Height - * @description The height of the panel. - */ - height: number; - /** - * type - * @default image_panel_coordinate_output - * @constant - */ - type: "image_panel_coordinate_output"; - }; - /** - * Image Panel Layout - * @description Get the coordinates of a single panel in a grid. (If the full image shape cannot be divided evenly into panels, - * then the grid may not cover the entire image.) - */ - ImagePanelLayoutInvocation: { - /** - * 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; - /** - * Width - * @description The width of the entire grid. - * @default null - */ - width?: number | null; - /** - * Height - * @description The height of the entire grid. - * @default null - */ - height?: number | null; - /** - * Num Cols - * @description The number of columns in the grid. - * @default 1 - */ - num_cols?: number; - /** - * Num Rows - * @description The number of rows in the grid. - * @default 1 - */ - num_rows?: number; - /** - * Panel Col Idx - * @description The column index of the panel to be processed. - * @default 0 - */ - panel_col_idx?: number; - /** - * Panel Row Idx - * @description The row index of the panel to be processed. - * @default 0 - */ - panel_row_idx?: number; - /** - * type - * @default image_panel_layout - * @constant - */ - type: "image_panel_layout"; - }; - /** - * Paste Image - * @description Pastes an image into another image. - */ - ImagePasteInvocation: { + bulk_download_item_name?: string | null; + }; + /** + * Solid Color Infill + * @description Infills transparent areas of an image with a solid color + */ + InfillColorInvocation: { /** * @description The board to save the image to * @default null @@ -12538,81 +12230,32 @@ export type components = { */ use_cache?: boolean; /** - * @description The base image - * @default null - */ - base_image?: components["schemas"]["ImageField"] | null; - /** - * @description The image to paste + * @description The image to process * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * @description The mask to use when pasting - * @default null - */ - mask?: components["schemas"]["ImageField"] | null; - /** - * X - * @description The left x coordinate at which to paste the image - * @default 0 - */ - x?: number; - /** - * Y - * @description The top y coordinate at which to paste the image - * @default 0 - */ - y?: number; - /** - * Crop - * @description Crop to base image dimensions - * @default false + * @description The color to use to infill + * @default { + * "r": 127, + * "g": 127, + * "b": 127, + * "a": 255 + * } */ - crop?: boolean; + color?: components["schemas"]["ColorField"]; /** * type - * @default img_paste + * @default infill_rgba * @constant */ - type: "img_paste"; - }; - /** - * ImageRecordChanges - * @description A set of changes to apply to an image record. - * - * Only limited changes are valid: - * - `image_category`: change the category of an image - * - `session_id`: change the session associated with an image - * - `is_intermediate`: change the image's `is_intermediate` flag - * - `starred`: change whether the image is starred - */ - ImageRecordChanges: { - /** @description The image's new category. */ - image_category?: components["schemas"]["ImageCategory"] | null; - /** - * Session Id - * @description The image's new session ID. - */ - session_id?: string | null; - /** - * Is Intermediate - * @description The image's new `is_intermediate` flag. - */ - is_intermediate?: boolean | null; - /** - * Starred - * @description The image's new `starred` state - */ - starred?: boolean | null; - } & { - [key: string]: unknown; + type: "infill_rgba"; }; /** - * Resize Image - * @description Resizes an image to specific dimensions + * PatchMatch Infill + * @description Infills transparent areas of an image using the PatchMatch algorithm */ - ImageResizeInvocation: { + InfillPatchMatchInvocation: { /** * @description The board to save the image to * @default null @@ -12641,22 +12284,16 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to resize + * @description The image to process * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Width - * @description The width to resize to (px) - * @default 512 - */ - width?: number; - /** - * Height - * @description The height to resize to (px) - * @default 512 + * Downscale + * @description Run patchmatch on downscaled image to speedup infill + * @default 2 */ - height?: number; + downscale?: number; /** * Resample Mode * @description The resampling mode @@ -12666,16 +12303,16 @@ export type components = { resample_mode?: "nearest" | "box" | "bilinear" | "hamming" | "bicubic" | "lanczos"; /** * type - * @default img_resize + * @default infill_patchmatch * @constant */ - type: "img_resize"; + type: "infill_patchmatch"; }; /** - * Scale Image - * @description Scales an image by a factor + * Tile Infill + * @description Infills transparent areas of an image with tiles of the image */ - ImageScaleInvocation: { + InfillTileInvocation: { /** * @description The board to save the image to * @default null @@ -12704,133 +12341,114 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to scale + * @description The image to process * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Scale Factor - * @description The factor by which to scale the image - * @default 2 + * Tile Size + * @description The tile size (px) + * @default 32 */ - scale_factor?: number; + tile_size?: number; /** - * Resample Mode - * @description The resampling mode - * @default bicubic - * @enum {string} + * Seed + * @description The seed to use for tile generation (omit for random) + * @default 0 */ - resample_mode?: "nearest" | "box" | "bilinear" | "hamming" | "bicubic" | "lanczos"; + seed?: number; /** * type - * @default img_scale + * @default infill_tile * @constant */ - type: "img_scale"; + type: "infill_tile"; }; /** - * Image to Latents - SD1.5, SDXL - * @description Encodes an image into latents. + * Input + * @description The type of input a field accepts. + * - `Input.Direct`: The field must have its value provided directly, when the invocation and field are instantiated. + * - `Input.Connection`: The field must have its value provided by a connection. + * - `Input.Any`: The field may have its value provided either directly or by a connection. + * @enum {string} */ - ImageToLatentsInvocation: { - /** - * 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; + Input: "connection" | "direct" | "any"; + /** + * InputFieldJSONSchemaExtra + * @description Extra attributes to be added to input fields and their OpenAPI schema. Used during graph execution, + * and by the workflow editor during schema parsing and UI rendering. + */ + InputFieldJSONSchemaExtra: { + input: components["schemas"]["Input"]; + field_kind: components["schemas"]["FieldKind"]; /** - * Use Cache - * @description Whether or not to use the cache + * Orig Required * @default true */ - use_cache?: boolean; + orig_required: boolean; /** - * @description The image to encode + * Default * @default null */ - image?: components["schemas"]["ImageField"] | null; + default: unknown | null; /** - * @description VAE + * Orig Default * @default null */ - vae?: components["schemas"]["VAEField"] | null; + orig_default: unknown | null; /** - * Tiled - * @description Processing using overlapping tiles (reduce memory consumption) + * Ui Hidden * @default false */ - tiled?: boolean; - /** - * Tile Size - * @description The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the model will be used. Larger tile sizes generally produce better results at the cost of higher memory usage. - * @default 0 - */ - tile_size?: number; + ui_hidden: boolean; + /** @default null */ + ui_type: components["schemas"]["UIType"] | null; + /** @default null */ + ui_component: components["schemas"]["UIComponent"] | null; /** - * Fp32 - * @description Whether or not to use full float32 precision - * @default false + * Ui Order + * @default null */ - fp32?: boolean; + ui_order: number | null; /** - * type - * @default i2l - * @constant + * Ui Choice Labels + * @default null */ - type: "i2l"; - }; - /** ImageUploadEntry */ - ImageUploadEntry: { - /** @description The image DTO */ - image_dto: components["schemas"]["ImageDTO"]; + ui_choice_labels: { + [key: string]: string; + } | null; /** - * Presigned Url - * @description The URL to get the presigned URL for the image upload + * Ui Model Base + * @default null */ - presigned_url: string; - }; - /** - * ImageUrlsDTO - * @description The URLs for an image and its thumbnail. - */ - ImageUrlsDTO: { + ui_model_base: components["schemas"]["BaseModelType"][] | null; /** - * Image Name - * @description The unique name of the image. + * Ui Model Type + * @default null */ - image_name: string; + ui_model_type: components["schemas"]["ModelType"][] | null; /** - * Image Url - * @description The URL of the image. + * Ui Model Variant + * @default null */ - image_url: string; + ui_model_variant: (components["schemas"]["ClipVariantType"] | components["schemas"]["ModelVariantType"])[] | null; /** - * Thumbnail Url - * @description The URL of the image's thumbnail. + * Ui Model Format + * @default null */ - thumbnail_url: string; + ui_model_format: components["schemas"]["ModelFormat"][] | null; }; /** - * Add Invisible Watermark - * @description Add an invisible watermark to an image + * InstallStatus + * @description State of an install job running in the background. + * @enum {string} */ - ImageWatermarkInvocation: { - /** - * @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; + InstallStatus: "waiting" | "downloading" | "downloads_done" | "running" | "completed" | "error" | "cancelled"; + /** + * Integer Batch + * @description Create a batched generation, where the workflow is executed once for each integer in the batch. + */ + IntegerBatchInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -12849,51 +12467,30 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to check - * @default null + * Batch Group + * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. + * @default None + * @enum {string} */ - image?: components["schemas"]["ImageField"] | null; + batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; /** - * Text - * @description Watermark text - * @default InvokeAI + * Integers + * @description The integers to batch over + * @default null */ - text?: string; + integers?: number[] | null; /** * type - * @default img_watermark + * @default integer_batch * @constant */ - type: "img_watermark"; - }; - /** ImagesDownloaded */ - ImagesDownloaded: { - /** - * Response - * @description The message to display to the user when images begin downloading - */ - response?: string | null; - /** - * Bulk Download Item Name - * @description The name of the bulk download item for which events will be emitted - */ - bulk_download_item_name?: string | null; + type: "integer_batch"; }; /** - * Solid Color Infill - * @description Infills transparent areas of an image with a solid color + * Integer Collection Primitive + * @description A collection of integer primitive values */ - InfillColorInvocation: { - /** - * @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; + IntegerCollectionInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -12912,42 +12509,40 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to process - * @default null - */ - image?: components["schemas"]["ImageField"] | null; - /** - * @description The color to use to infill - * @default { - * "r": 127, - * "g": 127, - * "b": 127, - * "a": 255 - * } + * Collection + * @description The collection of integer values + * @default [] */ - color?: components["schemas"]["ColorField"]; + collection?: number[]; /** * type - * @default infill_rgba + * @default integer_collection * @constant */ - type: "infill_rgba"; + type: "integer_collection"; }; /** - * PatchMatch Infill - * @description Infills transparent areas of an image using the PatchMatch algorithm + * IntegerCollectionOutput + * @description Base class for nodes that output a collection of integers */ - InfillPatchMatchInvocation: { + IntegerCollectionOutput: { /** - * @description The board to save the image to - * @default null + * Collection + * @description The int collection */ - board?: components["schemas"]["BoardField"] | null; + collection: number[]; /** - * @description Optional metadata to be saved with the image - * @default null + * type + * @default integer_collection_output + * @constant */ - metadata?: components["schemas"]["MetadataField"] | null; + type: "integer_collection_output"; + }; + /** + * Integer Generator + * @description Generated a range of integers for use in a batched generation + */ + IntegerGenerator: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -12966,45 +12561,38 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to process - * @default null - */ - image?: components["schemas"]["ImageField"] | null; - /** - * Downscale - * @description Run patchmatch on downscaled image to speedup infill - * @default 2 - */ - downscale?: number; - /** - * Resample Mode - * @description The resampling mode - * @default bicubic - * @enum {string} + * Generator Type + * @description The integer generator. */ - resample_mode?: "nearest" | "box" | "bilinear" | "hamming" | "bicubic" | "lanczos"; + generator: components["schemas"]["IntegerGeneratorField"]; /** * type - * @default infill_patchmatch + * @default integer_generator * @constant */ - type: "infill_patchmatch"; + type: "integer_generator"; }; - /** - * Tile Infill - * @description Infills transparent areas of an image with tiles of the image - */ - InfillTileInvocation: { + /** IntegerGeneratorField */ + IntegerGeneratorField: Record; + /** IntegerGeneratorOutput */ + IntegerGeneratorOutput: { /** - * @description The board to save the image to - * @default null + * Integers + * @description The generated integers */ - board?: components["schemas"]["BoardField"] | null; + integers: number[]; /** - * @description Optional metadata to be saved with the image - * @default null + * type + * @default integer_generator_output + * @constant */ - metadata?: components["schemas"]["MetadataField"] | null; + type: "integer_generator_output"; + }; + /** + * Integer Primitive + * @description An integer primitive value + */ + IntegerInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -13023,114 +12611,23 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to process - * @default null - */ - image?: components["schemas"]["ImageField"] | null; - /** - * Tile Size - * @description The tile size (px) - * @default 32 - */ - tile_size?: number; - /** - * Seed - * @description The seed to use for tile generation (omit for random) + * Value + * @description The integer value * @default 0 */ - seed?: number; + value?: number; /** * type - * @default infill_tile + * @default integer * @constant */ - type: "infill_tile"; - }; - /** - * Input - * @description The type of input a field accepts. - * - `Input.Direct`: The field must have its value provided directly, when the invocation and field are instantiated. - * - `Input.Connection`: The field must have its value provided by a connection. - * - `Input.Any`: The field may have its value provided either directly or by a connection. - * @enum {string} - */ - Input: "connection" | "direct" | "any"; - /** - * InputFieldJSONSchemaExtra - * @description Extra attributes to be added to input fields and their OpenAPI schema. Used during graph execution, - * and by the workflow editor during schema parsing and UI rendering. - */ - InputFieldJSONSchemaExtra: { - input: components["schemas"]["Input"]; - field_kind: components["schemas"]["FieldKind"]; - /** - * Orig Required - * @default true - */ - orig_required: boolean; - /** - * Default - * @default null - */ - default: unknown | null; - /** - * Orig Default - * @default null - */ - orig_default: unknown | null; - /** - * Ui Hidden - * @default false - */ - ui_hidden: boolean; - /** @default null */ - ui_type: components["schemas"]["UIType"] | null; - /** @default null */ - ui_component: components["schemas"]["UIComponent"] | null; - /** - * Ui Order - * @default null - */ - ui_order: number | null; - /** - * Ui Choice Labels - * @default null - */ - ui_choice_labels: { - [key: string]: string; - } | null; - /** - * Ui Model Base - * @default null - */ - ui_model_base: components["schemas"]["BaseModelType"][] | null; - /** - * Ui Model Type - * @default null - */ - ui_model_type: components["schemas"]["ModelType"][] | null; - /** - * Ui Model Variant - * @default null - */ - ui_model_variant: (components["schemas"]["ClipVariantType"] | components["schemas"]["ModelVariantType"])[] | null; - /** - * Ui Model Format - * @default null - */ - ui_model_format: components["schemas"]["ModelFormat"][] | null; + type: "integer"; }; /** - * InstallStatus - * @description State of an install job running in the background. - * @enum {string} - */ - InstallStatus: "waiting" | "downloading" | "downloads_done" | "running" | "completed" | "error" | "cancelled"; - /** - * Integer Batch - * @description Create a batched generation, where the workflow is executed once for each integer in the batch. + * Integer Math + * @description Performs integer math. */ - IntegerBatchInvocation: { + IntegerMathInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -13149,82 +12646,53 @@ export type components = { */ use_cache?: boolean; /** - * Batch Group - * @description The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size. - * @default None + * Operation + * @description The operation to perform + * @default ADD * @enum {string} */ - batch_group_id?: "None" | "Group 1" | "Group 2" | "Group 3" | "Group 4" | "Group 5"; - /** - * Integers - * @description The integers to batch over - * @default null - */ - integers?: number[] | null; - /** - * type - * @default integer_batch - * @constant - */ - type: "integer_batch"; - }; - /** - * Integer Collection Primitive - * @description A collection of integer primitive values - */ - IntegerCollectionInvocation: { - /** - * 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; + operation?: "ADD" | "SUB" | "MUL" | "DIV" | "EXP" | "MOD" | "ABS" | "MIN" | "MAX"; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * A + * @description The first number + * @default 1 */ - use_cache?: boolean; + a?: number; /** - * Collection - * @description The collection of integer values - * @default [] + * B + * @description The second number + * @default 1 */ - collection?: number[]; + b?: number; /** * type - * @default integer_collection + * @default integer_math * @constant */ - type: "integer_collection"; + type: "integer_math"; }; /** - * IntegerCollectionOutput - * @description Base class for nodes that output a collection of integers + * IntegerOutput + * @description Base class for nodes that output a single integer */ - IntegerCollectionOutput: { + IntegerOutput: { /** - * Collection - * @description The int collection + * Value + * @description The output integer */ - collection: number[]; + value: number; /** * type - * @default integer_collection_output + * @default integer_output * @constant */ - type: "integer_collection_output"; + type: "integer_output"; }; /** - * Integer Generator - * @description Generated a range of integers for use in a batched generation + * Invert Tensor Mask + * @description Inverts a tensor mask. */ - IntegerGenerator: { + InvertTensorMaskInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -13243,182 +12711,32 @@ export type components = { */ use_cache?: boolean; /** - * Generator Type - * @description The integer generator. + * @description The tensor mask to convert. + * @default null */ - generator: components["schemas"]["IntegerGeneratorField"]; + mask?: components["schemas"]["TensorField"] | null; /** * type - * @default integer_generator + * @default invert_tensor_mask * @constant */ - type: "integer_generator"; + type: "invert_tensor_mask"; }; - /** IntegerGeneratorField */ - IntegerGeneratorField: Record; - /** IntegerGeneratorOutput */ - IntegerGeneratorOutput: { + /** InvocationCacheStatus */ + InvocationCacheStatus: { /** - * Integers - * @description The generated integers + * Size + * @description The current size of the invocation cache */ - integers: number[]; + size: number; /** - * type - * @default integer_generator_output - * @constant + * Hits + * @description The number of cache hits */ - type: "integer_generator_output"; - }; - /** - * Integer Primitive - * @description An integer primitive value - */ - IntegerInvocation: { + hits: number; /** - * 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; - /** - * Value - * @description The integer value - * @default 0 - */ - value?: number; - /** - * type - * @default integer - * @constant - */ - type: "integer"; - }; - /** - * Integer Math - * @description Performs integer math. - */ - IntegerMathInvocation: { - /** - * 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; - /** - * Operation - * @description The operation to perform - * @default ADD - * @enum {string} - */ - operation?: "ADD" | "SUB" | "MUL" | "DIV" | "EXP" | "MOD" | "ABS" | "MIN" | "MAX"; - /** - * A - * @description The first number - * @default 1 - */ - a?: number; - /** - * B - * @description The second number - * @default 1 - */ - b?: number; - /** - * type - * @default integer_math - * @constant - */ - type: "integer_math"; - }; - /** - * IntegerOutput - * @description Base class for nodes that output a single integer - */ - IntegerOutput: { - /** - * Value - * @description The output integer - */ - value: number; - /** - * type - * @default integer_output - * @constant - */ - type: "integer_output"; - }; - /** - * Invert Tensor Mask - * @description Inverts a tensor mask. - */ - InvertTensorMaskInvocation: { - /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. - */ - id: string; - /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false - */ - is_intermediate?: boolean; - /** - * Use Cache - * @description Whether or not to use the cache - * @default true - */ - use_cache?: boolean; - /** - * @description The tensor mask to convert. - * @default null - */ - mask?: components["schemas"]["TensorField"] | null; - /** - * type - * @default invert_tensor_mask - * @constant - */ - type: "invert_tensor_mask"; - }; - /** InvocationCacheStatus */ - InvocationCacheStatus: { - /** - * Size - * @description The current size of the invocation cache - */ - size: number; - /** - * Hits - * @description The number of cache hits - */ - hits: number; - /** - * Misses - * @description The number of cache misses + * Misses + * @description The number of cache misses */ misses: number; /** @@ -14004,355 +13322,652 @@ export type components = { * @description Allow CORS credentials. * @default true */ - allow_credentials?: boolean; + allow_credentials?: boolean; + /** + * Allow Methods + * @description Methods allowed for CORS. + * @default [ + * "*" + * ] + */ + allow_methods?: string[]; + /** + * Allow Headers + * @description Headers allowed for CORS. + * @default [ + * "*" + * ] + */ + allow_headers?: string[]; + /** + * Ssl Certfile + * @description SSL certificate file for HTTPS. See https://www.uvicorn.org/settings/#https. + */ + ssl_certfile?: string | null; + /** + * Ssl Keyfile + * @description SSL key file for HTTPS. See https://www.uvicorn.org/settings/#https. + */ + ssl_keyfile?: string | null; + /** + * Log Tokenization + * @description Enable logging of parsed prompt tokens. + * @default false + */ + log_tokenization?: boolean; + /** + * Patchmatch + * @description Enable patchmatch inpaint code. + * @default true + */ + patchmatch?: boolean; + /** + * Models Dir + * Format: path + * @description Path to the models directory. + * @default models + */ + models_dir?: string; + /** + * Convert Cache Dir + * Format: path + * @description Path to the converted models cache directory (DEPRECATED, but do not delete because it is needed for migration from previous versions). + * @default models/.convert_cache + */ + convert_cache_dir?: string; + /** + * Download Cache Dir + * Format: path + * @description Path to the directory that contains dynamically downloaded models. + * @default models/.download_cache + */ + download_cache_dir?: string; + /** + * Legacy Conf Dir + * Format: path + * @description Path to directory of legacy checkpoint config files. + * @default configs + */ + legacy_conf_dir?: string; + /** + * Db Dir + * Format: path + * @description Path to InvokeAI databases directory. + * @default databases + */ + db_dir?: string; + /** + * Outputs Dir + * Format: path + * @description Path to directory for outputs. + * @default outputs + */ + outputs_dir?: string; + /** + * Custom Nodes Dir + * Format: path + * @description Path to directory for custom nodes. + * @default nodes + */ + custom_nodes_dir?: string; + /** + * Style Presets Dir + * Format: path + * @description Path to directory for style presets. + * @default style_presets + */ + style_presets_dir?: string; + /** + * Workflow Thumbnails Dir + * Format: path + * @description Path to directory for workflow thumbnails. + * @default workflow_thumbnails + */ + workflow_thumbnails_dir?: string; + /** + * Log Handlers + * @description Log handler. Valid options are "console", "file=", "syslog=path|address:host:port", "http=". + * @default [ + * "console" + * ] + */ + log_handlers?: string[]; + /** + * Log Format + * @description Log format. Use "plain" for text-only, "color" for colorized output, "legacy" for 2.3-style logging and "syslog" for syslog-style. + * @default color + * @enum {string} + */ + log_format?: "plain" | "color" | "syslog" | "legacy"; + /** + * Log Level + * @description Emit logging messages at this level or higher. + * @default info + * @enum {string} + */ + log_level?: "debug" | "info" | "warning" | "error" | "critical"; + /** + * Log Sql + * @description Log SQL queries. `log_level` must be `debug` for this to do anything. Extremely verbose. + * @default false + */ + log_sql?: boolean; + /** + * Log Level Network + * @description Log level for network-related messages. 'info' and 'debug' are very verbose. + * @default warning + * @enum {string} + */ + log_level_network?: "debug" | "info" | "warning" | "error" | "critical"; + /** + * Use Memory Db + * @description Use in-memory database. Useful for development. + * @default false + */ + use_memory_db?: boolean; + /** + * Dev Reload + * @description Automatically reload when Python sources are changed. Does not reload node definitions. + * @default false + */ + dev_reload?: boolean; + /** + * Profile Graphs + * @description Enable graph profiling using `cProfile`. + * @default false + */ + profile_graphs?: boolean; + /** + * Profile Prefix + * @description An optional prefix for profile output files. + */ + profile_prefix?: string | null; + /** + * Profiles Dir + * Format: path + * @description Path to profiles output directory. + * @default profiles + */ + profiles_dir?: string; + /** + * Max Cache Ram Gb + * @description The maximum amount of CPU RAM to use for model caching in GB. If unset, the limit will be configured based on the available RAM. In most cases, it is recommended to leave this unset. + */ + max_cache_ram_gb?: number | null; + /** + * Max Cache Vram Gb + * @description The amount of VRAM to use for model caching in GB. If unset, the limit will be configured based on the available VRAM and the device_working_mem_gb. In most cases, it is recommended to leave this unset. + */ + max_cache_vram_gb?: number | null; + /** + * Log Memory Usage + * @description If True, a memory snapshot will be captured before and after every model cache operation, and the result will be logged (at debug level). There is a time cost to capturing the memory snapshots, so it is recommended to only enable this feature if you are actively inspecting the model cache's behaviour. + * @default false + */ + log_memory_usage?: boolean; + /** + * Device Working Mem Gb + * @description The amount of working memory to keep available on the compute device (in GB). Has no effect if running on CPU. If you are experiencing OOM errors, try increasing this value. + * @default 3 + */ + device_working_mem_gb?: number; + /** + * Enable Partial Loading + * @description Enable partial loading of models. This enables models to run with reduced VRAM requirements (at the cost of slower speed) by streaming the model from RAM to VRAM as its used. In some edge cases, partial loading can cause models to run more slowly if they were previously being fully loaded into VRAM. + * @default false + */ + enable_partial_loading?: boolean; + /** + * Keep Ram Copy Of Weights + * @description Whether to keep a full RAM copy of a model's weights when the model is loaded in VRAM. Keeping a RAM copy increases average RAM usage, but speeds up model switching and LoRA patching (assuming there is sufficient RAM). Set this to False if RAM pressure is consistently high. + * @default true + */ + keep_ram_copy_of_weights?: boolean; + /** + * Ram + * @description DEPRECATED: This setting is no longer used. It has been replaced by `max_cache_ram_gb`, but most users will not need to use this config since automatic cache size limits should work well in most cases. This config setting will be removed once the new model cache behavior is stable. + */ + ram?: number | null; + /** + * Vram + * @description DEPRECATED: This setting is no longer used. It has been replaced by `max_cache_vram_gb`, but most users will not need to use this config since automatic cache size limits should work well in most cases. This config setting will be removed once the new model cache behavior is stable. + */ + vram?: number | null; + /** + * Lazy Offload + * @description DEPRECATED: This setting is no longer used. Lazy-offloading is enabled by default. This config setting will be removed once the new model cache behavior is stable. + * @default true + */ + lazy_offload?: boolean; + /** + * Pytorch Cuda Alloc Conf + * @description Configure the Torch CUDA memory allocator. This will impact peak reserved VRAM usage and performance. Setting to "backend:cudaMallocAsync" works well on many systems. The optimal configuration is highly dependent on the system configuration (device type, VRAM, CUDA driver version, etc.), so must be tuned experimentally. + */ + pytorch_cuda_alloc_conf?: string | null; + /** + * Device + * @description Preferred execution device. `auto` will choose the device depending on the hardware platform and the installed torch capabilities.
Valid values: `auto`, `cpu`, `cuda`, `mps`, `cuda:N` (where N is a device number) + * @default auto + */ + device?: string; + /** + * Precision + * @description Floating point precision. `float16` will consume half the memory of `float32` but produce slightly lower-quality images. The `auto` setting will guess the proper precision based on your video card and operating system. + * @default auto + * @enum {string} + */ + precision?: "auto" | "float16" | "bfloat16" | "float32"; + /** + * Sequential Guidance + * @description Whether to calculate guidance in serial instead of in parallel, lowering memory requirements. + * @default false + */ + sequential_guidance?: boolean; + /** + * Attention Type + * @description Attention type. + * @default auto + * @enum {string} + */ + attention_type?: "auto" | "normal" | "xformers" | "sliced" | "torch-sdp"; + /** + * Attention Slice Size + * @description Slice size, valid when attention_type=="sliced". + * @default auto + * @enum {unknown} + */ + attention_slice_size?: "auto" | "balanced" | "max" | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; + /** + * Force Tiled Decode + * @description Whether to enable tiled VAE decode (reduces memory consumption with some performance penalty). + * @default false + */ + force_tiled_decode?: boolean; + /** + * Pil Compress Level + * @description The compress_level setting of PIL.Image.save(), used for PNG encoding. All settings are lossless. 0 = no compression, 1 = fastest with slightly larger filesize, 9 = slowest with smallest filesize. 1 is typically the best setting. + * @default 1 + */ + pil_compress_level?: number; + /** + * Max Queue Size + * @description Maximum number of items in the session queue. + * @default 10000 + */ + max_queue_size?: number; + /** + * Clear Queue On Startup + * @description Empties session queue on startup. + * @default false + */ + clear_queue_on_startup?: boolean; + /** + * Allow Nodes + * @description List of nodes to allow. Omit to allow all. + */ + allow_nodes?: string[] | null; + /** + * Deny Nodes + * @description List of nodes to deny. Omit to deny none. + */ + deny_nodes?: string[] | null; /** - * Allow Methods - * @description Methods allowed for CORS. - * @default [ - * "*" - * ] + * Node Cache Size + * @description How many cached nodes to keep in memory. + * @default 512 */ - allow_methods?: string[]; + node_cache_size?: number; /** - * Allow Headers - * @description Headers allowed for CORS. - * @default [ - * "*" - * ] + * Hashing Algorithm + * @description Model hashing algorthim for model installs. 'blake3_multi' is best for SSDs. 'blake3_single' is best for spinning disk HDDs. 'random' disables hashing, instead assigning a UUID to models. Useful when using a memory db to reduce model installation time, or if you don't care about storing stable hashes for models. Alternatively, any other hashlib algorithm is accepted, though these are not nearly as performant as blake3. + * @default blake3_single + * @enum {string} */ - allow_headers?: string[]; + hashing_algorithm?: "blake3_multi" | "blake3_single" | "random" | "md5" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512" | "blake2b" | "blake2s" | "sha3_224" | "sha3_256" | "sha3_384" | "sha3_512" | "shake_128" | "shake_256"; /** - * Ssl Certfile - * @description SSL certificate file for HTTPS. See https://www.uvicorn.org/settings/#https. + * Remote Api Tokens + * @description List of regular expression and token pairs used when downloading models from URLs. The download URL is tested against the regex, and if it matches, the token is provided in as a Bearer token. */ - ssl_certfile?: string | null; + remote_api_tokens?: components["schemas"]["URLRegexTokenPair"][] | null; /** - * Ssl Keyfile - * @description SSL key file for HTTPS. See https://www.uvicorn.org/settings/#https. + * Scan Models On Startup + * @description Scan the models directory on startup, registering orphaned models. This is typically only used in conjunction with `use_memory_db` for testing purposes. + * @default false */ - ssl_keyfile?: string | null; + scan_models_on_startup?: boolean; /** - * Log Tokenization - * @description Enable logging of parsed prompt tokens. + * Unsafe Disable Picklescan + * @description UNSAFE. Disable the picklescan security check during model installation. Recommended only for development and testing purposes. This will allow arbitrary code execution during model installation, so should never be used in production. * @default false */ - log_tokenization?: boolean; + unsafe_disable_picklescan?: boolean; /** - * Patchmatch - * @description Enable patchmatch inpaint code. + * Allow Unknown Models + * @description Allow installation of models that we are unable to identify. If enabled, models will be marked as `unknown` in the database, and will not have any metadata associated with them. If disabled, unknown models will be rejected during installation. * @default true */ - patchmatch?: boolean; + allow_unknown_models?: boolean; + }; + /** + * InvokeAIAppConfigWithSetFields + * @description InvokeAI App Config with model fields set + */ + InvokeAIAppConfigWithSetFields: { /** - * Models Dir - * Format: path - * @description Path to the models directory. - * @default models + * Set Fields + * @description The set fields */ - models_dir?: string; + set_fields: string[]; + /** @description The InvokeAI App Config */ + config: components["schemas"]["InvokeAIAppConfig"]; + }; + /** + * Adjust Image Hue Plus + * @description Adjusts the Hue of an image by rotating it in the selected color space. Originally created by @dwringer + */ + InvokeAdjustImageHuePlusInvocation: { /** - * Convert Cache Dir - * Format: path - * @description Path to the converted models cache directory (DEPRECATED, but do not delete because it is needed for migration from previous versions). - * @default models/.convert_cache + * @description The board to save the image to + * @default null */ - convert_cache_dir?: string; + board?: components["schemas"]["BoardField"] | null; /** - * Download Cache Dir - * Format: path - * @description Path to the directory that contains dynamically downloaded models. - * @default models/.download_cache + * @description Optional metadata to be saved with the image + * @default null */ - download_cache_dir?: string; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Legacy Conf Dir - * Format: path - * @description Path to directory of legacy checkpoint config files. - * @default configs + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - legacy_conf_dir?: string; + id: string; /** - * Db Dir - * Format: path - * @description Path to InvokeAI databases directory. - * @default databases + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - db_dir?: string; + is_intermediate?: boolean; /** - * Outputs Dir - * Format: path - * @description Path to directory for outputs. - * @default outputs + * Use Cache + * @description Whether or not to use the cache + * @default true */ - outputs_dir?: string; + use_cache?: boolean; /** - * Custom Nodes Dir - * Format: path - * @description Path to directory for custom nodes. - * @default nodes + * @description The image to adjust + * @default null */ - custom_nodes_dir?: string; + image?: components["schemas"]["ImageField"] | null; /** - * Style Presets Dir - * Format: path - * @description Path to directory for style presets. - * @default style_presets + * Space + * @description Color space in which to rotate hue by polar coords (*: non-invertible) + * @default HSV / HSL / RGB + * @enum {string} */ - style_presets_dir?: string; + space?: "HSV / HSL / RGB" | "Okhsl" | "Okhsv" | "*Oklch / Oklab" | "*LCh / CIELab" | "*UPLab (w/CIELab_to_UPLab.icc)"; /** - * Workflow Thumbnails Dir - * Format: path - * @description Path to directory for workflow thumbnails. - * @default workflow_thumbnails + * Degrees + * @description Degrees by which to rotate image hue + * @default 0 */ - workflow_thumbnails_dir?: string; + degrees?: number; /** - * Log Handlers - * @description Log handler. Valid options are "console", "file=", "syslog=path|address:host:port", "http=". - * @default [ - * "console" - * ] + * Preserve Lightness + * @description Whether to preserve CIELAB lightness values + * @default false */ - log_handlers?: string[]; + preserve_lightness?: boolean; /** - * Log Format - * @description Log format. Use "plain" for text-only, "color" for colorized output, "legacy" for 2.3-style logging and "syslog" for syslog-style. - * @default color - * @enum {string} + * Ok Adaptive Gamut + * @description Higher preserves chroma at the expense of lightness (Oklab) + * @default 0.05 */ - log_format?: "plain" | "color" | "syslog" | "legacy"; + ok_adaptive_gamut?: number; /** - * Log Level - * @description Emit logging messages at this level or higher. - * @default info - * @enum {string} + * Ok High Precision + * @description Use more steps in computing gamut (Oklab/Okhsv/Okhsl) + * @default true */ - log_level?: "debug" | "info" | "warning" | "error" | "critical"; + ok_high_precision?: boolean; /** - * Log Sql - * @description Log SQL queries. `log_level` must be `debug` for this to do anything. Extremely verbose. - * @default false + * type + * @default invokeai_img_hue_adjust_plus + * @constant */ - log_sql?: boolean; + type: "invokeai_img_hue_adjust_plus"; + }; + /** + * Equivalent Achromatic Lightness + * @description Calculate Equivalent Achromatic Lightness from image. Originally created by @dwringer + */ + InvokeEquivalentAchromaticLightnessInvocation: { /** - * Log Level Network - * @description Log level for network-related messages. 'info' and 'debug' are very verbose. - * @default warning - * @enum {string} + * @description The board to save the image to + * @default null */ - log_level_network?: "debug" | "info" | "warning" | "error" | "critical"; + board?: components["schemas"]["BoardField"] | null; /** - * Use Memory Db - * @description Use in-memory database. Useful for development. - * @default false + * @description Optional metadata to be saved with the image + * @default null */ - use_memory_db?: boolean; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Dev Reload - * @description Automatically reload when Python sources are changed. Does not reload node definitions. - * @default false + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - dev_reload?: boolean; + id: string; /** - * Profile Graphs - * @description Enable graph profiling using `cProfile`. + * Is Intermediate + * @description Whether or not this is an intermediate invocation. * @default false */ - profile_graphs?: boolean; + is_intermediate?: boolean; /** - * Profile Prefix - * @description An optional prefix for profile output files. + * Use Cache + * @description Whether or not to use the cache + * @default true */ - profile_prefix?: string | null; + use_cache?: boolean; /** - * Profiles Dir - * Format: path - * @description Path to profiles output directory. - * @default profiles + * @description Image from which to get channel + * @default null */ - profiles_dir?: string; + image?: components["schemas"]["ImageField"] | null; /** - * Max Cache Ram Gb - * @description The maximum amount of CPU RAM to use for model caching in GB. If unset, the limit will be configured based on the available RAM. In most cases, it is recommended to leave this unset. + * type + * @default invokeai_ealightness + * @constant */ - max_cache_ram_gb?: number | null; + type: "invokeai_ealightness"; + }; + /** + * Image Layer Blend + * @description Blend two images together, with optional opacity, mask, and blend modes. Originally created by @dwringer + */ + InvokeImageBlendInvocation: { /** - * Max Cache Vram Gb - * @description The amount of VRAM to use for model caching in GB. If unset, the limit will be configured based on the available VRAM and the device_working_mem_gb. In most cases, it is recommended to leave this unset. + * @description The board to save the image to + * @default null */ - max_cache_vram_gb?: number | null; + board?: components["schemas"]["BoardField"] | null; /** - * Log Memory Usage - * @description If True, a memory snapshot will be captured before and after every model cache operation, and the result will be logged (at debug level). There is a time cost to capturing the memory snapshots, so it is recommended to only enable this feature if you are actively inspecting the model cache's behaviour. - * @default false + * @description Optional metadata to be saved with the image + * @default null */ - log_memory_usage?: boolean; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Device Working Mem Gb - * @description The amount of working memory to keep available on the compute device (in GB). Has no effect if running on CPU. If you are experiencing OOM errors, try increasing this value. - * @default 3 + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - device_working_mem_gb?: number; + id: string; /** - * Enable Partial Loading - * @description Enable partial loading of models. This enables models to run with reduced VRAM requirements (at the cost of slower speed) by streaming the model from RAM to VRAM as its used. In some edge cases, partial loading can cause models to run more slowly if they were previously being fully loaded into VRAM. + * Is Intermediate + * @description Whether or not this is an intermediate invocation. * @default false */ - enable_partial_loading?: boolean; + is_intermediate?: boolean; /** - * Keep Ram Copy Of Weights - * @description Whether to keep a full RAM copy of a model's weights when the model is loaded in VRAM. Keeping a RAM copy increases average RAM usage, but speeds up model switching and LoRA patching (assuming there is sufficient RAM). Set this to False if RAM pressure is consistently high. + * Use Cache + * @description Whether or not to use the cache * @default true */ - keep_ram_copy_of_weights?: boolean; + use_cache?: boolean; /** - * Ram - * @description DEPRECATED: This setting is no longer used. It has been replaced by `max_cache_ram_gb`, but most users will not need to use this config since automatic cache size limits should work well in most cases. This config setting will be removed once the new model cache behavior is stable. + * @description The top image to blend + * @default null */ - ram?: number | null; + layer_upper?: components["schemas"]["ImageField"] | null; /** - * Vram - * @description DEPRECATED: This setting is no longer used. It has been replaced by `max_cache_vram_gb`, but most users will not need to use this config since automatic cache size limits should work well in most cases. This config setting will be removed once the new model cache behavior is stable. + * Blend Mode + * @description Available blend modes + * @default Normal + * @enum {string} */ - vram?: number | null; + blend_mode?: "Normal" | "Lighten Only" | "Darken Only" | "Lighten Only (EAL)" | "Darken Only (EAL)" | "Hue" | "Saturation" | "Color" | "Luminosity" | "Linear Dodge (Add)" | "Subtract" | "Multiply" | "Divide" | "Screen" | "Overlay" | "Linear Burn" | "Difference" | "Hard Light" | "Soft Light" | "Vivid Light" | "Linear Light" | "Color Burn" | "Color Dodge"; /** - * Lazy Offload - * @description DEPRECATED: This setting is no longer used. Lazy-offloading is enabled by default. This config setting will be removed once the new model cache behavior is stable. - * @default true + * Opacity + * @description Desired opacity of the upper layer + * @default 1 */ - lazy_offload?: boolean; + opacity?: number; /** - * Pytorch Cuda Alloc Conf - * @description Configure the Torch CUDA memory allocator. This will impact peak reserved VRAM usage and performance. Setting to "backend:cudaMallocAsync" works well on many systems. The optimal configuration is highly dependent on the system configuration (device type, VRAM, CUDA driver version, etc.), so must be tuned experimentally. + * @description Optional mask, used to restrict areas from blending + * @default null */ - pytorch_cuda_alloc_conf?: string | null; + mask?: components["schemas"]["ImageField"] | null; /** - * Device - * @description Preferred execution device. `auto` will choose the device depending on the hardware platform and the installed torch capabilities.
Valid values: `auto`, `cpu`, `cuda`, `mps`, `cuda:N` (where N is a device number) - * @default auto + * Fit To Width + * @description Scale upper layer to fit base width + * @default false */ - device?: string; + fit_to_width?: boolean; /** - * Precision - * @description Floating point precision. `float16` will consume half the memory of `float32` but produce slightly lower-quality images. The `auto` setting will guess the proper precision based on your video card and operating system. - * @default auto - * @enum {string} + * Fit To Height + * @description Scale upper layer to fit base height + * @default true */ - precision?: "auto" | "float16" | "bfloat16" | "float32"; + fit_to_height?: boolean; /** - * Sequential Guidance - * @description Whether to calculate guidance in serial instead of in parallel, lowering memory requirements. - * @default false + * @description The bottom image to blend + * @default null */ - sequential_guidance?: boolean; + layer_base?: components["schemas"]["ImageField"] | null; /** - * Attention Type - * @description Attention type. - * @default auto + * Color Space + * @description Available color spaces for blend computations + * @default RGB * @enum {string} */ - attention_type?: "auto" | "normal" | "xformers" | "sliced" | "torch-sdp"; + color_space?: "RGB" | "Linear RGB" | "HSL (RGB)" | "HSV (RGB)" | "Okhsl" | "Okhsv" | "Oklch (Oklab)" | "LCh (CIELab)"; /** - * Attention Slice Size - * @description Slice size, valid when attention_type=="sliced". - * @default auto - * @enum {unknown} + * Adaptive Gamut + * @description Adaptive gamut clipping (0=off). Higher prioritizes chroma over lightness + * @default 0 */ - attention_slice_size?: "auto" | "balanced" | "max" | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; + adaptive_gamut?: number; /** - * Force Tiled Decode - * @description Whether to enable tiled VAE decode (reduces memory consumption with some performance penalty). - * @default false + * High Precision + * @description Use more steps in computing gamut when possible + * @default true */ - force_tiled_decode?: boolean; + high_precision?: boolean; /** - * Pil Compress Level - * @description The compress_level setting of PIL.Image.save(), used for PNG encoding. All settings are lossless. 0 = no compression, 1 = fastest with slightly larger filesize, 9 = slowest with smallest filesize. 1 is typically the best setting. - * @default 1 + * type + * @default invokeai_img_blend + * @constant */ - pil_compress_level?: number; + type: "invokeai_img_blend"; + }; + /** + * Image Compositor + * @description Removes backdrop from subject image then overlays subject on background image. Originally created by @dwringer + */ + InvokeImageCompositorInvocation: { /** - * Max Queue Size - * @description Maximum number of items in the session queue. - * @default 10000 + * @description The board to save the image to + * @default null */ - max_queue_size?: number; + board?: components["schemas"]["BoardField"] | null; /** - * Clear Queue On Startup - * @description Empties session queue on startup. - * @default false + * @description Optional metadata to be saved with the image + * @default null */ - clear_queue_on_startup?: boolean; + metadata?: components["schemas"]["MetadataField"] | null; /** - * Allow Nodes - * @description List of nodes to allow. Omit to allow all. + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. */ - allow_nodes?: string[] | null; + id: string; /** - * Deny Nodes - * @description List of nodes to deny. Omit to deny none. + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false */ - deny_nodes?: string[] | null; + is_intermediate?: boolean; /** - * Node Cache Size - * @description How many cached nodes to keep in memory. - * @default 512 + * Use Cache + * @description Whether or not to use the cache + * @default true */ - node_cache_size?: number; + use_cache?: boolean; /** - * Hashing Algorithm - * @description Model hashing algorthim for model installs. 'blake3_multi' is best for SSDs. 'blake3_single' is best for spinning disk HDDs. 'random' disables hashing, instead assigning a UUID to models. Useful when using a memory db to reduce model installation time, or if you don't care about storing stable hashes for models. Alternatively, any other hashlib algorithm is accepted, though these are not nearly as performant as blake3. - * @default blake3_single - * @enum {string} + * @description Image of the subject on a plain monochrome background + * @default null */ - hashing_algorithm?: "blake3_multi" | "blake3_single" | "random" | "md5" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512" | "blake2b" | "blake2s" | "sha3_224" | "sha3_256" | "sha3_384" | "sha3_512" | "shake_128" | "shake_256"; + image_subject?: components["schemas"]["ImageField"] | null; /** - * Remote Api Tokens - * @description List of regular expression and token pairs used when downloading models from URLs. The download URL is tested against the regex, and if it matches, the token is provided in as a Bearer token. + * @description Image of a background scene + * @default null */ - remote_api_tokens?: components["schemas"]["URLRegexTokenPair"][] | null; + image_background?: components["schemas"]["ImageField"] | null; /** - * Scan Models On Startup - * @description Scan the models directory on startup, registering orphaned models. This is typically only used in conjunction with `use_memory_db` for testing purposes. - * @default false + * Chroma Key + * @description Can be empty for corner flood select, or CSS-3 color or tuple + * @default */ - scan_models_on_startup?: boolean; + chroma_key?: string; /** - * Unsafe Disable Picklescan - * @description UNSAFE. Disable the picklescan security check during model installation. Recommended only for development and testing purposes. This will allow arbitrary code execution during model installation, so should never be used in production. + * Threshold + * @description Subject isolation flood-fill threshold + * @default 50 + */ + threshold?: number; + /** + * Fill X + * @description Scale base subject image to fit background width * @default false */ - unsafe_disable_picklescan?: boolean; + fill_x?: boolean; /** - * Allow Unknown Models - * @description Allow installation of models that we are unable to identify. If enabled, models will be marked as `unknown` in the database, and will not have any metadata associated with them. If disabled, unknown models will be rejected during installation. + * Fill Y + * @description Scale base subject image to fit background height * @default true */ - allow_unknown_models?: boolean; - }; - /** - * InvokeAIAppConfigWithSetFields - * @description InvokeAI App Config with model fields set - */ - InvokeAIAppConfigWithSetFields: { + fill_y?: boolean; /** - * Set Fields - * @description The set fields + * X Offset + * @description x-offset for the subject + * @default 0 */ - set_fields: string[]; - /** @description The InvokeAI App Config */ - config: components["schemas"]["InvokeAIAppConfig"]; + x_offset?: number; + /** + * Y Offset + * @description y-offset for the subject + * @default 0 + */ + y_offset?: number; + /** + * type + * @default invokeai_img_composite + * @constant + */ + type: "invokeai_img_composite"; }; /** - * Adjust Image Hue Plus - * @description Adjusts the Hue of an image by rotating it in the selected color space. Originally created by @dwringer + * Image Dilate or Erode + * @description Dilate (expand) or erode (contract) an image. Originally created by @dwringer */ - InvokeAdjustImageHuePlusInvocation: { - /** - * @description The board to save the image to - * @default null - */ - board?: components["schemas"]["BoardField"] | null; + InvokeImageDilateOrErodeInvocation: { /** * @description Optional metadata to be saved with the image * @default null @@ -14376,53 +13991,47 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to adjust + * @description The image from which to create a mask * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Space - * @description Color space in which to rotate hue by polar coords (*: non-invertible) - * @default HSV / HSL / RGB - * @enum {string} - */ - space?: "HSV / HSL / RGB" | "Okhsl" | "Okhsv" | "*Oklch / Oklab" | "*LCh / CIELab" | "*UPLab (w/CIELab_to_UPLab.icc)"; - /** - * Degrees - * @description Degrees by which to rotate image hue - * @default 0 + * Lightness Only + * @description If true, only applies to image lightness (CIELa*b*) + * @default false */ - degrees?: number; + lightness_only?: boolean; /** - * Preserve Lightness - * @description Whether to preserve CIELAB lightness values - * @default false + * Radius W + * @description Width (in pixels) by which to dilate(expand) or erode (contract) the image + * @default 4 */ - preserve_lightness?: boolean; + radius_w?: number; /** - * Ok Adaptive Gamut - * @description Higher preserves chroma at the expense of lightness (Oklab) - * @default 0.05 + * Radius H + * @description Height (in pixels) by which to dilate(expand) or erode (contract) the image + * @default 4 */ - ok_adaptive_gamut?: number; + radius_h?: number; /** - * Ok High Precision - * @description Use more steps in computing gamut (Oklab/Okhsv/Okhsl) - * @default true + * Mode + * @description How to operate on the image + * @default Dilate + * @enum {string} */ - ok_high_precision?: boolean; + mode?: "Dilate" | "Erode"; /** * type - * @default invokeai_img_hue_adjust_plus + * @default invokeai_img_dilate_erode * @constant */ - type: "invokeai_img_hue_adjust_plus"; + type: "invokeai_img_dilate_erode"; }; /** - * Equivalent Achromatic Lightness - * @description Calculate Equivalent Achromatic Lightness from image. Originally created by @dwringer + * Enhance Image + * @description Applies processing from PIL's ImageEnhance module. Originally created by @dwringer */ - InvokeEquivalentAchromaticLightnessInvocation: { + InvokeImageEnhanceInvocation: { /** * @description The board to save the image to * @default null @@ -14451,22 +14060,52 @@ export type components = { */ use_cache?: boolean; /** - * @description Image from which to get channel + * @description The image for which to apply processing * @default null */ image?: components["schemas"]["ImageField"] | null; + /** + * Invert + * @description Whether to invert the image colors + * @default false + */ + invert?: boolean; + /** + * Color + * @description Color enhancement factor + * @default 1 + */ + color?: number; + /** + * Contrast + * @description Contrast enhancement factor + * @default 1 + */ + contrast?: number; + /** + * Brightness + * @description Brightness enhancement factor + * @default 1 + */ + brightness?: number; + /** + * Sharpness + * @description Sharpness enhancement factor + * @default 1 + */ + sharpness?: number; /** * type - * @default invokeai_ealightness + * @default invokeai_img_enhance * @constant */ - type: "invokeai_ealightness"; + type: "invokeai_img_enhance"; }; /** - * Image Layer Blend - * @description Blend two images together, with optional opacity, mask, and blend modes. Originally created by @dwringer + * Image Value Thresholds + * @description Clip image to pure black/white past specified thresholds. Originally created by @dwringer */ - InvokeImageBlendInvocation: { + InvokeImageValueThresholdsInvocation: { /** * @description The board to save the image to * @default null @@ -14495,86 +14134,68 @@ export type components = { */ use_cache?: boolean; /** - * @description The top image to blend - * @default null - */ - layer_upper?: components["schemas"]["ImageField"] | null; - /** - * Blend Mode - * @description Available blend modes - * @default Normal - * @enum {string} - */ - blend_mode?: "Normal" | "Lighten Only" | "Darken Only" | "Lighten Only (EAL)" | "Darken Only (EAL)" | "Hue" | "Saturation" | "Color" | "Luminosity" | "Linear Dodge (Add)" | "Subtract" | "Multiply" | "Divide" | "Screen" | "Overlay" | "Linear Burn" | "Difference" | "Hard Light" | "Soft Light" | "Vivid Light" | "Linear Light" | "Color Burn" | "Color Dodge"; - /** - * Opacity - * @description Desired opacity of the upper layer - * @default 1 - */ - opacity?: number; - /** - * @description Optional mask, used to restrict areas from blending + * @description The image from which to create a mask * @default null */ - mask?: components["schemas"]["ImageField"] | null; + image?: components["schemas"]["ImageField"] | null; /** - * Fit To Width - * @description Scale upper layer to fit base width + * Invert Output + * @description Make light areas dark and vice versa * @default false */ - fit_to_width?: boolean; - /** - * Fit To Height - * @description Scale upper layer to fit base height - * @default true - */ - fit_to_height?: boolean; + invert_output?: boolean; /** - * @description The bottom image to blend - * @default null + * Renormalize Values + * @description Rescale remaining values from minimum to maximum + * @default false */ - layer_base?: components["schemas"]["ImageField"] | null; + renormalize_values?: boolean; /** - * Color Space - * @description Available color spaces for blend computations - * @default RGB - * @enum {string} + * Lightness Only + * @description If true, only applies to image lightness (CIELa*b*) + * @default false */ - color_space?: "RGB" | "Linear RGB" | "HSL (RGB)" | "HSV (RGB)" | "Okhsl" | "Okhsv" | "Oklch (Oklab)" | "LCh (CIELab)"; + lightness_only?: boolean; /** - * Adaptive Gamut - * @description Adaptive gamut clipping (0=off). Higher prioritizes chroma over lightness - * @default 0 + * Threshold Upper + * @description Threshold above which will be set to full value + * @default 0.5 */ - adaptive_gamut?: number; + threshold_upper?: number; /** - * High Precision - * @description Use more steps in computing gamut when possible - * @default true + * Threshold Lower + * @description Threshold below which will be set to minimum value + * @default 0.5 */ - high_precision?: boolean; + threshold_lower?: number; /** * type - * @default invokeai_img_blend + * @default invokeai_img_val_thresholds * @constant */ - type: "invokeai_img_blend"; + type: "invokeai_img_val_thresholds"; }; /** - * Image Compositor - * @description Removes backdrop from subject image then overlays subject on background image. Originally created by @dwringer + * ItemIdsResult + * @description Response containing ordered item ids with metadata for optimistic updates. */ - InvokeImageCompositorInvocation: { + ItemIdsResult: { /** - * @description The board to save the image to - * @default null + * Item Ids + * @description Ordered list of item ids */ - board?: components["schemas"]["BoardField"] | null; + item_ids: number[]; /** - * @description Optional metadata to be saved with the image - * @default null + * Total Count + * @description Total number of queue items matching the query */ - metadata?: components["schemas"]["MetadataField"] | null; + total_count: number; + }; + /** + * IterateInvocation + * @description Iterates over a list of items + */ + IterateInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -14593,63 +14214,62 @@ export type components = { */ use_cache?: boolean; /** - * @description Image of the subject on a plain monochrome background - * @default null - */ - image_subject?: components["schemas"]["ImageField"] | null; - /** - * @description Image of a background scene - * @default null - */ - image_background?: components["schemas"]["ImageField"] | null; - /** - * Chroma Key - * @description Can be empty for corner flood select, or CSS-3 color or tuple - * @default + * Collection + * @description The list of items to iterate over + * @default [] */ - chroma_key?: string; + collection?: unknown[]; /** - * Threshold - * @description Subject isolation flood-fill threshold - * @default 50 + * Index + * @description The index, will be provided on executed iterators + * @default 0 */ - threshold?: number; + index?: number; /** - * Fill X - * @description Scale base subject image to fit background width - * @default false + * type + * @default iterate + * @constant */ - fill_x?: boolean; + type: "iterate"; + }; + /** + * IterateInvocationOutput + * @description Used to connect iteration outputs. Will be expanded to a specific output. + */ + IterateInvocationOutput: { /** - * Fill Y - * @description Scale base subject image to fit background height - * @default true + * Collection Item + * @description The item being iterated over */ - fill_y?: boolean; + item: unknown; /** - * X Offset - * @description x-offset for the subject - * @default 0 + * Index + * @description The index of the item */ - x_offset?: number; + index: number; /** - * Y Offset - * @description y-offset for the subject - * @default 0 + * Total + * @description The total number of items */ - y_offset?: number; + total: number; /** * type - * @default invokeai_img_composite + * @default iterate_output * @constant */ - type: "invokeai_img_composite"; + type: "iterate_output"; }; + JsonValue: unknown; /** - * Image Dilate or Erode - * @description Dilate (expand) or erode (contract) an image. Originally created by @dwringer + * LaMa Infill + * @description Infills transparent areas of an image using the LaMa model */ - InvokeImageDilateOrErodeInvocation: { + LaMaInfillInvocation: { + /** + * @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 @@ -14673,57 +14293,91 @@ export type components = { */ use_cache?: boolean; /** - * @description The image from which to create a mask + * @description The image to process * @default null */ image?: components["schemas"]["ImageField"] | null; /** - * Lightness Only - * @description If true, only applies to image lightness (CIELa*b*) + * type + * @default infill_lama + * @constant + */ + type: "infill_lama"; + }; + /** + * Latents Collection Primitive + * @description A collection of latents tensor primitive values + */ + LatentsCollectionInvocation: { + /** + * 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 */ - lightness_only?: boolean; + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; /** - * Radius W - * @description Width (in pixels) by which to dilate(expand) or erode (contract) the image - * @default 4 + * Collection + * @description The collection of latents tensors + * @default null */ - radius_w?: number; + collection?: components["schemas"]["LatentsField"][] | null; /** - * Radius H - * @description Height (in pixels) by which to dilate(expand) or erode (contract) the image - * @default 4 + * type + * @default latents_collection + * @constant */ - radius_h?: number; + type: "latents_collection"; + }; + /** + * LatentsCollectionOutput + * @description Base class for nodes that output a collection of latents tensors + */ + LatentsCollectionOutput: { /** - * Mode - * @description How to operate on the image - * @default Dilate - * @enum {string} + * Collection + * @description Latents tensor */ - mode?: "Dilate" | "Erode"; + collection: components["schemas"]["LatentsField"][]; /** * type - * @default invokeai_img_dilate_erode + * @default latents_collection_output * @constant */ - type: "invokeai_img_dilate_erode"; + type: "latents_collection_output"; }; /** - * Enhance Image - * @description Applies processing from PIL's ImageEnhance module. Originally created by @dwringer + * LatentsField + * @description A latents tensor primitive field */ - InvokeImageEnhanceInvocation: { + LatentsField: { /** - * @description The board to save the image to - * @default null + * Latents Name + * @description The name of the latents */ - board?: components["schemas"]["BoardField"] | null; + latents_name: string; /** - * @description Optional metadata to be saved with the image + * Seed + * @description Seed used to generate this latents * @default null */ - metadata?: components["schemas"]["MetadataField"] | null; + seed?: number | null; + }; + /** + * Latents Primitive + * @description A latents tensor primitive value + */ + LatentsInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -14742,52 +14396,72 @@ export type components = { */ use_cache?: boolean; /** - * @description The image for which to apply processing + * @description The latents tensor * @default null */ - image?: components["schemas"]["ImageField"] | null; + latents?: components["schemas"]["LatentsField"] | null; /** - * Invert - * @description Whether to invert the image colors - * @default false + * type + * @default latents + * @constant */ - invert?: boolean; + type: "latents"; + }; + /** + * LatentsMetaOutput + * @description Latents + metadata + */ + LatentsMetaOutput: { + /** @description Metadata Dict */ + metadata: components["schemas"]["MetadataField"]; /** - * Color - * @description Color enhancement factor - * @default 1 + * type + * @default latents_meta_output + * @constant */ - color?: number; + type: "latents_meta_output"; + /** @description Latents tensor */ + latents: components["schemas"]["LatentsField"]; /** - * Contrast - * @description Contrast enhancement factor - * @default 1 + * Width + * @description Width of output (px) */ - contrast?: number; + width: number; /** - * Brightness - * @description Brightness enhancement factor - * @default 1 + * Height + * @description Height of output (px) */ - brightness?: number; + height: number; + }; + /** + * LatentsOutput + * @description Base class for nodes that output a single latents tensor + */ + LatentsOutput: { + /** @description Latents tensor */ + latents: components["schemas"]["LatentsField"]; /** - * Sharpness - * @description Sharpness enhancement factor - * @default 1 + * Width + * @description Width of output (px) */ - sharpness?: number; + width: number; + /** + * Height + * @description Height of output (px) + */ + height: number; /** * type - * @default invokeai_img_enhance + * @default latents_output * @constant */ - type: "invokeai_img_enhance"; + type: "latents_output"; }; /** - * Image Value Thresholds - * @description Clip image to pure black/white past specified thresholds. Originally created by @dwringer + * Latents to Image - SD1.5, SDXL + * @description Generates an image from latents. */ - InvokeImageValueThresholdsInvocation: { + LatentsToImageInvocation: { /** * @description The board to save the image to * @default null @@ -14816,68 +14490,55 @@ export type components = { */ use_cache?: boolean; /** - * @description The image from which to create a mask + * @description Latents tensor * @default null */ - image?: components["schemas"]["ImageField"] | null; - /** - * Invert Output - * @description Make light areas dark and vice versa - * @default false - */ - invert_output?: boolean; + latents?: components["schemas"]["LatentsField"] | null; /** - * Renormalize Values - * @description Rescale remaining values from minimum to maximum - * @default false + * @description VAE + * @default null */ - renormalize_values?: boolean; + vae?: components["schemas"]["VAEField"] | null; /** - * Lightness Only - * @description If true, only applies to image lightness (CIELa*b*) + * Tiled + * @description Processing using overlapping tiles (reduce memory consumption) * @default false */ - lightness_only?: boolean; + tiled?: boolean; /** - * Threshold Upper - * @description Threshold above which will be set to full value - * @default 0.5 + * Tile Size + * @description The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the model will be used. Larger tile sizes generally produce better results at the cost of higher memory usage. + * @default 0 */ - threshold_upper?: number; + tile_size?: number; /** - * Threshold Lower - * @description Threshold below which will be set to minimum value - * @default 0.5 + * Fp32 + * @description Whether or not to use full float32 precision + * @default false */ - threshold_lower?: number; + fp32?: boolean; /** * type - * @default invokeai_img_val_thresholds + * @default l2i * @constant */ - type: "invokeai_img_val_thresholds"; + type: "l2i"; }; /** - * ItemIdsResult - * @description Response containing ordered item ids with metadata for optimistic updates. + * Lineart Anime Edge Detection + * @description Geneartes an edge map using the Lineart model. */ - ItemIdsResult: { + LineartAnimeEdgeDetectionInvocation: { /** - * Item Ids - * @description Ordered list of item ids + * @description The board to save the image to + * @default null */ - item_ids: number[]; + board?: components["schemas"]["BoardField"] | null; /** - * Total Count - * @description Total number of queue items matching the query + * @description Optional metadata to be saved with the image + * @default null */ - total_count: number; - }; - /** - * IterateInvocation - * @description Iterates over a list of items - */ - IterateInvocation: { + metadata?: components["schemas"]["MetadataField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -14896,57 +14557,22 @@ export type components = { */ use_cache?: boolean; /** - * Collection - * @description The list of items to iterate over - * @default [] - */ - collection?: unknown[]; - /** - * Index - * @description The index, will be provided on executed iterators - * @default 0 - */ - index?: number; - /** - * type - * @default iterate - * @constant - */ - type: "iterate"; - }; - /** - * IterateInvocationOutput - * @description Used to connect iteration outputs. Will be expanded to a specific output. - */ - IterateInvocationOutput: { - /** - * Collection Item - * @description The item being iterated over - */ - item: unknown; - /** - * Index - * @description The index of the item - */ - index: number; - /** - * Total - * @description The total number of items + * @description The image to process + * @default null */ - total: number; + image?: components["schemas"]["ImageField"] | null; /** * type - * @default iterate_output + * @default lineart_anime_edge_detection * @constant */ - type: "iterate_output"; + type: "lineart_anime_edge_detection"; }; - JsonValue: unknown; /** - * LaMa Infill - * @description Infills transparent areas of an image using the LaMa model + * Lineart Edge Detection + * @description Generates an edge map using the Lineart model. */ - LaMaInfillInvocation: { + LineartEdgeDetectionInvocation: { /** * @description The board to save the image to * @default null @@ -14979,18 +14605,24 @@ export type components = { * @default null */ image?: components["schemas"]["ImageField"] | null; + /** + * Coarse + * @description Whether to use coarse mode + * @default false + */ + coarse?: boolean; /** * type - * @default infill_lama + * @default lineart_edge_detection * @constant */ - type: "infill_lama"; + type: "lineart_edge_detection"; }; /** - * Latents Collection Primitive - * @description A collection of latents tensor primitive values + * LLaVA OneVision VLLM + * @description Run a LLaVA OneVision VLLM model. */ - LatentsCollectionInvocation: { + LlavaOnevisionVllmInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -15009,57 +14641,113 @@ export type components = { */ use_cache?: boolean; /** - * Collection - * @description The collection of latents tensors + * Images + * @description Input image. * @default null */ - collection?: components["schemas"]["LatentsField"][] | null; + images?: (components["schemas"]["ImageField"][] | components["schemas"]["ImageField"]) | null; + /** + * Prompt + * @description Input text prompt. + * @default + */ + prompt?: string; + /** + * LLaVA Model Type + * @description The VLLM model to use + * @default null + */ + vllm_model?: components["schemas"]["ModelIdentifierField"] | null; /** * type - * @default latents_collection + * @default llava_onevision_vllm * @constant */ - type: "latents_collection"; + type: "llava_onevision_vllm"; }; /** - * LatentsCollectionOutput - * @description Base class for nodes that output a collection of latents tensors + * LlavaOnevision_Diffusers_Config + * @description Model config for Llava Onevision models. */ - LatentsCollectionOutput: { + LlavaOnevision_Diffusers_Config: { /** - * Collection - * @description Latents tensor + * Key + * @description A unique key for this model. */ - collection: components["schemas"]["LatentsField"][]; + key: string; /** - * type - * @default latents_collection_output + * 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; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Usage Info + * @description Usage information for this model + */ + usage_info: string | null; + /** + * Format + * @default diffusers * @constant */ - type: "latents_collection_output"; - }; - /** - * LatentsField - * @description A latents tensor primitive field - */ - LatentsField: { + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"]; /** - * Latents Name - * @description The name of the latents + * Type + * @default llava_onevision + * @constant */ - latents_name: string; + type: "llava_onevision"; /** - * Seed - * @description Seed used to generate this latents - * @default null + * Base + * @default any + * @constant */ - seed?: number | null; + base: "any"; }; /** - * Latents Primitive - * @description A latents tensor primitive value + * Apply LoRA Collection - SD1.5 + * @description Applies a collection of LoRAs to the provided UNet and CLIP models. */ - LatentsInvocation: { + LoRACollectionLoader: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -15078,82 +14766,45 @@ export type components = { */ use_cache?: boolean; /** - * @description The latents tensor + * LoRAs + * @description LoRA models and weights. May be a single LoRA or collection. * @default null */ - latents?: components["schemas"]["LatentsField"] | null; - /** - * type - * @default latents - * @constant - */ - type: "latents"; - }; - /** - * LatentsMetaOutput - * @description Latents + metadata - */ - LatentsMetaOutput: { - /** @description Metadata Dict */ - metadata: components["schemas"]["MetadataField"]; + loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null; /** - * type - * @default latents_meta_output - * @constant + * UNet + * @description UNet (scheduler, LoRAs) + * @default null */ - type: "latents_meta_output"; - /** @description Latents tensor */ - latents: components["schemas"]["LatentsField"]; + unet?: components["schemas"]["UNetField"] | null; /** - * Width - * @description Width of output (px) + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null */ - width: number; + clip?: components["schemas"]["CLIPField"] | null; /** - * Height - * @description Height of output (px) + * type + * @default lora_collection_loader + * @constant */ - height: number; + type: "lora_collection_loader"; }; - /** - * LatentsOutput - * @description Base class for nodes that output a single latents tensor - */ - LatentsOutput: { - /** @description Latents tensor */ - latents: components["schemas"]["LatentsField"]; - /** - * Width - * @description Width of output (px) - */ - width: number; - /** - * Height - * @description Height of output (px) - */ - height: number; + /** LoRAField */ + LoRAField: { + /** @description Info to load lora model */ + lora: components["schemas"]["ModelIdentifierField"]; /** - * type - * @default latents_output - * @constant + * Weight + * @description Weight to apply to lora model */ - type: "latents_output"; + weight: number; }; /** - * Latents to Image - SD1.5, SDXL - * @description Generates an image from latents. + * Apply LoRA - SD1.5 + * @description Apply selected lora to unet and text_encoder. */ - LatentsToImageInvocation: { - /** - * @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; + LoRALoaderInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -15172,55 +14823,78 @@ export type components = { */ use_cache?: boolean; /** - * @description Latents tensor + * LoRA + * @description LoRA model to load * @default null */ - latents?: components["schemas"]["LatentsField"] | null; + lora?: components["schemas"]["ModelIdentifierField"] | null; /** - * @description VAE + * Weight + * @description The weight at which the LoRA is applied to each model + * @default 0.75 + */ + weight?: number; + /** + * UNet + * @description UNet (scheduler, LoRAs) * @default null */ - vae?: components["schemas"]["VAEField"] | null; + unet?: components["schemas"]["UNetField"] | null; /** - * Tiled - * @description Processing using overlapping tiles (reduce memory consumption) - * @default false + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null */ - tiled?: boolean; + clip?: components["schemas"]["CLIPField"] | null; /** - * Tile Size - * @description The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the model will be used. Larger tile sizes generally produce better results at the cost of higher memory usage. - * @default 0 + * type + * @default lora_loader + * @constant */ - tile_size?: number; + type: "lora_loader"; + }; + /** + * LoRALoaderOutput + * @description Model loader output + */ + LoRALoaderOutput: { + /** + * UNet + * @description UNet (scheduler, LoRAs) + * @default null + */ + unet: components["schemas"]["UNetField"] | null; /** - * Fp32 - * @description Whether or not to use full float32 precision - * @default false + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + * @default null */ - fp32?: boolean; + clip: components["schemas"]["CLIPField"] | null; /** * type - * @default l2i + * @default lora_loader_output * @constant */ - type: "l2i"; + type: "lora_loader_output"; }; /** - * Lineart Anime Edge Detection - * @description Geneartes an edge map using the Lineart model. + * LoRAMetadataField + * @description LoRA Metadata Field */ - LineartAnimeEdgeDetectionInvocation: { - /** - * @description The board to save the image to - * @default null - */ - board?: components["schemas"]["BoardField"] | null; + LoRAMetadataField: { + /** @description LoRA model to load */ + model: components["schemas"]["ModelIdentifierField"]; /** - * @description Optional metadata to be saved with the image - * @default null + * Weight + * @description The weight at which the LoRA is applied to each model */ - metadata?: components["schemas"]["MetadataField"] | null; + weight: number; + }; + /** + * Select LoRA + * @description Selects a LoRA model and weight. + */ + LoRASelectorInvocation: { /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -15239,119 +14913,123 @@ export type components = { */ use_cache?: boolean; /** - * @description The image to process + * LoRA + * @description LoRA model to load * @default null */ - image?: components["schemas"]["ImageField"] | null; + lora?: components["schemas"]["ModelIdentifierField"] | null; + /** + * Weight + * @description The weight at which the LoRA is applied to each model + * @default 0.75 + */ + weight?: number; /** * type - * @default lineart_anime_edge_detection + * @default lora_selector * @constant */ - type: "lineart_anime_edge_detection"; + type: "lora_selector"; }; /** - * Lineart Edge Detection - * @description Generates an edge map using the Lineart model. + * LoRASelectorOutput + * @description Model loader output */ - LineartEdgeDetectionInvocation: { + LoRASelectorOutput: { /** - * @description The board to save the image to - * @default null + * LoRA + * @description LoRA model and weight */ - board?: components["schemas"]["BoardField"] | null; + lora: components["schemas"]["LoRAField"]; /** - * @description Optional metadata to be saved with the image - * @default null + * type + * @default lora_selector_output + * @constant */ - metadata?: components["schemas"]["MetadataField"] | null; + type: "lora_selector_output"; + }; + /** LoRA_Diffusers_FLUX_Config */ + LoRA_Diffusers_FLUX_Config: { /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Key + * @description A unique key for this model. */ - id: string; + key: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Hash + * @description The hash of the model file(s). */ - is_intermediate?: boolean; + hash: string; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - use_cache?: boolean; + path: string; /** - * @description The image to process - * @default null + * File Size + * @description The size of the model in bytes. */ - image?: components["schemas"]["ImageField"] | null; + file_size: number; /** - * Coarse - * @description Whether to use coarse mode - * @default false + * Name + * @description Name of the model. */ - coarse?: boolean; + name: string; /** - * type - * @default lineart_edge_detection - * @constant + * Description + * @description Model description */ - type: "lineart_edge_detection"; - }; - /** - * LLaVA OneVision VLLM - * @description Run a LLaVA OneVision VLLM model. - */ - LlavaOnevisionVllmInvocation: { + description: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Source + * @description The original source of the model (path, URL or repo_id). */ - id: string; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - is_intermediate?: boolean; + source_api_response: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Cover Image + * @description Url for image to preview model */ - use_cache?: boolean; + cover_image: string | null; /** - * Images - * @description Input image. - * @default null + * Usage Info + * @description Usage information for this model */ - images?: (components["schemas"]["ImageField"][] | components["schemas"]["ImageField"]) | null; + usage_info: string | null; /** - * Prompt - * @description Input text prompt. - * @default + * Type + * @default lora + * @constant */ - prompt?: string; + type: "lora"; /** - * LLaVA Model Type - * @description The VLLM model to use - * @default null + * Trigger Phrases + * @description Set of trigger phrases for this model */ - vllm_model?: components["schemas"]["ModelIdentifierField"] | null; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** - * type - * @default llava_onevision_vllm + * Format + * @default diffusers * @constant */ - type: "llava_onevision_vllm"; + format: "diffusers"; + /** + * Base + * @default flux + * @constant + */ + base: "flux"; }; - /** - * LlavaOnevision_Diffusers_Config - * @description Model config for Llava Onevision models. - */ - LlavaOnevision_Diffusers_Config: { + /** LoRA_Diffusers_SD1_Config */ + LoRA_Diffusers_SD1_Config: { /** * Key * @description A unique key for this model. @@ -15399,252 +15077,199 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model */ usage_info: string | null; /** - * Format - * @default diffusers + * Type + * @default lora * @constant */ - format: "diffusers"; - /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + type: "lora"; /** - * Type - * @default llava_onevision - * @constant + * Trigger Phrases + * @description Set of trigger phrases for this model */ - type: "llava_onevision"; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** - * Base - * @default any + * Format + * @default diffusers * @constant */ - base: "any"; + format: "diffusers"; /** - * Variant - * @default normal + * Base + * @default sd-1 * @constant */ - variant: "normal"; + base: "sd-1"; }; - /** - * Apply LoRA Collection - SD1.5 - * @description Applies a collection of LoRAs to the provided UNet and CLIP models. - */ - LoRACollectionLoader: { - /** - * 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; + /** LoRA_Diffusers_SD2_Config */ + LoRA_Diffusers_SD2_Config: { /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Key + * @description A unique key for this model. */ - use_cache?: boolean; + key: string; /** - * LoRAs - * @description LoRA models and weights. May be a single LoRA or collection. - * @default null + * Hash + * @description The hash of the model file(s). */ - loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null; + hash: string; /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - unet?: components["schemas"]["UNetField"] | null; + path: string; /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * File Size + * @description The size of the model in bytes. */ - clip?: components["schemas"]["CLIPField"] | null; + file_size: number; /** - * type - * @default lora_collection_loader - * @constant + * Name + * @description Name of the model. */ - type: "lora_collection_loader"; - }; - /** LoRAField */ - LoRAField: { - /** @description Info to load lora model */ - lora: components["schemas"]["ModelIdentifierField"]; + name: string; /** - * Weight - * @description Weight to apply to lora model + * Description + * @description Model description */ - weight: number; - }; - /** - * Apply LoRA - SD1.5 - * @description Apply selected lora to unet and text_encoder. - */ - LoRALoaderInvocation: { + description: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Source + * @description The original source of the model (path, URL or repo_id). */ - id: string; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - is_intermediate?: boolean; + source_api_response: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Cover Image + * @description Url for image to preview model */ - use_cache?: boolean; + cover_image: string | null; /** - * LoRA - * @description LoRA model to load - * @default null + * Usage Info + * @description Usage information for this model */ - lora?: components["schemas"]["ModelIdentifierField"] | null; + usage_info: string | null; /** - * Weight - * @description The weight at which the LoRA is applied to each model - * @default 0.75 + * Type + * @default lora + * @constant */ - weight?: number; + type: "lora"; /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null + * Trigger Phrases + * @description Set of trigger phrases for this model */ - unet?: components["schemas"]["UNetField"] | null; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * Format + * @default diffusers + * @constant */ - clip?: components["schemas"]["CLIPField"] | null; + format: "diffusers"; /** - * type - * @default lora_loader + * Base + * @default sd-2 * @constant */ - type: "lora_loader"; + base: "sd-2"; }; - /** - * LoRALoaderOutput - * @description Model loader output - */ - LoRALoaderOutput: { + /** LoRA_Diffusers_SDXL_Config */ + LoRA_Diffusers_SDXL_Config: { /** - * UNet - * @description UNet (scheduler, LoRAs) - * @default null + * Key + * @description A unique key for this model. */ - unet: components["schemas"]["UNetField"] | null; + key: string; /** - * CLIP - * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count - * @default null + * Hash + * @description The hash of the model file(s). */ - clip: components["schemas"]["CLIPField"] | null; + hash: string; /** - * type - * @default lora_loader_output - * @constant + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - type: "lora_loader_output"; - }; - /** - * LoRAMetadataField - * @description LoRA Metadata Field - */ - LoRAMetadataField: { - /** @description LoRA model to load */ - model: components["schemas"]["ModelIdentifierField"]; + path: string; /** - * Weight - * @description The weight at which the LoRA is applied to each model + * File Size + * @description The size of the model in bytes. */ - weight: number; - }; - /** - * Select LoRA - * @description Selects a LoRA model and weight. - */ - LoRASelectorInvocation: { + file_size: number; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Name + * @description Name of the model. */ - id: string; + name: string; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Description + * @description Model description */ - is_intermediate?: boolean; + description: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Source + * @description The original source of the model (path, URL or repo_id). */ - use_cache?: boolean; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * LoRA - * @description LoRA model to load - * @default null + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - lora?: components["schemas"]["ModelIdentifierField"] | null; + source_api_response: string | null; /** - * Weight - * @description The weight at which the LoRA is applied to each model - * @default 0.75 + * Cover Image + * @description Url for image to preview model */ - weight?: number; + cover_image: string | null; /** - * type - * @default lora_selector + * Usage Info + * @description Usage information for this model + */ + usage_info: string | null; + /** + * Type + * @default lora * @constant */ - type: "lora_selector"; - }; - /** - * LoRASelectorOutput - * @description Model loader output - */ - LoRASelectorOutput: { + type: "lora"; /** - * LoRA - * @description LoRA model and weight + * Trigger Phrases + * @description Set of trigger phrases for this model */ - lora: components["schemas"]["LoRAField"]; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** - * type - * @default lora_selector_output + * Format + * @default diffusers * @constant */ - type: "lora_selector_output"; + format: "diffusers"; + /** + * Base + * @default sdxl + * @constant + */ + base: "sdxl"; }; - /** LoRA_Diffusers_FLUX_Config */ - LoRA_Diffusers_FLUX_Config: { + /** LoRA_LyCORIS_FLUX_Config */ + LoRA_LyCORIS_FLUX_Config: { /** * Key * @description A unique key for this model. @@ -15692,13 +15317,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -15719,10 +15337,10 @@ export type components = { default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** * Format - * @default diffusers + * @default lycoris * @constant */ - format: "diffusers"; + format: "lycoris"; /** * Base * @default flux @@ -15730,8 +15348,8 @@ export type components = { */ base: "flux"; }; - /** LoRA_Diffusers_SD1_Config */ - LoRA_Diffusers_SD1_Config: { + /** LoRA_LyCORIS_SD1_Config */ + LoRA_LyCORIS_SD1_Config: { /** * Key * @description A unique key for this model. @@ -15779,13 +15397,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -15806,10 +15417,10 @@ export type components = { default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** * Format - * @default diffusers + * @default lycoris * @constant */ - format: "diffusers"; + format: "lycoris"; /** * Base * @default sd-1 @@ -15817,8 +15428,8 @@ export type components = { */ base: "sd-1"; }; - /** LoRA_Diffusers_SD2_Config */ - LoRA_Diffusers_SD2_Config: { + /** LoRA_LyCORIS_SD2_Config */ + LoRA_LyCORIS_SD2_Config: { /** * Key * @description A unique key for this model. @@ -15864,15 +15475,8 @@ export type components = { /** * Cover Image * @description Url for image to preview model - */ - cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + */ + cover_image: string | null; /** * Usage Info * @description Usage information for this model @@ -15893,10 +15497,10 @@ export type components = { default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** * Format - * @default diffusers + * @default lycoris * @constant */ - format: "diffusers"; + format: "lycoris"; /** * Base * @default sd-2 @@ -15904,8 +15508,8 @@ export type components = { */ base: "sd-2"; }; - /** LoRA_Diffusers_SDXL_Config */ - LoRA_Diffusers_SDXL_Config: { + /** LoRA_LyCORIS_SDXL_Config */ + LoRA_LyCORIS_SDXL_Config: { /** * Key * @description A unique key for this model. @@ -15953,13 +15557,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -15980,10 +15577,10 @@ export type components = { default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** * Format - * @default diffusers + * @default lycoris * @constant */ - format: "diffusers"; + format: "lycoris"; /** * Base * @default sdxl @@ -15991,8 +15588,8 @@ export type components = { */ base: "sdxl"; }; - /** LoRA_LyCORIS_FLUX_Config */ - LoRA_LyCORIS_FLUX_Config: { + /** LoRA_OMI_FLUX_Config */ + LoRA_OMI_FLUX_Config: { /** * Key * @description A unique key for this model. @@ -16040,13 +15637,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -16067,10 +15657,10 @@ export type components = { default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** * Format - * @default lycoris + * @default omi * @constant */ - format: "lycoris"; + format: "omi"; /** * Base * @default flux @@ -16078,8 +15668,8 @@ export type components = { */ base: "flux"; }; - /** LoRA_LyCORIS_SD1_Config */ - LoRA_LyCORIS_SD1_Config: { + /** LoRA_OMI_SDXL_Config */ + LoRA_OMI_SDXL_Config: { /** * Key * @description A unique key for this model. @@ -16127,13 +15717,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -16153,20 +15736,234 @@ export type components = { /** @description Default settings for this model */ default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; /** - * Format - * @default lycoris - * @constant + * Format + * @default omi + * @constant + */ + format: "omi"; + /** + * Base + * @default sdxl + * @constant + */ + base: "sdxl"; + }; + /** + * LocalModelSource + * @description A local file or directory path. + */ + LocalModelSource: { + /** Path */ + path: string; + /** + * Inplace + * @default false + */ + inplace?: boolean | null; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "local"; + }; + /** + * LogLevel + * @enum {integer} + */ + LogLevel: 0 | 10 | 20 | 30 | 40 | 50; + /** LoraModelDefaultSettings */ + LoraModelDefaultSettings: { + /** + * Weight + * @description Default weight for this model + */ + weight?: number | null; + }; + /** MDControlListOutput */ + MDControlListOutput: { + /** + * ControlNet-List + * @description ControlNet(s) to apply + */ + control_list: components["schemas"]["ControlField"] | components["schemas"]["ControlField"][] | null; + /** + * type + * @default md_control_list_output + * @constant + */ + type: "md_control_list_output"; + }; + /** MDIPAdapterListOutput */ + MDIPAdapterListOutput: { + /** + * IP-Adapter-List + * @description IP-Adapter to apply + */ + ip_adapter_list: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; + /** + * type + * @default md_ip_adapter_list_output + * @constant + */ + type: "md_ip_adapter_list_output"; + }; + /** MDT2IAdapterListOutput */ + MDT2IAdapterListOutput: { + /** + * T2I Adapter-List + * @description T2I-Adapter(s) to apply + */ + t2i_adapter_list: components["schemas"]["T2IAdapterField"] | components["schemas"]["T2IAdapterField"][] | null; + /** + * type + * @default md_ip_adapters_output + * @constant + */ + type: "md_ip_adapters_output"; + }; + /** + * MLSD Detection + * @description Generates an line segment map using MLSD. + */ + MLSDDetectionInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to process + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Score Threshold + * @description The threshold used to score points when determining line segments + * @default 0.1 + */ + score_threshold?: number; + /** + * Distance Threshold + * @description Threshold for including a line segment - lines shorter than this distance will be discarded + * @default 20 + */ + distance_threshold?: number; + /** + * type + * @default mlsd_detection + * @constant + */ + type: "mlsd_detection"; + }; + /** MainModelDefaultSettings */ + MainModelDefaultSettings: { + /** + * Vae + * @description Default VAE for this model (model key) + */ + vae?: string | null; + /** + * Vae Precision + * @description Default VAE precision for this model + */ + vae_precision?: ("fp16" | "fp32") | null; + /** + * Scheduler + * @description Default scheduler for this model + */ + scheduler?: ("ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd") | null; + /** + * Steps + * @description Default number of steps for this model + */ + steps?: number | null; + /** + * Cfg Scale + * @description Default CFG Scale for this model + */ + cfg_scale?: number | null; + /** + * Cfg Rescale Multiplier + * @description Default CFG Rescale Multiplier for this model + */ + cfg_rescale_multiplier?: number | null; + /** + * Width + * @description Default width for this model + */ + width?: number | null; + /** + * Height + * @description Default height for this model + */ + height?: number | null; + /** + * Guidance + * @description Default Guidance for this model + */ + guidance?: number | null; + }; + /** + * Main Model - SD1.5, SD2 + * @description Loads a main model, outputting its submodels. + */ + MainModelLoaderInvocation: { + /** + * 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 Main model (UNet, VAE, CLIP) to load + * @default null */ - format: "lycoris"; + model?: components["schemas"]["ModelIdentifierField"] | null; /** - * Base - * @default sd-1 + * type + * @default main_model_loader * @constant */ - base: "sd-1"; + type: "main_model_loader"; }; - /** LoRA_LyCORIS_SD2_Config */ - LoRA_LyCORIS_SD2_Config: { + /** + * Main_BnBNF4_FLUX_Config + * @description Model config for main checkpoint models. + */ + Main_BnBNF4_FLUX_Config: { /** * Key * @description A unique key for this model. @@ -16214,13 +16011,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -16228,32 +16018,41 @@ export type components = { usage_info: string | null; /** * Type - * @default lora + * @default main * @constant */ - type: "lora"; + 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"]["LoraModelDefaultSettings"] | null; + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * Format - * @default lycoris - * @constant + * Config Path + * @description Path to the config for this model, if any. */ - format: "lycoris"; + config_path: string | null; /** * Base - * @default sd-2 + * @default flux * @constant */ - base: "sd-2"; + base: "flux"; + /** + * Format + * @default bnb_quantized_nf4b + * @constant + */ + format: "bnb_quantized_nf4b"; + variant: components["schemas"]["FluxVariantType"]; }; - /** LoRA_LyCORIS_SDXL_Config */ - LoRA_LyCORIS_SDXL_Config: { + /** + * Main_Checkpoint_FLUX_Config + * @description Model config for main checkpoint models. + */ + Main_Checkpoint_FLUX_Config: { /** * Key * @description A unique key for this model. @@ -16301,13 +16100,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -16315,32 +16107,38 @@ export type components = { usage_info: string | null; /** * Type - * @default lora + * @default main * @constant */ - type: "lora"; + 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"]["LoraModelDefaultSettings"] | null; + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; + /** + * Config Path + * @description Path to the config for this model, if any. + */ + config_path: string | null; /** * Format - * @default lycoris + * @default checkpoint * @constant */ - format: "lycoris"; + format: "checkpoint"; /** * Base - * @default sdxl + * @default flux * @constant */ - base: "sdxl"; + base: "flux"; + variant: components["schemas"]["FluxVariantType"]; }; - /** LoRA_OMI_FLUX_Config */ - LoRA_OMI_FLUX_Config: { + /** Main_Checkpoint_SD1_Config */ + Main_Checkpoint_SD1_Config: { /** * Key * @description A unique key for this model. @@ -16388,13 +16186,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -16402,32 +16193,39 @@ export type components = { usage_info: string | null; /** * Type - * @default lora + * @default main * @constant */ - type: "lora"; + 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"]["LoraModelDefaultSettings"] | null; + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; + /** + * Config Path + * @description Path to the config for this model, if any. + */ + config_path: string | null; /** * Format - * @default omi + * @default checkpoint * @constant */ - format: "omi"; + format: "checkpoint"; + prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; /** * Base - * @default flux + * @default sd-1 * @constant */ - base: "flux"; + base: "sd-1"; }; - /** LoRA_OMI_SDXL_Config */ - LoRA_OMI_SDXL_Config: { + /** Main_Checkpoint_SD2_Config */ + Main_Checkpoint_SD2_Config: { /** * Key * @description A unique key for this model. @@ -16464,271 +16262,144 @@ export type components = { */ 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; - /** - * Cover Image - * @description Url for image to preview model - */ - cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; - /** - * Usage Info - * @description Usage information for this model - */ - usage_info: 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 omi - * @constant - */ - format: "omi"; - /** - * Base - * @default sdxl - * @constant - */ - base: "sdxl"; - }; - /** - * LocalModelSource - * @description A local file or directory path. - */ - LocalModelSource: { - /** Path */ - path: string; - /** - * Inplace - * @default false - */ - inplace?: boolean | null; - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - type: "local"; - }; - /** - * LogLevel - * @enum {integer} - */ - LogLevel: 0 | 10 | 20 | 30 | 40 | 50; - /** LoraModelDefaultSettings */ - LoraModelDefaultSettings: { - /** - * Weight - * @description Default weight for this model - */ - weight?: number | null; - }; - /** MDControlListOutput */ - MDControlListOutput: { - /** - * ControlNet-List - * @description ControlNet(s) to apply - */ - control_list: components["schemas"]["ControlField"] | components["schemas"]["ControlField"][] | null; - /** - * type - * @default md_control_list_output - * @constant - */ - type: "md_control_list_output"; - }; - /** MDIPAdapterListOutput */ - MDIPAdapterListOutput: { - /** - * IP-Adapter-List - * @description IP-Adapter to apply - */ - ip_adapter_list: components["schemas"]["IPAdapterField"] | components["schemas"]["IPAdapterField"][] | null; - /** - * type - * @default md_ip_adapter_list_output - * @constant - */ - type: "md_ip_adapter_list_output"; - }; - /** MDT2IAdapterListOutput */ - MDT2IAdapterListOutput: { - /** - * T2I Adapter-List - * @description T2I-Adapter(s) to apply - */ - t2i_adapter_list: components["schemas"]["T2IAdapterField"] | components["schemas"]["T2IAdapterField"][] | null; - /** - * type - * @default md_ip_adapters_output - * @constant - */ - type: "md_ip_adapters_output"; - }; - /** - * MLSD Detection - * @description Generates an line segment map using MLSD. - */ - MLSDDetectionInvocation: { - /** - * @description The board to save the image to - * @default null - */ - board?: components["schemas"]["BoardField"] | null; + source_type: components["schemas"]["ModelSourceType"]; /** - * @description Optional metadata to be saved with the image - * @default null + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - metadata?: components["schemas"]["MetadataField"] | null; + source_api_response: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Cover Image + * @description Url for image to preview model */ - id: string; + cover_image: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Usage Info + * @description Usage information for this model */ - is_intermediate?: boolean; + usage_info: string | null; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Type + * @default main + * @constant */ - use_cache?: boolean; + type: "main"; /** - * @description The image to process - * @default null + * Trigger Phrases + * @description Set of trigger phrases for this model */ - image?: components["schemas"]["ImageField"] | null; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * Score Threshold - * @description The threshold used to score points when determining line segments - * @default 0.1 + * Config Path + * @description Path to the config for this model, if any. */ - score_threshold?: number; + config_path: string | null; /** - * Distance Threshold - * @description Threshold for including a line segment - lines shorter than this distance will be discarded - * @default 20 + * Format + * @default checkpoint + * @constant */ - distance_threshold?: number; + format: "checkpoint"; + prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; /** - * type - * @default mlsd_detection + * Base + * @default sd-2 * @constant */ - type: "mlsd_detection"; + base: "sd-2"; }; - /** MainModelDefaultSettings */ - MainModelDefaultSettings: { + /** Main_Checkpoint_SDXLRefiner_Config */ + Main_Checkpoint_SDXLRefiner_Config: { /** - * Vae - * @description Default VAE for this model (model key) + * Key + * @description A unique key for this model. */ - vae?: string | null; + key: string; /** - * Vae Precision - * @description Default VAE precision for this model + * Hash + * @description The hash of the model file(s). */ - vae_precision?: ("fp16" | "fp32") | null; + hash: string; /** - * Scheduler - * @description Default scheduler for this model + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. */ - scheduler?: ("ddim" | "ddpm" | "deis" | "deis_k" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_k" | "kdpm_2_a" | "kdpm_2_a_k" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_3m" | "dpmpp_3m_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc" | "unipc_k" | "lcm" | "tcd") | null; + path: string; /** - * Steps - * @description Default number of steps for this model + * File Size + * @description The size of the model in bytes. */ - steps?: number | null; + file_size: number; /** - * Cfg Scale - * @description Default CFG Scale for this model + * Name + * @description Name of the model. */ - cfg_scale?: number | null; + name: string; /** - * Cfg Rescale Multiplier - * @description Default CFG Rescale Multiplier for this model + * Description + * @description Model description */ - cfg_rescale_multiplier?: number | null; + description: string | null; /** - * Width - * @description Default width for this model + * Source + * @description The original source of the model (path, URL or repo_id). */ - width?: number | null; + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; /** - * Height - * @description Default height for this model + * Source Api Response + * @description The original API response from the source, as stringified JSON. */ - height?: number | null; + source_api_response: string | null; /** - * Guidance - * @description Default Guidance for this model + * Cover Image + * @description Url for image to preview model */ - guidance?: number | null; - }; - /** - * Main Model - SD1.5, SD2 - * @description Loads a main model, outputting its submodels. - */ - MainModelLoaderInvocation: { + cover_image: string | null; /** - * Id - * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + * Usage Info + * @description Usage information for this model */ - id: string; + usage_info: string | null; /** - * Is Intermediate - * @description Whether or not this is an intermediate invocation. - * @default false + * Type + * @default main + * @constant */ - is_intermediate?: boolean; + type: "main"; /** - * Use Cache - * @description Whether or not to use the cache - * @default true + * Trigger Phrases + * @description Set of trigger phrases for this model */ - use_cache?: boolean; + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** - * @description Main model (UNet, VAE, CLIP) to load - * @default null + * Config Path + * @description Path to the config for this model, if any. */ - model?: components["schemas"]["ModelIdentifierField"] | null; + config_path: string | null; /** - * type - * @default main_model_loader + * Format + * @default checkpoint * @constant */ - type: "main_model_loader"; + format: "checkpoint"; + prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; + /** + * Base + * @default sdxl-refiner + * @constant + */ + base: "sdxl-refiner"; }; - /** - * Main_BnBNF4_FLUX_Config - * @description Model config for main checkpoint models. - */ - Main_BnBNF4_FLUX_Config: { + /** Main_Checkpoint_SDXL_Config */ + Main_Checkpoint_SDXL_Config: { /** * Key * @description A unique key for this model. @@ -16776,13 +16447,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -16807,29 +16471,22 @@ export type components = { */ config_path: string | null; /** - * Converted At - * @description When this model was last converted to diffusers - */ - converted_at: number | null; - /** - * Base - * @default flux + * Format + * @default checkpoint * @constant */ - base: "flux"; + format: "checkpoint"; + prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; /** - * Format - * @default bnb_quantized_nf4b + * Base + * @default sdxl * @constant */ - format: "bnb_quantized_nf4b"; - variant: components["schemas"]["FluxVariantType"]; + base: "sdxl"; }; - /** - * Main_Checkpoint_FLUX_Config - * @description Model config for main checkpoint models. - */ - Main_Checkpoint_FLUX_Config: { + /** Main_Diffusers_CogView4_Config */ + Main_Diffusers_CogView4_Config: { /** * Key * @description A unique key for this model. @@ -16877,13 +16534,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -16902,32 +16552,23 @@ export type components = { 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; - /** - * Converted At - * @description When this model was last converted to diffusers - */ - converted_at: number | null; /** * Format - * @default checkpoint + * @default diffusers * @constant */ - format: "checkpoint"; + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"]; /** * Base - * @default flux + * @default cogview4 * @constant */ - base: "flux"; - variant: components["schemas"]["FluxVariantType"]; + base: "cogview4"; }; - /** Main_Checkpoint_SD1_Config */ - Main_Checkpoint_SD1_Config: { + /** Main_Diffusers_SD1_Config */ + Main_Diffusers_SD1_Config: { /** * Key * @description A unique key for this model. @@ -16975,13 +16616,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -17000,22 +16634,14 @@ export type components = { 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; - /** - * Converted At - * @description When this model was last converted to diffusers - */ - converted_at: number | null; /** * Format - * @default checkpoint + * @default diffusers * @constant */ - format: "checkpoint"; + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"]; prediction_type: components["schemas"]["SchedulerPredictionType"]; variant: components["schemas"]["ModelVariantType"]; /** @@ -17025,8 +16651,8 @@ export type components = { */ base: "sd-1"; }; - /** Main_Checkpoint_SD2_Config */ - Main_Checkpoint_SD2_Config: { + /** Main_Diffusers_SD2_Config */ + Main_Diffusers_SD2_Config: { /** * Key * @description A unique key for this model. @@ -17074,13 +16700,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -17099,22 +16718,14 @@ export type components = { 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; - /** - * Converted At - * @description When this model was last converted to diffusers - */ - converted_at: number | null; /** * Format - * @default checkpoint + * @default diffusers * @constant */ - format: "checkpoint"; + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"]; prediction_type: components["schemas"]["SchedulerPredictionType"]; variant: components["schemas"]["ModelVariantType"]; /** @@ -17124,8 +16735,8 @@ export type components = { */ base: "sd-2"; }; - /** Main_Checkpoint_SDXLRefiner_Config */ - Main_Checkpoint_SDXLRefiner_Config: { + /** Main_Diffusers_SD3_Config */ + Main_Diffusers_SD3_Config: { /** * Key * @description A unique key for this model. @@ -17173,13 +16784,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -17198,33 +16802,30 @@ export type components = { 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; - /** - * Converted At - * @description When this model was last converted to diffusers - */ - converted_at: number | null; /** * Format - * @default checkpoint + * @default diffusers * @constant */ - format: "checkpoint"; - prediction_type: components["schemas"]["SchedulerPredictionType"]; - variant: components["schemas"]["ModelVariantType"]; + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"]; /** * Base - * @default sdxl-refiner + * @default sd-3 * @constant */ - base: "sdxl-refiner"; + base: "sd-3"; + /** + * Submodels + * @description Loadable submodels in this model + */ + submodels: { + [key: string]: components["schemas"]["SubmodelDefinition"]; + } | null; }; - /** Main_Checkpoint_SDXL_Config */ - Main_Checkpoint_SDXL_Config: { + /** Main_Diffusers_SDXLRefiner_Config */ + Main_Diffusers_SDXLRefiner_Config: { /** * Key * @description A unique key for this model. @@ -17272,13 +16873,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -17297,33 +16891,25 @@ export type components = { 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; - /** - * Converted At - * @description When this model was last converted to diffusers - */ - converted_at: number | null; /** * Format - * @default checkpoint + * @default diffusers * @constant */ - format: "checkpoint"; + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"]; prediction_type: components["schemas"]["SchedulerPredictionType"]; variant: components["schemas"]["ModelVariantType"]; /** * Base - * @default sdxl + * @default sdxl-refiner * @constant */ - base: "sdxl"; + base: "sdxl-refiner"; }; - /** Main_Diffusers_CogView4_Config */ - Main_Diffusers_CogView4_Config: { + /** Main_Diffusers_SDXL_Config */ + Main_Diffusers_SDXL_Config: { /** * Key * @description A unique key for this model. @@ -17371,13 +16957,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -17403,16 +16982,18 @@ export type components = { */ format: "diffusers"; /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"]; + prediction_type: components["schemas"]["SchedulerPredictionType"]; + variant: components["schemas"]["ModelVariantType"]; /** * Base - * @default cogview4 + * @default sdxl * @constant */ - base: "cogview4"; + base: "sdxl"; }; - /** Main_Diffusers_SD1_Config */ - Main_Diffusers_SD1_Config: { + /** Main_ExternalAPI_ChatGPT4o_Config */ + Main_ExternalAPI_ChatGPT4o_Config: { /** * Key * @description A unique key for this model. @@ -17460,13 +17041,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -17487,23 +17061,19 @@ export type components = { default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** * Format - * @default diffusers + * @default api * @constant */ - format: "diffusers"; - /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; - prediction_type: components["schemas"]["SchedulerPredictionType"]; - variant: components["schemas"]["ModelVariantType"]; + format: "api"; /** * Base - * @default sd-1 + * @default chatgpt-4o * @constant */ - base: "sd-1"; + base: "chatgpt-4o"; }; - /** Main_Diffusers_SD2_Config */ - Main_Diffusers_SD2_Config: { + /** Main_ExternalAPI_FluxKontext_Config */ + Main_ExternalAPI_FluxKontext_Config: { /** * Key * @description A unique key for this model. @@ -17551,13 +17121,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -17578,23 +17141,19 @@ export type components = { default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** * Format - * @default diffusers + * @default api * @constant */ - format: "diffusers"; - /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; - prediction_type: components["schemas"]["SchedulerPredictionType"]; - variant: components["schemas"]["ModelVariantType"]; + format: "api"; /** * Base - * @default sd-2 + * @default flux-kontext * @constant */ - base: "sd-2"; + base: "flux-kontext"; }; - /** Main_Diffusers_SD3_Config */ - Main_Diffusers_SD3_Config: { + /** Main_ExternalAPI_Gemini2_5_Config */ + Main_ExternalAPI_Gemini2_5_Config: { /** * Key * @description A unique key for this model. @@ -17642,13 +17201,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -17669,21 +17221,19 @@ export type components = { default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** * Format - * @default diffusers + * @default api * @constant */ - format: "diffusers"; - /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + format: "api"; /** * Base - * @default sd-3 + * @default gemini-2.5 * @constant */ - base: "sd-3"; + base: "gemini-2.5"; }; - /** Main_Diffusers_SDXLRefiner_Config */ - Main_Diffusers_SDXLRefiner_Config: { + /** Main_ExternalAPI_Imagen3_Config */ + Main_ExternalAPI_Imagen3_Config: { /** * Key * @description A unique key for this model. @@ -17731,13 +17281,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -17758,23 +17301,19 @@ export type components = { default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** * Format - * @default diffusers + * @default api * @constant */ - format: "diffusers"; - /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; - prediction_type: components["schemas"]["SchedulerPredictionType"]; - variant: components["schemas"]["ModelVariantType"]; + format: "api"; /** * Base - * @default sdxl-refiner + * @default imagen3 * @constant */ - base: "sdxl-refiner"; + base: "imagen3"; }; - /** Main_Diffusers_SDXL_Config */ - Main_Diffusers_SDXL_Config: { + /** Main_ExternalAPI_Imagen4_Config */ + Main_ExternalAPI_Imagen4_Config: { /** * Key * @description A unique key for this model. @@ -17822,13 +17361,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -17849,20 +17381,16 @@ export type components = { default_settings: components["schemas"]["MainModelDefaultSettings"] | null; /** * Format - * @default diffusers + * @default api * @constant */ - format: "diffusers"; - /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; - prediction_type: components["schemas"]["SchedulerPredictionType"]; - variant: components["schemas"]["ModelVariantType"]; + format: "api"; /** * Base - * @default sdxl + * @default imagen4 * @constant */ - base: "sdxl"; + base: "imagen4"; }; /** * Main_GGUF_FLUX_Config @@ -17916,13 +17444,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -17946,11 +17467,6 @@ export type components = { * @description Path to the config for this model, if any. */ config_path: string | null; - /** - * Converted At - * @description When this model was last converted to diffusers - */ - converted_at: number | null; /** * Base * @default flux @@ -19793,7 +19309,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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["Main_ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["Main_ExternalAPI_Gemini2_5_Config"] | components["schemas"]["Main_ExternalAPI_Imagen3_Config"] | components["schemas"]["Main_ExternalAPI_Imagen4_Config"] | components["schemas"]["Main_ExternalAPI_FluxKontext_Config"] | components["schemas"]["Video_ExternalAPI_Veo3_Config"] | components["schemas"]["Video_ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; }; /** * ModelInstallDownloadProgressEvent @@ -19959,7 +19475,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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["Main_ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["Main_ExternalAPI_Gemini2_5_Config"] | components["schemas"]["Main_ExternalAPI_Imagen3_Config"] | components["schemas"]["Main_ExternalAPI_Imagen4_Config"] | components["schemas"]["Main_ExternalAPI_FluxKontext_Config"] | components["schemas"]["Video_ExternalAPI_Veo3_Config"] | components["schemas"]["Video_ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]) | null; /** * Inplace * @description Leave model in its current location; otherwise install under models directory @@ -20045,7 +19561,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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["Main_ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["Main_ExternalAPI_Gemini2_5_Config"] | components["schemas"]["Main_ExternalAPI_Imagen3_Config"] | components["schemas"]["Main_ExternalAPI_Imagen4_Config"] | components["schemas"]["Main_ExternalAPI_FluxKontext_Config"] | components["schemas"]["Video_ExternalAPI_Veo3_Config"] | components["schemas"]["Video_ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; /** * @description The submodel type, if any * @default null @@ -20066,7 +19582,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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["Main_ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["Main_ExternalAPI_Gemini2_5_Config"] | components["schemas"]["Main_ExternalAPI_Imagen3_Config"] | components["schemas"]["Main_ExternalAPI_Imagen4_Config"] | components["schemas"]["Main_ExternalAPI_FluxKontext_Config"] | components["schemas"]["Video_ExternalAPI_Veo3_Config"] | components["schemas"]["Video_ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; /** * @description The submodel type, if any * @default null @@ -20235,7 +19751,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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["Main_ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["Main_ExternalAPI_Gemini2_5_Config"] | components["schemas"]["Main_ExternalAPI_Imagen3_Config"] | components["schemas"]["Main_ExternalAPI_Imagen4_Config"] | components["schemas"]["Main_ExternalAPI_FluxKontext_Config"] | components["schemas"]["Video_ExternalAPI_Veo3_Config"] | components["schemas"]["Video_ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"])[]; }; /** * Multiply Integers @@ -22868,13 +22384,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -22887,7 +22396,7 @@ export type components = { */ format: "diffusers"; /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"]; /** * Type * @default siglip @@ -23077,13 +22586,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -23930,19 +23432,11 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model */ usage_info: string | null; - default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** * Format * @default diffusers @@ -23950,13 +23444,14 @@ export type components = { */ format: "diffusers"; /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"]; /** * Type * @default t2i_adapter * @constant */ type: "t2i_adapter"; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** * Base * @default sd-1 @@ -24013,19 +23508,11 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model */ usage_info: string | null; - default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** * Format * @default diffusers @@ -24033,13 +23520,14 @@ export type components = { */ format: "diffusers"; /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"]; /** * Type * @default t2i_adapter * @constant */ type: "t2i_adapter"; + default_settings: components["schemas"]["ControlAdapterDefaultSettings"] | null; /** * Base * @default sdxl @@ -24059,7 +23547,10 @@ export type components = { */ loras: components["schemas"]["LoRAField"][]; }; - /** T5Encoder_BnBLLMint8_Config */ + /** + * T5Encoder_BnBLLMint8_Config + * @description Configuration for T5 Encoder models quantized by bitsandbytes' LLM.int8. + */ T5Encoder_BnBLLMint8_Config: { /** * Key @@ -24108,13 +23599,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -24139,7 +23623,11 @@ export type components = { */ format: "bnb_quantized_int8b"; }; - /** T5Encoder_T5Encoder_Config */ + /** + * T5Encoder_T5Encoder_Config + * @description Configuration for T5 Encoder models in a bespoke, diffusers-like format. The model weights are expected to be in + * a folder called text_encoder_2 inside the model directory, with a config file named model.safetensors.index.json. + */ T5Encoder_T5Encoder_Config: { /** * Key @@ -24185,16 +23673,9 @@ export type components = { source_api_response: string | null; /** * Cover Image - * @description Url for image to preview model - */ - cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model + * @description Url for image to preview model */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; + cover_image: string | null; /** * Usage Info * @description Usage information for this model @@ -24279,13 +23760,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -24359,13 +23833,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -24439,13 +23906,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -24519,13 +23979,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -24599,13 +24052,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -24679,13 +24125,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -25155,13 +24594,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -25392,13 +24824,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -25409,11 +24834,6 @@ export type components = { * @description Path to the config for this model, if any. */ config_path: string | null; - /** - * Converted At - * @description When this model was last converted to diffusers - */ - converted_at: number | null; /** * Type * @default vae @@ -25482,13 +24902,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -25499,11 +24912,6 @@ export type components = { * @description Path to the config for this model, if any. */ config_path: string | null; - /** - * Converted At - * @description When this model was last converted to diffusers - */ - converted_at: number | null; /** * Type * @default vae @@ -25572,13 +24980,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -25589,11 +24990,6 @@ export type components = { * @description Path to the config for this model, if any. */ config_path: string | null; - /** - * Converted At - * @description When this model was last converted to diffusers - */ - converted_at: number | null; /** * Type * @default vae @@ -25662,13 +25058,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -25679,11 +25068,6 @@ export type components = { * @description Path to the config for this model, if any. */ config_path: string | null; - /** - * Converted At - * @description When this model was last converted to diffusers - */ - converted_at: number | null; /** * Type * @default vae @@ -25752,13 +25136,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -25771,7 +25148,7 @@ export type components = { */ format: "diffusers"; /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"]; /** * Type * @default vae @@ -25834,13 +25211,6 @@ export type components = { * @description Url for image to preview model */ cover_image: string | null; - /** - * Submodels - * @description Loadable submodels in this model - */ - submodels: { - [key: string]: components["schemas"]["SubmodelDefinition"]; - } | null; /** * Usage Info * @description Usage information for this model @@ -25853,7 +25223,7 @@ export type components = { */ format: "diffusers"; /** @default */ - repo_variant: components["schemas"]["ModelRepoVariant"] | null; + repo_variant: components["schemas"]["ModelRepoVariant"]; /** * Type * @default vae @@ -26052,6 +25422,166 @@ export type components = { */ starred?: boolean | null; }; + /** Video_ExternalAPI_Runway_Config */ + Video_ExternalAPI_Runway_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; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Usage Info + * @description Usage information for this model + */ + usage_info: string | null; + /** + * Type + * @default video + * @constant + */ + type: "video"; + /** + * 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 api + * @constant + */ + format: "api"; + /** + * Base + * @default runway + * @constant + */ + base: "runway"; + }; + /** Video_ExternalAPI_Veo3_Config */ + Video_ExternalAPI_Veo3_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; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Usage Info + * @description Usage information for this model + */ + usage_info: string | null; + /** + * Type + * @default video + * @constant + */ + type: "video"; + /** + * 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 api + * @constant + */ + format: "api"; + /** + * Base + * @default veo3 + * @constant + */ + base: "veo3"; + }; /** Workflow */ Workflow: { /** @@ -26460,7 +25990,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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["Main_ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["Main_ExternalAPI_Gemini2_5_Config"] | components["schemas"]["Main_ExternalAPI_Imagen3_Config"] | components["schemas"]["Main_ExternalAPI_Imagen4_Config"] | components["schemas"]["Main_ExternalAPI_FluxKontext_Config"] | components["schemas"]["Video_ExternalAPI_Veo3_Config"] | components["schemas"]["Video_ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Validation Error */ @@ -26492,7 +26022,25 @@ 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; + /** @example { + * "path": "string", + * "name": "string", + * "base": "sd-1", + * "type": "main", + * "format": "checkpoint", + * "config_path": "string", + * "key": "string", + * "hash": "string", + * "file_size": 1, + * "description": "string", + * "source": "string", + * "converted_at": 0, + * "variant": "normal", + * "prediction_type": "epsilon", + * "repo_variant": "fp16", + * "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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["Main_ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["Main_ExternalAPI_Gemini2_5_Config"] | components["schemas"]["Main_ExternalAPI_Imagen3_Config"] | components["schemas"]["Main_ExternalAPI_Imagen4_Config"] | components["schemas"]["Main_ExternalAPI_FluxKontext_Config"] | components["schemas"]["Video_ExternalAPI_Veo3_Config"] | components["schemas"]["Video_ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ @@ -26579,7 +26127,25 @@ 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; + /** @example { + * "path": "string", + * "name": "string", + * "base": "sd-1", + * "type": "main", + * "format": "checkpoint", + * "config_path": "string", + * "key": "string", + * "hash": "string", + * "file_size": 1, + * "description": "string", + * "source": "string", + * "converted_at": 0, + * "variant": "normal", + * "prediction_type": "epsilon", + * "repo_variant": "fp16", + * "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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["Main_ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["Main_ExternalAPI_Gemini2_5_Config"] | components["schemas"]["Main_ExternalAPI_Imagen3_Config"] | components["schemas"]["Main_ExternalAPI_Imagen4_Config"] | components["schemas"]["Main_ExternalAPI_FluxKontext_Config"] | components["schemas"]["Video_ExternalAPI_Veo3_Config"] | components["schemas"]["Video_ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ @@ -27075,7 +26641,25 @@ 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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["ExternalAPI_Gemini2_5_Config"] | components["schemas"]["ExternalAPI_Imagen3_Config"] | components["schemas"]["ExternalAPI_Imagen4_Config"] | components["schemas"]["ExternalAPI_FluxKontext_Config"] | components["schemas"]["ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; + /** @example { + * "path": "string", + * "name": "string", + * "base": "sd-1", + * "type": "main", + * "format": "checkpoint", + * "config_path": "string", + * "key": "string", + * "hash": "string", + * "file_size": 1, + * "description": "string", + * "source": "string", + * "converted_at": 0, + * "variant": "normal", + * "prediction_type": "epsilon", + * "repo_variant": "fp16", + * "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_CogView4_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_FLUX_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_FLUX_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_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_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_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_FLUX_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_FLUX_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_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"]["Main_ExternalAPI_ChatGPT4o_Config"] | components["schemas"]["Main_ExternalAPI_Gemini2_5_Config"] | components["schemas"]["Main_ExternalAPI_Imagen3_Config"] | components["schemas"]["Main_ExternalAPI_Imagen4_Config"] | components["schemas"]["Main_ExternalAPI_FluxKontext_Config"] | components["schemas"]["Video_ExternalAPI_Veo3_Config"] | components["schemas"]["Video_ExternalAPI_Runway_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ @@ -28931,6 +28515,11 @@ export interface operations { [name: string]: unknown; }; content: { + /** @example [ + * "15e9eb28-8cfe-47c9-b610-37907a79fc3c", + * "71272e82-0e5f-46d5-bca9-9a61f4bd8a82", + * "a5d7cd49-1b98-4534-a475-aeee4ccf5fa2" + * ] */ "application/json": string[]; }; }; @@ -29069,6 +28658,15 @@ export interface operations { [name: string]: unknown; }; content: { + /** @example [ + * "ca562b14-995e-4a42-90c1-9528f1a5921d", + * "cc0c2b8a-c62e-41d6-878e-cc74dde5ca8f", + * "18ca7649-6a9e-47d5-bc17-41ab1e8cec81", + * "7c12d1b2-0ef9-4bec-ba55-797b2d8f2ee1", + * "c382eaa3-0e28-4ab0-9446-408667699aeb", + * "71272e82-0e5f-46d5-bca9-9a61f4bd8a82", + * "a5d7cd49-1b98-4534-a475-aeee4ccf5fa2" + * ] */ "application/json": string[]; }; }; diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts index 7506b29356f..8b0cafe46b3 100644 --- a/invokeai/frontend/web/src/services/api/types.ts +++ b/invokeai/frontend/web/src/services/api/types.ts @@ -108,6 +108,7 @@ export const isVideoDTO = (dto: ImageDTO | VideoDTO): dto is VideoDTO => { // Model Configs export type AnyModelConfig = S['AnyModelConfig']; export type MainModelConfig = Extract; +export type FLUXModelConfig = Extract; export type ControlLoRAModelConfig = Extract; export type LoRAModelConfig = Extract; export type VAEModelConfig = Extract; @@ -134,6 +135,7 @@ type UnknownModelConfig = Extract; export type FLUXKontextModelConfig = MainModelConfig; export type ChatGPT4oModelConfig = ApiModelConfig; export type Gemini2_5ModelConfig = ApiModelConfig; +type SubmodelDefinition = S['SubmodelDefinition']; /** * Checks if a list of submodels contains any that match a given variant or type @@ -141,7 +143,7 @@ export type Gemini2_5ModelConfig = ApiModelConfig; * @param checkStr The string to check against for variant or type * @returns A boolean */ -const checkSubmodel = (submodels: AnyModelConfig['submodels'], checkStr: string): boolean => { +const checkSubmodel = (submodels: Record, checkStr: string): boolean => { for (const submodel in submodels) { if ( submodel && @@ -164,6 +166,7 @@ const checkSubmodels = (identifiers: string[], config: AnyModelConfig): boolean return identifiers.every( (identifier) => config.type === 'main' && + 'submodels' in config && config.submodels && (identifier in config.submodels || checkSubmodel(config.submodels, identifier)) ); @@ -332,7 +335,7 @@ export const isRefinerMainModelModelConfig = (config: AnyModelConfig): config is }; export const isFluxFillMainModelModelConfig = (config: AnyModelConfig): config is MainModelConfig => { - return config.type === 'main' && config.base === 'flux' && config.variant === 'inpaint'; + return config.type === 'main' && config.base === 'flux' && config.variant === 'dev_fill'; }; export const isTIModelConfig = (config: AnyModelConfig): config is MainModelConfig => { diff --git a/scripts/classify-model.py b/scripts/classify-model.py index 2ae253b72fe..a9129860a7f 100755 --- a/scripts/classify-model.py +++ b/scripts/classify-model.py @@ -8,7 +8,7 @@ from invokeai.backend.model_hash.model_hash import HASHING_ALGORITHMS from invokeai.backend.model_manager import InvalidModelConfigException, ModelProbe -from invokeai.backend.model_manager.config import ModelConfigFactory +from invokeai.backend.model_manager.configs.factory import ModelConfigFactory algos = ", ".join(set(get_args(HASHING_ALGORITHMS))) diff --git a/tests/test_model_probe.py b/tests/test_model_probe.py index 03a7428382a..f1249e4dc1b 100644 --- a/tests/test_model_probe.py +++ b/tests/test_model_probe.py @@ -13,9 +13,9 @@ from invokeai.backend.model_manager import BaseModelType, ModelFormat, ModelRepoVariant, ModelType, ModelVariantType from invokeai.backend.model_manager.config import ( AnyModelConfig, + Config_Base, InvalidModelConfigException, MainDiffusersConfig, - Config_Base, ModelConfigFactory, get_model_discriminator_value, ) From cffffd05775c236ac9f50e872adb0ba93d872c33 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 14:56:25 +1100 Subject: [PATCH 50/62] fix(mm): inverted condition --- invokeai/app/api/routers/model_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/app/api/routers/model_manager.py b/invokeai/app/api/routers/model_manager.py index 7add1d09cf2..ba84339a0e6 100644 --- a/invokeai/app/api/routers/model_manager.py +++ b/invokeai/app/api/routers/model_manager.py @@ -744,7 +744,7 @@ async def convert_model( logger.error(str(e)) raise HTTPException(status_code=424, detail=str(e)) - if isinstance( + if not isinstance( model_config, ( Main_Checkpoint_SD1_Config, From 76e4487003f0d9996d32d5a006bdf3b2360a8bbc Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 15:02:18 +1100 Subject: [PATCH 51/62] docs(mm): update docsstrings in factory.py --- .../backend/model_manager/configs/factory.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/invokeai/backend/model_manager/configs/factory.py b/invokeai/backend/model_manager/configs/factory.py index 6ab16cd5f69..39f588f232a 100644 --- a/invokeai/backend/model_manager/configs/factory.py +++ b/invokeai/backend/model_manager/configs/factory.py @@ -210,6 +210,12 @@ ] AnyModelConfigValidator = TypeAdapter[AnyModelConfig](AnyModelConfig) +"""Pydantic TypeAdapter for the AnyModelConfig union, used for parsing and validation. + +If you need to parse/validate a dict or JSON into an AnyModelConfig, you should probably use +ModelConfigFactory.from_dict or ModelConfigFactory.from_json instead as they may implement +additional logic in the future. +""" class ModelConfigFactory: @@ -219,6 +225,12 @@ def from_dict(fields: dict[str, Any]) -> AnyModelConfig: model = AnyModelConfigValidator.validate_python(fields) return model + @staticmethod + def from_json(json: str | bytes | bytearray) -> AnyModelConfig: + """Return the appropriate config object from json.""" + model = AnyModelConfigValidator.validate_json(json) + return model + @staticmethod def build_common_fields( mod: ModelOnDisk, @@ -320,8 +332,9 @@ def from_model_on_disk( # - SD main models can look like a LoRA when they have merged in LoRA weights. Prefer the main model. # - SD main models in diffusers format can look like a CLIP Embed; they have a text_encoder folder with # a config.json file. Prefer the main model. - - # Sort the matching according to known special cases. + # + # Given the above cases, we can prioritize the matches by type. If we find more cases, we may need a more + # sophisticated approach. def sort_key(m: AnyModelConfig) -> int: match m.type: case ModelType.Main: From b4350c0b04da6de7a1799e03ad13732412c8633f Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 15:22:16 +1100 Subject: [PATCH 52/62] docs(mm): document flux variant attr --- invokeai/backend/model_manager/configs/main.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/invokeai/backend/model_manager/configs/main.py b/invokeai/backend/model_manager/configs/main.py index 26f6b5b60e0..890a4f84622 100644 --- a/invokeai/backend/model_manager/configs/main.py +++ b/invokeai/backend/model_manager/configs/main.py @@ -264,6 +264,18 @@ class Main_Checkpoint_FLUX_Config(Checkpoint_Config_Base, Main_Config_Base, Conf base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) variant: FluxVariantType = Field() + # Prior to v6.8.0, we used an awkward combination of `config_path` and `variant` to distinguish between FLUX + # variants. + # + # `config_path` was set to one of: + # - flux-dev + # - flux-dev-fill + # - flux-schnell + # + # `variant` was set to ModelVariantType.Inpaint for FLUX Fill models and ModelVariantType.Normal for all other FLUX + # models. + # + # We now use the `variant` field to directly represent the FLUX variant type, and `config_path` is no longer used. @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: From 22918f28e54b28a71a0e7eae1ee2aefe620305fa Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 15:22:36 +1100 Subject: [PATCH 53/62] feat(mm): add helper method for legacy configs --- invokeai/backend/model_manager/single_file_config_files.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/invokeai/backend/model_manager/single_file_config_files.py b/invokeai/backend/model_manager/single_file_config_files.py index 22fe646b550..fa4b9e934b8 100644 --- a/invokeai/backend/model_manager/single_file_config_files.py +++ b/invokeai/backend/model_manager/single_file_config_files.py @@ -1,5 +1,6 @@ from dataclasses import dataclass +from invokeai.backend.model_manager.configs.factory import AnyModelConfig from invokeai.backend.model_manager.taxonomy import ( BaseModelType, ModelType, @@ -15,6 +16,12 @@ class LegacyConfigKey: variant: ModelVariantType | None = None pred: SchedulerPredictionType | None = None + @classmethod + def from_model_config(cls, config: AnyModelConfig) -> "LegacyConfigKey": + variant = getattr(config, "variant", None) + pred = getattr(config, "prediction_type", None) + return cls(type=config.type, base=config.base, variant=variant, pred=pred) + LEGACY_CONFIG_MAP: dict[LegacyConfigKey, str] = { LegacyConfigKey( From 9abca8a9190740df1d3e70baef4285e64fd0812d Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 16:37:57 +1100 Subject: [PATCH 54/62] feat(mm): satisfy type checker in flux denoise --- invokeai/app/invocations/flux_denoise.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/invokeai/app/invocations/flux_denoise.py b/invokeai/app/invocations/flux_denoise.py index 1599e8428cb..b6d0399108d 100644 --- a/invokeai/app/invocations/flux_denoise.py +++ b/invokeai/app/invocations/flux_denoise.py @@ -48,7 +48,7 @@ unpack, ) from invokeai.backend.flux.text_conditioning import FluxReduxConditioning, FluxTextConditioning -from invokeai.backend.model_manager.taxonomy import FluxVariantType, ModelFormat +from invokeai.backend.model_manager.taxonomy import BaseModelType, FluxVariantType, ModelFormat, ModelType from invokeai.backend.patches.layer_patcher import LayerPatcher from invokeai.backend.patches.lora_conversions.flux_lora_constants import FLUX_LORA_TRANSFORMER_PREFIX from invokeai.backend.patches.model_patch_raw import ModelPatchRaw @@ -232,6 +232,7 @@ def _run_diffusion( ) transformer_config = context.models.get_config(self.transformer.transformer) + assert transformer_config.base is BaseModelType.Flux and transformer_config.type is ModelType.Main is_schnell = transformer_config.variant is FluxVariantType.Schnell # Calculate the timestep schedule. From 05b04dfffb0727c1f638e8ea8cf58a88e6830e89 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 16:38:21 +1100 Subject: [PATCH 55/62] docs(mm): remove extraneous comment --- invokeai/backend/model_manager/load/load_base.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/invokeai/backend/model_manager/load/load_base.py b/invokeai/backend/model_manager/load/load_base.py index c4d71bfe314..a4004afba75 100644 --- a/invokeai/backend/model_manager/load/load_base.py +++ b/invokeai/backend/model_manager/load/load_base.py @@ -89,14 +89,6 @@ def __init__(self, config: Optional[AnyModelConfig], cache_record: CacheRecord, self.config = config -# TODO(MM2): -# Some "intermediary" subclasses in the ModelLoaderBase class hierarchy define methods that their subclasses don't -# know about. I think the problem may be related to this class being an ABC. -# -# For example, GenericDiffusersLoader defines `get_hf_load_class()`, and StableDiffusionDiffusersModel attempts to -# call it. However, the method is not defined in the ABC, so it is not guaranteed to be implemented. - - class ModelLoaderBase(ABC): """Abstract base class for loading models into RAM/VRAM.""" From 468ae2f53da4dff81111b1cd80887ff9fc2106bc Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 17:49:42 +1100 Subject: [PATCH 56/62] fix(mm): ensure unknown model configs get unknown attrs --- invokeai/backend/model_manager/configs/factory.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/invokeai/backend/model_manager/configs/factory.py b/invokeai/backend/model_manager/configs/factory.py index 39f588f232a..8ff48e097f7 100644 --- a/invokeai/backend/model_manager/configs/factory.py +++ b/invokeai/backend/model_manager/configs/factory.py @@ -323,7 +323,13 @@ def from_model_on_disk( if not matches and app_config.allow_unknown_models: logger.warning(f"Unable to identify model {mod.name}, falling back to Unknown_Config") - return Unknown_Config(**fields) + return Unknown_Config( + **fields, + # Override the type/format/base to ensure it's marked as unknown. + base=BaseModelType.Unknown, + type=ModelType.Unknown, + format=ModelFormat.Unknown, + ) if len(matches) > 1: # We have multiple matches, in which case at most 1 is correct. We need to pick one. From cf0af13408d5ce708b1abfc3e1bda90e6f9d8591 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 17:49:51 +1100 Subject: [PATCH 57/62] fix(mm): t5 identification --- .../model_manager/configs/t5_encoder.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/invokeai/backend/model_manager/configs/t5_encoder.py b/invokeai/backend/model_manager/configs/t5_encoder.py index 3b6f2e733dd..ed682e14304 100644 --- a/invokeai/backend/model_manager/configs/t5_encoder.py +++ b/invokeai/backend/model_manager/configs/t5_encoder.py @@ -5,7 +5,6 @@ from invokeai.backend.model_manager.configs.base import Config_Base from invokeai.backend.model_manager.configs.identification_utils import ( NotAMatchError, - common_config_paths, raise_for_class_name, raise_for_override_fields, raise_if_not_dir, @@ -29,12 +28,9 @@ def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) - raise_for_override_fields(cls, override_fields) - raise_for_class_name( - common_config_paths(mod.path), - { - "T5EncoderModel", - }, - ) + expected_config_path = mod.path / "text_encoder_2" / "config.json" + expected_class_name = "T5EncoderModel" + raise_for_class_name(expected_config_path, expected_class_name) cls.raise_if_doesnt_have_unquantized_config_file(mod) @@ -61,12 +57,9 @@ def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) - raise_for_override_fields(cls, override_fields) - raise_for_class_name( - common_config_paths(mod.path), - { - "T5EncoderModel", - }, - ) + expected_config_path = mod.path / "text_encoder_2" / "config.json" + expected_class_name = "T5EncoderModel" + raise_for_class_name(expected_config_path, expected_class_name) cls.raise_if_filename_doesnt_look_like_bnb_quantized(mod) From a36911441a45c86cdd024b43be26e4d7ff24dea9 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 17:49:59 +1100 Subject: [PATCH 58/62] fix(mm): sdxl ip adapter identification --- invokeai/backend/model_manager/configs/ip_adapter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/invokeai/backend/model_manager/configs/ip_adapter.py b/invokeai/backend/model_manager/configs/ip_adapter.py index dc6f80d922b..ba27f176201 100644 --- a/invokeai/backend/model_manager/configs/ip_adapter.py +++ b/invokeai/backend/model_manager/configs/ip_adapter.py @@ -81,12 +81,12 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatchError(f"unable to determine cross attention dimension: {e}") from e match cross_attention_dim: - case 1280: - return BaseModelType.StableDiffusionXL case 768: return BaseModelType.StableDiffusion1 case 1024: return BaseModelType.StableDiffusion2 + case 2048: + return BaseModelType.StableDiffusionXL case _: raise NotAMatchError(f"unrecognized cross attention dimension {cross_attention_dim}") @@ -154,12 +154,12 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: raise NotAMatchError(f"unable to determine cross attention dimension: {e}") from e match cross_attention_dim: - case 1280: - return BaseModelType.StableDiffusionXL case 768: return BaseModelType.StableDiffusion1 case 1024: return BaseModelType.StableDiffusion2 + case 2048: + return BaseModelType.StableDiffusionXL case _: raise NotAMatchError(f"unrecognized cross attention dimension {cross_attention_dim}") From 08b30ffc9ab5013011072485f9de4ee3cf2290c0 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 17:50:13 +1100 Subject: [PATCH 59/62] feat(mm): more flexible config matching utils --- .../model_manager/configs/identification_utils.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/invokeai/backend/model_manager/configs/identification_utils.py b/invokeai/backend/model_manager/configs/identification_utils.py index c2c310a332e..ce7d2c792de 100644 --- a/invokeai/backend/model_manager/configs/identification_utils.py +++ b/invokeai/backend/model_manager/configs/identification_utils.py @@ -54,7 +54,7 @@ def get_config_dict_or_raise(config_path: Path | set[Path]) -> dict[str, Any]: raise NotAMatchError(f"unable to load config file(s): {problems}") -def get_class_name_from_config_dict_or_raise(config_path: Path | set[Path]) -> str: +def get_class_name_from_config_dict_or_raise(config: Path | set[Path] | dict[str, Any]) -> str: """Load the diffusers/transformers model config file and return the class name. Args: @@ -67,7 +67,8 @@ def get_class_name_from_config_dict_or_raise(config_path: Path | set[Path]) -> s NotAMatch if the config file is missing or does not contain a valid class name. """ - config = get_config_dict_or_raise(config_path) + if not isinstance(config, dict): + config = get_config_dict_or_raise(config) try: if "_class_name" in config: @@ -79,7 +80,7 @@ def get_class_name_from_config_dict_or_raise(config_path: Path | set[Path]) -> s else: raise ValueError("missing _class_name or architectures field") except Exception as e: - raise NotAMatchError(f"unable to determine class name from config file: {config_path}") from e + raise NotAMatchError(f"unable to determine class name from config file: {config}") from e if not isinstance(config_class_name, str): raise NotAMatchError(f"_class_name or architectures field is not a string: {config_class_name}") @@ -87,7 +88,7 @@ def get_class_name_from_config_dict_or_raise(config_path: Path | set[Path]) -> s return config_class_name -def raise_for_class_name(config_path: Path | set[Path], class_name: str | set[str]) -> None: +def raise_for_class_name(config: Path | set[Path] | dict[str, Any], class_name: str | set[str]) -> None: """Get the class name from the config file and raise NotAMatch if it is not in the expected set. Args: @@ -100,7 +101,7 @@ def raise_for_class_name(config_path: Path | set[Path], class_name: str | set[st class_name = {class_name} if isinstance(class_name, str) else class_name - actual_class_name = get_class_name_from_config_dict_or_raise(config_path) + actual_class_name = get_class_name_from_config_dict_or_raise(config) if actual_class_name not in class_name: raise NotAMatchError(f"invalid class name from config: {actual_class_name}") From d81a3d6b329628236153e8cfc59e9a4722184b5c Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 17:50:20 +1100 Subject: [PATCH 60/62] fix(mm): clip vision identification --- .../model_manager/configs/clip_vision.py | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/invokeai/backend/model_manager/configs/clip_vision.py b/invokeai/backend/model_manager/configs/clip_vision.py index eb17fb9e338..af5a539bc18 100644 --- a/invokeai/backend/model_manager/configs/clip_vision.py +++ b/invokeai/backend/model_manager/configs/clip_vision.py @@ -8,8 +8,9 @@ from invokeai.backend.model_manager.configs.base import Config_Base, Diffusers_Config_Base from invokeai.backend.model_manager.configs.identification_utils import ( - common_config_paths, - raise_for_class_name, + NotAMatchError, + get_class_name_from_config_dict_or_raise, + get_config_dict_or_raise, raise_for_override_fields, raise_if_not_dir, ) @@ -34,11 +35,23 @@ def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) - raise_for_override_fields(cls, override_fields) - raise_for_class_name( - common_config_paths(mod.path), - { - "CLIPVisionModelWithProjection", - }, - ) + cls.raise_if_config_doesnt_look_like_clip_vision(mod) return cls(**override_fields) + + @classmethod + def raise_if_config_doesnt_look_like_clip_vision(cls, mod: ModelOnDisk) -> None: + config_dict = get_config_dict_or_raise(mod.path / "config.json") + class_name = get_class_name_from_config_dict_or_raise(config_dict) + + if class_name == "CLIPVisionModelWithProjection": + looks_like_clip_vision = True + elif class_name == "CLIPModel" and "vision_config" in config_dict: + looks_like_clip_vision = True + else: + looks_like_clip_vision = False + + if not looks_like_clip_vision: + raise NotAMatchError( + f"config class name is {class_name}, not CLIPVisionModelWithProjection or CLIPModel with vision_config" + ) From 7c70701f35890d59be10bd0bb9f85674fb2d6a05 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 19:30:57 +1100 Subject: [PATCH 61/62] feat(mm): add sanity checks before probing paths --- .../backend/model_manager/configs/factory.py | 160 ++++++++++++++---- .../backend/model_manager/configs/unknown.py | 23 ++- 2 files changed, 147 insertions(+), 36 deletions(-) diff --git a/invokeai/backend/model_manager/configs/factory.py b/invokeai/backend/model_manager/configs/factory.py index 8ff48e097f7..03ab40ca5a8 100644 --- a/invokeai/backend/model_manager/configs/factory.py +++ b/invokeai/backend/model_manager/configs/factory.py @@ -109,6 +109,29 @@ logger = logging.getLogger(__name__) app_config = get_config() +# Known model file extensions for sanity checking +_MODEL_EXTENSIONS = { + ".safetensors", + ".ckpt", + ".pt", + ".pth", + ".bin", + ".gguf", + ".onnx", +} + +# Known config file names for diffusers/transformers models +_CONFIG_FILES = { + "model_index.json", + "config.json", +} + +# Maximum number of files in a directory to be considered a model +_MAX_FILES_IN_MODEL_DIR = 50 + +# Maximum depth to search for model files in directories +_MAX_SEARCH_DEPTH = 2 + # The types are listed explicitly because IDEs/LSPs can't identify the correct types # when AnyModelConfig is constructed dynamically using ModelConfigBase.all_config_classes @@ -276,6 +299,68 @@ def build_common_fields( return fields + @staticmethod + def _validate_path_looks_like_model(path: Path) -> None: + """Perform basic sanity checks to ensure a path looks like a model. + + This prevents wasting time trying to identify obviously non-model paths like + home directories or downloads folders. Raises RuntimeError if the path doesn't + pass basic checks. + + Args: + path: The path to validate + + Raises: + RuntimeError: If the path doesn't look like a model + """ + if path.is_file(): + # For files, just check the extension + if path.suffix.lower() not in _MODEL_EXTENSIONS: + raise RuntimeError( + f"File extension {path.suffix} is not a recognized model format. " + f"Expected one of: {', '.join(sorted(_MODEL_EXTENSIONS))}" + ) + else: + # For directories, do a quick file count check with early exit + total_files = 0 + for item in path.rglob("*"): + if item.is_file(): + total_files += 1 + if total_files > _MAX_FILES_IN_MODEL_DIR: + raise RuntimeError( + f"Directory contains more than {_MAX_FILES_IN_MODEL_DIR} files. " + "This looks like a general-purpose directory rather than a model. " + "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: + return False + try: + for item in current_path.iterdir(): + if item.is_file() and item.suffix.lower() in _MODEL_EXTENSIONS: + return True + elif item.is_dir() and find_model_files(item, depth + 1): + return True + except PermissionError: + pass + return False + + if not find_model_files(path, 0): + raise RuntimeError( + f"No model files or config files found in directory {path}. " + f"Expected to find model files with extensions: {', '.join(sorted(_MODEL_EXTENSIONS))} " + f"or config files: {', '.join(sorted(_CONFIG_FILES))}" + ) + @staticmethod def from_model_on_disk( mod: str | Path | ModelOnDisk, @@ -290,6 +375,10 @@ def from_model_on_disk( if isinstance(mod, Path | str): mod = ModelOnDisk(Path(mod), hash_algo) + # Perform basic sanity checks before attempting any config matching + # This rejects obviously non-model paths early, saving time + ModelConfigFactory._validate_path_looks_like_model(mod.path) + # We will always need these fields to build any model config. fields = ModelConfigFactory.build_common_fields(mod, override_fields) @@ -317,48 +406,53 @@ def from_model_on_disk( logger.warning(f"Schema validation error for {config_class.__name__} on model {mod.name}: {e}") except Exception as e: results[class_name] = e - logger.warning(f"Unexpected exception while matching {mod.name} to {config_class.__name__}: {e}") + logger.debug(f"Unexpected exception while matching {mod.name} to {config_class.__name__}: {e}") matches = [r for r in results.values() if isinstance(r, Config_Base)] - if not matches and app_config.allow_unknown_models: - logger.warning(f"Unable to identify model {mod.name}, falling back to Unknown_Config") - return Unknown_Config( - **fields, - # Override the type/format/base to ensure it's marked as unknown. - base=BaseModelType.Unknown, - type=ModelType.Unknown, - format=ModelFormat.Unknown, - ) + if not matches: + # No matches at all. This should be very rare, but just in case, we will fall back to Unknown_Config. + msg = f"No model config matched for model {mod.path}" + logger.error(msg) + raise RuntimeError(msg) + + # It is possible that we have multiple matches. We need to prioritize them. + # + # Known cases where multiple matches can occur: + # - SD main models can look like a LoRA when they have merged in LoRA weights. Prefer the main model. + # - SD main models in diffusers format can look like a CLIP Embed; they have a text_encoder folder with + # a config.json file. Prefer the main model. + # + # Given the above cases, we can prioritize the matches by type. If we find more cases, we may need a more + # sophisticated approach. + # + # Unknown models should always be the last resort fallback. + def sort_key(m: AnyModelConfig) -> int: + match m.type: + case ModelType.Main: + return 0 + case ModelType.LoRA: + return 1 + case ModelType.CLIPEmbed: + return 2 + case ModelType.Unknown: + # Unknown should always be tried last as a fallback + return 999 + case _: + return 3 + + matches.sort(key=sort_key) if len(matches) > 1: - # We have multiple matches, in which case at most 1 is correct. We need to pick one. - # - # Known cases: - # - SD main models can look like a LoRA when they have merged in LoRA weights. Prefer the main model. - # - SD main models in diffusers format can look like a CLIP Embed; they have a text_encoder folder with - # a config.json file. Prefer the main model. - # - # Given the above cases, we can prioritize the matches by type. If we find more cases, we may need a more - # sophisticated approach. - def sort_key(m: AnyModelConfig) -> int: - match m.type: - case ModelType.Main: - return 0 - case ModelType.LoRA: - return 1 - case ModelType.CLIPEmbed: - return 2 - case _: - return 3 - - matches.sort(key=sort_key) logger.warning( - f"Multiple model config classes matched for model {mod.name}: {[type(m).__name__ for m in matches]}." + f"Multiple model config classes matched for model {mod.path}: {[type(m).__name__ for m in matches]}." ) instance = matches[0] - logger.info(f"Model {mod.name} classified as {type(instance).__name__}") + if isinstance(instance, Unknown_Config): + logger.warning(f"Unable to identify model {mod.path}, falling back to Unknown_Config") + else: + logger.info(f"Model {mod.path} classified as {type(instance).__name__}") # Now do any post-processing needed for specific model types/bases/etc. match instance.type: diff --git a/invokeai/backend/model_manager/configs/unknown.py b/invokeai/backend/model_manager/configs/unknown.py index 10aad755664..2371cca089d 100644 --- a/invokeai/backend/model_manager/configs/unknown.py +++ b/invokeai/backend/model_manager/configs/unknown.py @@ -2,6 +2,7 @@ from pydantic import Field +from invokeai.app.services.config.config_default import get_config from invokeai.backend.model_manager.configs.base import Config_Base from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError from invokeai.backend.model_manager.model_on_disk import ModelOnDisk @@ -11,14 +12,30 @@ ModelType, ) +app_config = get_config() + class Unknown_Config(Config_Base): - """Model config for unknown models, used as a fallback when we cannot identify a model.""" + """Model config for unknown models, used as a fallback when we cannot positively identify a model.""" base: Literal[BaseModelType.Unknown] = Field(default=BaseModelType.Unknown) type: Literal[ModelType.Unknown] = Field(default=ModelType.Unknown) format: Literal[ModelFormat.Unknown] = Field(default=ModelFormat.Unknown) @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, fields: dict[str, Any]) -> Self: - raise NotAMatchError("unknown model config cannot match any model") + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + """Create an Unknown_Config for models that couldn't be positively identified. + + Note: Basic path validation (file extensions, directory structure) is already + performed by ModelConfigFactory before this method is called. + """ + if not app_config.allow_unknown_models: + raise NotAMatchError("unknown models are not allowed by configuration") + + return cls( + **override_fields, + # Override the type/format/base to ensure it's marked as unknown. + base=BaseModelType.Unknown, + type=ModelType.Unknown, + format=ModelFormat.Unknown, + ) From d21707a9252777015f95bab62df8579be179ad0e Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 7 Oct 2025 19:35:07 +1100 Subject: [PATCH 62/62] docs(mm): add reminder for self for field migrations --- .../migrations/migration_22.py | 25 +++++++++++++++++-- .../backend/model_manager/configs/main.py | 12 --------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/invokeai/app/services/shared/sqlite_migrator/migrations/migration_22.py b/invokeai/app/services/shared/sqlite_migrator/migrations/migration_22.py index 1d9c81529e8..2cd2101341e 100644 --- a/invokeai/app/services/shared/sqlite_migrator/migrations/migration_22.py +++ b/invokeai/app/services/shared/sqlite_migrator/migrations/migration_22.py @@ -2,7 +2,7 @@ import sqlite3 from logging import Logger from pathlib import Path -from typing import NamedTuple +from typing import Any, NamedTuple from pydantic import ValidationError @@ -29,8 +29,9 @@ def __call__(self, cursor: sqlite3.Cursor) -> None: for model_id, config_json in rows: try: + migrated_config_dict = self._migrate_config(config_json) # Get the model config as a pydantic object - config = AnyModelConfigValidator.validate_json(config_json) + config = AnyModelConfigValidator.validate_python(migrated_config_dict) except ValidationError: # This could happen if the config schema changed in a way that makes old configs invalid. Unlikely # for users, more likely for devs testing out migration paths. @@ -76,6 +77,26 @@ def __call__(self, cursor: sqlite3.Cursor) -> None: self._prune_empty_directories() + def _migrate_config(self, config_json: Any) -> str | None: + config_dict = json.loads(config_json) + + # TODO: migrate fields, review changes to ensure we hit all cases for v6.7.0 to v6.8.0 upgrade. + + # Prior to v6.8.0, we used an awkward combination of `config_path` and `variant` to distinguish between FLUX + # variants. + # + # `config_path` was set to one of: + # - flux-dev + # - flux-dev-fill + # - flux-schnell + # + # `variant` was set to ModelVariantType.Inpaint for FLUX Fill models and ModelVariantType.Normal for all other FLUX + # models. + # + # We now use the `variant` field to directly represent the FLUX variant type, and `config_path` is no longer used. + + return config_dict + def _normalize_model_storage(self, key: str, path_value: str) -> NormalizeResult: models_dir = self._models_dir stored_path = Path(path_value) diff --git a/invokeai/backend/model_manager/configs/main.py b/invokeai/backend/model_manager/configs/main.py index 890a4f84622..26f6b5b60e0 100644 --- a/invokeai/backend/model_manager/configs/main.py +++ b/invokeai/backend/model_manager/configs/main.py @@ -264,18 +264,6 @@ class Main_Checkpoint_FLUX_Config(Checkpoint_Config_Base, Main_Config_Base, Conf base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) variant: FluxVariantType = Field() - # Prior to v6.8.0, we used an awkward combination of `config_path` and `variant` to distinguish between FLUX - # variants. - # - # `config_path` was set to one of: - # - flux-dev - # - flux-dev-fill - # - flux-schnell - # - # `variant` was set to ModelVariantType.Inpaint for FLUX Fill models and ModelVariantType.Normal for all other FLUX - # models. - # - # We now use the `variant` field to directly represent the FLUX variant type, and `config_path` is no longer used. @classmethod def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: