Skip to content

Commit f543f2f

Browse files
qiachengmschuettlerTNGKyleHagyDanielHirschTNG
authored
Merge 2.6.1-beta to main for release (#332)
Co-authored-by: Markus Schüttler <[email protected]> Co-authored-by: kyle <[email protected]> Co-authored-by: KyleHagy <[email protected]> Co-authored-by: Daniel Hirsch <[email protected]>
1 parent 62ee7d5 commit f543f2f

31 files changed

+1086
-584
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ llama-cpp-env/
2323
*-env/
2424
build-envs/
2525
portable-git/
26-
llm_cache/*
26+
llm_cache*/
2727
emb_model_cache/

OpenVINO/openvino_backend.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,14 @@ def load_model(self, params: LLMParams, callback: Callable[[str], None] = None):
2525
model_path = path.abspath(path.join(model_base_path, model_name))
2626

2727
enable_compile_cache = dict()
28-
enable_compile_cache["CACHE_DIR"] = "llm_cache"
29-
self._model = openvino_genai.LLMPipeline(model_path, environ.get("OPENVINO_DEVICE", "AUTO"), **enable_compile_cache)
28+
device = environ.get("OPENVINO_DEVICE", "AUTO")
29+
if device == "NPU":
30+
enable_compile_cache["MAX_PROMPT_LEN"] = int(environ.get("MAX_PROMPT_LEN", 1024))
31+
cache_postfix = f"NPU_{str(enable_compile_cache["MAX_PROMPT_LEN"])}"
32+
else:
33+
cache_postfix = device
34+
enable_compile_cache["CACHE_DIR"] = f"llm_cache_{cache_postfix}"
35+
self._model = openvino_genai.LLMPipeline(model_path, device, **enable_compile_cache)
3036
self._tokenizer = self._model.get_tokenizer()
3137

3238
self._last_repo_id = model_repo_id

WebUI/build/build-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
{
3535
"from": "../OpenVINO",
3636
"to": "OpenVINO",
37-
"filter": ["!__pycache__/", "!.cache/", "!db/", "!llm_cache/", "!emb_model_cache/"]
37+
"filter": ["!__pycache__/", "!.cache/", "!db/", "!llm_cache*/**", "!emb_model_cache/"]
3838
},
3939
{
4040
"from": "external/workflows",

WebUI/electron/main.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ import type { ModelPaths } from '@/assets/js/store/models.ts'
5454
import type { IndexedDocument, EmbedInquiry } from '@/assets/js/store/textInference.ts'
5555
import { BackendServiceName } from '@/assets/js/store/backendServices.ts'
5656
import z from 'zod'
57-
import { ModelSchema } from '../src/types/shared.ts'
5857

5958
// }
6059
// The built directory structure
@@ -412,6 +411,18 @@ function initEventHandle() {
412411
return appSize
413412
})
414413

414+
ipcMain.handle('zoomIn', (event: IpcMainInvokeEvent) => {
415+
const win = BrowserWindow.fromWebContents(event.sender)
416+
if (!win) return
417+
win.webContents.setZoomLevel(win.webContents.getZoomLevel() + 1)
418+
})
419+
420+
ipcMain.handle('zoomOut', (event: IpcMainInvokeEvent) => {
421+
const win = BrowserWindow.fromWebContents(event.sender)
422+
if (!win) return
423+
win.webContents.setZoomLevel(win.webContents.getZoomLevel() - 1)
424+
})
425+
415426
ipcMain.on('openUrl', (_event, url: string) => {
416427
return shell.openExternal(url)
417428
})
@@ -735,24 +746,24 @@ function initEventHandle() {
735746
ipcMain.handle('startService', (_event: IpcMainInvokeEvent, serviceName: string) => {
736747
if (!serviceRegistry) {
737748
appLogger.warn('received start signal too early during aipg startup', 'electron-backend')
738-
return
749+
return 'failed'
739750
}
740751
const service = serviceRegistry.getService(serviceName)
741752
if (!service) {
742753
appLogger.warn(`Tried to start service ${serviceName} which is not known`, 'electron-backend')
743-
return
754+
return 'failed'
744755
}
745756
return service.start()
746757
})
747758
ipcMain.handle('stopService', (_event: IpcMainInvokeEvent, serviceName: string) => {
748759
if (!serviceRegistry) {
749760
appLogger.warn('received stop signal too early during aipg startup', 'electron-backend')
750-
return
761+
return 'failed'
751762
}
752763
const service = serviceRegistry.getService(serviceName)
753764
if (!service) {
754765
appLogger.warn(`Tried to stop service ${serviceName} which is not known`, 'electron-backend')
755-
return
766+
return 'failed'
756767
}
757768
return service.stop()
758769
})

WebUI/electron/preload.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
7575
refreshLLMModles: () => ipcRenderer.invoke('refreshLLMModles'),
7676
refreshLora: () => ipcRenderer.invoke('refreshLora'),
7777
loadModels: () => ipcRenderer.invoke('loadModels'),
78+
zoomIn: () => ipcRenderer.invoke('zoomIn'),
79+
zoomOut: () => ipcRenderer.invoke('zoomOut'),
7880
getDownloadedDiffusionModels: () => ipcRenderer.invoke('getDownloadedDiffusionModels'),
7981
getDownloadedInpaintModels: () => ipcRenderer.invoke('getDownloadedInpaintModels'),
8082
getDownloadedLoras: () => ipcRenderer.invoke('getDownloadedLoras'),

WebUI/electron/subprocesses/aiBackendService.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export class AiBackendService extends LongLivedPythonApiService {
3131
async detectDevices() {
3232
const availableDevices = await detectLevelZeroDevices(this.python)
3333
this.appLogger.info(`detected devices: ${JSON.stringify(availableDevices, null, 2)}`, this.name)
34+
if (availableDevices.length === 0) {
35+
this.appLogger.error(`No devices detected`, this.name)
36+
return
37+
}
3438

3539
let bestDeviceId: string
3640
try {
@@ -47,6 +51,10 @@ export class AiBackendService extends LongLivedPythonApiService {
4751
this.devices = availableDevices.map((d) => ({ ...d, selected: d.id === bestDeviceId }))
4852
}
4953

54+
getServiceForPipFreeze(): UvPipService {
55+
return this.uvPip
56+
}
57+
5058
async *set_up(): AsyncIterable<SetupProgress> {
5159
this.setStatus('installing')
5260
this.appLogger.info('setting up service', this.name)
@@ -137,7 +145,7 @@ export class AiBackendService extends LongLivedPythonApiService {
137145
this.appLogger.warn(`Aborting set up of ${this.name} service environment`, this.name, true)
138146
this.setStatus('installationFailed')
139147

140-
const errorDetails = createEnhancedErrorDetails(e, `${currentStep} operation`)
148+
const errorDetails = await createEnhancedErrorDetails(e, `${currentStep} operation`, this.uvPip)
141149

142150
yield {
143151
serviceName: this.name,

WebUI/electron/subprocesses/comfyUIBackendService.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {
8686
this.devices = availableDevices.map((d) => ({ ...d, selected: d.id == '0' }))
8787
}
8888

89+
getServiceForPipFreeze(): UvPipService {
90+
return this.uvPip
91+
}
92+
8993
async *set_up(): AsyncIterable<SetupProgress> {
9094
this.appLogger.info('setting up service', this.name)
9195
this.setStatus('installing')
@@ -292,7 +296,7 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {
292296
this.appLogger.warn(`Aborting set up of ${this.name} service environment`, this.name, true)
293297
this.setStatus('installationFailed')
294298

295-
const errorDetails = createEnhancedErrorDetails(e, `${currentStep} operation`)
299+
const errorDetails = await createEnhancedErrorDetails(e, `${currentStep} operation`, this.uvPip)
296300

297301
yield {
298302
serviceName: this.name,

WebUI/electron/subprocesses/llamaCppBackendService.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ export class LlamaCppBackendService extends LongLivedPythonApiService {
151151
}
152152
}
153153

154+
getServiceForPipFreeze(): UvPipService {
155+
return this.uvPip
156+
}
157+
154158
async *set_up(): AsyncIterable<SetupProgress> {
155159
this.setStatus('installing')
156160
this.appLogger.info('setting up service', this.name)
@@ -231,7 +235,7 @@ export class LlamaCppBackendService extends LongLivedPythonApiService {
231235
this.appLogger.warn(`Aborting set up of ${this.name} service environment`, this.name, true)
232236
this.setStatus('installationFailed')
233237

234-
const errorDetails = createEnhancedErrorDetails(e, `${currentStep} operation`)
238+
const errorDetails = await createEnhancedErrorDetails(e, `${currentStep} operation`, this.uvPip)
235239

236240
yield {
237241
serviceName: this.name,

WebUI/electron/subprocesses/ollamaBackendService.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from 'node:path'
33
import * as filesystem from 'fs-extra'
44
import { app, BrowserWindow, net } from 'electron'
55
import { appLoggerInstance } from '../logging/logger.ts'
6-
import { ApiService, DeviceService, PythonService, createEnhancedErrorDetails } from './service.ts'
6+
import { ApiService, DeviceService, PythonService, createEnhancedErrorDetails, ErrorDetails } from './service.ts'
77
import { promisify } from 'util'
88
import { exec } from 'child_process'
99
import { detectLevelZeroDevices } from './deviceDetection.ts'
@@ -45,6 +45,9 @@ export class OllamaBackendService implements ApiService {
4545
encapsulatedProcess: ChildProcess | null = null
4646
desiredStatus: BackendStatus = 'uninitializedStatus'
4747

48+
// Store last startup error details for persistence
49+
private lastStartupErrorDetails: ErrorDetails | null = null
50+
4851
// Logger
4952
readonly appLogger = appLoggerInstance
5053

@@ -117,6 +120,7 @@ export class OllamaBackendService implements ApiService {
117120
isSetUp: this.isSetUp,
118121
isRequired: this.isRequired,
119122
devices: this.devices,
123+
errorDetails: this.lastStartupErrorDetails,
120124
}
121125
}
122126

@@ -220,7 +224,7 @@ export class OllamaBackendService implements ApiService {
220224
this.setStatus('installationFailed')
221225

222226
// Create detailed error information for any type of error
223-
const errorDetails = createEnhancedErrorDetails(e, `${currentStep} operation`)
227+
const errorDetails = await createEnhancedErrorDetails(e, `${currentStep} operation`, this.aiBackend)
224228

225229
yield {
226230
serviceName: this.name,
@@ -326,6 +330,8 @@ export class OllamaBackendService implements ApiService {
326330
throw new Error('Server currently stopping. Cannot start it.')
327331
}
328332
if (this.currentStatus === 'running') {
333+
// Clear error on successful running status
334+
this.clearLastStartupError()
329335
return 'running'
330336
}
331337
if (this.desiredStatus === 'running') {
@@ -343,12 +349,23 @@ export class OllamaBackendService implements ApiService {
343349
this.currentStatus = 'running'
344350
this.appLogger.info(`started server ${this.name} on ${this.baseUrl}`, this.name)
345351
this.isSetUp = true
352+
// Clear error on successful startup
353+
this.clearLastStartupError()
346354
} else {
347355
this.currentStatus = 'failed'
348356
this.desiredStatus = 'failed'
349357
this.isSetUp = false
350358
this.appLogger.error(`server ${this.name} failed to boot`, this.name)
351359
this.encapsulatedProcess?.kill()
360+
361+
// Capture detailed error information for startup failure
362+
const startupError = new Error(`Server ${this.name} failed to boot - health check timeout or early process exit`)
363+
const errorDetails = await createEnhancedErrorDetails(
364+
startupError,
365+
'service startup',
366+
this.aiBackend
367+
)
368+
this.setLastStartupError(errorDetails)
352369
}
353370
} catch (error) {
354371
this.appLogger.error(`failed to start server due to ${error}`, this.name)
@@ -357,6 +374,14 @@ export class OllamaBackendService implements ApiService {
357374
this.isSetUp = false
358375
this.encapsulatedProcess?.kill()
359376
this.encapsulatedProcess = null
377+
378+
// Capture detailed error information for startup exception
379+
const errorDetails = await createEnhancedErrorDetails(
380+
error,
381+
'service startup',
382+
this.aiBackend
383+
)
384+
this.setLastStartupError(errorDetails)
360385
} finally {
361386
this.win.webContents.send('serviceInfoUpdate', this.get_info())
362387
}
@@ -382,13 +407,28 @@ export class OllamaBackendService implements ApiService {
382407
return 'stopped'
383408
}
384409

410+
// Error management methods for startup failures
411+
setLastStartupError(errorDetails: ErrorDetails): void {
412+
this.lastStartupErrorDetails = errorDetails
413+
}
414+
415+
getLastStartupError(): ErrorDetails | null {
416+
return this.lastStartupErrorDetails
417+
}
418+
419+
clearLastStartupError(): void {
420+
this.lastStartupErrorDetails = null
421+
}
422+
385423
async uninstall(): Promise<void> {
386424
await this.stop()
387425
this.appLogger.info(`removing Ollama service directory`, this.name)
388426
await filesystem.remove(this.serviceDir)
389427
this.appLogger.info(`removed Ollama service directory`, this.name)
390428
this.setStatus('notInstalled')
391429
this.isSetUp = false
430+
// Clear startup errors when uninstalling
431+
this.clearLastStartupError()
392432
}
393433

394434
pipeProcessLogs(process: ChildProcess) {

WebUI/electron/subprocesses/openVINOBackendService.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export class OpenVINOBackendService extends LongLivedPythonApiService {
2020
readonly uvPip = new UvPipService(this.pythonEnvDir, serviceFolder)
2121
readonly python = this.uvPip.python
2222
devices: InferenceDevice[] = [{ id: 'AUTO', name: 'Use best device', selected: true }]
23+
currentContextSize: number | null = null
2324

2425
serviceIsSetUp(): boolean {
2526
return filesystem.existsSync(this.python.getExePath())
@@ -36,6 +37,10 @@ export class OpenVINOBackendService extends LongLivedPythonApiService {
3637
]
3738
}
3839

40+
getServiceForPipFreeze(): UvPipService {
41+
return this.uvPip
42+
}
43+
3944
async *set_up(): AsyncIterable<SetupProgress> {
4045
this.setStatus('installing')
4146
this.appLogger.info('setting up service', this.name)
@@ -111,7 +116,7 @@ export class OpenVINOBackendService extends LongLivedPythonApiService {
111116
this.appLogger.warn(`Aborting set up of ${this.name} service environment`, this.name, true)
112117
this.setStatus('installationFailed')
113118

114-
const errorDetails = createEnhancedErrorDetails(e, `${currentStep} operation`)
119+
const errorDetails = await createEnhancedErrorDetails(e, `${currentStep} operation`, this.uvPip)
115120

116121
yield {
117122
serviceName: this.name,
@@ -138,6 +143,35 @@ export class OpenVINOBackendService extends LongLivedPythonApiService {
138143
}
139144
}
140145

146+
async ensureBackendReadiness(
147+
llmModelName: string,
148+
embeddingModelName?: string,
149+
contextSize?: number,
150+
): Promise<void> {
151+
this.appLogger.info(
152+
`Ensuring openVINO backend readiness for contextSize: ${contextSize}`,
153+
this.name,
154+
)
155+
156+
try {
157+
// Handle context size
158+
if (contextSize && contextSize !== this.currentContextSize) {
159+
this.currentContextSize = contextSize
160+
await this.stop()
161+
await this.start()
162+
this.appLogger.info(`openVINO ready with context size: ${contextSize}`, this.name)
163+
} else {
164+
this.appLogger.info(`openVINO already running with context size: ${contextSize}`, this.name)
165+
}
166+
} catch (error) {
167+
this.appLogger.error(
168+
`Failed to ensure backend readiness - LLM: ${llmModelName}, Embedding: ${embeddingModelName ?? 'none'}: ${error}`,
169+
this.name,
170+
)
171+
throw error
172+
}
173+
}
174+
141175
async spawnAPIProcess(): Promise<{
142176
process: ChildProcess
143177
didProcessExitEarlyTracker: Promise<boolean>
@@ -147,6 +181,7 @@ export class OpenVINOBackendService extends LongLivedPythonApiService {
147181
SYCL_ENABLE_DEFAULT_CONTEXTS: '1',
148182
SYCL_CACHE_PERSISTENT: '1',
149183
PYTHONIOENCODING: 'utf-8',
184+
...(this.currentContextSize ? { MAX_PROMPT_LEN: this.currentContextSize.toString() } : {}),
150185
...openVinoDeviceSelectorEnv(this.devices.find((d) => d.selected)?.id),
151186
}
152187

0 commit comments

Comments
 (0)