diff --git a/packages/backend/src/studio-api-impl.spec.ts b/packages/backend/src/studio-api-impl.spec.ts index beff36db0..dd1785862 100644 --- a/packages/backend/src/studio-api-impl.spec.ts +++ b/packages/backend/src/studio-api-impl.spec.ts @@ -24,6 +24,7 @@ import userContent from './ai-user-test.json'; import type { ApplicationManager } from './managers/applicationManager'; import { StudioApiImpl } from './studio-api-impl'; import type { PlayGroundManager } from './managers/playground'; +import type { InferenceManager } from './managers/inference/inferenceManager'; import type { TelemetryLogger, Webview } from '@podman-desktop/api'; import { EventEmitter } from '@podman-desktop/api'; import { CatalogManager } from './managers/catalogManager'; @@ -103,6 +104,7 @@ beforeEach(async () => { {} as TelemetryLogger, {} as LocalRepositoryRegistry, {} as unknown as TaskRegistry, + {} as unknown as InferenceManager, ); vi.mock('node:fs'); diff --git a/packages/backend/src/studio-api-impl.ts b/packages/backend/src/studio-api-impl.ts index a3e0451a9..88f36f83f 100644 --- a/packages/backend/src/studio-api-impl.ts +++ b/packages/backend/src/studio-api-impl.ts @@ -33,6 +33,9 @@ import type { TaskRegistry } from './registries/TaskRegistry'; import type { LocalRepository } from '@shared/src/models/ILocalRepository'; import type { LocalRepositoryRegistry } from './registries/LocalRepositoryRegistry'; import path from 'node:path'; +import type { InferenceServer } from '@shared/src/models/IInference'; +import type { InferenceServerConfig } from '@shared/src/models/InferenceServerConfig'; +import type { InferenceManager } from './managers/inference/inferenceManager'; export class StudioApiImpl implements StudioAPI { constructor( @@ -43,8 +46,30 @@ export class StudioApiImpl implements StudioAPI { private telemetry: podmanDesktopApi.TelemetryLogger, private localRepositories: LocalRepositoryRegistry, private taskRegistry: TaskRegistry, + private inferenceManager: InferenceManager, ) {} + async getInferenceServer(): Promise { + return this.inferenceManager.getServers(); + } + + createInferenceServer(config: InferenceServerConfig): Promise { + try { + return this.inferenceManager.createInferenceServer(config); + } catch (err: unknown) { + console.error('Something went wrong while trying to start inference server', err); + throw err; + } + } + + startInferenceServer(containerId: string): Promise { + return this.inferenceManager.startInferenceServer(containerId); + } + + stopInferenceServer(containerId: string): Promise { + return this.inferenceManager.stopInferenceServer(containerId); + } + async ping(): Promise { return 'pong'; } diff --git a/packages/backend/src/studio.ts b/packages/backend/src/studio.ts index 9a4c94906..91e849d74 100644 --- a/packages/backend/src/studio.ts +++ b/packages/backend/src/studio.ts @@ -38,6 +38,7 @@ import fs from 'node:fs'; import { ContainerRegistry } from './registries/ContainerRegistry'; import { PodmanConnection } from './managers/podmanConnection'; import { LocalRepositoryRegistry } from './registries/LocalRepositoryRegistry'; +import { InferenceManager } from './managers/inference/inferenceManager'; // TODO: Need to be configured export const AI_STUDIO_FOLDER = path.join('podman-desktop', 'ai-studio'); @@ -54,6 +55,8 @@ export class Studio { modelsManager: ModelsManager; telemetry: TelemetryLogger; + #inferenceManager: InferenceManager; + constructor(readonly extensionContext: ExtensionContext) { this.#extensionContext = extensionContext; } @@ -147,7 +150,21 @@ export class Studio { localRepositoryRegistry, ); + this.#inferenceManager = new InferenceManager( + this.#panel.webview, + containerRegistry, + podmanConnection, + this.modelsManager, + this.telemetry, + ); + this.#panel.onDidChangeViewState((e: WebviewPanelOnDidChangeViewStateEvent) => { + // Lazily init inference manager + if (!this.#inferenceManager.isInitialize()) { + this.#inferenceManager.init(); + this.#extensionContext.subscriptions.push(this.#inferenceManager); + } + this.telemetry.logUsage(e.webviewPanel.visible ? 'opened' : 'closed'); }); @@ -160,6 +177,7 @@ export class Studio { this.telemetry, localRepositoryRegistry, taskRegistry, + this.#inferenceManager, ); this.catalogManager.init(); diff --git a/packages/frontend/src/stores/inferenceServers.ts b/packages/frontend/src/stores/inferenceServers.ts new file mode 100644 index 000000000..f7feb0de2 --- /dev/null +++ b/packages/frontend/src/stores/inferenceServers.ts @@ -0,0 +1,27 @@ +/********************************************************************** + * Copyright (C) 2024 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ +import { RPCReadable } from '/@/stores/rpcReadable'; +import { Messages } from '@shared/Messages'; +import { studioClient } from '/@/utils/client'; +import type { InferenceServer } from '@shared/src/models/IInference'; + +export const inferenceServers = RPCReadable( + [], + [Messages.MSG_INFERENCE_SERVERS_UPDATE], + studioClient.getInferenceServer, +); diff --git a/packages/shared/src/StudioAPI.ts b/packages/shared/src/StudioAPI.ts index e52ce9158..33bb6a396 100644 --- a/packages/shared/src/StudioAPI.ts +++ b/packages/shared/src/StudioAPI.ts @@ -24,6 +24,8 @@ import type { TelemetryTrustedValue } from '@podman-desktop/api'; import type { ApplicationState } from './models/IApplicationState'; import type { Task } from './models/ITask'; import type { LocalRepository } from './models/ILocalRepository'; +import type { InferenceServer } from './models/IInference'; +import type { InferenceServerConfig } from './models/InferenceServerConfig'; export abstract class StudioAPI { abstract ping(): Promise; @@ -71,4 +73,27 @@ export abstract class StudioAPI { * @param modelId the id of the model we want to download */ abstract downloadModel(modelId: string): Promise; + + /** + * Get inference servers + */ + abstract getInferenceServer(): Promise; + + /** + * Start an inference server + * @param config The configuration to use + */ + abstract createInferenceServer(config: InferenceServerConfig): Promise; + + /** + * Start an inference server + * @param containerId the container id of the inference server + */ + abstract startInferenceServer(containerId: string): Promise; + + /** + * Stop an inference server + * @param containerId the container id of the inference server + */ + abstract stopInferenceServer(containerId: string): Promise; }