Add Image v2 Generate for Klein text-to-image.
New sibling graph and mode so T2I does not borrow Edit, Compose, or Refine. Stills are ignored; size is width x height only. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -13,7 +13,7 @@ import type { EditImageFile } from '~/server/utils/imageChain'
|
||||
export type EditV2RunParams = {
|
||||
mode: ImageV2Mode
|
||||
task: ImageV2Task
|
||||
image: EditImageFile
|
||||
image?: EditImageFile | null
|
||||
reference: EditImageFile | null
|
||||
mask?: EditImageFile | null
|
||||
prompt: string
|
||||
@@ -27,6 +27,9 @@ export type EditV2RunParams = {
|
||||
consistencyClip: number
|
||||
megapixels: number
|
||||
strength?: number
|
||||
width?: number
|
||||
height?: number
|
||||
turbo?: boolean
|
||||
}
|
||||
|
||||
export async function runEditV2(job: Job, params: EditV2RunParams) {
|
||||
@@ -41,6 +44,9 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
||||
if (params.mode === 'edit' && params.reference) {
|
||||
throw new Error('Edit mode takes one image. Use Compose for two stills.')
|
||||
}
|
||||
if (params.mode !== 'generate' && !params.image) {
|
||||
throw new Error('This v2 mode requires still A.')
|
||||
}
|
||||
|
||||
try {
|
||||
await ensureComfyReady((status) => {
|
||||
@@ -55,15 +61,18 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
||||
job.imageComfyHost = getComfyHost()
|
||||
|
||||
await withImageComfyHost(job.imageComfyHost, async () => {
|
||||
const generate = params.mode === 'generate'
|
||||
emitChainJob(job, {
|
||||
type: 'status',
|
||||
message: params.mode === 'refine'
|
||||
? 'Uploading canvas and mask to Beast...'
|
||||
: params.mode === 'compose' ? 'Uploading stills A and B to Beast...' : 'Uploading still A to Beast...',
|
||||
progress: 8
|
||||
message: generate
|
||||
? 'Queueing Klein v2 generate on Beast...'
|
||||
: params.mode === 'refine'
|
||||
? 'Uploading canvas and mask to Beast...'
|
||||
: params.mode === 'compose' ? 'Uploading stills A and B to Beast...' : 'Uploading still A to Beast...',
|
||||
progress: generate ? 12 : 8
|
||||
})
|
||||
const uploaded = await uploadImage(params.image, job.id)
|
||||
const uploadedRef = params.mode === 'refine'
|
||||
const uploaded = generate || !params.image ? null : await uploadImage(params.image, job.id)
|
||||
const uploadedRef = generate || params.mode === 'refine'
|
||||
? null
|
||||
: params.reference
|
||||
? await uploadImage({
|
||||
@@ -71,7 +80,7 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
||||
filename: `ref_${params.reference.filename || 'image_b.png'}`
|
||||
}, job.id)
|
||||
: null
|
||||
const uploadedMask = params.mode === 'refine' && params.mask
|
||||
const uploadedMask = !generate && params.mode === 'refine' && params.mask
|
||||
? await uploadImage({
|
||||
...params.mask,
|
||||
filename: `mask_${params.mask.filename || 'refine-mask.png'}`
|
||||
@@ -79,23 +88,25 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
||||
: null
|
||||
if (job.status === 'cancelled') throw new Error('Job interrupted.')
|
||||
|
||||
emitChainJob(job, {
|
||||
type: 'status',
|
||||
message: params.mode === 'refine'
|
||||
? 'Queueing Klein v2 refine on Beast...'
|
||||
: params.mode === 'compose'
|
||||
? `Queueing Klein v2 compose (${params.task}) on Beast...`
|
||||
: 'Queueing Klein v2 edit on Beast...',
|
||||
progress: 12
|
||||
})
|
||||
if (!generate) {
|
||||
emitChainJob(job, {
|
||||
type: 'status',
|
||||
message: params.mode === 'refine'
|
||||
? 'Queueing Klein v2 refine on Beast...'
|
||||
: params.mode === 'compose'
|
||||
? `Queueing Klein v2 compose (${params.task}) on Beast...`
|
||||
: 'Queueing Klein v2 edit on Beast...',
|
||||
progress: 12
|
||||
})
|
||||
}
|
||||
await ensureComfyLoraNames('image')
|
||||
await assertImageScaleToTotalPixelsNode()
|
||||
if (!generate) await assertImageScaleToTotalPixelsNode()
|
||||
const built = buildImageV2Workflow({
|
||||
mode: params.mode,
|
||||
task: params.task,
|
||||
prompt: params.prompt,
|
||||
negative: params.negative,
|
||||
imageAName: uploaded.name,
|
||||
imageAName: uploaded?.name,
|
||||
imageBName: uploadedRef?.name,
|
||||
maskName: uploadedMask?.name,
|
||||
strength: params.strength,
|
||||
@@ -107,6 +118,9 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
||||
cfg: params.cfg,
|
||||
seed: params.seed,
|
||||
megapixels: params.megapixels,
|
||||
width: params.width,
|
||||
height: params.height,
|
||||
turbo: params.turbo === true,
|
||||
filenamePrefix: `aigen_v2_${job.id.slice(0, 8)}`
|
||||
})
|
||||
const queued = await queuePrompt(built.graph, job.clientId)
|
||||
@@ -154,8 +168,8 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
||||
job.stillId = still?.id
|
||||
await purgeComfyArtifacts({
|
||||
video: { filename: output.filename, subfolder: output.subfolder, type: output.type },
|
||||
imageName: uploaded.name,
|
||||
imageSubfolder: uploaded.subfolder,
|
||||
imageName: uploaded?.name,
|
||||
imageSubfolder: uploaded?.subfolder,
|
||||
extraImageNames: [uploadedRef?.name, uploadedMask?.name].filter((name): name is string => Boolean(name)),
|
||||
promptId: job.promptId
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user