From ade23585792b3f84b09ebece796ae670a62c3cb9 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Fri, 19 Jan 2024 15:28:21 +0100 Subject: [PATCH 1/3] send application tasks to frontend when updated --- .../src/managers/applicationManager.ts | 6 +++- .../src/registries/RecipeStatusRegistry.ts | 16 ++++++++++- packages/backend/src/studio-api-impl.ts | 4 +++ packages/backend/src/studio.ts | 2 +- packages/frontend/src/pages/Recipe.svelte | 28 ++----------------- packages/frontend/src/stores/recipe.ts | 18 ++++++++++++ packages/shared/Messages.ts | 1 + packages/shared/src/StudioAPI.ts | 1 + 8 files changed, 48 insertions(+), 28 deletions(-) create mode 100644 packages/frontend/src/stores/recipe.ts diff --git a/packages/backend/src/managers/applicationManager.ts b/packages/backend/src/managers/applicationManager.ts index 08a72253a..de7819d6c 100644 --- a/packages/backend/src/managers/applicationManager.ts +++ b/packages/backend/src/managers/applicationManager.ts @@ -256,11 +256,15 @@ export class ApplicationManager { } } + let previousProgressValue = -1; resp.on('data', chunk => { progress += chunk.length; const progressValue = (progress * 100) / totalFileSize; - taskUtil.setTaskProgress(modelId, progressValue); + if ((progressValue - previousProgressValue) > 1) { + previousProgressValue = progressValue; + taskUtil.setTaskProgress(modelId, progressValue); + } // send progress in percentage (ex. 1.2%, 2.6%, 80.1%) to frontend //this.sendProgress(progressValue); diff --git a/packages/backend/src/registries/RecipeStatusRegistry.ts b/packages/backend/src/registries/RecipeStatusRegistry.ts index 0a8779817..ac96899e7 100644 --- a/packages/backend/src/registries/RecipeStatusRegistry.ts +++ b/packages/backend/src/registries/RecipeStatusRegistry.ts @@ -18,11 +18,13 @@ import type { RecipeStatus } from '@shared/src/models/IRecipeStatus'; import type { TaskRegistry } from './TaskRegistry'; +import type { Webview } from '@podman-desktop/api'; +import { MSG_NEW_RECIPE_STATE } from '@shared/Messages'; export class RecipeStatusRegistry { private statuses: Map = new Map(); - constructor(private taskRegistry: TaskRegistry) {} + constructor(private taskRegistry: TaskRegistry, private webview: Webview) {} setStatus(recipeId: string, status: RecipeStatus) { // Update the TaskRegistry @@ -30,9 +32,21 @@ export class RecipeStatusRegistry { status.tasks.map(task => this.taskRegistry.set(task)); } this.statuses.set(recipeId, status); + this.dispatchState(); // we don't want to wait } getStatus(recipeId: string): RecipeStatus | undefined { return this.statuses.get(recipeId); } + + getStatuses(): Map { + return this.statuses; + } + + private async dispatchState() { + await this.webview.postMessage({ + id: MSG_NEW_RECIPE_STATE, + body: this.statuses, + }); + } } diff --git a/packages/backend/src/studio-api-impl.ts b/packages/backend/src/studio-api-impl.ts index a36f268e5..f9ee00255 100644 --- a/packages/backend/src/studio-api-impl.ts +++ b/packages/backend/src/studio-api-impl.ts @@ -53,6 +53,10 @@ export class StudioApiImpl implements StudioAPI { return this.recipeStatusRegistry.getStatus(recipeId); } + async getPullingStatuses(): Promise> { + return this.recipeStatusRegistry.getStatuses(); + } + async getModelById(modelId: string): Promise { // TODO: move logic to catalog manager const model = this.catalogManager.getModels().find(m => modelId === m.id); diff --git a/packages/backend/src/studio.ts b/packages/backend/src/studio.ts index 15db733c0..4f61f260c 100644 --- a/packages/backend/src/studio.ts +++ b/packages/backend/src/studio.ts @@ -92,7 +92,7 @@ export class Studio { this.rpcExtension = new RpcExtension(this.#panel.webview); const gitManager = new GitManager(); const taskRegistry = new TaskRegistry(); - const recipeStatusRegistry = new RecipeStatusRegistry(taskRegistry); + const recipeStatusRegistry = new RecipeStatusRegistry(taskRegistry, this.#panel.webview); const applicationManager = new ApplicationManager(gitManager, recipeStatusRegistry, this.#extensionContext); this.playgroundManager = new PlayGroundManager(this.#panel.webview); // Create catalog manager, responsible for loading the catalog files and watching for changes diff --git a/packages/frontend/src/pages/Recipe.svelte b/packages/frontend/src/pages/Recipe.svelte index 635c64217..8cf5beddc 100644 --- a/packages/frontend/src/pages/Recipe.svelte +++ b/packages/frontend/src/pages/Recipe.svelte @@ -1,6 +1,5 @@ diff --git a/packages/frontend/src/pages/Recipe.spec.ts b/packages/frontend/src/pages/Recipe.spec.ts index a12e52b3b..fdbf2cd3d 100644 --- a/packages/frontend/src/pages/Recipe.spec.ts +++ b/packages/frontend/src/pages/Recipe.spec.ts @@ -6,6 +6,7 @@ import Recipe from './Recipe.svelte'; const mocks = vi.hoisted(() => { return { getCatalogMock: vi.fn(), + getPullingStatusesMock: vi.fn(), }; }); @@ -13,6 +14,7 @@ vi.mock('../utils/client', async () => { return { studioClient: { getCatalog: mocks.getCatalogMock, + getPullingStatuses: mocks.getPullingStatusesMock, }, rpcBrowser: { subscribe: () => { @@ -29,6 +31,7 @@ test('should display recipe information', async () => { expect(recipe).not.toBeUndefined(); mocks.getCatalogMock.mockResolvedValue(catalog); + mocks.getPullingStatusesMock.mockResolvedValue(new Map()); render(Recipe, { recipeId: 'recipe 1', }); diff --git a/packages/frontend/src/stores/recipe.ts b/packages/frontend/src/stores/recipe.ts index 888251376..b4d7d400c 100644 --- a/packages/frontend/src/stores/recipe.ts +++ b/packages/frontend/src/stores/recipe.ts @@ -1,18 +1,27 @@ import type { Readable } from 'svelte/store'; -import { readable } from 'svelte/store'; +import { derived, readable } from 'svelte/store'; import { MSG_NEW_RECIPE_STATE } from '@shared/Messages'; import { rpcBrowser, studioClient } from '/@/utils/client'; import type { RecipeStatus } from '@shared/src/models/IRecipeStatus'; -export const recipes: Readable> = readable>(new Map(), set => { - const sub = rpcBrowser.subscribe(MSG_NEW_RECIPE_STATE, msg => { - set(msg); - }); - // Initialize the store manually - studioClient.getPullingStatuses().then(state => { - set(state); - }); - return () => { - sub.unsubscribe(); - }; +export const recipes: Readable> = readable>( + new Map(), + set => { + const sub = rpcBrowser.subscribe(MSG_NEW_RECIPE_STATE, msg => { + set(msg); + }); + // Initialize the store manually + studioClient.getPullingStatuses().then(state => { + set(state); + }); + return () => { + sub.unsubscribe(); + }; + }, +); + +export const modelsPulling = derived(recipes, $recipes => { + return Array.from($recipes.values()) + .flatMap(recipe => recipe.tasks) + .filter(task => 'model-pulling' in (task.labels || {})); }); diff --git a/packages/shared/src/StudioAPI.ts b/packages/shared/src/StudioAPI.ts index 2ec821058..5ea1990eb 100644 --- a/packages/shared/src/StudioAPI.ts +++ b/packages/shared/src/StudioAPI.ts @@ -1,6 +1,5 @@ import type { RecipeStatus } from './models/IRecipeStatus'; import type { ModelInfo } from './models/IModelInfo'; -import type { Task } from './models/ITask'; import type { QueryState } from './models/IPlaygroundQueryState'; import type { Catalog } from './models/ICatalog'; import type { PlaygroundState } from './models/IPlaygroundState'; @@ -20,14 +19,6 @@ export abstract class StudioAPI { abstract startPlayground(modelId: string): Promise; abstract stopPlayground(modelId: string): Promise; abstract askPlayground(modelId: string, prompt: string): Promise; - - /** - * Get task by label - * @param label - */ - abstract getTasksByLabel(label: string): Promise; - abstract getPlaygroundQueriesState(): Promise; - abstract getPlaygroundsState(): Promise; } From 3af3920bf63d437f7c0fbb98bb48e9f148b9ae37 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Mon, 22 Jan 2024 11:19:26 +0100 Subject: [PATCH 3/3] fix import --- packages/frontend/src/pages/Recipe.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/Recipe.svelte b/packages/frontend/src/pages/Recipe.svelte index 8cf5beddc..3736ed6ff 100644 --- a/packages/frontend/src/pages/Recipe.svelte +++ b/packages/frontend/src/pages/Recipe.svelte @@ -14,7 +14,7 @@ import { getDisplayName } from '/@/utils/versionControlUtils'; import { getIcon } from '/@/utils/categoriesUtils'; import RecipeModels from './RecipeModels.svelte'; import { catalog } from '/@/stores/catalog'; - import { recipes } from '../stores/recipe'; + import { recipes } from '/@/stores/recipe'; export let recipeId: string;