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

Add runtime-installer improvements #25

Merged
merged 2 commits into from
Dec 17, 2024
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
2 changes: 1 addition & 1 deletion WebUI/electron/electron-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,4 @@ type SetupData = {
version:string,
}

type BackendStatus = 'notYetStarted' | 'starting' | 'running' | 'stopped' | 'failed' | 'notInstalled' | 'installationFailed' | 'installing' | 'uninitializedStatus'
type BackendStatus = 'notYetStarted' | 'starting' | 'running' | 'stopped' | 'stopping' | 'failed' | 'notInstalled' | 'installationFailed' | 'installing' | 'uninitializedStatus'
8 changes: 3 additions & 5 deletions WebUI/electron/subprocesses/aiBackendService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class AiBackendService extends LongLivedPythonApiService {
isSetUp = this.serviceIsSetUp();

async *set_up(): AsyncIterable<SetupProgress> {
this.currentStatus = 'installing'
this.setStatus('installing')
this.appLogger.info("setting up service", this.name)
const self = this

Expand Down Expand Up @@ -47,14 +47,12 @@ export class AiBackendService extends LongLivedPythonApiService {
yield {serviceName: self.name, step: `move python environment to target`, status: "executing", debugMessage: `Moving python environment to target place at ${self.pythonEnvDir}`};
await self.commonSetupSteps.moveToFinalTarget(pythonEnvContainmentDir, self.pythonEnvDir)
yield {serviceName: self.name, step: `move python environment to target`, status: "executing", debugMessage: `Moved to ${self.pythonEnvDir}`};
self.currentStatus = 'notYetStarted'
this.updateStatus()
this.setStatus('notYetStarted')
yield {serviceName: self.name, step: "end", status: "success", debugMessage: `service set up completely`};
} catch (e) {
self.appLogger.warn(`Set up of service failed due to ${e}`, self.name, true)
self.appLogger.warn(`Aborting set up of ${self.name} service environment`, self.name, true)
self.currentStatus = 'installationFailed'
this.updateStatus()
this.setStatus('installationFailed')
yield {serviceName: self.name, step: "end", status: "failed", debugMessage: `Failed to setup python environment due to ${e}`};
}
}
Expand Down
7 changes: 7 additions & 0 deletions WebUI/electron/subprocesses/apiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ export abstract class LongLivedPythonApiService implements ApiService {

abstract serviceIsSetUp(): boolean

setStatus(status: BackendStatus) {
this.currentStatus = status
this.updateStatus()
}

updateStatus() {
this.isSetUp = this.serviceIsSetUp();
this.win.webContents.send("serviceInfoUpdate", this.get_info());
Expand Down Expand Up @@ -106,6 +111,7 @@ export abstract class LongLivedPythonApiService implements ApiService {
}

this.desiredStatus = "running"
this.setStatus('starting')
try {
this.appLogger.info(` trying to start ${this.name} python API`, this.name)
const trackedProcess = await this.spawnAPIProcess()
Expand Down Expand Up @@ -137,6 +143,7 @@ export abstract class LongLivedPythonApiService implements ApiService {
async stop(): Promise<BackendStatus> {
this.appLogger.info(`Stopping backend ${this.name}. It was in state ${this.currentStatus}`, this.name)
this.desiredStatus = "stopped"
this.setStatus('stopping')
this.encapsulatedProcess?.kill()
await new Promise(resolve => {
setTimeout(() => {
Expand Down
8 changes: 3 additions & 5 deletions WebUI/electron/subprocesses/comfyUIBackendService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {

async *set_up(): AsyncIterable<SetupProgress> {
this.appLogger.info("setting up service", this.name)
this.currentStatus = "installing"
this.setStatus('installing')
const self = this

const logToFileHandler = (data: string) => self.appLogger.logMessageToFile(data, self.name)
Expand Down Expand Up @@ -184,14 +184,12 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {
await this.commonSetupSteps.moveToFinalTarget(comfyUiTmpServiceDir, self.serviceDir)
yield {serviceName: self.name, step: `move service to target`, status: "executing", debugMessage: `Moved to ${self.pythonEnvDir}`};

this.currentStatus = "notYetStarted"
this.updateStatus()
this.setStatus('notYetStarted')
yield {serviceName: self.name, step: "end", status: "success", debugMessage: `service set up completely`};
} catch (e) {
self.appLogger.warn(`Set up of service failed due to ${e}`, self.name, true)
self.appLogger.warn(`Aborting set up of ${self.name} service environment`, self.name, true)
this.currentStatus = "installationFailed"
this.updateStatus()
this.setStatus('installationFailed')
yield {serviceName: self.name, step: "end", status: "failed", debugMessage: `Failed to setup comfyUI service due to ${e}`};
}
}
Expand Down
8 changes: 3 additions & 5 deletions WebUI/electron/subprocesses/llamaCppBackendService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class LlamaCppBackendService extends LongLivedPythonApiService {
isSetUp = this.serviceIsSetUp();

async *set_up(): AsyncIterable<SetupProgress> {
this.currentStatus = 'installing'
this.setStatus('installing')
this.appLogger.info("setting up service", this.name)
const self = this

Expand All @@ -50,14 +50,12 @@ export class LlamaCppBackendService extends LongLivedPythonApiService {
yield {serviceName: self.name, step: `move python environment to target`, status: "executing", debugMessage: `Moving python environment to target place at ${self.pythonEnvDir}`};
await self.commonSetupSteps.moveToFinalTarget(pythonEnvContainmentDir, self.pythonEnvDir)
yield {serviceName: self.name, step: `move python environment to target`, status: "executing", debugMessage: `Moved to ${self.pythonEnvDir}`};
self.currentStatus = 'notYetStarted'
this.updateStatus()
this.setStatus('notYetStarted')
yield {serviceName: self.name, step: "end", status: "success", debugMessage: `service set up completely`};
} catch (e) {
self.appLogger.warn(`Set up of service failed due to ${e}`, self.name, true)
self.appLogger.warn(`Aborting set up of ${self.name} service environment`, self.name, true)
self.currentStatus = 'installationFailed'
this.updateStatus()
this.setStatus('installationFailed')
yield {serviceName: self.name, step: "end", status: "failed", debugMessage: `Failed to setup python environment due to ${e}`};
}
}
Expand Down
3 changes: 2 additions & 1 deletion WebUI/src/assets/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@
"SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Standard Default Inpaint/OutPaint Model",
"SETTINGS_MODEL_SD_HD_MODEL": "HD Default Model",
"SETTINGS_MODEL_RAG_MODEL": "Rag Query Model",
"SETTINGS_MODEL_BACKEND": "Backend Components",
"SETTINGS_MODEL_BACKEND": "Inference Backends",
"SETTINGS_BACKEND_STATUS": "Backend Status",
"SETTINGS_MODEL_MANAGE_BACKEND": "Manage Backend Components",
"SETTINGS_MODEL_EXIST": "The model already exist. Repeating the download is unnecessary.",
"SETTINGS_MODEL_DOWNLOAD": "Model Download",
Expand Down
22 changes: 5 additions & 17 deletions WebUI/src/components/InstallationManagement.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@
</template>

<script setup lang="ts">
import {useGlobalSetup} from '@/assets/js/store/globalSetup';
import {mapStatusToColor, mapToDisplayStatus} from "@/lib/utils.ts";
import {mapServiceNameToDisplayName, mapStatusToColor, mapToDisplayStatus} from "@/lib/utils.ts";
import {toast} from "@/assets/js/toast.ts";
import {useBackendServices} from '@/assets/js/store/backendServices';

Expand All @@ -130,11 +129,11 @@ const somethingChanged = ref(false)
const enabledComponents = ref(new Set(backendServices.info.filter((item) => item.isSetUp || item.isRequired).map((item) => item.serviceName)))
const loadingComponents = ref(new Set<string>())

const components = computed(() => backendServices.info.map((item) => ({
const components = computed(() => {return backendServices.info.map((item) => ({
enabled: enabledComponents.value.has(item.serviceName),
isLoading: loadingComponents.value.has(item.serviceName),
...item
})))
}))})


function isSomethingLoading(): boolean {
Expand Down Expand Up @@ -200,6 +199,8 @@ function closeInstallations() {

function getInfoURL(serviceName: string) {
switch (serviceName) {
case "ai-backend":
return "https://github.com/intel/ai-playground"
case "comfyui-backend":
return "https://github.com/comfyanonymous/ComfyUI"
case "llamacpp-backend":
Expand All @@ -221,19 +222,6 @@ function areBoxesChecked() {
return components.value.some((i) => i.status !== 'running' && i.enabled)
}

function mapServiceNameToDisplayName(serviceName: string) {
switch (serviceName) {
case "comfyui-backend":
return "ComfyUI"
case "ai-backend":
return "AI Playground"
case "llamacpp-backend":
return "llama.cpp"
default:
return serviceName
}
}

function convertVisibility(shouldBeVisible: boolean) {
if (shouldBeVisible) {
return 'visible'
Expand Down
173 changes: 105 additions & 68 deletions WebUI/src/components/SettingsBasic.vue
Original file line number Diff line number Diff line change
@@ -1,99 +1,136 @@
<template>
<div class="flex flex-col gap-2">
<p>{{ languages.SETTINGS_BASIC_LANGUAGE }}</p>
<drop-selector :array="i18n.languageOptions" @change="i18n.changeLanguage">
<template #selected>
<div class="flex gap-2 items-center">
<span class="rounded-full bg-green-500 w-2 h-2"></span>
<span>{{ i18n.currentLanguageName }}</span>
</div>
</template>
<template #list="slotItem">
<div class="flex gap-2 items-center">
<span class="rounded-full bg-green-500 w-2 h-2"></span>
<span>{{ slotItem.item.name }}</span>
</div>
</template>
</drop-selector>
</div>
<div v-if="theme.availableThemes.length > 1" class="flex flex-col gap-2">
<p>Theme</p>
<div class="grid gap-2" :class="{[`grid-cols-${theme.availableThemes.length}`]: true}">
<radio-block v-for="themeName in theme.availableThemes" :checked="theme.active === themeName" :text="themeToDisplayName(themeName)"
@click="() => theme.selected = themeName"></radio-block>
<div class="flex flex-col gap-2">
<p>{{ languages.SETTINGS_BASIC_LANGUAGE }}</p>
<drop-selector :array="i18n.languageOptions" @change="i18n.changeLanguage">
<template #selected>
<div class="flex gap-2 items-center">
<span class="rounded-full bg-green-500 w-2 h-2"></span>
<span>{{ i18n.currentLanguageName }}</span>
</div>
</div>
<div class="flex flex-col gap-2">
<p>{{ languages.SETTINGS_INFERENCE_DEVICE }}</p>
<div class="flex items-center gap-2 flex-wrap">
<drop-selector :array="globalSetup.graphicsList" @change="changeGraphics">
<template #selected>
<div class="flex gap-2 items-center">
<span class="rounded-full bg-green-500 w-2 h-2"></span>
<span>{{ graphicsName }}</span>
</div>
</template>
<template #list="slotItem">
<div class="flex gap-2 items-center">
<span class="rounded-full bg-green-500 w-2 h-2"></span>
<span>{{ slotItem.item.name }}</span>
</div>
</template>
</drop-selector>
</template>
<template #list="slotItem">
<div class="flex gap-2 items-center">
<span class="rounded-full bg-green-500 w-2 h-2"></span>
<span>{{ slotItem.item.name }}</span>
</div>
</template>
</drop-selector>
</div>
<div v-if="theme.availableThemes.length > 1" class="flex flex-col gap-2">
<p>Theme</p>
<div class="grid gap-2" :class="{[`grid-cols-${theme.availableThemes.length}`]: true}">
<radio-block v-for="themeName in theme.availableThemes" :checked="theme.active === themeName"
:text="themeToDisplayName(themeName)"
@click="() => theme.selected = themeName"></radio-block>
</div>
</div>
<div class="flex flex-col gap-2">
<p>{{ languages.SETTINGS_INFERENCE_DEVICE }}</p>
<div class="flex items-center gap-2 flex-wrap">
<drop-selector :array="globalSetup.graphicsList" @change="changeGraphics">
<template #selected>
<div class="flex gap-2 items-center">
<span class="rounded-full bg-green-500 w-2 h-2"></span>
<span>{{ graphicsName }}</span>
</div>
</template>
<template #list="slotItem">
<div class="flex gap-2 items-center">
<span class="rounded-full bg-green-500 w-2 h-2"></span>
<span>{{ slotItem.item.name }}</span>
</div>
</template>
</drop-selector>
</div>
</div>
<div class="border-b border-color-spilter flex flex-col gap-5 py-4">
<h2 class="text-center font-bold">{{ languages.SETTINGS_MODEL_BACKEND }}</h2>
<div class="flex flex-col gap-2">
<p>{{ languages.SETTINGS_LLM_BACKEND }}</p>
<div class="flex items-center gap-2">
<drop-selector :array="[...backendTypes]" @change="(item) => textInference.backend = item">
<template #selected>
<div class="flex gap-2 items-center">
<span class="rounded-full bg-green-500 w-2 h-2"></span>
<span>{{ textInference.backend }}</span>
</div>
</template>
<template #list="slotItem">
<div class="flex gap-2 items-center">
<span class="rounded-full bg-green-500 w-2 h-2"></span>
<span>{{ slotItem.item }}</span>
</div>
</template>
</drop-selector>
<p>{{ languages.SETTINGS_LLM_BACKEND }}</p>
<div class="flex items-center gap-2">
<drop-selector :array="[...backendTypes]" @change="(item) => textInference.backend = item">
<template #selected>
<div class="flex gap-2 items-center">
<span class="rounded-full bg-green-500 w-2 h-2"></span>
<span>{{ textInferenceBackendDisplayName[textInference.backend] }}</span>
</div>
</template>
<template #list="slotItem">
<div class="flex gap-2 items-center">
<span class="rounded-full bg-green-500 w-2 h-2"></span>
<span>{{ textInferenceBackendDisplayName[slotItem.item as typeof backendTypes[number]] }}</span>
</div>
</template>
</drop-selector>
</div>
</div>
<div class="flex flex-col gap-3">
<p>{{ languages.SETTINGS_BACKEND_STATUS }}</p>
<!-- Required -->
<table class="text-center w-full mx-2 table-fixed">
<tbody>
<tr v-for="item in apiServiceInformation">
<td style="text-align: left">{{ mapServiceNameToDisplayName(item.serviceName) }}</td>
<td :style="{ color: mapStatusToColor(item.status) }">{{ mapToDisplayStatus(item.status) }}</td>
</tr>
</tbody>
</table>
<div class="flex flex-col pt-5">
<button @click="globalSetup.loadingState = 'manageInstallations'"
class="confirm-btn">{{ languages.SETTINGS_MODEL_MANAGE_BACKEND }}
</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">

import DropSelector from "../components/DropSelector.vue";
import RadioBlock from "../components/RadioBlock.vue";

import { useGlobalSetup } from "@/assets/js/store/globalSetup";
import { useI18N } from '@/assets/js/store/i18n';
import { useTheme } from '@/assets/js/store/theme';
import { useTextInference, backendTypes } from "@/assets/js/store/textInference";
import {useGlobalSetup} from "@/assets/js/store/globalSetup";
import {useI18N} from '@/assets/js/store/i18n';
import {useTheme} from '@/assets/js/store/theme';
import {useTextInference, backendTypes} from "@/assets/js/store/textInference";
import {mapServiceNameToDisplayName, mapStatusToColor, mapToDisplayStatus} from "@/lib/utils.ts";

const apiServiceInformation = ref<ApiServiceInformation[]>([])
const globalSetup = useGlobalSetup();
const textInference = useTextInference();
const i18n = useI18N();
const theme = useTheme();

const textInferenceBackendDisplayName: Record<typeof backendTypes[number], string> = {
"IPEX-LLM": "IPEX-LLM",
"LLAMA.CPP": "Llama.cpp - GGUF"
}


onBeforeMount(async () => {
apiServiceInformation.value = await window.electronAPI.getServices()
})

const themeToDisplayName = (theme: Theme) => {
switch (theme) {
case 'dark': return 'Default';
case 'lnl': return 'Intel® Core™ Ultra';
case 'bmg': return 'Intel® Arc™';
default: return theme;
}
switch (theme) {
case 'dark':
return 'Default';
case 'lnl':
return 'Intel® Core™ Ultra';
case 'bmg':
return 'Intel® Arc™';
default:
return theme;
}
}

const modelSettings = reactive<KVObject>(Object.assign({}, toRaw(globalSetup.modelSettings)));

const graphicsName = computed(() => {
return globalSetup.graphicsList.find(item => modelSettings.graphics as number == item.index)?.name || "";
return globalSetup.graphicsList.find(item => modelSettings.graphics as number == item.index)?.name || "";
})

function changeGraphics(value: any, index: number) {
globalSetup.applyModelSettings({ graphics: (value as GraphicsItem).index });
globalSetup.applyModelSettings({graphics: (value as GraphicsItem).index});
}

</script>
Loading
Loading