diff --git a/py/routes/checkpoints_routes.py b/py/routes/checkpoints_routes.py index 5fc5fa73..712b73bd 100644 --- a/py/routes/checkpoints_routes.py +++ b/py/routes/checkpoints_routes.py @@ -62,6 +62,9 @@ class CheckpointsRoutes: app.router.add_get('/api/checkpoints/find-duplicates', self.find_duplicate_checkpoints) app.router.add_get('/api/checkpoints/find-filename-conflicts', self.find_filename_conflicts) + # Add new endpoint for bulk deleting checkpoints + app.router.add_post('/api/checkpoints/bulk-delete', self.bulk_delete_checkpoints) + async def get_checkpoints(self, request): """Get paginated checkpoint data""" try: @@ -793,3 +796,18 @@ class CheckpointsRoutes: "success": False, "error": str(e) }, status=500) + + async def bulk_delete_checkpoints(self, request: web.Request) -> web.Response: + """Handle bulk deletion of checkpoint models""" + try: + if self.scanner is None: + self.scanner = await ServiceRegistry.get_checkpoint_scanner() + + return await ModelRouteUtils.handle_bulk_delete_models(request, self.scanner) + + except Exception as e: + logger.error(f"Error in bulk delete checkpoints: {e}", exc_info=True) + return web.json_response({ + 'success': False, + 'error': str(e) + }, status=500) diff --git a/py/services/model_scanner.py b/py/services/model_scanner.py index 29058136..e3d70f0c 100644 --- a/py/services/model_scanner.py +++ b/py/services/model_scanner.py @@ -133,7 +133,7 @@ class ModelScanner: os.rename(temp_path, cache_path) logger.info(f"Saved {self.model_type} cache with {len(self._cache.raw_data)} models to {cache_path}") - logger.info(f"Hash index stats - hash_to_path: {len(self._hash_index._hash_to_path)}, filename_to_hash: {len(self._hash_index._filename_to_hash)}, duplicate_hashes: {len(self._hash_index._duplicate_hashes)}, duplicate_filenames: {len(self._hash_index._duplicate_filenames)}") + logger.debug(f"Hash index stats - hash_to_path: {len(self._hash_index._hash_to_path)}, filename_to_hash: {len(self._hash_index._filename_to_hash)}, duplicate_hashes: {len(self._hash_index._duplicate_hashes)}, duplicate_filenames: {len(self._hash_index._duplicate_filenames)}") return True except Exception as e: logger.error(f"Error saving {self.model_type} cache to disk: {e}") diff --git a/static/js/checkpoints.js b/static/js/checkpoints.js index f2ccd20e..68b5cb8b 100644 --- a/static/js/checkpoints.js +++ b/static/js/checkpoints.js @@ -16,7 +16,7 @@ class CheckpointsPageManager { window.checkpointDownloadManager = new CheckpointDownloadManager(); // Initialize the ModelDuplicatesManager - this.duplicatesManager = new ModelDuplicatesManager(this); + this.duplicatesManager = new ModelDuplicatesManager(this, 'checkpoints'); // Expose only necessary functions to global scope this._exposeRequiredGlobalFunctions(); diff --git a/static/js/components/ModelDuplicatesManager.js b/static/js/components/ModelDuplicatesManager.js index 8b0dc549..c9652759 100644 --- a/static/js/components/ModelDuplicatesManager.js +++ b/static/js/components/ModelDuplicatesManager.js @@ -1,16 +1,15 @@ // Model Duplicates Manager Component for LoRAs and Checkpoints import { showToast } from '../utils/uiHelpers.js'; import { state, getCurrentPageState } from '../state/index.js'; -import { initializeInfiniteScroll } from '../utils/infiniteScroll.js'; import { formatDate } from '../utils/formatters.js'; export class ModelDuplicatesManager { - constructor(pageManager) { + constructor(pageManager, modelType = 'loras') { this.pageManager = pageManager; this.duplicateGroups = []; this.inDuplicateMode = false; this.selectedForDeletion = new Set(); - this.modelType = 'loras'; // Default to loras, could be 'checkpoints' + this.modelType = modelType; // Use the provided modelType or default to 'loras' // Bind methods this.renderModelCard = this.renderModelCard.bind(this); @@ -91,11 +90,11 @@ export class ModelDuplicatesManager { // Instead of trying to restore the virtual scroller, // simply redirect to reload the page // TODO: While this is a workaround rather than a deep fix, it's a pragmatic solution that will immediately resolve the issue for users. We can investigate the underlying cause more thoroughly later when there's time for more extensive debugging. - window.location.href = '/loras'; + window.location.href = `/${this.modelType}`; } renderDuplicateGroups() { - const modelGrid = document.getElementById('loraGrid'); + const modelGrid = document.getElementById(this.modelType === 'loras' ? 'loraGrid' : 'checkpointGrid'); if (!modelGrid) return; // Clear existing content diff --git a/templates/checkpoints.html b/templates/checkpoints.html index a455030f..024e4f9f 100644 --- a/templates/checkpoints.html +++ b/templates/checkpoints.html @@ -31,6 +31,23 @@ {% block content %} {% include 'components/controls.html' %} + + +
+