cddb07a176
docs / deploy (push) Has been cancelled
docs / changes (push) Has been cancelled
docs / check-and-build (push) Has been cancelled
build container image / cpu (push) Has been cancelled
build container image / cuda (push) Has been cancelled
build container image / rocm (push) Has been cancelled
frontend checks / frontend-checks (push) Has been cancelled
frontend tests / frontend-tests (push) Has been cancelled
lfs checks / lfs-check (push) Has been cancelled
python checks / python-checks (push) Has been cancelled
python tests / py3.12: macos-default (push) Has been cancelled
python tests / py3.11: windows-cpu (push) Has been cancelled
python tests / py3.12: windows-cpu (push) Has been cancelled
python tests / py3.11: linux-cpu (push) Has been cancelled
typegen checks / typegen-checks (push) Has been cancelled
uv lock checks / uv-lock-checks (push) Has been cancelled
openapi checks / openapi-checks (push) Has been cancelled
python tests / py3.11: macos-default (push) Has been cancelled
python tests / py3.12: linux-cpu (push) Has been cancelled
157 lines
5.4 KiB
Python
157 lines
5.4 KiB
Python
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)
|