[enhancement]: Multi-select delete for models and loras
Is there an existing issue for this?
- [X] I have searched the existing issues
Contact Details
Discord: @kingsman
What should this feature add?
When in the Model Manager, I decided to delete a bunch of loras that I no longer wanted by going to the loras folder and deleting them as I thought it would clear them out of InvokeAI Model Manager since there wasn't an option to select multiple and then delete. The one's I deleted are still in the Model Manager.
This feature should allow the user to select a checkbox next to each model/lora they would like to delete and when done selecting, click the Delete button which then will remove them from the Model Manager.
Alternatives
No response
Additional Content
No response
+1
I also need this (takes way too much time to delete each one by one)
I think the best approach would be to have a button on the models page
next to + Add Models something like Resolve Missing Models, which
would automatically remove entries from the DB by missing files.
Besides the select-option variant OP mentioned, I think both are needed.
Model management is tedious when you need to prune models. When you boot up Invoke it does a scan of the model library so it knows which models are missing it should reflect it in the ui, we could dim the missing models and add a multi selection option. IMHO it would be a huge usability upgrade with data that is already available.
Here's a simple Python code I wrote to do this directly via CLI:
import sqlite3
import json
import os
import shutil as sh
from typing import Any
ROOT_PATH = os.path.dirname(__file__)
DB_FILE_PATH = os.path.join(ROOT_PATH, 'databases', 'invokeai.db')
MODELS_DIR_PREFIX = os.path.join(ROOT_PATH, 'autoimport').replace('\\', '/')
def run_query(connection:sqlite3.Connection, query:str, params:list[Any]|tuple[Any, ...]|None = None, commit:bool = False) -> list[Any]:
results = []
if not query.endswith(';'):
query = f'{query};'
try:
cursor = connection.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
results = cursor.fetchall()
if commit is True:
connection.commit()
except sqlite3.Error as exc:
print(f'> ERROR {exc.__class__.__name__}: {exc}\n', flush=True, end='')
results = []
return results
def create_backup() -> bool:
created = False
try:
db_backup = DB_FILE_PATH.replace('.db', '_BACKUP.db')
sh.copy(DB_FILE_PATH, db_backup)
print(f'> Created database backup: {db_backup}\n', flush=True, end='')
created = True
except IOError as exc:
print(f'> ERROR {exc.__class__.__name__}: {exc}\n', flush=True, end='')
return created
def clean_database(connection:sqlite3.Connection):
for path in (DB_FILE_PATH, MODELS_DIR_PREFIX):
if not os.path.exists(path):
return print(f'> ERROR: Path "{path}" not found\n', flush=True, end='')
table_name = 'models'
results = run_query(connection, 'SELECT id, config FROM `{0}`;'.format(table_name))
if not results:
print(f'> OK - database table `{table_name}` has no entries (nothing to operate on)\n', flush=True, end='')
else:
missing_model_ids = []
for row in results:
id = row[0]
config = json.loads(row[1])
path = config.get('path', '')
if path.startswith(MODELS_DIR_PREFIX) and not os.path.exists(path):
missing_model_ids.append(id)
missing_length = len(missing_model_ids)
if missing_length > 0:
if create_backup() is False:
return
run_query(connection, 'DELETE FROM `{0}` WHERE id IN ({1});'.format(table_name, ','.join('?'*missing_length)), missing_model_ids, True)
print(f'> OK - database table `{table_name}` cleaned (deleted {missing_length} entries with missing model-file)\n', flush=True, end='')
else:
print(f'> OK - database table `{table_name}` already cleaned (missing model-file entries not found)\n', flush=True, end='')
def main() -> int:
conn = None
try:
conn = sqlite3.connect(DB_FILE_PATH)
clean_database(conn)
except sqlite3.Error as exc:
print(f'> ERROR {exc.__class__.__name__}: {exc}\n', flush=True, end='')
finally:
if conn:
conn.close()
print('Done.')
return 0
if __name__ == '__main__':
raise SystemExit(main())
It cleans the database of missing model-file entries, you must put it in the InvokeAI installation directory for the script to work.
Also change the MODELS_DIR_PREFIX to you preferred
directory path from where you import models, I use the default
autoimport directory for everything and this script currently
checks only <InvokeAI-directory>/autoimport missing model-file entries.
And to expand on my previous comment the Resolve Missing Models
button could just call something alike this code and
refresh the list in-browser, but I don't know the implications,
just sharing.