From fc473e76d4dc237a2aa3272d9515cb50aba0826e Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 25 Jan 2024 11:01:39 +0100 Subject: [PATCH] feat: delete model --- .../backend/src/managers/modelsManager.ts | 42 ++++++++++++--- packages/backend/src/studio-api-impl.ts | 4 ++ .../src/lib/button/ListItemButtonIcon.svelte | 51 +++++++++++++++++++ .../lib/table/model/ModelColumnActions.svelte | 19 +++++++ packages/frontend/src/pages/Models.svelte | 4 +- packages/shared/src/StudioAPI.ts | 7 ++- packages/shared/src/models/IModelInfo.ts | 1 + 7 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 packages/frontend/src/lib/button/ListItemButtonIcon.svelte create mode 100644 packages/frontend/src/lib/table/model/ModelColumnActions.svelte diff --git a/packages/backend/src/managers/modelsManager.ts b/packages/backend/src/managers/modelsManager.ts index b2a266222..3da4da79d 100644 --- a/packages/backend/src/managers/modelsManager.ts +++ b/packages/backend/src/managers/modelsManager.ts @@ -4,10 +4,13 @@ import * as path from 'node:path'; import { type Webview, fs as apiFs } from '@podman-desktop/api'; import { MSG_NEW_LOCAL_MODELS_STATE } from '@shared/Messages'; import type { CatalogManager } from './catalogManager'; +import { ModelInfo } from '@shared/src/models/IModelInfo'; export class ModelsManager { #modelsDir: string; #localModels: Map; + // models being deleted + #deleted: Set; constructor( private appUserDirectory: string, @@ -16,16 +19,13 @@ export class ModelsManager { ) { this.#modelsDir = path.join(this.appUserDirectory, 'models'); this.#localModels = new Map(); + this.#deleted = new Set(); } async loadLocalModels() { const reloadLocalModels = async () => { this.getLocalModelsFromDisk(); - const models = this.getModelsInfo(); - await this.webview.postMessage({ - id: MSG_NEW_LOCAL_MODELS_STATE, - body: models, - }); + await this.sendModelsInfo(); }; const watcher = apiFs.createFileSystemWatcher(this.#modelsDir); watcher.onDidCreate(reloadLocalModels); @@ -39,7 +39,22 @@ export class ModelsManager { return this.catalogManager .getModels() .filter(m => this.#localModels.has(m.id)) - .map(m => ({ ...m, file: this.#localModels.get(m.id) })); + .map( + m => + ({ + ...m, + file: this.#localModels.get(m.id), + state: this.#deleted.has(m.id) ? 'deleting' : undefined, + }) as ModelInfo, + ); + } + + async sendModelsInfo() { + const models = this.getModelsInfo(); + await this.webview.postMessage({ + id: MSG_NEW_LOCAL_MODELS_STATE, + body: models, + }); } getLocalModelsFromDisk(): void { @@ -88,4 +103,19 @@ export class ModelsManager { getLocalModels(): LocalModelInfo[] { return Array.from(this.#localModels.values()); } + + async deleteLocalModel(modelId: string): Promise { + const modelDir = this.getLocalModelPath(modelId); + this.#deleted.add(modelId); + await this.sendModelsInfo(); + fs.rm(modelDir, { recursive: true }, async (err: unknown) => { + if (err) { + console.error('unable to delete model', modelId); + } else { + this.#localModels.delete(modelId); + } + this.#deleted.delete(modelId); + await this.sendModelsInfo(); + }); + } } diff --git a/packages/backend/src/studio-api-impl.ts b/packages/backend/src/studio-api-impl.ts index cc340f9f4..e166faa69 100644 --- a/packages/backend/src/studio-api-impl.ts +++ b/packages/backend/src/studio-api-impl.ts @@ -107,4 +107,8 @@ export class StudioApiImpl implements StudioAPI { async getCatalog(): Promise { return this.catalogManager.getCatalog(); } + + async deleteLocalModel(modelId: string): Promise { + await this.modelsManager.deleteLocalModel(modelId); + } } diff --git a/packages/frontend/src/lib/button/ListItemButtonIcon.svelte b/packages/frontend/src/lib/button/ListItemButtonIcon.svelte new file mode 100644 index 000000000..66e083598 --- /dev/null +++ b/packages/frontend/src/lib/button/ListItemButtonIcon.svelte @@ -0,0 +1,51 @@ + + + diff --git a/packages/frontend/src/lib/table/model/ModelColumnActions.svelte b/packages/frontend/src/lib/table/model/ModelColumnActions.svelte new file mode 100644 index 000000000..629f6b0d7 --- /dev/null +++ b/packages/frontend/src/lib/table/model/ModelColumnActions.svelte @@ -0,0 +1,19 @@ + + + deleteModel()} + title="Delete Model" + enabled={!object.state} +/> diff --git a/packages/frontend/src/pages/Models.svelte b/packages/frontend/src/pages/Models.svelte index 4420ec43b..964d97476 100644 --- a/packages/frontend/src/pages/Models.svelte +++ b/packages/frontend/src/pages/Models.svelte @@ -15,7 +15,8 @@ import Card from '/@/lib/Card.svelte'; import { modelsPulling } from '../stores/recipe'; import { onMount } from 'svelte'; import ModelColumnSize from '../lib/table/model/ModelColumnSize.svelte'; - import ModelColumnCreation from '../lib/table/model/ModelColumnCreation.svelte'; +import ModelColumnCreation from '../lib/table/model/ModelColumnCreation.svelte'; +import ModelColumnActions from '../lib/table/model/ModelColumnActions.svelte'; const columns: Column[] = [ new Column('Name', { width: '3fr', renderer: ModelColumnName }), @@ -25,6 +26,7 @@ const columns: Column[] = [ new Column('Registry', { width: '2fr', renderer: ModelColumnRegistry }), new Column('Popularity', { width: '1fr', renderer: ModelColumnPopularity }), new Column('License', { width: '2fr', renderer: ModelColumnLicense }), + new Column('Actions', { align: 'right', width: '1fr', renderer: ModelColumnActions }), ]; const row = new Row({}); diff --git a/packages/shared/src/StudioAPI.ts b/packages/shared/src/StudioAPI.ts index 5ea1990eb..98928932b 100644 --- a/packages/shared/src/StudioAPI.ts +++ b/packages/shared/src/StudioAPI.ts @@ -12,10 +12,13 @@ export abstract class StudioAPI { abstract pullApplication(recipeId: string): Promise; abstract openURL(url: string): Promise; /** - * Get the information of models saved locally into the extension's storage directory + * Get the information of models saved locally into the user's directory */ abstract getLocalModels(): Promise; - + /** + * Delete the folder containing the model from local storage + */ + abstract deleteLocalModel(modelId: string): Promise; abstract startPlayground(modelId: string): Promise; abstract stopPlayground(modelId: string): Promise; abstract askPlayground(modelId: string, prompt: string): Promise; diff --git a/packages/shared/src/models/IModelInfo.ts b/packages/shared/src/models/IModelInfo.ts index 422078fce..ac5eea068 100644 --- a/packages/shared/src/models/IModelInfo.ts +++ b/packages/shared/src/models/IModelInfo.ts @@ -10,4 +10,5 @@ export interface ModelInfo { license: string; url: string; file?: LocalModelInfo; + state?: 'deleting'; }