Add sidecar Qwen VL prompt recommend, fix queued shot bursts, and leave unused MiniMax identity slots empty.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
import { checkComfyHttp, fetchQueue } from '~/server/utils/comfyLifecycle'
|
||||
import { comfyConfigured } from '~/server/utils/comfy'
|
||||
import { imageComfyConfigured, getBeastImageHost } from '~/server/utils/imageComfy'
|
||||
import { imageComfyConfigured, getBeastImageHost, getSidecarImageHost } from '~/server/utils/imageComfy'
|
||||
import { checkImageComfyHttp, fetchQueueAt } from '~/server/utils/imageComfyLifecycle'
|
||||
|
||||
export default defineEventHandler(async () => {
|
||||
const videoConfigured = comfyConfigured()
|
||||
const imageConfigured = imageComfyConfigured()
|
||||
const beastHost = getBeastImageHost()
|
||||
const [comfy, queue, beast, beastQueue] = await Promise.all([
|
||||
const sidecarHost = getSidecarImageHost()
|
||||
const [comfy, queue, beast, beastQueue, sidecar, sidecarQueue] = await Promise.all([
|
||||
videoConfigured
|
||||
? checkComfyHttp().catch(() => ({ ok: false, host: '', stats: null }))
|
||||
: Promise.resolve({ ok: false, host: '', stats: null }),
|
||||
@@ -15,10 +16,15 @@ export default defineEventHandler(async () => {
|
||||
beastHost
|
||||
? checkImageComfyHttp(2500, beastHost).catch(() => ({ ok: false, host: beastHost, stats: null }))
|
||||
: Promise.resolve({ ok: false, host: beastHost, stats: null }),
|
||||
beastHost ? fetchQueueAt(beastHost) : Promise.resolve({ running: 0, pending: 0 })
|
||||
beastHost ? fetchQueueAt(beastHost) : Promise.resolve({ running: 0, pending: 0 }),
|
||||
sidecarHost
|
||||
? checkImageComfyHttp(2500, sidecarHost).catch(() => ({ ok: false, host: sidecarHost, stats: null }))
|
||||
: Promise.resolve({ ok: false, host: sidecarHost, stats: null }),
|
||||
sidecarHost ? fetchQueueAt(sidecarHost) : Promise.resolve({ running: 0, pending: 0 })
|
||||
])
|
||||
const busy = Boolean(comfy.ok && queue.running > 0)
|
||||
const beastBusy = Boolean(beast.ok && beastQueue.running > 0)
|
||||
const sidecarBusy = Boolean(sidecar.ok && sidecarQueue.running > 0)
|
||||
const imageOk = Boolean(beast.ok || comfy.ok)
|
||||
const imageBusy = Boolean(imageConfigured && (beastBusy || busy))
|
||||
const preferredHost = beastHost || comfy.host || ''
|
||||
@@ -50,10 +56,12 @@ export default defineEventHandler(async () => {
|
||||
},
|
||||
sidecar: {
|
||||
label: 'Sidecar',
|
||||
configured: false,
|
||||
ok: false,
|
||||
busy: false,
|
||||
host: ''
|
||||
configured: Boolean(sidecarHost),
|
||||
ok: Boolean(sidecar.ok),
|
||||
busy: sidecarBusy,
|
||||
host: sidecarHost,
|
||||
queueRunning: sidecarQueue.running || 0,
|
||||
queuePending: sidecarQueue.pending || 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { listPromptPresets } from '~/server/utils/promptPresets'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const owner = libraryOwnerKey(event)
|
||||
return {
|
||||
presets: listPromptPresets(owner).map(item => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
description: item.description || '',
|
||||
builtin: Boolean(item.builtin),
|
||||
systemPrompt: item.systemPrompt
|
||||
}))
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,15 @@
|
||||
import { createPromptPreset, listPromptPresets } from '~/server/utils/promptPresets'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const body = await readBody<{ name?: string; systemPrompt?: string; description?: string }>(event).catch(() => ({}))
|
||||
const owner = libraryOwnerKey(event)
|
||||
const created = await createPromptPreset(owner, {
|
||||
name: body.name || '',
|
||||
systemPrompt: body.systemPrompt || '',
|
||||
description: body.description
|
||||
})
|
||||
return {
|
||||
preset: created,
|
||||
presets: listPromptPresets(owner)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
import { deletePromptPreset, listPromptPresets } from '~/server/utils/promptPresets'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const id = getRouterParam(event, 'id')
|
||||
if (!id) throw createError({ statusCode: 400, statusMessage: 'Missing instruction set id' })
|
||||
const owner = libraryOwnerKey(event)
|
||||
await deletePromptPreset(owner, id)
|
||||
return {
|
||||
ok: true,
|
||||
presets: listPromptPresets(owner)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
import { updatePromptPreset, listPromptPresets } from '~/server/utils/promptPresets'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const id = getRouterParam(event, 'id')
|
||||
if (!id) throw createError({ statusCode: 400, statusMessage: 'Missing instruction set id' })
|
||||
const body = await readBody<{ name?: string; systemPrompt?: string; description?: string }>(event).catch(() => ({}))
|
||||
const owner = libraryOwnerKey(event)
|
||||
const preset = await updatePromptPreset(owner, id, body)
|
||||
return {
|
||||
preset,
|
||||
presets: listPromptPresets(owner)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,48 @@
|
||||
import { promptComfyConfigured, createPromptJob, runPromptRecommend, failPromptJob, promptJobSnapshot } from '~/server/utils/promptComfy'
|
||||
import { getPromptPreset } from '~/server/utils/promptPresets'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (!promptComfyConfigured()) {
|
||||
throw createError({
|
||||
statusCode: 503,
|
||||
statusMessage: 'Sidecar Qwen VL is not configured. Set IMAGE_COMFY_HOST.'
|
||||
})
|
||||
}
|
||||
|
||||
const form = await readMultipartFormData(event).catch(() => null)
|
||||
if (!form?.length) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Expected an image and a draft prompt' })
|
||||
}
|
||||
|
||||
const fields: Record<string, string> = {}
|
||||
let image: { filename: string; data: Buffer; type?: string } | null = null
|
||||
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 && part.data) {
|
||||
fields[part.name] = part.data.toString('utf8')
|
||||
}
|
||||
}
|
||||
|
||||
if (!image) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Load a still before recommending a prompt' })
|
||||
}
|
||||
const draftPrompt = (fields.prompt || '').trim()
|
||||
if (!draftPrompt) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Type a draft prompt to enhance' })
|
||||
}
|
||||
|
||||
const owner = libraryOwnerKey(event)
|
||||
const preset = getPromptPreset(owner, fields.presetId || 'enhance')
|
||||
const mode = ['edit', 'shot-script', 'video'].includes(fields.mode) ? fields.mode : 'video'
|
||||
const job = createPromptJob()
|
||||
|
||||
void runPromptRecommend(job, {
|
||||
image,
|
||||
draftPrompt,
|
||||
systemPrompt: preset.systemPrompt,
|
||||
mode
|
||||
}).catch(error => failPromptJob(job, error))
|
||||
|
||||
return promptJobSnapshot(job)
|
||||
})
|
||||
@@ -0,0 +1,10 @@
|
||||
import { getPromptJob, promptJobSnapshot } from '~/server/utils/promptComfy'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const id = getRouterParam(event, 'id')
|
||||
const job = id ? getPromptJob(id) : undefined
|
||||
if (!job) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Recommend job not found' })
|
||||
}
|
||||
return promptJobSnapshot(job)
|
||||
})
|
||||
@@ -1,10 +1,17 @@
|
||||
import { startQueueBurst } from '~/server/utils/videoChain'
|
||||
|
||||
function parseCount(raw: unknown): number | 'all' {
|
||||
if (raw === 'all' || raw === 'remaining') return 'all'
|
||||
const n = typeof raw === 'number' ? raw : Number(raw)
|
||||
if (!Number.isFinite(n)) return 1
|
||||
return Math.max(1, Math.min(50, Math.floor(n)))
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { owner } = assertLibraryOwner(event)
|
||||
const id = String(getRouterParam(event, 'id') || '')
|
||||
const body = await readBody<{ count?: number | 'all' }>().catch(() => ({}))
|
||||
const raw = body?.count
|
||||
const count = raw === 'all' || raw === 'remaining' ? 'all' as const : Math.max(1, Math.min(50, Number(raw) || 1))
|
||||
const body = await readBody<{ count?: number | 'all' | 'remaining' }>(event).catch(() => ({} as { count?: number | 'all' }))
|
||||
const query = getQuery(event)
|
||||
const count = parseCount(body?.count ?? query.count)
|
||||
return startQueueBurst(owner, id, count)
|
||||
})
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { clearShotQueues } from '~/server/utils/shotQueue'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { owner } = assertLibraryOwner(event)
|
||||
return clearShotQueues(owner)
|
||||
})
|
||||
Reference in New Issue
Block a user