Ship MiniMax v2 identity refs and Qwen sidecar edits.

Keep v1 as the default video graph, relay image edits to the Qwen Lightning box, and keep /api/health up if one Comfy host is unset.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-26 14:29:15 -05:00
co-authored by Cursor
parent 551c29a62a
commit c55a48a57e
22 changed files with 2061 additions and 75 deletions
+41 -5
View File
@@ -83,9 +83,14 @@ export default defineEventHandler(async (event) => {
const fields: Record<string, string> = {}
let image: { filename: string; data: Buffer; type?: string } | null = null
const referenceImages: { filename: string; data: Buffer; type?: string }[] = []
for (const part of form) {
if (part.name === 'image' && part.filename && part.data?.length) {
image = { filename: part.filename, data: part.data, type: part.type }
} else if (part.name === 'refImage' && part.filename && part.data?.length) {
if (referenceImages.length < 4) {
referenceImages.push({ filename: part.filename, data: part.data, type: part.type })
}
} else if (part.name && part.data) {
fields[part.name] = part.data.toString('utf8')
}
@@ -99,6 +104,8 @@ export default defineEventHandler(async (event) => {
throw createError({ statusCode: 400, statusMessage: 'A motion prompt is required' })
}
const extensions = parseExtensions(fields.extensions)
const workflow = parseVideoWorkflow(fields.workflow)
const useIdentityRefs = workflow === 'v2' && fields.useIdentityRefs === 'true' && referenceImages.length > 0
const { width, height } = resolveOutputSize(fields.aspect, image.data, Number(fields.width), Number(fields.height))
const turbo = fields.turbo !== 'false'
@@ -170,7 +177,8 @@ export default defineEventHandler(async (event) => {
chainStep: 1,
chainTotal,
chainLabel: extensions.length ? 'Initial' : undefined,
familyId: crypto.randomUUID()
familyId: crypto.randomUUID(),
workflow
}
emitChainJob(job, { type: 'status', message: 'Checking ComfyUI...', progress: 1 })
@@ -188,7 +196,11 @@ export default defineEventHandler(async (event) => {
fps,
samplerName,
scheduler,
extensions
extensions,
workflow,
duration: durationSeconds,
useIdentityRefs,
referenceImages
}).catch(async (error) => {
removeExtendTemp(job.library?.extendTmpDir)
if (isComfyBusyTimeout(error) && job.library) {
@@ -215,7 +227,8 @@ export default defineEventHandler(async (event) => {
fps: job.library.fps,
samplerName: job.library.samplerName,
scheduler: job.library.scheduler,
extensions: job.library.extensions
extensions: job.library.extensions,
workflow: job.library.workflow
})
job.library.draftId = draft.id
job.status = 'deferred'
@@ -268,6 +281,10 @@ type GenerateParams = {
samplerName: string
scheduler: string
extensions: { prompt: string; duration: number }[]
workflow: 'v1' | 'v2'
duration: number
useIdentityRefs: boolean
referenceImages: { filename: string; data: Buffer; type?: string }[]
}
async function queueMiniMax(
@@ -290,6 +307,17 @@ async function queueMiniMax(
: 'Queueing MiniMax H3 job...'
emitChainJob(job, { type: 'status', message: uploading, progress: 4 })
const uploaded = await uploadImage(params.image, job.id)
const referenceNames: string[] = []
const chainIndex = job.library?.chainIndex || 0
if (chainIndex === 0) {
for (const [index, ref] of (params.referenceImages || []).entries()) {
const next = await uploadImage({
...ref,
filename: `ref${index + 1}_${ref.filename || 'identity.png'}`
}, job.id)
referenceNames.push(next.name)
}
}
if (job.library) {
job.library.imageName = uploaded.name
job.library.imageSubfolder = uploaded.subfolder
@@ -311,7 +339,11 @@ async function queueMiniMax(
samplerName: params.samplerName,
scheduler: params.scheduler,
filenamePrefix: comfyFilenamePrefix(),
sound: params.sound
sound: params.sound,
workflow: params.workflow,
duration: params.duration,
useIdentityRefs: chainIndex === 0 && params.useIdentityRefs,
referenceImageNames: chainIndex === 0 ? referenceNames : []
})
const queued = await queuePrompt(graph, job.clientId)
@@ -432,7 +464,11 @@ async function runGeneration(
fps: params.fps,
samplerName: params.samplerName,
scheduler: params.scheduler,
persist: isLast
persist: isLast,
workflow: params.workflow,
duration: ext.duration,
useIdentityRefs: false,
referenceImages: []
})
if (!isLast) {