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:
Towsty
2026-09-26 15:14:20 -05:00
co-authored by Cursor
parent c46e827c68
commit 58c5458c87
9 changed files with 177 additions and 46 deletions
+44 -17
View File
@@ -1,33 +1,60 @@
/** Qwen Edit / PE-I2I prompt helpers. Start still = <image1>; Hero is never a slot. */
export const QWEN21_EDIT_KEEP =
'Keep the identity, face, body, and pose of the person in <image1>.'
'Keep the identity, face, body, pose, and likeness of the person in <image1>.\nDo not invent a different person.'
export const QWEN21_EDIT_KEEP_CHANGE =
`${QWEN21_EDIT_KEEP} Apply only this change:`
`${QWEN21_EDIT_KEEP}\nApply only this change:`
/** User omitted <image1> — prepend the keep-identity instruction once. */
const EDIT_VERB =
/\b(?:change|replace|put|swap|keep|remove|add|edit|make|turn|convert|transform|modify|update|set|apply|wear|place|swap|switch)\b/i
const DESCRIBE_LEAD =
/^(?:the image is\b|this is a photograph of\b|a vertical photorealistic\b|a photorealistic\b|this (?:image|photo|picture) (?:shows|depicts|features)\b|the (?:photo|picture) (?:shows|depicts|features)\b)/i
/** Fixed identity stanza + the user's raw edit instruction. Always includes <image1>. */
export function buildQwen21EditKeepStanza(rawInstruction) {
const raw = String(rawInstruction || '').trim()
if (!raw) return QWEN21_EDIT_KEEP.trim()
return `${QWEN21_EDIT_KEEP_CHANGE} ${raw}`.trim()
}
/** Enhance off / refusal: ensure the sample prompt is the keep stanza, never PE-only. */
export function ensureQwen21EditPrompt(prompt) {
const text = String(prompt || '').trim()
if (!text) return text
if (/<image1>/i.test(text)) return text
return `${QWEN21_EDIT_KEEP_CHANGE} ${text}`.trim()
if (/Keep the identity, face, body, pose, and likeness of the person in <image1>/i.test(text)) return text
return buildQwen21EditKeepStanza(text)
}
/**
* After PE-I2I: official PE told N=1 not to use tags, so rewrites often say
* "the photo" / "the image" with zero <image1>. Qwen Edit only binds Start still
* when the text contains the tag — force it onto every Edit rewrite.
*/
export function stitchQwen21PeEditPrompt(rewritten) {
let text = String(rewritten || '').trim()
if (!text) return text
// Prefer the tag over natural substitutes (do not invent <image2>).
text = text
/** PE rewrite looks like a T2I observer caption, not an edit directive. */
export function isQwen21PeEditDescribe(rewritten) {
const text = String(rewritten || '').trim()
if (!text) return true
if (DESCRIBE_LEAD.test(text)) return true
if (!EDIT_VERB.test(text)) return true
return false
}
function retagNaturalImageRefs(text) {
return String(text || '')
.replace(/\bthe woman in the (?:input )?(?:photo|picture|image)\b/gi, 'the person in <image1>')
.replace(/\bthe (?:person|man|subject) in the (?:input )?(?:photo|picture|image)\b/gi, 'the person in <image1>')
.replace(/\bin the (?:input )?(?:photo|picture|image)\b/gi, 'in <image1>')
.replace(/\bthe (?:input )?(?:photo|picture|image)\b/gi, '<image1>')
if (/<image1>/i.test(text)) return text
return `${QWEN21_EDIT_KEEP} ${text}`.trim()
}
/**
* After PE-I2I: never let the PE chunk be the entire prompt.
* Always lead with the keep/<image1> stanza + raw instruction; append PE only when it is an edit directive.
*/
export function stitchQwen21PeEditPrompt(rawInstruction, rewritten) {
const stanza = buildQwen21EditKeepStanza(rawInstruction)
let pe = retagNaturalImageRefs(String(rewritten || '').trim()).trim()
if (!pe) return { prompt: stanza, skippedAsDescribe: false }
if (isQwen21PeEditDescribe(pe)) {
return { prompt: stanza, skippedAsDescribe: true }
}
// No <image1> in the PE chunk is fine — stanza already has the tag; keep PE as extra detail.
return { prompt: `${stanza}\n\n${pe}`.trim(), skippedAsDescribe: false }
}