Store the Edit TextEncode prompt on library stills, not a Start still caption.
Edit + Enhance always leads with the keep/<image1> stanza and typed change; PE describe dumps are dropped, and Typed stays promptRaw. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -105,8 +105,11 @@ async function runQwen21PromptEnhance(r: any, job: Job) {
|
||||
throw new Error('Qwen Edit requires Start still (image_1). Hero is ignored.')
|
||||
update(r, 'enhancing')
|
||||
emitJob(job, { type: 'status', message: 'Enhancing prompt…' })
|
||||
// promptRaw = exactly what they typed (compiled from sections before PE).
|
||||
const userPrompt = String(q.compiledPrompt || '')
|
||||
const peInput = edit ? ensureQwen21EditPrompt(userPrompt) : userPrompt
|
||||
r.request.promptRaw = userPrompt
|
||||
// Edit PE gets the raw edit instruction + Start still — not the keep stanza.
|
||||
const peInput = userPrompt
|
||||
const graph = structuredClone(edit ? qwen21PeEditTemplate : qwen21PeT2iTemplate)
|
||||
graph['9'].inputs.prompt = peInput
|
||||
graph['9'].inputs.seed = s.seed
|
||||
@@ -137,12 +140,14 @@ async function runQwen21PromptEnhance(r: any, job: Job) {
|
||||
saveRecord(r)
|
||||
const history = await waitPromptHistory(r, job, queued.prompt_id)
|
||||
const result = harvestQwen21Pe(history, queued.prompt_id, edit)
|
||||
r.request.promptRaw = userPrompt
|
||||
const refusal = qwen21PeRefusal(userPrompt, result)
|
||||
if (refusal.refused) {
|
||||
// Fail open: keep the job, send the raw brief (Edit keep-identity prefix still applies).
|
||||
r.request.compiledPrompt = edit ? ensureQwen21EditPrompt(userPrompt) : userPrompt
|
||||
// Fail open: keep the job, send the raw brief (Edit keep-identity stanza still applies).
|
||||
const compiled = edit ? ensureQwen21EditPrompt(userPrompt) : userPrompt
|
||||
r.request.compiledPrompt = compiled
|
||||
r.request.prompt = compiled
|
||||
r.request.enhance = {
|
||||
used: false,
|
||||
wh_ratio: '',
|
||||
...(edit ? { ratio_follow: '' } : {}),
|
||||
parse_ok: !!result.parse_ok,
|
||||
@@ -151,20 +156,35 @@ async function runQwen21PromptEnhance(r: any, job: Job) {
|
||||
thinking: result.thinking || '',
|
||||
comfyPromptId: queued.prompt_id,
|
||||
}
|
||||
} else {
|
||||
// PE-I2I: if rewrite dropped every <imageN>, stitch keep-identity onto the rewrite.
|
||||
r.request.compiledPrompt = edit
|
||||
? stitchQwen21PeEditPrompt(result.positive_prompt)
|
||||
: result.positive_prompt
|
||||
} else if (edit) {
|
||||
// Edit only: stanza + raw first; PE chunk only if it is an edit directive (never PE-alone).
|
||||
// Generate keeps the long observer paragraph as the product — no describe sanitize.
|
||||
const stitched = stitchQwen21PeEditPrompt(userPrompt, result.positive_prompt)
|
||||
r.request.compiledPrompt = stitched.prompt
|
||||
r.request.prompt = stitched.prompt
|
||||
r.request.enhance = {
|
||||
used: !stitched.skippedAsDescribe,
|
||||
wh_ratio: result.wh_ratio || '',
|
||||
ratio_follow: result.ratio_follow || '',
|
||||
parse_ok: !!result.parse_ok,
|
||||
refused: false,
|
||||
...(stitched.skippedAsDescribe ? { skippedAsDescribe: true } : {}),
|
||||
thinking: result.thinking || '',
|
||||
comfyPromptId: queued.prompt_id,
|
||||
}
|
||||
} else {
|
||||
r.request.compiledPrompt = result.positive_prompt
|
||||
r.request.prompt = result.positive_prompt
|
||||
r.request.enhance = {
|
||||
used: true,
|
||||
wh_ratio: result.wh_ratio || '',
|
||||
...(edit ? { ratio_follow: result.ratio_follow || '' } : {}),
|
||||
parse_ok: !!result.parse_ok,
|
||||
refused: false,
|
||||
thinking: result.thinking || '',
|
||||
comfyPromptId: queued.prompt_id,
|
||||
}
|
||||
}
|
||||
if (edit || q.mode === 'generate') r.request.task = q.mode
|
||||
// Stage flip PE → generate: clear PE prompt id and reset the bar (no leftover 100%).
|
||||
r.pePromptId = ''
|
||||
r.promptId = ''
|
||||
@@ -287,7 +307,12 @@ async function prepareGraph(r: any) {
|
||||
// Start still → image_1 only. Never Hero. Never T2I / EmptyLatent.
|
||||
if (!q.imageAId || !a)
|
||||
throw new Error('Qwen Edit requires Start still (image_1). Hero is ignored.')
|
||||
// promptRaw stays typed; prompt/compiledPrompt = what TextEncode samples.
|
||||
if (q.promptRaw == null) q.promptRaw = String(q.compiledPrompt || '')
|
||||
const prompt = ensureQwen21EditPrompt(String(q.compiledPrompt || ''))
|
||||
q.compiledPrompt = prompt
|
||||
q.prompt = prompt
|
||||
q.task = 'edit'
|
||||
graph = structuredClone(turbo ? qwen21EditTurboTemplate : qwen21EditTemplate)
|
||||
graph['10'].inputs.image = a
|
||||
graph['9'].inputs['images.image_1'] = ['10', 0]
|
||||
@@ -325,6 +350,9 @@ async function prepareGraph(r: any) {
|
||||
r.heroReferenceAttached = false
|
||||
} else {
|
||||
// Bench aspect is law for Generate. PE wh_ratio is advisory only (never sizes the canvas).
|
||||
if (q.promptRaw == null) q.promptRaw = String(q.compiledPrompt || '')
|
||||
q.prompt = String(q.compiledPrompt || '')
|
||||
q.task = 'generate'
|
||||
const size = resolveQwen21Size(s.aspect)
|
||||
s.width = size.width
|
||||
s.height = size.height
|
||||
@@ -462,7 +490,21 @@ async function run(r: any, job: Job) {
|
||||
}
|
||||
else if (!asset) {
|
||||
const data = await downloadComfyImage(file), size = imageDimensions(data) || { width: s.width, height: s.height };
|
||||
asset = await saveStill({ ownerKey: r.owner, folderId: q.folderId, filename: file.filename, data, ...size, role: 'output', prompt: q.compiledPrompt, familyId: r.familyId, chainIndex: r.index, settings: s });
|
||||
// prompt = TextEncode string (after PE + stitch); promptRaw = typed brief.
|
||||
const samplePrompt = String(q.prompt || q.compiledPrompt || '')
|
||||
asset = await saveStill({
|
||||
ownerKey: r.owner,
|
||||
folderId: q.folderId,
|
||||
filename: file.filename,
|
||||
data,
|
||||
...size,
|
||||
role: 'output',
|
||||
prompt: samplePrompt,
|
||||
promptRaw: q.promptRaw != null ? String(q.promptRaw) : undefined,
|
||||
familyId: r.familyId,
|
||||
chainIndex: r.index,
|
||||
settings: s,
|
||||
});
|
||||
}
|
||||
if (!asset)
|
||||
throw new Error('Library did not save the output');
|
||||
@@ -470,6 +512,11 @@ async function run(r: any, job: Job) {
|
||||
saveRecord(r);
|
||||
r.savedAt = Date.now();
|
||||
r.wallTime = (r.savedAt - r.startedAt) / 1000;
|
||||
if (q.engine === 'qwen21' && ['generate', 'edit'].includes(q.mode)) {
|
||||
q.task = q.mode
|
||||
q.prompt = String(q.prompt || q.compiledPrompt || '')
|
||||
if (q.promptRaw == null) q.promptRaw = String(q.compiledPrompt || '')
|
||||
}
|
||||
const metadata = { ...structuredClone(q), id: r.id, sourceStillId:r.sourceStillId,heroReferenceAttached:r.heroReferenceAttached,sampleLatent:r.sampleLatent,sampleDenoise:r.sampleDenoise,kind: video ? 'video' : 'image', graphId: r.graphId, promptId: r.promptId, queuedAt: r.queuedAt, startedAt: r.startedAt, savedAt: r.savedAt, gpuSeconds: r.gpuSeconds, wallTime: r.wallTime, outputWidth: asset.width || s.width, outputHeight: asset.height || s.height, purgeResult: 'Left on host', handoffSource: r.handoffSource || null, fitted: video && q.startClipId ? !!r.handoffFitted : null };
|
||||
await attachStudio2Metadata(r.owner, asset.id, metadata);
|
||||
if (!r.outputs.some((o:any)=>o.id===asset.id)) r.outputs.push({id:asset.id,kind:video?'clip':'still',studio2:metadata});
|
||||
|
||||
Reference in New Issue
Block a user