Add LoRA stacks with per-item strengths and named video/image generation presets so setups survive reload without mixing Klein onto MiniMax. Keep LTX Pink Cherry and xAIGen LoRAs on xAIGen, and run Recommend on Beast.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+33
-12
@@ -3,8 +3,9 @@ import { join } from 'node:path'
|
||||
import { getJob, listJobs, emitJob, type Job } from '~/server/utils/jobs'
|
||||
import { listPendingJobs, patchPendingJob, readPendingJob, deletePendingJob } from '~/server/utils/pending'
|
||||
import { getShotQueue } from '~/server/utils/shotQueue'
|
||||
import { parseVideoWorkflow, type VideoWorkflowId } from '~/utils/videoModels'
|
||||
import { allowIdentityRefs, type PermanenceRef } from '~/utils/globalLocks'
|
||||
import { isLtxWorkflow, LTX_DISABLED_MESSAGE, ltxWorkflowEnabled, parseVideoWorkflow, type VideoWorkflowId } from '~/utils/videoModels'
|
||||
import { persistLoraFields } from '~/server/utils/loras'
|
||||
import { resolveLoraStack } from '~/utils/loras'
|
||||
|
||||
export type StudioJobStatus = 'waiting' | 'running' | 'held' | 'complete' | 'error' | 'cancelled'
|
||||
export type StudioJobKind = 'video' | 'edit'
|
||||
@@ -33,11 +34,15 @@ export interface StudioJobPayload {
|
||||
hideInput?: boolean
|
||||
folderLocked?: boolean
|
||||
referenceStillIds: Array<string | null>
|
||||
extensions: { prompt: string; duration: number; permanenceRefs?: PermanenceRef[] }[]
|
||||
extensions: { prompt: string; duration: number; permanenceRefs?: PermanenceRef[]; loraName?: string; loraStack?: import('~/utils/loras').LoraStackItem[] }[]
|
||||
queueAutoRun: boolean
|
||||
globalLocks?: string
|
||||
permanenceRefs?: PermanenceRef[]
|
||||
shotPermanenceRefs?: PermanenceRef[][]
|
||||
loraName?: string
|
||||
loraStack?: import('~/utils/loras').LoraStackItem[]
|
||||
shotLoras?: string[]
|
||||
shotLoraStacks?: import('~/utils/loras').LoraStackItem[][]
|
||||
negative?: string
|
||||
passes?: { prompt: string }[]
|
||||
referenceStillId?: string
|
||||
@@ -636,7 +641,8 @@ async function startStudioEditJob(item: StudioJob) {
|
||||
chainStep: 1,
|
||||
chainTotal: 1 + passes.length,
|
||||
chainLabel: passes.length ? 'Pass 1' : undefined,
|
||||
passes
|
||||
passes,
|
||||
...persistLoraFields(payload.loraStack || payload.loraName)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -649,7 +655,8 @@ async function startStudioEditJob(item: StudioJob) {
|
||||
negative: payload.negative || '',
|
||||
steps: payload.steps,
|
||||
seed: job.library?.seed || payload.seed,
|
||||
cfg: payload.cfg
|
||||
cfg: payload.cfg,
|
||||
...persistLoraFields(payload.loraStack || payload.loraName)
|
||||
}).catch((error) => {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
if (job.status !== 'error' && job.status !== 'cancelled' && job.status !== 'deferred') {
|
||||
@@ -680,6 +687,10 @@ export async function startStudioJob(item: StudioJob) {
|
||||
const { existsSync, readFileSync } = await import('node:fs')
|
||||
|
||||
const payload = item.payload
|
||||
const workflow = parseVideoWorkflow(payload.workflow)
|
||||
if (isLtxWorkflow(workflow) && !ltxWorkflowEnabled()) {
|
||||
throw new Error(LTX_DISABLED_MESSAGE)
|
||||
}
|
||||
const identity = allowIdentityRefs(
|
||||
payload.useIdentityRefs,
|
||||
payload.permanenceRefs,
|
||||
@@ -720,13 +731,16 @@ export async function startStudioJob(item: StudioJob) {
|
||||
chainTotal: 1 + extensions.length,
|
||||
chainLabel: extensions.length ? 'Initial' : undefined,
|
||||
familyId: item.familyId,
|
||||
workflow: parseVideoWorkflow(payload.workflow),
|
||||
workflow: workflow,
|
||||
useIdentityRefs: identity,
|
||||
queueAutoRun: extensions.length ? payload.queueAutoRun : false,
|
||||
queueBudget: extensions.length && payload.queueAutoRun ? extensions.length : 0,
|
||||
globalLocks: payload.globalLocks,
|
||||
permanenceRefs: payload.permanenceRefs,
|
||||
shotPermanenceRefs: payload.shotPermanenceRefs
|
||||
shotPermanenceRefs: payload.shotPermanenceRefs,
|
||||
...persistLoraFields(resolveLoraStack(payload.loraStack || payload.loraName, payload.shotLoraStacks?.[0] || payload.shotLoras?.[0])),
|
||||
shotLoras: payload.shotLoras,
|
||||
shotLoraStacks: payload.shotLoraStacks
|
||||
}
|
||||
|
||||
if (extensions.length) {
|
||||
@@ -749,14 +763,18 @@ export async function startStudioJob(item: StudioJob) {
|
||||
fps: payload.fps,
|
||||
samplerName: payload.samplerName,
|
||||
scheduler: payload.scheduler,
|
||||
workflow: parseVideoWorkflow(payload.workflow),
|
||||
workflow: workflow,
|
||||
sound: payload.sound,
|
||||
useIdentityRefs: identity,
|
||||
referenceStillIds: payload.referenceStillIds,
|
||||
globalLocks: payload.globalLocks,
|
||||
permanenceRefs: payload.permanenceRefs,
|
||||
initial: { prompt: payload.prompt, duration: payload.duration, permanenceRefs: payload.shotPermanenceRefs?.[0] },
|
||||
extensions,
|
||||
...persistLoraFields(payload.loraStack || payload.loraName),
|
||||
initial: { prompt: payload.prompt, duration: payload.duration, permanenceRefs: payload.shotPermanenceRefs?.[0], ...persistLoraFields(resolveLoraStack(payload.loraStack || payload.loraName, payload.shotLoraStacks?.[0] || payload.shotLoras?.[0])) },
|
||||
extensions: extensions.map((item, index) => ({
|
||||
...item,
|
||||
...persistLoraFields(item.loraStack || item.loraName || payload.shotLoraStacks?.[index + 1] || payload.shotLoras?.[index + 1])
|
||||
})),
|
||||
jobId: job.id
|
||||
})
|
||||
job.library.queueId = queue.id
|
||||
@@ -802,10 +820,13 @@ export async function startStudioJob(item: StudioJob) {
|
||||
samplerName: payload.samplerName,
|
||||
scheduler: payload.scheduler,
|
||||
extensions,
|
||||
workflow: parseVideoWorkflow(payload.workflow),
|
||||
workflow: workflow,
|
||||
duration: payload.duration,
|
||||
useIdentityRefs: identity,
|
||||
referenceImages
|
||||
referenceImages,
|
||||
...persistLoraFields(resolveLoraStack(payload.loraStack || payload.loraName, payload.shotLoraStacks?.[0] || payload.shotLoras?.[0])),
|
||||
shotLoras: payload.shotLoras,
|
||||
shotLoraStacks: payload.shotLoraStacks
|
||||
}).catch((error) => {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
if (job.status !== 'error' && job.status !== 'cancelled' && job.status !== 'deferred') {
|
||||
|
||||
Reference in New Issue
Block a user