diff --git a/packages/backend/src/ai.json b/packages/backend/src/ai.json index 2ed682e49..779726806 100644 --- a/packages/backend/src/ai.json +++ b/packages/backend/src/ai.json @@ -4,7 +4,7 @@ "id": "chatbot", "description" : "Chat bot application", "name" : "ChatBot", - "repository": "https://github.com/axel7083/locallm", + "repository": "https://github.com/redhat-et/locallm", "icon": "natural-language-processing", "categories": [ "natural-language-processing" diff --git a/packages/backend/src/managers/applicationManager.spec.ts b/packages/backend/src/managers/applicationManager.spec.ts index db061192a..3ba39257a 100644 --- a/packages/backend/src/managers/applicationManager.spec.ts +++ b/packages/backend/src/managers/applicationManager.spec.ts @@ -12,6 +12,7 @@ import type { ModelsManager } from './modelsManager'; import path from 'node:path'; import type { AIConfig, ContainerConfig } from '../models/AIConfig'; import * as portsUtils from '../utils/ports'; +import { goarch } from '../utils/arch'; const mocks = vi.hoisted(() => { return { @@ -95,6 +96,8 @@ describe('pullApplication', () => { name: 'container1', contextdir: 'contextdir1', containerfile: 'Containerfile', + arch: [goarch()], + gpu_env: [], }, ], }, @@ -433,8 +436,9 @@ describe('filterContainers', () => { name: 'container2', contextdir: 'contextdir2', containerfile: 'Containerfile', - arch: 'arm64', + arch: ['arm64'], modelService: false, + gpu_env: [], }, ], }, @@ -459,15 +463,17 @@ describe('filterContainers', () => { name: 'container1', contextdir: 'contextdir1', containerfile: 'Containerfile', - arch: 'amd64', + arch: ['amd64'], modelService: false, + gpu_env: [], }, { name: 'container2', contextdir: 'contextdir2', containerfile: 'Containerfile', - arch: 'arm64', + arch: ['arm64'], modelService: false, + gpu_env: [], }, ], }, @@ -491,22 +497,25 @@ describe('filterContainers', () => { name: 'container1', contextdir: 'contextdir1', containerfile: 'Containerfile', - arch: 'amd64', + arch: ['amd64'], modelService: false, + gpu_env: [], }, { name: 'container2', contextdir: 'contextdir2', containerfile: 'Containerfile', - arch: 'arm64', + arch: ['arm64'], modelService: false, + gpu_env: [], }, { name: 'container3', contextdir: 'contextdir3', containerfile: 'Containerfile', - arch: 'amd64', + arch: ['amd64'], modelService: false, + gpu_env: [], }, ]; const aiConfig: AIConfig = { @@ -560,8 +569,9 @@ describe('buildImages', () => { name: 'container1', contextdir: 'contextdir1', containerfile: 'Containerfile', - arch: 'amd64', + arch: ['amd64'], modelService: false, + gpu_env: [], }, ]; const manager = new ApplicationManager( diff --git a/packages/backend/src/managers/applicationManager.ts b/packages/backend/src/managers/applicationManager.ts index c72a12dbe..a5afb60b4 100644 --- a/packages/backend/src/managers/applicationManager.ts +++ b/packages/backend/src/managers/applicationManager.ts @@ -17,7 +17,6 @@ ***********************************************************************/ import type { Recipe } from '@shared/src/models/IRecipe'; -import { arch } from 'node:os'; import type { GitManager } from './gitManager'; import fs from 'fs'; import * as https from 'node:https'; @@ -32,6 +31,7 @@ import { getParentDirectory } from '../utils/pathUtils'; import type { ModelInfo } from '@shared/src/models/IModelInfo'; import type { ModelsManager } from './modelsManager'; import { getPortsInfo } from '../utils/ports'; +import { goarch } from '../utils/arch'; export const CONFIG_FILENAME = 'ai-studio.yaml'; @@ -342,7 +342,7 @@ export class ApplicationManager { filterContainers(aiConfig: AIConfig): ContainerConfig[] { return aiConfig.application.containers.filter( - container => container.arch === undefined || container.arch === arch(), + container => container.gpu_env.length === 0 && container.arch.some(arc => arc === goarch()), ); } @@ -398,7 +398,7 @@ export class ApplicationManager { const rawConfiguration = fs.readFileSync(configFile, 'utf-8'); let aiConfig: AIConfig; try { - aiConfig = parseYaml(rawConfiguration, arch()); + aiConfig = parseYaml(rawConfiguration, goarch()); } catch (err) { throw new Error('Cannot load configuration file.'); } diff --git a/packages/backend/src/models/AIConfig.ts b/packages/backend/src/models/AIConfig.ts index e0e573cfc..9cd8dc164 100644 --- a/packages/backend/src/models/AIConfig.ts +++ b/packages/backend/src/models/AIConfig.ts @@ -22,8 +22,9 @@ export interface ContainerConfig { name: string; contextdir: string; containerfile?: string; - arch: string; + arch: string[]; modelService: boolean; + gpu_env: string[]; } export interface AIConfig { application: { @@ -55,11 +56,12 @@ export function parseYaml(raw: string, defaultArch: string): AIConfig { return { application: { containers: containers.map(container => ({ - arch: isString(container['arch']) ? container['arch'] : defaultArch, + arch: Array.isArray(container['arch']) ? container['arch'] : [defaultArch], modelService: container['model-service'] === true, containerfile: isString(container['containerfile']) ? container['containerfile'] : undefined, contextdir: assertString(container['contextdir']), name: assertString(container['name']), + gpu_env: Array.isArray(container['gpu-env']) ? container['gpu-env'] : [], })), }, }; diff --git a/packages/backend/src/utils/arch.ts b/packages/backend/src/utils/arch.ts new file mode 100644 index 000000000..52394c5e9 --- /dev/null +++ b/packages/backend/src/utils/arch.ts @@ -0,0 +1,31 @@ +/********************************************************************** + * 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 { arch } from 'node:os'; + +const nodeArch2GoArch = new Map([ + ['ia32', '386'], + ['x64', 'amd64'], +]); + +export function goarch(): string { + let localArch = arch(); + if (nodeArch2GoArch.has(localArch)) { + localArch = nodeArch2GoArch.get(localArch); + } + return localArch; +}