From e8b030427d75c0570e667caa4da10e2de69a371d Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 15 Mar 2024 21:43:04 +1100 Subject: [PATCH] fix(config): do not discard `conf_path`, migrate custom models.yaml Hold onto `conf_path` temporarily while migrating `invokeai.yaml` so that it gets migrated correctly as the model installer starts up. Stashed as `legacy_models_yaml_path` in the config, excluded from serialization. --- invokeai/app/services/config/config_default.py | 13 +++++++++++-- .../model_install/model_install_default.py | 15 ++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/invokeai/app/services/config/config_default.py b/invokeai/app/services/config/config_default.py index 52d904e30f..fddc5e5fac 100644 --- a/invokeai/app/services/config/config_default.py +++ b/invokeai/app/services/config/config_default.py @@ -175,6 +175,11 @@ class InvokeAIAppConfig(BaseSettings): hashing_algorithm: HASHING_ALGORITHMS = Field(default="blake3", description="Model hashing algorthim for model installs. 'blake3' 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.") remote_api_tokens: Optional[list[URLRegexTokenPair]] = Field(default=None, 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.") + # HIDDEN FIELDS + # v4 (MM2) doesn't use `models.yaml` files, but users were able to set paths in the v3 config. When we migrate a + # v3 config, we need to save the path to the models.yaml. This is only used during migration. + legacy_models_yaml_path: Optional[Path] = Field(default=None, description="The `conf_path` setting from a v3 `invokeai.yaml` file. Only present this app session migrated a config file, and it had `conf_test` on it.", exclude=True) + # fmt: on model_config = SettingsConfigDict(env_prefix="INVOKEAI_", env_ignore_empty=True) @@ -349,7 +354,7 @@ def generate_config_docstrings() -> str: def migrate_v3_config_dict(config_dict: dict[str, Any]) -> InvokeAIAppConfig: - """Migrate a v3 config dictionary to the latest version. + """Migrate a v3 config dictionary to a current config object. Args: config_dict: A dictionary of settings from a v3 config file. @@ -370,10 +375,14 @@ def migrate_v3_config_dict(config_dict: dict[str, Any]) -> InvokeAIAppConfig: # `max_vram_cache_size` was renamed to `vram` some time in v3, but both names were used if k == "max_vram_cache_size" and "vram" not in category_dict: parsed_config_dict["vram"] = v + if k == "conf_path": + parsed_config_dict["legacy_models_yaml_path"] = v elif k in InvokeAIAppConfig.model_fields: # skip unknown fields parsed_config_dict[k] = v - return InvokeAIAppConfig.model_validate(parsed_config_dict) + config = InvokeAIAppConfig.model_validate(parsed_config_dict) + + return config def load_and_migrate_config(config_path: Path) -> InvokeAIAppConfig: diff --git a/invokeai/app/services/model_install/model_install_default.py b/invokeai/app/services/model_install/model_install_default.py index c12b43d05c..807c18bcff 100644 --- a/invokeai/app/services/model_install/model_install_default.py +++ b/invokeai/app/services/model_install/model_install_default.py @@ -287,12 +287,17 @@ class ModelInstallService(ModelInstallServiceBase): def _migrate_yaml(self) -> None: db_models = self.record_store.all_models() - legacy_models_yaml_path = self._app_config.root_path / "configs" / "models.yaml" - try: - legacy_models_yaml = yaml.safe_load(legacy_models_yaml_path.read_text()) - except OSError: + + legacy_models_yaml_path = ( + self._app_config.legacy_models_yaml_path or self._app_config.root_path / "configs" / "models.yaml" + ) + + if not legacy_models_yaml_path.exists(): + # No yaml to migrate return + legacy_models_yaml = yaml.safe_load(legacy_models_yaml_path.read_text()) + yaml_metadata = legacy_models_yaml.pop("__metadata__") yaml_version = yaml_metadata.get("version") @@ -302,7 +307,7 @@ class ModelInstallService(ModelInstallServiceBase): ) self._logger.info( - f"Starting one-time migration of {len(legacy_models_yaml.items())} models from `models.yaml` to database. This may take a few minutes." + f"Starting one-time migration of {len(legacy_models_yaml.items())} models from {str(legacy_models_yaml_path)}. This may take a few minutes." ) if len(db_models) == 0 and len(legacy_models_yaml.items()) != 0: