Stop idle Comfy after 30 minutes and add a Wake GPU poke.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+71
-16
@@ -10,14 +10,27 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 text-sm">
|
||||
<span
|
||||
class="hidden items-center gap-2 rounded-full border px-3 py-1 sm:inline-flex"
|
||||
:class="rigBadge.className"
|
||||
:title="rigBadge.title"
|
||||
>
|
||||
<span class="h-2 w-2 rounded-full" :class="rigBadge.dot" />
|
||||
{{ rigBadge.label }}
|
||||
</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<span
|
||||
class="hidden items-center gap-2 rounded-full border px-3 py-1 sm:inline-flex"
|
||||
:class="rigBadge.className"
|
||||
:title="rigBadge.title"
|
||||
>
|
||||
<span class="h-2 w-2 rounded-full" :class="rigBadge.dot" />
|
||||
{{ rigBadge.label }}
|
||||
</span>
|
||||
<button
|
||||
v-if="showWakeGpu"
|
||||
type="button"
|
||||
class="rounded-full border px-3 py-1 text-zinc-300 hover:border-amber-300/50 hover:text-white disabled:cursor-wait disabled:opacity-80"
|
||||
:class="gpuWaking ? 'border-amber-300/40 text-amber-100' : 'border-white/10'"
|
||||
:disabled="gpuWaking"
|
||||
:title="gpuWakeError || (gpuWaking ? 'Starting Comfy Desktop on the GPU' : 'Poke / Wake GPU — start Comfy before generating')"
|
||||
@click="pokeGpu"
|
||||
>
|
||||
{{ gpuWaking ? 'Starting GPU…' : 'Wake GPU' }}
|
||||
</button>
|
||||
</div>
|
||||
<span class="hidden md:block text-zinc-400">{{ userName }}</span>
|
||||
<button type="button" class="rounded-full border border-white/10 px-3 py-1 text-zinc-300 hover:border-amber-300/50 hover:text-white" @click="settingsOpen = true">
|
||||
Settings
|
||||
@@ -2358,6 +2371,9 @@ const playerSize = ref({ width: 1344, height: 768 })
|
||||
const toasts = ref<{ id: string; message: string }[]>([])
|
||||
const comfyOk = ref(false)
|
||||
const comfyBusy = ref(false)
|
||||
const comfyAsleep = ref(false)
|
||||
const gpuWaking = ref(false)
|
||||
const gpuWakeError = ref('')
|
||||
const userName = ref('')
|
||||
const authEnabled = ref(false)
|
||||
const authMode = ref('')
|
||||
@@ -2574,13 +2590,24 @@ const liveMaxStep = computed(() => (
|
||||
const rigBusy = computed(() => (
|
||||
comfyBusy.value || imageComfyBusy.value || videoBusy.value || editBusy.value
|
||||
))
|
||||
const showWakeGpu = computed(() => gpuWaking.value || (!comfyOk.value && !imageComfyOk.value))
|
||||
const rigBadge = computed(() => {
|
||||
if (gpuWaking.value) {
|
||||
return {
|
||||
label: 'Starting…',
|
||||
className: 'border-amber-400/40 text-amber-200',
|
||||
dot: 'bg-amber-400 animate-pulse',
|
||||
title: 'Starting Comfy Desktop on the GPU'
|
||||
}
|
||||
}
|
||||
if (!comfyOk.value && !imageComfyOk.value) {
|
||||
return {
|
||||
label: 'Offline',
|
||||
className: 'border-red-400/30 text-red-300',
|
||||
dot: 'bg-red-400',
|
||||
title: 'Desktop GPU is unreachable'
|
||||
title: comfyAsleep.value
|
||||
? 'Desktop GPU is asleep. Click Wake GPU or generate to start Comfy.'
|
||||
: 'Desktop GPU is unreachable'
|
||||
}
|
||||
}
|
||||
if (rigBusy.value) {
|
||||
@@ -2696,7 +2723,7 @@ const recommendHint = computed(() => {
|
||||
const editBlocked = computed(() => {
|
||||
if (!file.value || !prompt.value.trim() || !folderId.value) return true
|
||||
if (editPassQueue.value.some(item => !item.prompt.trim())) return true
|
||||
if (!comfyOk.value && !imageComfyOk.value) return true
|
||||
if (!comfyOk.value && !imageComfyOk.value && !gpuWaking.value) return true
|
||||
return false
|
||||
})
|
||||
const editBlockReason = computed(() => {
|
||||
@@ -2705,7 +2732,7 @@ const editBlockReason = computed(() => {
|
||||
if (!folderId.value) return 'Choose a library folder before editing.'
|
||||
if (editPassQueue.value.some(item => !item.prompt.trim())) return 'Each extra pass needs a prompt.'
|
||||
if (!imageComfyConfigured.value && !comfyOk.value) return 'Image edit is not configured. Set COMFY_HOST.'
|
||||
if (!comfyOk.value && !imageComfyOk.value) return 'Desktop GPU is offline.'
|
||||
if (!comfyOk.value && !imageComfyOk.value && !gpuWaking.value) return 'Desktop GPU is offline. Click Wake GPU first.'
|
||||
return ''
|
||||
})
|
||||
const stillBHint = computed(() => {
|
||||
@@ -2719,7 +2746,7 @@ const editV2Blocked = computed(() => {
|
||||
if (v2Mode.value !== 'generate' && !file.value) return true
|
||||
if (v2Mode.value === 'compose' && !editRefFile.value) return true
|
||||
if (v2Mode.value === 'refine' && !refineMaskDirty.value) return true
|
||||
if (!comfyOk.value && !imageComfyOk.value) return true
|
||||
if (!comfyOk.value && !imageComfyOk.value && !gpuWaking.value) return true
|
||||
return false
|
||||
})
|
||||
const editV2BlockReason = computed(() => {
|
||||
@@ -2729,7 +2756,7 @@ const editV2BlockReason = computed(() => {
|
||||
if (!prompt.value.trim()) return 'Write a prompt first.'
|
||||
if (!folderId.value) return 'Choose a library folder before generating.'
|
||||
if (!imageComfyConfigured.value && !comfyOk.value) return 'Image v2 is not configured. Set COMFY_HOST.'
|
||||
if (!comfyOk.value && !imageComfyOk.value) return 'Desktop GPU is offline.'
|
||||
if (!comfyOk.value && !imageComfyOk.value && !gpuWaking.value) return 'Desktop GPU is offline. Click Wake GPU first.'
|
||||
return ''
|
||||
})
|
||||
const editV2SubmitLabel = computed(() => {
|
||||
@@ -3206,7 +3233,7 @@ function scheduleLibraryRefresh() {
|
||||
|
||||
let healthFailStreak = 0
|
||||
function applyComfyHealth(health: {
|
||||
comfy?: { ok?: boolean; busy?: boolean }
|
||||
comfy?: { ok?: boolean; busy?: boolean; asleep?: boolean }
|
||||
imageComfy?: {
|
||||
ok?: boolean
|
||||
busy?: boolean
|
||||
@@ -3219,7 +3246,11 @@ function applyComfyHealth(health: {
|
||||
const nextComfyOk = Boolean(health?.comfy?.ok)
|
||||
const nextImageOk = Boolean(health?.imageComfy?.ok)
|
||||
const reachable = nextComfyOk || nextImageOk
|
||||
if (reachable) healthFailStreak = 0
|
||||
if (reachable) {
|
||||
healthFailStreak = 0
|
||||
gpuWaking.value = false
|
||||
gpuWakeError.value = ''
|
||||
}
|
||||
else healthFailStreak += 1
|
||||
const keepLast = !reachable && (healthFailStreak < 3 || videoBusy.value || editBusy.value)
|
||||
if (!keepLast) {
|
||||
@@ -3227,6 +3258,7 @@ function applyComfyHealth(health: {
|
||||
imageComfyOk.value = nextImageOk
|
||||
imageBeastOk.value = Boolean(health?.imageComfy?.beast?.ok)
|
||||
imageSidecarOk.value = Boolean(health?.imageComfy?.sidecar?.ok)
|
||||
comfyAsleep.value = Boolean(health?.comfy?.asleep) && !reachable
|
||||
}
|
||||
comfyBusy.value = Boolean((keepLast ? comfyOk.value : nextComfyOk) && health?.comfy?.busy)
|
||||
imageComfyConfigured.value = Boolean(health?.imageComfy?.configured)
|
||||
@@ -3246,10 +3278,33 @@ async function pollLoras() {
|
||||
}
|
||||
|
||||
async function pollComfyHealth() {
|
||||
const health = await $fetch<{ comfy?: { ok?: boolean; busy?: boolean } }>('/api/health').catch(() => ({ comfy: { ok: false, busy: false } }))
|
||||
const health = await $fetch<{ comfy?: { ok?: boolean; busy?: boolean; asleep?: boolean } }>('/api/health').catch(() => ({ comfy: { ok: false, busy: false } }))
|
||||
applyComfyHealth(health)
|
||||
}
|
||||
|
||||
async function pokeGpu() {
|
||||
if (gpuWaking.value) return
|
||||
gpuWaking.value = true
|
||||
gpuWakeError.value = ''
|
||||
try {
|
||||
await $fetch('/api/comfy/wake', { method: 'POST' })
|
||||
const deadline = Date.now() + 180_000
|
||||
while (Date.now() < deadline) {
|
||||
await pollComfyHealth()
|
||||
if (comfyOk.value || imageComfyOk.value) {
|
||||
gpuWaking.value = false
|
||||
return
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 3000))
|
||||
}
|
||||
gpuWakeError.value = 'Started Comfy but it is not answering yet. If the instance picker is showing, open ComfyUI (1).'
|
||||
} catch (error: any) {
|
||||
gpuWakeError.value = error?.data?.statusMessage || error?.statusMessage || error?.message || 'Could not wake the GPU.'
|
||||
} finally {
|
||||
gpuWaking.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
withSound.value = localStorage.getItem('aigen-generate-sound') !== 'false'
|
||||
|
||||
Reference in New Issue
Block a user