import glob import os.path from copy import deepcopy import torch from backend import memory_management, utils from modules import hashes, paths, sd_models, shared vae_path = os.path.abspath(os.path.join(paths.models_path, "VAE")) vae_ignore_keys: set[str] = {"model_ema.decay", "model_ema.num_updates"} vae_dict: dict[str, os.PathLike] = {} base_vae: dict[str, torch.Tensor] = None loaded_vae_file: os.PathLike = None checkpoint_info: "sd_models.CheckpointInfo" = None @torch.inference_mode() def _load_vae_dict(model, vae_sd: dict): sd = {k: v for k, v in vae_sd.items() if k[0:4] != "loss" and k not in vae_ignore_keys} model.first_stage_model.load_state_dict(sd) def get_loaded_vae_name() -> str: if loaded_vae_file is None: return None return os.path.basename(loaded_vae_file) def get_loaded_vae_hash() -> str: if loaded_vae_file is None: return None sha256 = hashes.sha256(loaded_vae_file, "vae") return sha256[0:10] if sha256 else None # def get_base_vae(model): # if base_vae is not None and checkpoint_info == model.sd_checkpoint_info and model: # return base_vae # return None def store_base_vae(model): global base_vae, checkpoint_info assert loaded_vae_file is None memory_management.logger.debug("Storing Original VAE...") base_vae = deepcopy(model.first_stage_model.state_dict()) checkpoint_info = model.sd_checkpoint_info def delete_base_vae(): global base_vae, checkpoint_info base_vae = None checkpoint_info = None memory_management.soft_empty_cache() def restore_base_vae(model): global loaded_vae_file if base_vae is None: return memory_management.logger.debug("Restoring Original VAE...") _load_vae_dict(model, base_vae) loaded_vae_file = None delete_base_vae() def get_filename(filepath: os.PathLike) -> str: return os.path.basename(filepath) def refresh_vae_list(): vae_dict.clear() paths = [] file_extensions = ("ckpt", "pt", "pth", "bin", "safetensors", "sft", "gguf") for ext in file_extensions: paths.append(os.path.join(sd_models.model_path, f"**/*.vae.{ext}")) paths.append(os.path.join(vae_path, f"**/*.{ext}")) for _dir in shared.cmd_opts.vae_dirs: for ext in file_extensions: paths.append(os.path.join(_dir, f"**/*.{ext}")) candidates = [] for path in paths: candidates += glob.iglob(path, recursive=True) for filepath in candidates: name = get_filename(filepath) vae_dict[name] = filepath vae_dict.update(dict(sorted(vae_dict.items(), key=lambda item: shared.natural_sort_key(item[0])))) # def find_vae_near_checkpoint(checkpoint_file): # checkpoint_path = os.path.basename(checkpoint_file).rsplit(".", 1)[0] # for vae_file in vae_dict.values(): # if os.path.basename(vae_file).startswith(checkpoint_path): # return vae_file # return None # @dataclass # class VaeResolution: # vae: str = None # source: str = None # resolved: bool = True # def tuple(self): # return self.vae, self.source # def is_automatic(): # return shared.opts.sd_vae in {"Automatic", "auto"} # "auto" for people with old config # def resolve_vae_from_setting() -> VaeResolution: # if shared.opts.sd_vae == "None": # return VaeResolution() # vae_from_options = vae_dict.get(shared.opts.sd_vae, None) # if vae_from_options is not None: # return VaeResolution(vae_from_options, "specified in settings") # if not is_automatic(): # print(f"Couldn't find VAE named {shared.opts.sd_vae}; using None instead") # return VaeResolution(resolved=False) # def resolve_vae_from_user_metadata(checkpoint_file) -> VaeResolution: # metadata = extra_networks.get_user_metadata(checkpoint_file) # vae_metadata = metadata.get("vae", None) # if vae_metadata is not None and vae_metadata != "Automatic": # if vae_metadata == "None": # return VaeResolution() # vae_from_metadata = vae_dict.get(vae_metadata, None) # if vae_from_metadata is not None: # return VaeResolution(vae_from_metadata, "from user metadata") # return VaeResolution(resolved=False) # def resolve_vae_near_checkpoint(checkpoint_file) -> VaeResolution: # vae_near_checkpoint = find_vae_near_checkpoint(checkpoint_file) # if vae_near_checkpoint is not None and (not shared.opts.sd_vae_overrides_per_model_preferences or is_automatic()): # return VaeResolution(vae_near_checkpoint, "found near the checkpoint") # return VaeResolution(resolved=False) # def resolve_vae(checkpoint_file) -> VaeResolution: # if shared.cmd_opts.vae_path is not None: # return VaeResolution(shared.cmd_opts.vae_path, "from commandline argument") # if shared.opts.sd_vae_overrides_per_model_preferences and not is_automatic(): # return resolve_vae_from_setting() # res = resolve_vae_from_user_metadata(checkpoint_file) # if res.resolved: # return res # res = resolve_vae_near_checkpoint(checkpoint_file) # if res.resolved: # return res # res = resolve_vae_from_setting() # return res def reload_vae_weights(vae: str): if vae in (None, "None", "Automatic"): return store_base_vae(shared.sd_model) vae_sd = utils.load_torch_file(vae) _load_vae_dict(shared.sd_model, vae_sd) def restore_vae_weights(): restore_base_vae(shared.sd_model)