Replace Comfy YuE with standalone YuEGP profile 1

This commit is contained in:
Towsty
2026-09-07 20:24:47 -05:00
parent eeff12cdab
commit c2b6032e49
33 changed files with 976 additions and 294 deletions
+37 -2
View File
@@ -1,5 +1,6 @@
import { createGpuReservation } from './gpu-reservation.mjs'
import { createGpuProxy } from './gpu-proxy.mjs'
import { createYueGpHost } from './yuegp-host.mjs'
import http from 'node:http'
import net from 'node:net'
import { execFile, spawn } from 'node:child_process'
@@ -143,7 +144,7 @@ let proxyTarget = 0
function ensureProxyListening() {
if (proxyServer) return
proxyServer = createGpuProxy({ target: () => proxyTarget, reservation: gpuReservation, authorized, markWork })
proxyServer = createGpuProxy({ target: () => proxyTarget, reservation: gpuReservation, authorized, markWork, externalBusy: () => yueGp.busy() })
proxyServer.on('error', (error) => {
console.log(JSON.stringify({ src: 'comfy-host-agent', event: 'proxy-error', error: String(error.message || error) }))
})
@@ -812,6 +813,7 @@ function purgeDesktopFiles(body) {
}
const gpuReservation = createGpuReservation({ idle: async () => {
if (yueGp.busy()) return false
if ((await trainingLock()).busy) return false
const healthy = await syncProxy()
if (healthy) {
@@ -822,9 +824,39 @@ const gpuReservation = createGpuReservation({ idle: async () => {
return !(await processUp()) && !(await pythonMainUp().catch(() => true))
} })
const yueGp = createYueGpHost({
leaseValid: lease => gpuReservation.isOwner(lease),
prepare: async () => {
if ((await trainingLock()).busy) throw new Error('GPU is busy with training.')
const healthy = await syncProxy()
if (healthy) {
const queue = await fetchLocalQueue(healthy)
if (!queue.ok || queue.running || queue.pending) throw new Error('Comfy is busy; YuEGP cannot start.')
await stopComfyProcesses()
markAsleep()
}
if (await pythonMainUp()) throw new Error('Comfy has not stopped; retry after the GPU is free.')
}
})
async function handleControl(req, res) {
if (!authorized(req)) return json(res, 401, { ok: false, error: 'unauthorized' })
const url = new URL(req.url || '/', 'http://localhost')
if (url.pathname.startsWith('/yuegp/')) {
const match = url.pathname.match(/^\/yuegp\/jobs\/([a-zA-Z0-9-]{12,80})(\/audio|\/cancel)?$/)
if (req.method === 'GET' && url.pathname === '/yuegp/status') return json(res, 200, { configured: yueGp.configured(), busy: yueGp.busy(), backend: 'yuegp' })
if (req.method === 'POST' && url.pathname === '/yuegp/jobs') return json(res, 200, await yueGp.start(await readJson(req), String(req.headers['x-aigen-gpu-lease'] || '')))
if (match && req.method === 'POST' && match[2] === '/cancel') return json(res, 200, await yueGp.cancel(match[1]))
if (match && req.method === 'GET' && match[2] === '/audio') {
const path = yueGp.audio(match[1])
return path ? streamFile(res, path) : json(res, 404, { error: 'Audio not ready' })
}
if (match && req.method === 'GET' && !match[2]) {
const job = yueGp.read(match[1])
return json(res, job ? 200 : 404, job || { error: 'YuEGP job not found' })
}
return json(res, 404, { error: 'Unknown YuEGP endpoint' })
}
if (req.method === 'POST' && url.pathname.startsWith('/gpu/')) {
const body = await readJson(req)
let result
@@ -855,10 +887,12 @@ async function handleControl(req, res) {
port: healthyPort || null,
proxyPort,
gpu: gpuReservation.availability(),
training: { busy: lastTraining.busy }
training: { busy: lastTraining.busy },
yuegp: { busy: yueGp.busy(), configured: yueGp.configured() }
})
}
if (req.method === 'POST' && url.pathname === '/start') {
if (yueGp.busy()) return json(res, 409, { message: 'YuEGP is using the GPU.' })
const training = await trainingLock()
if (training.busy) {
return json(res, 409, {
@@ -954,6 +988,7 @@ const server = http.createServer(async (req, res) => {
} else await handleControl(req, res)
} catch (error) {
req.resume()
if (String(req.url || '').startsWith('/yuegp/') && !res.headersSent) return json(res, error.statusCode || 400, { error: error.message || 'YuEGP request failed' })
if (!res.headersSent) json(res, error.statusCode || 400, { ok: false, message: error.statusCode === 409 ? 'GPU is in use. Waiting for availability.' : 'GPU coordination request failed.' })
}
})