Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve inference server details page #1113

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/frontend/src/lib/table/service/ServiceAction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,20 @@ test('should be disabled on transition', async () => {
const deleteBtn = screen.getByTitle('Delete service');
expect(deleteBtn.classList).toContain('text-gray-900');
});

test('should have background on details', async () => {
render(ServiceAction, {
object: {
health: undefined,
models: [],
connection: { port: 8888 },
status: 'stopped',
container: { containerId: 'dummyContainerId', engineId: 'dummyEngineId' },
},
detailed: true,
});

const startBtn = screen.getByTitle('Start service');
expect(startBtn.classList).toContain('bg-charcoal-800');
expect(startBtn.classList).toContain('rounded-lg');
});
17 changes: 14 additions & 3 deletions packages/frontend/src/lib/table/service/ServiceAction.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { studioClient } from '/@/utils/client';
import { faPlay, faStop, faTrash } from '@fortawesome/free-solid-svg-icons';
import ListItemButtonIcon from '/@/lib/button/ListItemButtonIcon.svelte';
export let object: InferenceServer;
export let detailed: boolean = false;

function stopInferenceServer() {
studioClient.stopInferenceServer(object.container.containerId).catch((err: unknown) => {
Expand All @@ -30,8 +31,18 @@ $: {
</script>

{#if object.status === 'running'}
<ListItemButtonIcon icon="{faStop}" onClick="{stopInferenceServer}" title="Stop service" />
<ListItemButtonIcon detailed="{detailed}" icon="{faStop}" onClick="{stopInferenceServer}" title="Stop service" />
{:else}
<ListItemButtonIcon enabled="{!loading}" icon="{faPlay}" onClick="{startInferenceServer}" title="Start service" />
<ListItemButtonIcon
detailed="{detailed}"
enabled="{!loading}"
icon="{faPlay}"
onClick="{startInferenceServer}"
title="Start service" />
{/if}
<ListItemButtonIcon enabled="{!loading}" icon="{faTrash}" onClick="{deleteInferenceServer}" title="Delete service" />
<ListItemButtonIcon
detailed="{detailed}"
enabled="{!loading}"
icon="{faTrash}"
onClick="{deleteInferenceServer}"
title="Delete service" />
27 changes: 26 additions & 1 deletion packages/frontend/src/pages/InferenceServerDetails.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import InferenceServerDetails from '/@/pages/InferenceServerDetails.svelte';
import type { Language } from 'postman-code-generators';
import { studioClient } from '/@/utils/client';
import { router } from 'tinro';
import type { ModelInfo } from '@shared/src/models/IModelInfo';

const mocks = vi.hoisted(() => {
return {
Expand Down Expand Up @@ -99,7 +100,12 @@ beforeEach(() => {
Log: [],
FailingStreak: 0,
},
models: [],
models: [
{
id: 'dummyModelId',
name: 'Dummy model id',
} as unknown as ModelInfo,
],
connection: { port: 9999 },
status: 'running',
container: {
Expand Down Expand Up @@ -181,3 +187,22 @@ test('copy snippet should call copyToClipboard', async () => {
expect(studioClient.copyToClipboard).toHaveBeenCalledWith('dummy generated snippet');
});
});

test('ensure dummyContainerId is visible', async () => {
render(InferenceServerDetails, {
containerId: 'dummyContainerId',
});

const span = screen.getByText('dummyContainerId');
expect(span).toBeDefined();
});

test('ensure models to be clickable', async () => {
render(InferenceServerDetails, {
containerId: 'dummyContainerId',
});

const a = screen.getByText('Dummy model id');
expect(a).toBeDefined();
expect(a.getAttribute('href')).toBe('/model/dummyModelId');
});
48 changes: 29 additions & 19 deletions packages/frontend/src/pages/InferenceServerDetails.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { studioClient } from '/@/utils/client';
import { onMount } from 'svelte';
import { router } from 'tinro';
import Button from '/@/lib/button/Button.svelte';
import Badge from '/@/lib/Badge.svelte';

export let containerId: string | undefined = undefined;

Expand Down Expand Up @@ -118,35 +119,44 @@ onMount(() => {
});
</script>

<NavPage lastPage="{{ name: 'Model Services', path: '/services' }}" title="Service Details" searchEnabled="{false}">
<NavPage lastPage="{{ name: 'Model Services', path: '/services' }}" title="Service details" searchEnabled="{false}">
<svelte:fragment slot="icon">
<div class="mr-3">
{#if service !== undefined}
<ServiceStatus object="{service}" />
{/if}
</div>
</svelte:fragment>
<svelte:fragment slot="subtitle">
<div class="flex gap-x-2 items-center">
{#if service}
<span class="text-xs">{service.container.containerId}</span>
{#each service.models as model}
<Badge icon="{faMicrochip}" content="{model.hw}" background="bg-charcoal-700" />
{/each}
{/if}
</div>
</svelte:fragment>
<svelte:fragment slot="additional-actions">
{#if service !== undefined}
<ServiceAction detailed object="{service}" />
{/if}
</svelte:fragment>
<svelte:fragment slot="content">
<div slot="content" class="flex flex-col min-w-full min-h-full">
<div class="min-w-full min-h-full flex-1">
<div class="mt-4 px-5 space-y-5">
{#if service !== undefined}
<!-- container details -->
<!-- models info -->
<div class="bg-charcoal-800 rounded-md w-full px-4 pt-2 pb-4">
<!-- container info -->
<span class="text-sm">Container</span>
<div class="w-full bg-charcoal-600 rounded-md p-2 flex items-center">
<div class="grow ml-2 flex flex-row">
{#key service.health?.Status}
<ServiceStatus object="{service}" />
{/key}
<div class="flex flex-col text-xs ml-2 items-center justify-center">
<span>{service.container.containerId}</span>
</div>
</div>
<ServiceAction object="{service}" />
</div>

<!-- models info -->
<div class="mt-4">
<div>
<span class="text-sm">Models</span>
<div class="w-full bg-charcoal-600 rounded-md p-2 flex flex-col gap-y-4">
{#each service.models as model}
<div class="flex flex-row gap-2 items-center">
<div class="grow text-sm">{model.name}</div>
<div class="grow text-sm" aria-label="Model name">
<a href="/model/{model.id}">{model.name}</a>
</div>
<div>
<div
class="bg-charcoal-800 rounded-md p-2 flex flex-row w-min h-min text-xs text-charcoal-100 text-nowrap items-center">
Expand Down