Refuse to start Comfy while AITraining has the GPU.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -12,18 +12,43 @@ const proxyPort = Number(process.env.COMFY_PROXY_PORT || 8198)
|
|||||||
const token = process.env.COMFY_CONTROL_TOKEN || ''
|
const token = process.env.COMFY_CONTROL_TOKEN || ''
|
||||||
const defaultHttp = (process.env.COMFY_HOST || 'http://127.0.0.1:8188').replace(/\/$/, '')
|
const defaultHttp = (process.env.COMFY_HOST || 'http://127.0.0.1:8188').replace(/\/$/, '')
|
||||||
const idleMs = Math.max(60_000, Number(process.env.COMFY_IDLE_MS || 30 * 60 * 1000) || 30 * 60 * 1000)
|
const idleMs = Math.max(60_000, Number(process.env.COMFY_IDLE_MS || 30 * 60 * 1000) || 30 * 60 * 1000)
|
||||||
|
const trainingUrl = String(process.env.TRAINING_CONTROL_URL || 'http://127.0.0.1:8200').replace(/\/$/, '')
|
||||||
|
|
||||||
let lastWorkAt = Date.now()
|
let lastWorkAt = Date.now()
|
||||||
let lastQueueRunning = 0
|
let lastQueueRunning = 0
|
||||||
let stoppedByAgent = false
|
let stoppedByAgent = false
|
||||||
let lastHealthyPort = 0
|
let lastHealthyPort = 0
|
||||||
let lastProcessUp = false
|
let lastProcessUp = false
|
||||||
|
let lastTraining = { busy: false, jobId: null, status: null, name: null, message: '' }
|
||||||
|
|
||||||
function markWork() {
|
function markWork() {
|
||||||
lastWorkAt = Date.now()
|
lastWorkAt = Date.now()
|
||||||
stoppedByAgent = false
|
stoppedByAgent = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function trainingLock() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${trainingUrl}/status`, { signal: AbortSignal.timeout(2500) })
|
||||||
|
const body = await res.json().catch(() => null)
|
||||||
|
const job = body?.job
|
||||||
|
const status = String(job?.status || '')
|
||||||
|
const busy = Boolean(job && ['preparing', 'queued', 'running', 'stopping'].includes(status))
|
||||||
|
lastTraining = {
|
||||||
|
busy,
|
||||||
|
jobId: job?.id || null,
|
||||||
|
status: busy ? status : null,
|
||||||
|
name: busy ? (job?.outputName || null) : null,
|
||||||
|
message: busy
|
||||||
|
? `AITraining is ${status}${job?.outputName ? ` (${job.outputName})` : ''}. Stop that job before poking Comfy.`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
return lastTraining
|
||||||
|
} catch {
|
||||||
|
lastTraining = { busy: false, jobId: null, status: null, name: null, message: '' }
|
||||||
|
return lastTraining
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function json(res, status, body) {
|
function json(res, status, body) {
|
||||||
const payload = JSON.stringify(body)
|
const payload = JSON.stringify(body)
|
||||||
res.writeHead(status, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) })
|
res.writeHead(status, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) })
|
||||||
@@ -603,10 +628,20 @@ const server = http.createServer(async (req, res) => {
|
|||||||
lastActivityAt: new Date(lastWorkAt).toISOString(),
|
lastActivityAt: new Date(lastWorkAt).toISOString(),
|
||||||
idleMs,
|
idleMs,
|
||||||
port: healthyPort || null,
|
port: healthyPort || null,
|
||||||
proxyPort: healthyPort ? proxyPort : null
|
proxyPort: healthyPort ? proxyPort : null,
|
||||||
|
training: lastTraining
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (req.method === 'POST' && url.pathname === '/start') {
|
if (req.method === 'POST' && url.pathname === '/start') {
|
||||||
|
const training = await trainingLock()
|
||||||
|
if (training.busy) {
|
||||||
|
return json(res, 409, {
|
||||||
|
ok: false,
|
||||||
|
error: 'train-busy',
|
||||||
|
message: training.message,
|
||||||
|
training
|
||||||
|
})
|
||||||
|
}
|
||||||
const healthyPort = await syncProxy()
|
const healthyPort = await syncProxy()
|
||||||
if (healthyPort) {
|
if (healthyPort) {
|
||||||
markWork()
|
markWork()
|
||||||
@@ -663,6 +698,8 @@ server.listen(port, '0.0.0.0', async () => {
|
|||||||
idleMs,
|
idleMs,
|
||||||
candidates: candidatePorts()
|
candidates: candidatePorts()
|
||||||
}))
|
}))
|
||||||
|
void trainingLock()
|
||||||
|
setInterval(() => { void trainingLock() }, 3000)
|
||||||
let lastIdleCheck = 0
|
let lastIdleCheck = 0
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
syncProxy().then(async (nextPort) => {
|
syncProxy().then(async (nextPort) => {
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ async function controlRequest(path: string, method = 'GET', timeoutMs = 5000) {
|
|||||||
stopped?: boolean
|
stopped?: boolean
|
||||||
killed?: unknown
|
killed?: unknown
|
||||||
error?: string
|
error?: string
|
||||||
|
message?: string
|
||||||
|
training?: { busy?: boolean; jobId?: string | null; status?: string | null; name?: string | null; message?: string }
|
||||||
running?: number
|
running?: number
|
||||||
pending?: number
|
pending?: number
|
||||||
status?: number
|
status?: number
|
||||||
@@ -118,6 +120,8 @@ async function controlRequest(path: string, method = 'GET', timeoutMs = 5000) {
|
|||||||
ok: false as const,
|
ok: false as const,
|
||||||
status: res.status,
|
status: res.status,
|
||||||
error: body?.error,
|
error: body?.error,
|
||||||
|
message: body?.message,
|
||||||
|
training: body?.training,
|
||||||
running: body?.running,
|
running: body?.running,
|
||||||
pending: body?.pending,
|
pending: body?.pending,
|
||||||
killed: body?.killed
|
killed: body?.killed
|
||||||
@@ -133,6 +137,15 @@ export async function fetchComfyControlStatus() {
|
|||||||
return await controlRequest('/status')
|
return await controlRequest('/status')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function throwIfTrainingBusy(remote: { training?: { busy?: boolean; message?: string }; error?: string; message?: string } | null) {
|
||||||
|
const busy = Boolean(remote?.training?.busy || remote?.error === 'train-busy')
|
||||||
|
if (!busy) return
|
||||||
|
throw createError({
|
||||||
|
statusCode: 409,
|
||||||
|
statusMessage: remote?.training?.message || remote?.message || 'AITraining is using the GPU. Stop that job before poking Comfy.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export async function requestComfyWake() {
|
export async function requestComfyWake() {
|
||||||
const { controlUrl } = settings()
|
const { controlUrl } = settings()
|
||||||
if (!controlUrl) {
|
if (!controlUrl) {
|
||||||
@@ -148,10 +161,11 @@ export async function requestComfyWake() {
|
|||||||
statusMessage: 'Beast host agent is unreachable. Confirm it is running on the desktop.'
|
statusMessage: 'Beast host agent is unreachable. Confirm it is running on the desktop.'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
throwIfTrainingBusy(remote)
|
||||||
if (remote.ok === false) {
|
if (remote.ok === false) {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: Number(remote.status) || 502,
|
statusCode: Number(remote.status) || 502,
|
||||||
statusMessage: 'Beast host agent refused to start Comfy.'
|
statusMessage: String(remote.message || remote.error || 'Beast host agent refused to start Comfy.')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -250,6 +264,7 @@ async function startComfy() {
|
|||||||
lastStartAt = now
|
lastStartAt = now
|
||||||
|
|
||||||
const remote = await controlRequest('/start', 'POST')
|
const remote = await controlRequest('/start', 'POST')
|
||||||
|
throwIfTrainingBusy(remote)
|
||||||
if (remote?.ok || remote?.started) {
|
if (remote?.ok || remote?.started) {
|
||||||
log('start-remote', { pid: remote.pid || null })
|
log('start-remote', { pid: remote.pid || null })
|
||||||
return { started: true, reason: 'remote' as const }
|
return { started: true, reason: 'remote' as const }
|
||||||
@@ -322,6 +337,8 @@ function hostWithPort(host: string, port: number) {
|
|||||||
async function ensureUnlocked(onStatus: StatusFn, skipBusyWait = false) {
|
async function ensureUnlocked(onStatus: StatusFn, skipBusyWait = false) {
|
||||||
const cfg = settings()
|
const cfg = settings()
|
||||||
const host = cfg.host
|
const host = cfg.host
|
||||||
|
const lock = await controlRequest('/status')
|
||||||
|
throwIfTrainingBusy(lock)
|
||||||
onStatus({ state: 'offline', message: 'Checking ComfyUI...', host })
|
onStatus({ state: 'offline', message: 'Checking ComfyUI...', host })
|
||||||
|
|
||||||
const health = await checkComfyHttp(cfg.healthTimeoutMs)
|
const health = await checkComfyHttp(cfg.healthTimeoutMs)
|
||||||
|
|||||||
Reference in New Issue
Block a user