Skip to content

Commit e75ae6e

Browse files
authored
Merge pull request #116 from TNG/runtime_installer_improvements
Improve runtime installer UX
2 parents c2d83f1 + 922c918 commit e75ae6e

File tree

10 files changed

+156
-143
lines changed

10 files changed

+156
-143
lines changed

WebUI/electron/electron-env.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,4 @@ type SetupData = {
6868
version:string,
6969
}
7070

71-
type BackendStatus = 'notYetStarted' | 'starting' | 'running' | 'stopped' | 'failed' | 'notInstalled' | 'installationFailed' | 'installing' | 'uninitializedStatus'
71+
type BackendStatus = 'notYetStarted' | 'starting' | 'running' | 'stopped' | 'stopping' | 'failed' | 'notInstalled' | 'installationFailed' | 'installing' | 'uninitializedStatus'

WebUI/electron/subprocesses/aiBackendService.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export class AiBackendService extends LongLivedPythonApiService {
1717
isSetUp = this.serviceIsSetUp();
1818

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

@@ -47,14 +47,12 @@ export class AiBackendService extends LongLivedPythonApiService {
4747
yield {serviceName: self.name, step: `move python environment to target`, status: "executing", debugMessage: `Moving python environment to target place at ${self.pythonEnvDir}`};
4848
await self.commonSetupSteps.moveToFinalTarget(pythonEnvContainmentDir, self.pythonEnvDir)
4949
yield {serviceName: self.name, step: `move python environment to target`, status: "executing", debugMessage: `Moved to ${self.pythonEnvDir}`};
50-
self.currentStatus = 'notYetStarted'
51-
this.updateStatus()
50+
this.setStatus('notYetStarted')
5251
yield {serviceName: self.name, step: "end", status: "success", debugMessage: `service set up completely`};
5352
} catch (e) {
5453
self.appLogger.warn(`Set up of service failed due to ${e}`, self.name, true)
5554
self.appLogger.warn(`Aborting set up of ${self.name} service environment`, self.name, true)
56-
self.currentStatus = 'installationFailed'
57-
this.updateStatus()
55+
this.setStatus('installationFailed')
5856
yield {serviceName: self.name, step: "end", status: "failed", debugMessage: `Failed to setup python environment due to ${e}`};
5957
}
6058
}

