Skip to content

Commit c5459f4

Browse files
wip: more fixes
1 parent 1021488 commit c5459f4

5 files changed

Lines changed: 115 additions & 19 deletions

File tree

WebUI/electron/subprocesses/comfyUIBackendService.ts

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {
6868

6969
private comfyUiParametersString: string = COMFYUI_DEFAULT_PARAMETERS
7070
private comfyUiVariant: ComfyUiVariant = 'xpu'
71+
private variantMismatchToastSent = false
7172

7273
private readonly variantMarkerPath = path.join(this.serviceDir, 'aipg-variant.json')
7374

@@ -88,14 +89,19 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {
8889
filesystem.writeFileSync(this.variantMarkerPath, JSON.stringify({ variant }), 'utf-8')
8990
}
9091

91-
private getEffectiveVariant(): ComfyUiVariant {
92-
const restored = this.readInstalledVariant()
93-
if (restored) return restored
92+
private getDesiredVariant(): ComfyUiVariant {
9493
if (this.settings.productMode === 'nvidia') return 'cuda'
9594
if (process.platform === 'win32') return 'xpu'
9695
return 'cpu'
9796
}
9897

98+
private getEffectiveVariant(): ComfyUiVariant {
99+
if (this.settings.productMode) return this.getDesiredVariant()
100+
const restored = this.readInstalledVariant()
101+
if (restored) return restored
102+
return this.getDesiredVariant()
103+
}
104+
99105
async serviceIsSetUp(): Promise<boolean> {
100106
this.appLogger.info(`Checking if comfyUI directories exist`, this.name)
101107
const dirsExist = filesystem.existsSync(this.serviceDir)
@@ -108,6 +114,23 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {
108114
this.revision = installedVersion
109115
}
110116

117+
const installedVariant = this.readInstalledVariant()
118+
const desiredVariant = this.getDesiredVariant()
119+
if (installedVariant && installedVariant !== desiredVariant) {
120+
this.appLogger.info(
121+
`ComfyUI variant mismatch: installed '${installedVariant}' but '${desiredVariant}' is required for current mode. Reinstallation needed.`,
122+
this.name,
123+
)
124+
if (!this.variantMismatchToastSent) {
125+
this.variantMismatchToastSent = true
126+
this.win.webContents.send('show-toast', {
127+
type: 'warning',
128+
message: `ComfyUI needs reinstallation to switch from ${installedVariant.toUpperCase()} to ${desiredVariant.toUpperCase()} backend.`,
129+
})
130+
}
131+
return false
132+
}
133+
111134
try {
112135
const marker = await this.readDepsMarker()
113136
const normRev = normalizeComfyUiRef(this.revision)
@@ -306,7 +329,7 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {
306329
}
307330
}
308331

309-
private async installComfyUiFlexibleDeps(): Promise<void> {
332+
private async installComfyUiFlexibleDeps(reinstallTorch = false): Promise<void> {
310333
const flexiblePyprojectSource = path.join(
311334
aipgBaseDir,
312335
'comfyui-deps',
@@ -358,6 +381,7 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {
358381
})
359382
},
360383
this.getTorchBackendEnv(),
384+
reinstallTorch ? ['torch', 'torchvision', 'torchaudio'] : undefined,
361385
)
362386
} finally {
363387
try {
@@ -467,10 +491,19 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {
467491
const normRev = normalizeComfyUiRef(this.revision)
468492
const existingMarker = await this.readDepsMarker()
469493
const markerMatches = existingMarker?.revision === normRev
494+
const installedVariant = this.readInstalledVariant()
495+
const variantChanged = installedVariant !== null && installedVariant !== this.comfyUiVariant
496+
497+
if (variantChanged) {
498+
this.appLogger.info(
499+
`ComfyUI variant changed from '${installedVariant}' to '${this.comfyUiVariant}', forcing dependency reinstall`,
500+
this.name,
501+
)
502+
}
470503

471504
if (useLocked) {
472505
let needsInstall = true
473-
if (existingMarker?.mode === 'locked' && markerMatches) {
506+
if (existingMarker?.mode === 'locked' && markerMatches && !variantChanged) {
474507
try {
475508
await checkBackend(this.serviceFolder)
476509
needsInstall = false
@@ -513,6 +546,7 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {
513546
if (
514547
existingMarker?.mode === 'flexible' &&
515548
markerMatches &&
549+
!variantChanged &&
516550
filesystem.existsSync(this.pythonEnvDir)
517551
) {
518552
needsInstall = false
@@ -528,7 +562,7 @@ export class ComfyUiBackendService extends LongLivedPythonApiService {
528562
message:
529563
'Installing custom ComfyUI versions may not be compatible with the bundled workflows. If you encounter issues, please clear the version override and reinstall the ComfyUI backend.',
530564
})
531-
await this.installComfyUiFlexibleDeps()
565+
await this.installComfyUiFlexibleDeps(variantChanged)
532566
}
533567
await this.writeDepsMarker({ mode: 'flexible', revision: normRev })
534568
}

WebUI/electron/subprocesses/openVINOBackendService.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,11 @@ export class OpenVINOBackendService implements ApiService {
712712
}
713713

714714
async start(): Promise<BackendStatus> {
715+
if (this.settings.productMode === 'nvidia') {
716+
this.appLogger.info('Skipping OpenVINO start in NVIDIA mode', this.name)
717+
return this.currentStatus
718+
}
719+
715720
// In this architecture, model server is started on-demand via ensureBackendReadiness
716721
// This method is kept for ApiService interface compatibility
717722
if (this.currentStatus === 'running') {

WebUI/electron/subprocesses/uvBasedBackends/uv.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,17 @@ export const pipInstallRequirementsFromFile = async (
174174
requirementsTxtPath: string,
175175
onCacheCorruptionDetected?: () => void,
176176
extraEnv?: Record<string, string>,
177+
reinstallPackages?: string[],
177178
) => {
178179
const logger = loggerFor(`uv.pip-req.${backend}`)
179180
await assertUv(logger)
180181
const projectDir = path.join(aipgBaseDir, backend)
181182
const uvCommand = ['pip', 'install', '--directory', projectDir, '-r', requirementsTxtPath]
183+
if (reinstallPackages) {
184+
for (const pkg of reinstallPackages) {
185+
uvCommand.push('--reinstall-package', pkg)
186+
}
187+
}
182188
logger.info(`pip install -r via uv: ${JSON.stringify(uvCommand)}`)
183189
try {
184190
await uv(uvCommand, logger, extraEnv)

WebUI/src/assets/js/store/backendServices.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ export const useBackendServices = defineStore(
274274
}
275275

276276
const versions = versionState.value[serviceName]
277-
const targetVersionSettings = versions.uiOverride ?? versions.target
277+
const targetVersionSettings = versions.uiOverride ?? versions.installed ?? versions.target
278278
const serviceSettings: ServiceSettings = { serviceName, ...targetVersionSettings }
279279
if (serviceName === 'comfyui-backend') {
280280
serviceSettings.comfyUiParameters = effectiveComfyUiParameters.value

WebUI/src/assets/js/store/setupWizard.ts

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,19 +83,40 @@ export const useSetupWizard = defineStore('setupWizard', () => {
8383
const disabledBackends = ref(new Set<BackendServiceName>())
8484
const wizardDirty = ref(false)
8585

86+
const wizardActivity = ref(new Map<BackendServiceName, string>())
87+
8688
const errorModalOpen = ref(false)
8789
const errorModalServiceName = ref<BackendServiceName | null>(null)
8890
const errorModalDetails = ref<ErrorDetails | null>(null)
8991

92+
const comfyUiNeedsVariantSwitch = computed(() => {
93+
const current = productModeStore.productMode
94+
const pending = pendingProductMode.value
95+
if (!current || !pending || current === pending) return false
96+
const crossesNvidiaBoundary = current === 'nvidia' || pending === 'nvidia'
97+
if (!crossesNvidiaBoundary) return false
98+
const comfyInfo = backendServices.info.find((s) => s.serviceName === 'comfyui-backend')
99+
return comfyInfo?.isSetUp === true
100+
})
101+
90102
const backendRows = computed<BackendRowViewModel[]>(() => {
91103
return backends.map((serviceName) => {
92104
const info = backendServices.info.find((s) => s.serviceName === serviceName)
93105
const available = isBackendAvailableInProductMode(pendingProductMode.value, serviceName)
94106
const isRequired = info?.isRequired ?? serviceName === 'ai-backend'
95-
const isSetUp = info?.isSetUp ?? false
96-
const status = info?.status ?? ('notInstalled' as BackendStatus)
107+
let isSetUp = info?.isSetUp ?? false
108+
let status = info?.status ?? ('notInstalled' as BackendStatus)
109+
110+
if (serviceName === 'comfyui-backend' && comfyUiNeedsVariantSwitch.value) {
111+
isSetUp = false
112+
status = 'notInstalled' as BackendStatus
113+
}
114+
97115
const isInstalling =
98-
status === 'installing' || status === 'starting' || status === 'stopping'
116+
status === 'installing' ||
117+
status === 'starting' ||
118+
status === 'stopping' ||
119+
wizardActivity.value.has(serviceName)
99120
const enabled = isRequired || installSelection.value.has(serviceName)
100121
const toggleDisabled = isRequired || !available || isInstalling
101122

@@ -125,20 +146,23 @@ export const useSetupWizard = defineStore('setupWizard', () => {
125146
versionDisplay = vs.installed.releaseTag
126147
? `${vs.installed.releaseTag} / ${vs.installed.version}`
127148
: vs.installed.version
128-
} else if (!isSetUp) {
149+
} else if (!isSetUp && !(serviceName === 'comfyui-backend' && comfyUiNeedsVariantSwitch.value)) {
129150
versionDisplay = 'Not installed'
130151
}
131152
}
132153

154+
const activityMessage = wizardActivity.value.get(serviceName)
133155
let installProgressText: string | null = null
134-
if (isInstalling) {
156+
if (isInstalling || activityMessage) {
135157
const progress = backendServices.latestSetupProgress.get(serviceName)
136158
if (progress) {
137159
const steps = knownSteps[serviceName] ?? []
138160
const stepIdx = steps.indexOf(progress.step)
139161
const label = stepDisplayNames[progress.step] ?? progress.debugMessage
140162
installProgressText =
141163
stepIdx >= 0 ? `${label} (${stepIdx + 1}/${steps.length})` : label
164+
} else if (activityMessage) {
165+
installProgressText = activityMessage
142166
} else if (status === 'stopping') {
143167
installProgressText = 'Stopping...'
144168
} else if (status === 'starting') {
@@ -159,7 +183,10 @@ export const useSetupWizard = defineStore('setupWizard', () => {
159183
toggleDisabled,
160184
isInstalling,
161185
statusColor: mapStatusToColor(status),
162-
statusText: mapToDisplayStatus(status) ?? status,
186+
statusText:
187+
serviceName === 'comfyui-backend' && comfyUiNeedsVariantSwitch.value
188+
? `Needs reinstall for ${pendingProductMode.value === 'nvidia' ? 'CUDA' : 'XPU'}`
189+
: (mapToDisplayStatus(status) ?? status),
163190
versionDisplay,
164191
errorDetails: backendServices.getServiceErrorDetails(serviceName),
165192
toggleTooltip,
@@ -280,7 +307,20 @@ export const useSetupWizard = defineStore('setupWizard', () => {
280307
initialLoadingPollHandle = null
281308
}
282309

283-
await productModeStore.ensureReady()
310+
const modeStatus = await productModeStore.ensureReady()
311+
312+
if (modeStatus === 'ready') {
313+
const allRequiredSetUp = backendServices.info
314+
.filter((s) => s.isRequired)
315+
.every((s) => s.isSetUp)
316+
317+
if (allRequiredSetUp) {
318+
pendingProductMode.value = productModeStore.productMode
319+
seedInstallSelection()
320+
await dismiss()
321+
return
322+
}
323+
}
284324

285325
if (!productModeStore.hardwareRecommendation) {
286326
await productModeStore.detectRecommendation()
@@ -304,18 +344,20 @@ export const useSetupWizard = defineStore('setupWizard', () => {
304344
async function commitAndInstall() {
305345
if (!pendingProductMode.value) return
306346

307-
if (pendingProductMode.value !== productModeStore.productMode) {
308-
await productModeStore.selectMode(pendingProductMode.value)
309-
}
310-
await syncPresetsForCurrentProductMode()
311-
347+
// Capture what needs installing BEFORE syncing mode — syncing resets the
348+
// variant-switch detection because current and pending modes become equal.
312349
const toInstall = backendRows.value.filter(
313350
(r) =>
314351
r.enabled &&
315352
r.availableInCurrentMode &&
316353
(r.status === 'notInstalled' || r.status === 'failed' || r.status === 'installationFailed'),
317354
)
318355

356+
if (pendingProductMode.value !== productModeStore.productMode) {
357+
await productModeStore.selectMode(pendingProductMode.value)
358+
}
359+
await syncPresetsForCurrentProductMode()
360+
319361
if (toInstall.length > 0) {
320362
wizardDirty.value = true
321363

@@ -369,6 +411,12 @@ export const useSetupWizard = defineStore('setupWizard', () => {
369411
}
370412

371413
try {
414+
wizardActivity.value.set(name, 'Detecting devices...')
415+
wizardActivity.value = new Map(wizardActivity.value)
416+
await backendServices.detectDevices(name)
417+
418+
wizardActivity.value.set(name, 'Starting...')
419+
wizardActivity.value = new Map(wizardActivity.value)
372420
const startStatus = await backendServices.startService(name)
373421
if (startStatus !== 'running') {
374422
const errorDetails = backendServices.getServiceErrorDetails(name)
@@ -383,6 +431,9 @@ export const useSetupWizard = defineStore('setupWizard', () => {
383431
? 'Service startup failed — see error log for details'
384432
: `Service startup failed: ${error instanceof Error ? error.message : String(error)}`
385433
toast.error(msg)
434+
} finally {
435+
wizardActivity.value.delete(name)
436+
wizardActivity.value = new Map(wizardActivity.value)
386437
}
387438
}
388439

0 commit comments

Comments
 (0)