From 613fa15ee726e8548d3e55da4a77ab0d339fa128 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 19 Sep 2025 12:53:53 +1000 Subject: [PATCH] fix(mm): normalized multi-file/diffusers model installation no worky now worky --- .../model_install/model_install_default.py | 19 ++++++++++++------- .../model_install/test_model_install.py | 15 ++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/invokeai/app/services/model_install/model_install_default.py b/invokeai/app/services/model_install/model_install_default.py index 161bf59d65..454697ea5a 100644 --- a/invokeai/app/services/model_install/model_install_default.py +++ b/invokeai/app/services/model_install/model_install_default.py @@ -187,17 +187,22 @@ class ModelInstallService(ModelInstallServiceBase): dest_dir = self.app_config.models_path / info.key try: + if dest_dir.exists(): + raise FileExistsError( + f"Cannot install model {model_path.name} to {dest_dir}: destination already exists" + ) dest_dir.mkdir(parents=True) dest_path = dest_dir / model_path.name if model_path.is_file() else dest_dir - if dest_path.exists(): - raise FileExistsError( - f"Cannot install model {model_path.name} to {dest_path}: destination already exists" - ) - move(model_path, dest_path) - except FileExistsError as excp: + if model_path.is_file(): + move(model_path, dest_path) + elif model_path.is_dir(): + # Move the contents of the directory, not the directory itself + for item in model_path.iterdir(): + move(item, dest_dir / item.name) + except FileExistsError as e: raise DuplicateModelException( f"A model named {model_path.name} is already installed at {dest_dir.as_posix()}" - ) from excp + ) from e return self._register( dest_path, diff --git a/tests/app/services/model_install/test_model_install.py b/tests/app/services/model_install/test_model_install.py index 3295d4b60c..846cbc2906 100644 --- a/tests/app/services/model_install/test_model_install.py +++ b/tests/app/services/model_install/test_model_install.py @@ -95,7 +95,7 @@ def test_install( store = mm2_installer.record_store key = mm2_installer.install_path(embedding_file) model_record = store.get_model(key) - assert model_record.path.endswith("sd-1/embedding/test_embedding.safetensors") + assert model_record.path.endswith(f"{key}/test_embedding.safetensors") assert (mm2_app_config.models_path / model_record.path).exists() assert model_record.source == embedding_file.as_posix() @@ -106,23 +106,24 @@ def test_rename( store = mm2_installer.record_store key = mm2_installer.install_path(embedding_file) model_record = store.get_model(key) - assert model_record.path.endswith("sd-1/embedding/test_embedding.safetensors") + assert model_record.path.endswith(f"{key}/test_embedding.safetensors") new_model_record = store.update_model(key, ModelRecordChanges(name="new model name", base=BaseModelType("sd-2"))) # Renaming the model record shouldn't rename the file assert new_model_record.name == "new model name" - assert new_model_record.path.endswith("sd-2/embedding/test_embedding.safetensors") + assert model_record.path.endswith(f"{key}/test_embedding.safetensors") @pytest.mark.parametrize( - "fixture_name,size,destination", + "fixture_name,size,key,destination", [ - ("embedding_file", 15440, "sd-1/embedding/test_embedding.safetensors"), - ("diffusers_dir", 8241 if OS == "Windows" else 7907, "sdxl/main/test-diffusers-main"), # EOL chars + ("embedding_file", 15440, "foo", "foo/test_embedding.safetensors"), + ("diffusers_dir", 8241 if OS == "Windows" else 7907, "bar", "bar"), # EOL chars ], ) def test_background_install( mm2_installer: ModelInstallServiceBase, fixture_name: str, + key: str, size: int, destination: str, mm2_app_config: InvokeAIAppConfig, @@ -132,7 +133,7 @@ def test_background_install( path: Path = request.getfixturevalue(fixture_name) description = "Test of metadata assignment" source = LocalModelSource(path=path, inplace=False) - job = mm2_installer.import_model(source, config=ModelRecordChanges(description=description)) + job = mm2_installer.import_model(source, config=ModelRecordChanges(key=key, description=description)) assert job is not None assert isinstance(job, ModelInstallJob)