Skip to content

Commit 7c9705a

Browse files
authored
Delete model artifacts (#5030)
1 parent 329429e commit 7c9705a

File tree

4 files changed

+77
-8
lines changed

4 files changed

+77
-8
lines changed

application/backend/app/api/dependencies.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,11 @@ def get_system_service() -> SystemService:
131131

132132

133133
def get_model_service(
134+
data_dir: Annotated[Path, Depends(get_data_dir)],
134135
db: Annotated[Session, Depends(get_db)],
135136
) -> ModelService:
136137
"""Provides a ModelService instance with the model reload event from the scheduler."""
137-
return ModelService(db_session=db)
138+
return ModelService(data_dir=data_dir, db_session=db)
138139

139140

140141
def get_webrtc_manager(request: Request) -> WebRTCManager:

application/backend/app/lifecycle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def setup_job_controller(data_dir: Path, max_parallel_jobs: int) -> tuple[JobQue
5656
subset_service=SubsetService(),
5757
subset_assigner=SubsetAssigner(),
5858
dataset_service=DatasetService(data_dir=data_dir, label_service=LabelService()),
59-
model_service=ModelService(),
59+
model_service=ModelService(data_dir=data_dir),
6060
training_configuration_service=TrainingConfigurationService(),
6161
data_dir=data_dir,
6262
db_session_factory=get_db_session,

application/backend/app/services/model_service.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
# Copyright (C) 2025 Intel Corporation
22
# SPDX-License-Identifier: Apache-2.0
33

4+
import shutil
45
from dataclasses import dataclass
6+
from pathlib import Path
57
from uuid import UUID
68

79
from sqlalchemy.exc import IntegrityError
10+
from sqlalchemy.orm import Session
811

912
from app.db.schema import ModelRevisionDB
1013
from app.models.training_configuration.configuration import TrainingConfiguration
@@ -31,6 +34,10 @@ class ModelRevisionMetadata:
3134
class ModelService(BaseSessionManagedService):
3235
"""Service to register and activate models"""
3336

37+
def __init__(self, data_dir: Path, db_session: Session | None = None) -> None:
38+
super().__init__(db_session)
39+
self._projects_dir = data_dir / "projects"
40+
3441
def get_model(self, project_id: UUID, model_id: UUID) -> ModelSchema:
3542
"""
3643
Get a model.
@@ -56,6 +63,9 @@ def delete_model(self, project_id: UUID, model_id: UUID) -> None:
5663
"""
5764
Delete a model.
5865
66+
Deletes a model revision from the database and deletes the folder from the filesystem
67+
associated with this model.
68+
5969
Args:
6070
project_id (UUID): The unique identifier of the project whose models to delete.
6171
model_id (UUID): The unique identifier of the model to delete.
@@ -69,8 +79,12 @@ def delete_model(self, project_id: UUID, model_id: UUID) -> None:
6979
(e.g., the model is referenced by other entities).
7080
"""
7181
model_rev_repo = ModelRevisionRepository(project_id=str(project_id), db=self.db_session)
82+
83+
path = self._projects_dir / str(project_id) / "models" / str(model_id)
84+
if path.exists():
85+
shutil.rmtree(path)
86+
7287
try:
73-
# TODO: delete model artifacts from filesystem when implemented
7488
deleted = model_rev_repo.delete(str(model_id))
7589
if not deleted:
7690
raise ResourceNotFoundError(ResourceType.MODEL, str(model_id))

application/backend/tests/integration/services/test_model_service.py

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright (C) 2025 Intel Corporation
22
# SPDX-License-Identifier: Apache-2.0
3+
from pathlib import Path
34
from uuid import UUID, uuid4
45

56
import pytest
@@ -28,9 +29,9 @@ def setup_project_with_models(
2829

2930

3031
@pytest.fixture
31-
def fxt_model_service(db_session: Session) -> ModelService:
32+
def fxt_model_service(tmp_path: Path, db_session: Session) -> ModelService:
3233
"""Fixture to create a ModelService instance."""
33-
return ModelService(db_session=db_session)
34+
return ModelService(data_dir=tmp_path, db_session=db_session)
3435

3536

3637
class TestModelServiceIntegration:
@@ -65,10 +66,63 @@ def test_non_existent_model(self, model_operation, fxt_project_id, fxt_db_projec
6566
assert excinfo.value.resource_type == ResourceType.MODEL
6667
assert excinfo.value.resource_id == str(model_id)
6768

68-
def test_delete_model(
69-
self, fxt_project_id: UUID, fxt_model_id: UUID, fxt_model_service: ModelService, db_session: Session
69+
def test_delete_model_no_files(
70+
self,
71+
tmp_path: Path,
72+
fxt_project_id: UUID,
73+
fxt_model_id: UUID,
74+
fxt_model_service: ModelService,
75+
db_session: Session,
7076
):
71-
"""Test deleting a model by ID."""
77+
"""Test that deleting a model removes its database record when no filesystem artifacts exist."""
78+
model_rev_path = tmp_path / "projects" / str(fxt_project_id) / "models" / str(fxt_model_id)
79+
7280
fxt_model_service.delete_model(project_id=fxt_project_id, model_id=fxt_model_id)
7381

7482
assert db_session.get(ModelRevisionDB, str(fxt_model_id)) is None
83+
assert not model_rev_path.exists()
84+
85+
def test_delete_model_with_files(
86+
self,
87+
tmp_path: Path,
88+
fxt_project_id: UUID,
89+
fxt_model_id: UUID,
90+
fxt_model_service: ModelService,
91+
db_session: Session,
92+
):
93+
"""Test that deleting a model removes both its database record and filesystem artifacts."""
94+
model_rev_path = tmp_path / "projects" / str(fxt_project_id) / "models" / str(fxt_model_id)
95+
model_rev_path.mkdir(parents=True, exist_ok=True)
96+
(model_rev_path / "config.yaml").touch()
97+
(model_rev_path / "training.log").touch()
98+
99+
fxt_model_service.delete_model(project_id=fxt_project_id, model_id=fxt_model_id)
100+
101+
assert db_session.get(ModelRevisionDB, str(fxt_model_id)) is None
102+
assert not model_rev_path.exists()
103+
104+
def test_delete_model_with_files_no_permission(
105+
self,
106+
tmp_path: Path,
107+
fxt_project_id: UUID,
108+
fxt_model_id: UUID,
109+
fxt_model_service: ModelService,
110+
db_session: Session,
111+
):
112+
"""Test that deleting a model removes both its database record and filesystem artifacts."""
113+
model_rev_path = tmp_path / "projects" / str(fxt_project_id) / "models" / str(fxt_model_id)
114+
model_rev_path.mkdir(parents=True, exist_ok=True)
115+
(model_rev_path / "config.yaml").touch()
116+
117+
# Make directory read-only to simulate permission error
118+
model_rev_path.chmod(0o444)
119+
120+
try:
121+
with pytest.raises(OSError):
122+
fxt_model_service.delete_model(project_id=fxt_project_id, model_id=fxt_model_id)
123+
124+
assert db_session.get(ModelRevisionDB, str(fxt_model_id)) is not None
125+
assert model_rev_path.exists()
126+
finally:
127+
# Cleanup: restore permissions so pytest can clean up temp directory
128+
model_rev_path.chmod(0o755)

0 commit comments

Comments
 (0)