From d927a74e748ea700a9a39a5849fa5e95113f9f1d Mon Sep 17 00:00:00 2001 From: axel7083 <42176370+axel7083@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:18:46 +0100 Subject: [PATCH] feat: playground navigate to container (#192) * feat: playground navigate to container * test: ensuring api is called when use container icon Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> * fix: prettier Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> * test: fix missing mockReturnValue Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> --------- Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> --- packages/backend/src/studio-api-impl.ts | 4 ++ .../frontend/src/lib/button/Button.svelte | 2 +- .../src/lib/images/ContainerIcon.svelte | 45 +++++++++++++++++ .../src/pages/ModelPlayground.spec.ts | 48 +++++++++++++++++++ .../frontend/src/pages/ModelPlayground.svelte | 15 ++++++ packages/shared/src/StudioAPI.ts | 2 +- 6 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 packages/frontend/src/lib/images/ContainerIcon.svelte diff --git a/packages/backend/src/studio-api-impl.ts b/packages/backend/src/studio-api-impl.ts index f6742e6b8..87efd751f 100644 --- a/packages/backend/src/studio-api-impl.ts +++ b/packages/backend/src/studio-api-impl.ts @@ -122,4 +122,8 @@ export class StudioApiImpl implements StudioAPI { async getModelsDirectory(): Promise { return this.modelsManager.getModelsDirectory(); } + + navigateToContainer(containerId: string): Promise { + return podmanDesktopApi.navigation.navigateToContainer(containerId); + } } diff --git a/packages/frontend/src/lib/button/Button.svelte b/packages/frontend/src/lib/button/Button.svelte index 23bdb127f..58c84939f 100644 --- a/packages/frontend/src/lib/button/Button.svelte +++ b/packages/frontend/src/lib/button/Button.svelte @@ -73,7 +73,7 @@ $: { {:else if iconType === 'fa'} {:else if iconType === 'unknown'} - + {/if} {#if $$slots.default} diff --git a/packages/frontend/src/lib/images/ContainerIcon.svelte b/packages/frontend/src/lib/images/ContainerIcon.svelte new file mode 100644 index 000000000..7b4bbce47 --- /dev/null +++ b/packages/frontend/src/lib/images/ContainerIcon.svelte @@ -0,0 +1,45 @@ + + + + {#if solid} + + + + + + + + + + + + {:else} + + + + + + {/if} + diff --git a/packages/frontend/src/pages/ModelPlayground.spec.ts b/packages/frontend/src/pages/ModelPlayground.spec.ts index e62c37737..d795e7591 100644 --- a/packages/frontend/src/pages/ModelPlayground.spec.ts +++ b/packages/frontend/src/pages/ModelPlayground.spec.ts @@ -6,6 +6,7 @@ import type { ModelInfo } from '@shared/src/models/IModelInfo'; const mocks = vi.hoisted(() => { return { + navigateToContainerMock: vi.fn(), startPlaygroundMock: vi.fn(), askPlaygroundMock: vi.fn(), getPlaygroundsStateMock: vi.fn().mockImplementation(() => Promise.resolve([])), @@ -16,12 +17,20 @@ const mocks = vi.hoisted(() => { return () => {}; }, }, + playgroundStatesSubscribeMock: vi.fn().mockReturnValue([]), + playgroundStatesMock: { + subscribe: (f: (msg: any) => void) => { + f(mocks.playgroundStatesSubscribeMock()); + return () => {}; + }, + }, }; }); vi.mock('../utils/client', async () => { return { studioClient: { + navigateToContainer: mocks.navigateToContainerMock, getPlaygroundsState: mocks.getPlaygroundsStateMock, startPlayground: mocks.startPlaygroundMock, askPlayground: mocks.askPlaygroundMock, @@ -43,6 +52,12 @@ vi.mock('../stores/playground-queries', async () => { }; }); +vi.mock('../stores/playground-states', async () => { + return { + playgroundStates: mocks.playgroundStatesMock, + }; +}); + beforeEach(() => { vi.clearAllMocks(); }); @@ -168,3 +183,36 @@ test('should display error alert', async () => { expect(alert).toBeDefined(); }); }); + +test('playground container icon should redirect', async () => { + mocks.playgroundQueriesSubscribeMock.mockReturnValue([]); + mocks.playgroundStatesSubscribeMock.mockReturnValue([ + { + modelId: 'model1', + container: { + containerId: 'dummy-container-id', + }, + }, + ]); + render(ModelPlayground, { + model: { + id: 'model1', + name: 'Model 1', + description: 'A description', + hw: 'CPU', + registry: 'Hugging Face', + popularity: 3, + license: '?', + url: 'https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGUF/resolve/main/llama-2-7b-chat.Q5_K_S.gguf', + } as ModelInfo, + }); + + await waitFor(async () => { + const navigateTo = screen.getByTitle('navigate-to-container'); + expect(navigateTo).toBeDefined(); + + await fireEvent.click(navigateTo); + }); + + expect(mocks.navigateToContainerMock).toHaveBeenNthCalledWith(1, 'dummy-container-id'); +}); diff --git a/packages/frontend/src/pages/ModelPlayground.svelte b/packages/frontend/src/pages/ModelPlayground.svelte index 96b317f3b..c6b7f8585 100644 --- a/packages/frontend/src/pages/ModelPlayground.svelte +++ b/packages/frontend/src/pages/ModelPlayground.svelte @@ -12,6 +12,7 @@ export let model: ModelInfo | undefined; import Fa from 'svelte-fa'; import { faPlay, faStop, faInfo, faWarning } from '@fortawesome/free-solid-svg-icons'; + import ContainerIcon from '/@/lib/images/ContainerIcon.svelte'; import ErrorMessage from '/@/lib/ErrorMessage.svelte'; let prompt = ''; @@ -157,6 +158,17 @@ return true; } } + + const navigateToContainer = () => { + if(playgroundState?.container?.containerId === undefined) + return; + + try { + studioClient.navigateToContainer(playgroundState?.container?.containerId); + } catch(err) { + console.error(err); + } + }
@@ -170,6 +182,9 @@ {#key playgroundState?.status} Playground {playgroundState?.status}
diff --git a/packages/shared/src/StudioAPI.ts b/packages/shared/src/StudioAPI.ts index 07c81f97b..76b0b9bd2 100644 --- a/packages/shared/src/StudioAPI.ts +++ b/packages/shared/src/StudioAPI.ts @@ -25,6 +25,6 @@ export abstract class StudioAPI { abstract askPlayground(modelId: string, prompt: string): Promise; abstract getPlaygroundQueriesState(): Promise; abstract getPlaygroundsState(): Promise; - abstract getModelsDirectory(): Promise; + abstract navigateToContainer(containerId: string): Promise; }