Skip to content

Commit

Permalink
feat: add openDirectory api
Browse files Browse the repository at this point in the history
Signed-off-by: axel7083 <[email protected]>
  • Loading branch information
axel7083 committed Jan 24, 2024
1 parent 60eca54 commit f405100
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 10 deletions.
15 changes: 11 additions & 4 deletions packages/backend/src/managers/modelsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ import fs from 'fs';
import * as path from 'node:path';

export class ModelsManager {
constructor(private appUserDirectory: string) {}
private readonly modelsDir: string;

constructor(private appUserDirectory: string) {
this.modelsDir = path.join(this.appUserDirectory, 'models');
}

getModelsDirectory(): string {
return this.modelsDir;
}

getLocalModels(): LocalModelInfo[] {
const result: LocalModelInfo[] = [];
const modelsDir = path.join(this.appUserDirectory, 'models');
if (!fs.existsSync(modelsDir)) {
if (!fs.existsSync(this.modelsDir)) {
return [];
}
const entries = fs.readdirSync(modelsDir, { withFileTypes: true });
const entries = fs.readdirSync(this.modelsDir, { withFileTypes: true });
const dirs = entries.filter(dir => dir.isDirectory());
for (const d of dirs) {
const modelEntries = fs.readdirSync(path.resolve(d.path, d.name));
Expand Down
1 change: 0 additions & 1 deletion packages/backend/src/studio-api-impl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ beforeEach(async () => {

// Creating StudioApiImpl
studioApiImpl = new StudioApiImpl(
appUserDirectory,
{} as unknown as ApplicationManager,
{} as unknown as RecipeStatusRegistry,
{} as unknown as PlayGroundManager,
Expand Down
10 changes: 8 additions & 2 deletions packages/backend/src/studio-api-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import type { LocalModelInfo } from '@shared/src/models/ILocalModelInfo';

export class StudioApiImpl implements StudioAPI {
constructor(
private appUserDirectory: string,
private applicationManager: ApplicationManager,
private recipeStatusRegistry: RecipeStatusRegistry,
private playgroundManager: PlayGroundManager,
Expand Down Expand Up @@ -101,7 +100,7 @@ export class StudioApiImpl implements StudioAPI {
}

// TODO: we need to stop doing that.
const modelPath = path.resolve(this.appUserDirectory, 'models', modelId, localModelInfo[0].file);
const modelPath = path.resolve(this.modelsManager.getModelsDirectory(), modelId, localModelInfo[0].file);

await this.playgroundManager.startPlayground(modelId, modelPath);
}
Expand Down Expand Up @@ -129,4 +128,11 @@ export class StudioApiImpl implements StudioAPI {
async getCatalog(): Promise<Catalog> {
return this.catalogManager.getCatalog();
}

async getModelsDirectory(): Promise<string> {
return this.modelsManager.getModelsDirectory();
}
async openDirectory(path: string): Promise<void> {
void podmanDesktopApi.env.openExternal(podmanDesktopApi.Uri.file(path));
}
}
1 change: 0 additions & 1 deletion packages/backend/src/studio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ export class Studio {

// Creating StudioApiImpl
this.studioApi = new StudioApiImpl(
appUserDirectory,
applicationManager,
recipeStatusRegistry,
this.playgroundManager,
Expand Down
68 changes: 68 additions & 0 deletions packages/frontend/src/pages/Models.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import '@testing-library/jest-dom/vitest';
import { vi, test, expect } from 'vitest';
import { screen, fireEvent, render, waitFor } from '@testing-library/svelte';
import Models from '/@/pages/Models.svelte';

const mocks = vi.hoisted(() => {
return {
getModelsDirectoryMock: vi.fn(),
openDirectoryMock: vi.fn(),
localModelsMock: {
subscribe: (_f: (msg: any) => void) => {
return () => {};
},
},
modelsPullingSubscribeMock: vi.fn(),
modelsPullingMock: {
subscribe: (f: (msg: any) => void) => {
f(mocks.modelsPullingSubscribeMock());
return () => {};
},
},
};
});

vi.mock('../utils/client', async () => {
return {
studioClient: {
getModelsDirectory: mocks.getModelsDirectoryMock,
openDirectory: mocks.openDirectoryMock,
},
rpcBrowser: {
subscribe: () => {
return {
unsubscribe: () => {},
};
},
},
};
});

vi.mock('../stores/local-models', async () => {
return {
localModels: mocks.localModelsMock,
};
});

vi.mock('../stores/recipe', async () => {
return {
modelsPulling: mocks.modelsPullingMock,
};
});

test('open models directory should call the api', async () => {
mocks.getModelsDirectoryMock.mockResolvedValue('fake');
mocks.modelsPullingSubscribeMock.mockReturnValue([]);
render(Models);

await waitFor(async () => {
const open = screen.getByTitle('open-models-directory');
expect(open).toBeDefined();

await fireEvent.click(open);
});

await waitFor(() => {
expect(mocks.openDirectoryMock).toHaveBeenNthCalledWith(1, 'fake');
});
});
30 changes: 28 additions & 2 deletions packages/frontend/src/pages/Models.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ 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 { studioClient } from '/@/utils/client';
import { faFolderOpen } from '@fortawesome/free-solid-svg-icons';
import Button from '/@/lib/button/Button.svelte';
const columns: Column<ModelInfo>[] = [
new Column<ModelInfo>('Name', { width: '3fr', renderer: ModelColumnName }),
Expand All @@ -33,6 +36,7 @@ let loading: boolean = true;
let tasks: Task[] = [];
let models: ModelInfo[] = [];
let filteredModels: ModelInfo[] = [];
let modelsDir: string | undefined = undefined;
function filterModels(): void {
// Let's collect the models we do not want to show (loading, error).
Expand All @@ -49,7 +53,10 @@ function filterModels(): void {
}
onMount(() => {
// Pulling update
studioClient.getModelsDirectory().then((modelsDirectory) => {
modelsDir = modelsDirectory;
});
const modelsPullingUnsubscribe = modelsPulling.subscribe(runningTasks => {
tasks = runningTasks;
loading = false;
Expand All @@ -67,12 +74,31 @@ onMount(() => {
localModelsUnsubscribe();
}
});
const openModelsDir = () => {
if(modelsDir === undefined)
return;
studioClient.openDirectory(modelsDir);
}
</script>

<NavPage title="Models on disk" searchEnabled="{false}" loading="{loading}">
<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 h-full">
<div class="mx-4">
{#if modelsDir}
<Card classes="bg-charcoal-800 mt-4">
<div slot="content" class="text-base font-normal p-2 flex w-full">
<div class="text-base flex-grow">
Models are stored in
<code class="text-sm sm:text-base inline-flex text-left items-center space-x-4 px-2 bg-charcoal-500 rounded-lg">{modelsDir}</code>
</div>
<Button title="open-models-directory" on:click={openModelsDir} icon={faFolderOpen}/>
</div>
</Card>
{/if}
</div>
{#if !loading}
{#if tasks.length > 0}
<div class="mx-4">
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/StudioAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export abstract class StudioAPI {
abstract getPullingStatuses(): Promise<Map<string, RecipeStatus>>;
abstract pullApplication(recipeId: string): Promise<void>;
abstract openURL(url: string): Promise<boolean>;
abstract openDirectory(path: string): Promise<void>;
/**
* Get the information of models saved locally into the extension's storage directory
*/
Expand All @@ -21,4 +22,5 @@ export abstract class StudioAPI {
abstract askPlayground(modelId: string, prompt: string): Promise<number>;
abstract getPlaygroundQueriesState(): Promise<QueryState[]>;
abstract getPlaygroundsState(): Promise<PlaygroundState[]>;
abstract getModelsDirectory(): Promise<string>;
}

0 comments on commit f405100

Please sign in to comment.