Skip to content

Commit

Permalink
feat: delete model
Browse files Browse the repository at this point in the history
  • Loading branch information
feloy committed Jan 25, 2024
1 parent 6c5ca63 commit bd11b63
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 9 deletions.
42 changes: 36 additions & 6 deletions packages/backend/src/managers/modelsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 type { ModelInfo } from '@shared/src/models/IModelInfo';

export class ModelsManager {
#modelsDir: string;
#localModels: Map<string, LocalModelInfo>;
// models being deleted
#deleted: Set<string>;

constructor(
private appUserDirectory: string,
Expand All @@ -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);
Expand All @@ -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 {
Expand Down Expand Up @@ -88,4 +103,19 @@ export class ModelsManager {
getLocalModels(): LocalModelInfo[] {
return Array.from(this.#localModels.values());
}

async deleteLocalModel(modelId: string): Promise<void> {
const modelDir = this.getLocalModelPath(modelId);
this.#deleted.add(modelId);
await this.sendModelsInfo();
try {
await fs.promises.rm(modelDir, { recursive: true });
this.#localModels.delete(modelId);
} catch (err: unknown) {
console.error('unable to delete model', modelId);
} finally {
this.#deleted.delete(modelId);
await this.sendModelsInfo();
}
}
}
4 changes: 4 additions & 0 deletions packages/backend/src/studio-api-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,8 @@ export class StudioApiImpl implements StudioAPI {
async getCatalog(): Promise<Catalog> {
return this.catalogManager.getCatalog();
}

async deleteLocalModel(modelId: string): Promise<void> {
await this.modelsManager.deleteLocalModel(modelId);
}
}
51 changes: 51 additions & 0 deletions packages/frontend/src/lib/button/ListItemButtonIcon.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script lang="ts">
import type { IconDefinition } from '@fortawesome/free-solid-svg-icons';
import Fa from 'svelte-fa';
export let title: string;
export let icon: IconDefinition;
export let hidden = false;
export let enabled: boolean = true;
export let onClick: () => void = () => {console.log('==> 0')};
export let detailed = false;
export let inProgress = false;
export let iconOffset = '';
let positionLeftClass = 'left-1';
if (detailed) positionLeftClass = 'left-2';
let positionTopClass = 'top-1';
if (detailed) positionTopClass = '[0.2rem]';
const buttonDetailedClass =
'text-gray-400 bg-charcoal-800 hover:text-violet-600 font-medium rounded-lg text-sm inline-flex items-center px-3 py-2 text-center';
const buttonDetailedDisabledClass =
'text-gray-900 bg-charcoal-800 font-medium rounded-lg text-sm inline-flex items-center px-3 py-2 text-center';
const buttonClass =
'm-0.5 text-gray-400 hover:bg-charcoal-600 hover:text-violet-600 font-medium rounded-full inline-flex items-center px-2 py-2 text-center';
const buttonDisabledClass =
'm-0.5 text-gray-900 font-medium rounded-full inline-flex items-center px-2 py-2 text-center';
$: handleClick = enabled && !inProgress ? onClick : () => { console.log('==> 1')};
$: styleClass = detailed
? enabled && !inProgress
? buttonDetailedClass
: buttonDetailedDisabledClass
: enabled && !inProgress
? buttonClass
: buttonDisabledClass;
</script>

<button
title="{title}"
aria-label="{title}"
on:click="{handleClick}"
class="{styleClass} relative"
class:disabled="{inProgress}"
class:hidden="{hidden}">
<Fa class="h-4 w-4 {iconOffset}" icon="{icon}" />
<div
aria-label="spinner"
class="w-6 h-6 rounded-full animate-spin border border-solid border-violet-500 border-t-transparent absolute {positionTopClass} {positionLeftClass}"
class:hidden="{!inProgress}">
</div>
</button>
19 changes: 19 additions & 0 deletions packages/frontend/src/lib/table/model/ModelColumnActions.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script lang="ts">
import type { ModelInfo } from "@shared/src/models/IModelInfo";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import ListItemButtonIcon from "../../button/ListItemButtonIcon.svelte";
import { studioClient } from "/@/utils/client";
export let object: ModelInfo;
function deleteModel() {
console.log('delete model', object.id);
studioClient.deleteLocalModel(object.id);
}
</script>

<ListItemButtonIcon
icon={faTrash}
onClick={() => deleteModel()}
title="Delete Model"
enabled={!object.state}
/>
4 changes: 3 additions & 1 deletion packages/frontend/src/pages/Models.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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<ModelInfo>[] = [
new Column<ModelInfo>('Name', { width: '3fr', renderer: ModelColumnName }),
Expand All @@ -25,6 +26,7 @@ const columns: Column<ModelInfo>[] = [
new Column<ModelInfo>('Registry', { width: '2fr', renderer: ModelColumnRegistry }),
new Column<ModelInfo>('Popularity', { width: '1fr', renderer: ModelColumnPopularity }),
new Column<ModelInfo>('License', { width: '2fr', renderer: ModelColumnLicense }),
new Column<ModelInfo>('Actions', { align: 'right', width: '1fr', renderer: ModelColumnActions }),
];
const row = new Row<ModelInfo>({});
Expand Down
7 changes: 5 additions & 2 deletions packages/shared/src/StudioAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ export abstract class StudioAPI {
abstract pullApplication(recipeId: string): Promise<void>;
abstract openURL(url: string): Promise<boolean>;
/**
* 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<ModelInfo[]>;

/**
* Delete the folder containing the model from local storage
*/
abstract deleteLocalModel(modelId: string): Promise<void>;
abstract startPlayground(modelId: string): Promise<void>;
abstract stopPlayground(modelId: string): Promise<void>;
abstract askPlayground(modelId: string, prompt: string): Promise<number>;
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/models/IModelInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export interface ModelInfo {
license: string;
url: string;
file?: LocalModelInfo;
state?: 'deleting';
}

0 comments on commit bd11b63

Please sign in to comment.