WebUI/electron/subprocesses/apiService.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ export abstract class LongLivedPythonApiService implements ApiService {
7373

7474
abstract serviceIsSetUp(): boolean
7575

76+
setStatus(status: BackendStatus) {
77+
this.currentStatus = status
78+
this.updateStatus()
79+
}
80+
7681
updateStatus() {
7782
this.isSetUp = this.serviceIsSetUp();
7883
this.win.webContents.send("serviceInfoUpdate", this.get_info());
@@ -106,6 +111,7 @@ export abstract class LongLivedPythonApiService implements ApiService {
106111
}
107112

108113
this.desiredStatus = "running"
114+
this.setStatus('starting')
109115
try {
110116
this.appLogger.info(` trying to start ${this.name} python API`, this.name)
111117
const trackedProcess = await this.spawnAPIProcess()
@@ -137,6 +143,7 @@ export abstract class LongLivedPythonApiService implements ApiService {
137143
async stop(): Promise<BackendStatus> {
138144
this.appLogger.info(`Stopping backend ${this.name}. It was in state ${this.currentStatus}`, this.name)
139145
this.desiredStatus = "stopped"
146+
this.setStatus('stopping')
140147
this.encapsulatedProcess?.kill()
141148
await new Promise(resolve => {
142149
setTimeout(() => {

WebUI/electron/subprocesses/comfyUIBackendService.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {
3232

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

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

187-
this.currentStatus = "notYetStarted"
188-
this.updateStatus()
187+
this.setStatus('notYetStarted')
189188
yield {serviceName: self.name, step: "end", status: "success", debugMessage: `service set up completely`};
190189
} catch (e) {
191190
self.appLogger.warn(`Set up of service failed due to ${e}`, self.name, true)
192191
self.appLogger.warn(`Aborting set up of ${self.name} service environment`, self.name, true)
193-
this.currentStatus = "installationFailed"
194-
this.updateStatus()
192+
this.setStatus('installationFailed')
195193
yield {serviceName: self.name, step: "end", status: "failed", debugMessage: `Failed to setup comfyUI service due to ${e}`};
196194
}
197195
}

WebUI/electron/subprocesses/llamaCppBackendService.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export class LlamaCppBackendService extends LongLivedPythonApiService {
2525
isSetUp = this.serviceIsSetUp();
2626

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

@@ -50,14 +50,12 @@ export class LlamaCppBackendService extends LongLivedPythonApiService {
5050
yield {serviceName: self.name, step: `move python environment to target`, status: "executing", debugMessage: `Moving python environment to target place at ${self.pythonEnvDir}`};
5151
await self.commonSetupSteps.moveToFinalTarget(pythonEnvContainmentDir, self.pythonEnvDir)
5252
yield {serviceName: self.name, step: `move python environment to target`, status: "executing", debugMessage: `Moved to ${self.pythonEnvDir}`};
53-
self.currentStatus = 'notYetStarted'
54-
this.updateStatus()
53+
this.setStatus('notYetStarted')
5554
yield {serviceName: self.name, step: "end", status: "success", debugMessage: `service set up completely`};
5655
} catch (e) {
5756
self.appLogger.warn(`Set up of service failed due to ${e}`, self.name, true)
5857
self.appLogger.warn(`Aborting set up of ${self.name} service environment`, self.name, true)
59-
self.currentStatus = 'installationFailed'
60-
this.updateStatus()
58+
this.setStatus('installationFailed')
6159
yield {serviceName: self.name, step: "end", status: "failed", debugMessage: `Failed to setup python environment due to ${e}`};
6260
}
6361
}

WebUI/src/assets/i18n/en-US.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@
9797
"SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Standard Default Inpaint/OutPaint Model",
9898
"SETTINGS_MODEL_SD_HD_MODEL": "HD Default Model",
9999
"SETTINGS_MODEL_RAG_MODEL": "Rag Query Model",
100-
"SETTINGS_MODEL_BACKEND": "Backend Components",
100+
"SETTINGS_MODEL_BACKEND": "Inference Backends",
101+
"SETTINGS_BACKEND_STATUS": "Backend Status",
101102
"SETTINGS_MODEL_MANAGE_BACKEND": "Manage Backend Components",
102103
"SETTINGS_MODEL_EXIST": "The model already exist. Repeating the download is unnecessary.",
103104
"SETTINGS_MODEL_DOWNLOAD": "Model Download",

WebUI/src/components/InstallationManagement.vue

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,7 @@
109109
</template>
110110

111111
<script setup lang="ts">
112-
import {useGlobalSetup} from '@/assets/js/store/globalSetup';
113-
import {mapStatusToColor, mapToDisplayStatus} from "@/lib/utils.ts";
112+
import {mapServiceNameToDisplayName, mapStatusToColor, mapToDisplayStatus} from "@/lib/utils.ts";
114113
import {toast} from "@/assets/js/toast.ts";
115114
import {useBackendServices} from '@/assets/js/store/backendServices';
116115
@@ -130,11 +129,11 @@ const somethingChanged = ref(false)
130129
const enabledComponents = ref(new Set(backendServices.info.filter((item) => item.isSetUp || item.isRequired).map((item) => item.serviceName)))
131130
const loadingComponents = ref(new Set<string>())
132131
133-
const components = computed(() => backendServices.info.map((item) => ({
132+
const components = computed(() => {return backendServices.info.map((item) => ({
134133
enabled: enabledComponents.value.has(item.serviceName),
135134
isLoading: loadingComponents.value.has(item.serviceName),
136135
...item
137-
})))
136+
}))})
138137
139138
140139
function isSomethingLoading(): boolean {
@@ -200,6 +199,8 @@ function closeInstallations() {
200199
201200
function getInfoURL(serviceName: string) {
202201
switch (serviceName) {
202+
case "ai-backend":
203+
return "https://github.com/intel/ai-playground"
203204
case "comfyui-backend":
204205
return "https://github.com/comfyanonymous/ComfyUI"
205206
case "llamacpp-backend":
@@ -221,19 +222,6 @@ function areBoxesChecked() {
221222
return components.value.some((i) => i.status !== 'running' && i.enabled)
222223
}
223224
224-
function mapServiceNameToDisplayName(serviceName: string) {
225-
switch (serviceName) {
226-
case "comfyui-backend":
227-
return "ComfyUI"
228-
case "ai-backend":
229-
return "AI Playground"
230-
case "llamacpp-backend":
231-
return "llama.cpp"
232-
default:
233-
return serviceName
234-
}
235-
}
236-
237225
function convertVisibility(shouldBeVisible: boolean) {
238226
if (shouldBeVisible) {
239227
return 'visible'
Lines changed: 105 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,136 @@
11
<template>
2-
<div class="flex flex-col gap-2">
3-
<p>{{ languages.SETTINGS_BASIC_LANGUAGE }}</p>
4-
<drop-selector :array="i18n.languageOptions" @change="i18n.changeLanguage">
5-
<template #selected>
6-
<div class="flex gap-2 items-center">
7-
<span class="rounded-full bg-green-500 w-2 h-2"></span>
8-
<span>{{ i18n.currentLanguageName }}</span>
9-
</div>
10-
</template>
11-
<template #list="slotItem">
12-
<div class="flex gap-2 items-center">
13-
<span class="rounded-full bg-green-500 w-2 h-2"></span>
14-
<span>{{ slotItem.item.name }}</span>
15-
</div>
16-
</template>
17-
</drop-selector>
18-
</div>
19-
<div v-if="theme.availableThemes.length > 1" class="flex flex-col gap-2">
20-
<p>Theme</p>
21-
<div class="grid gap-2" :class="{[`grid-cols-${theme.availableThemes.length}`]: true}">
22-
<radio-block v-for="themeName in theme.availableThemes" :checked="theme.active === themeName" :text="themeToDisplayName(themeName)"
23-
@click="() => theme.selected = themeName"></radio-block>
2+
<div class="flex flex-col gap-2">
3+
<p>{{ languages.SETTINGS_BASIC_LANGUAGE }}</p>
4+
<drop-selector :array="i18n.languageOptions" @change="i18n.changeLanguage">
5+
<template #selected>
6+
<div class="flex gap-2 items-center">
7+
<span class="rounded-full bg-green-500 w-2 h-2"></span>
8+
<span>{{ i18n.currentLanguageName }}</span>
249
</div>
25-
</div>
26-
<div class="flex flex-col gap-2">
27-
<p>{{ languages.SETTINGS_INFERENCE_DEVICE }}</p>
28-
<div class="flex items-center gap-2 flex-wrap">
29-
<drop-selector :array="globalSetup.graphicsList" @change="changeGraphics">
30-
<template #selected>
31-
<div class="flex gap-2 items-center">
32-
<span class="rounded-full bg-green-500 w-2 h-2"></span>
33-
<span>{{ graphicsName }}</span>
34-
</div>
35-
</template>
36-
<template #list="slotItem">
37-
<div class="flex gap-2 items-center">
38-
<span class="rounded-full bg-green-500 w-2 h-2"></span>
39-
<span>{{ slotItem.item.name }}</span>
40-
</div>
41-
</template>
42-
</drop-selector>
10+
</template>
11+
<template #list="slotItem">
12+
<div class="flex gap-2 items-center">
13+
<span class="rounded-full bg-green-500 w-2 h-2"></span>
14+
<span>{{ slotItem.item.name }}</span>
4315
</div>
16+
</template>
17+
</drop-selector>
18+
</div>
19+
<div v-if="theme.availableThemes.length > 1" class="flex flex-col gap-2">
20+
<p>Theme</p>
21+
<div class="grid gap-2" :class="{[`grid-cols-${theme.availableThemes.length}`]: true}">
22+
<radio-block v-for="themeName in theme.availableThemes" :checked="theme.active === themeName"
23+
:text="themeToDisplayName(themeName)"
24+
@click="() => theme.selected = themeName"></radio-block>
25+
</div>
26+
</div>
27+
<div class="flex flex-col gap-2">
28+
<p>{{ languages.SETTINGS_INFERENCE_DEVICE }}</p>
29+
<div class="flex items-center gap-2 flex-wrap">
30+
<drop-selector :array="globalSetup.graphicsList" @change="changeGraphics">
31+
<template #selected>
32+
<div class="flex gap-2 items-center">
33+
<span class="rounded-full bg-green-500 w-2 h-2"></span>
34+
<span>{{ graphicsName }}</span>
35+
</div>
36+
</template>
37+
<template #list="slotItem">
38+
<div class="flex gap-2 items-center">
39+
<span class="rounded-full bg-green-500 w-2 h-2"></span>
40+
<span>{{ slotItem.item.name }}</span>
41+
</div>
42+
</template>
43+
</drop-selector>
4444
</div>
45+
</div>
46+
<div class="border-b border-color-spilter flex flex-col gap-5 py-4">
47+
<h2 class="text-center font-bold">{{ languages.SETTINGS_MODEL_BACKEND }}</h2>
4548
<div class="flex flex-col gap-2">
46-
<p>{{ languages.SETTINGS_LLM_BACKEND }}</p>
47-
<div class="flex items-center gap-2">
48-
<drop-selector :array="[...backendTypes]" @change="(item) => textInference.backend = item">
49-
<template #selected>
50-
<div class="flex gap-2 items-center">
51-
<span class="rounded-full bg-green-500 w-2 h-2"></span>
52-
<span>{{ textInference.backend }}</span>
53-
</div>
54-
</template>
55-
<template #list="slotItem">
56-
<div class="flex gap-2 items-center">
57-
<span class="rounded-full bg-green-500 w-2 h-2"></span>
58-
<span>{{ slotItem.item }}</span>
59-
</div>
60-
</template>
61-
</drop-selector>
49+
<p>{{ languages.SETTINGS_LLM_BACKEND }}</p>
50+
<div class="flex items-center gap-2">
51+
<drop-selector :array="[...backendTypes]" @change="(item) => textInference.backend = item">
52+
<template #selected>
53+
<div class="flex gap-2 items-center">
54+
<span class="rounded-full bg-green-500 w-2 h-2"></span>
55+
<span>{{ textInferenceBackendDisplayName[textInference.backend] }}</span>
56+
</div>
57+
</template>
58+
<template #list="slotItem">
59+
<div class="flex gap-2 items-center">
60+
<span class="rounded-full bg-green-500 w-2 h-2"></span>
61+
<span>{{ textInferenceBackendDisplayName[slotItem.item as typeof backendTypes[number]] }}</span>
6262
</div>
63+
</template>
64+
</drop-selector>
65+
</div>
66+
</div>
67+
<div class="flex flex-col gap-3">
68+
<p>{{ languages.SETTINGS_BACKEND_STATUS }}</p>
69+
<!-- Required -->
70+
<table class="text-center w-full mx-2 table-fixed">
71+
<tbody>
72+
<tr v-for="item in apiServiceInformation">
73+
<td style="text-align: left">{{ mapServiceNameToDisplayName(item.serviceName) }}</td>
74+
<td :style="{ color: mapStatusToColor(item.status) }">{{ mapToDisplayStatus(item.status) }}</td>
75+
</tr>
76+
</tbody>
77+
</table>
78+
<div class="flex flex-col pt-5">
79+
<button @click="globalSetup.loadingState = 'manageInstallations'"
80+
class="confirm-btn">{{ languages.SETTINGS_MODEL_MANAGE_BACKEND }}
81+
</button>
82+
</div>
6383
</div>
84+
</div>
6485
</template>
6586
<script setup lang="ts">
6687
6788
import DropSelector from "../components/DropSelector.vue";
6889
import RadioBlock from "../components/RadioBlock.vue";
6990
70-
import { useGlobalSetup } from "@/assets/js/store/globalSetup";
71-
import { useI18N } from '@/assets/js/store/i18n';
72-
import { useTheme } from '@/assets/js/store/theme';
73-
import { useTextInference, backendTypes } from "@/assets/js/store/textInference";
91+
import {useGlobalSetup} from "@/assets/js/store/globalSetup";
92+
import {useI18N} from '@/assets/js/store/i18n';
93+
import {useTheme} from '@/assets/js/store/theme';
94+
import {useTextInference, backendTypes} from "@/assets/js/store/textInference";
95+
import {mapServiceNameToDisplayName, mapStatusToColor, mapToDisplayStatus} from "@/lib/utils.ts";
7496
97+
const apiServiceInformation = ref<ApiServiceInformation[]>([])
7598
const globalSetup = useGlobalSetup();
7699
const textInference = useTextInference();
77100
const i18n = useI18N();
78101
const theme = useTheme();
79102
103+
const textInferenceBackendDisplayName: Record<typeof backendTypes[number], string> = {
104+
"IPEX-LLM": "IPEX-LLM",
105+
"LLAMA.CPP": "Llama.cpp - GGUF"
106+
}
107+
108+
109+
onBeforeMount(async () => {
110+
apiServiceInformation.value = await window.electronAPI.getServices()
111+
})
112+
80113
const themeToDisplayName = (theme: Theme) => {
81-
switch (theme) {
82-
case 'dark': return 'Default';
83-
case 'lnl': return 'Intel® Core™ Ultra';
84-
case 'bmg': return 'Intel® Arc™';
85-
default: return theme;
86-
}
114+
switch (theme) {
115+
case 'dark':
116+
return 'Default';
117+
case 'lnl':
118+
return 'Intel® Core™ Ultra';
119+
case 'bmg':
120+
return 'Intel® Arc™';
121+
default:
122+
return theme;
123+
}
87124
}
88125
89126
const modelSettings = reactive<KVObject>(Object.assign({}, toRaw(globalSetup.modelSettings)));
90127
91128
const graphicsName = computed(() => {
92-
return globalSetup.graphicsList.find(item => modelSettings.graphics as number == item.index)?.name || "";
129+
return globalSetup.graphicsList.find(item => modelSettings.graphics as number == item.index)?.name || "";
93130
})
94131
95132
function changeGraphics(value: any, index: number) {
96-
globalSetup.applyModelSettings({ graphics: (value as GraphicsItem).index });
133+
globalSetup.applyModelSettings({graphics: (value as GraphicsItem).index});
97134
}
98135
99136
</script>

0 commit comments

Comments
 (0)