/** Qwen Edit / PE-I2I prompt helpers. Start still = ; Hero is never a slot. */ export const QWEN21_EDIT_KEEP = 'Keep the identity, face, body, pose, and likeness of the person in .\nDo not invent a different person.' export const QWEN21_EDIT_KEEP_CHANGE = `${QWEN21_EDIT_KEEP}\nApply only this change:` 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 . */ 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 (/Keep the identity, face, body, pose, and likeness of the person in /i.test(text)) return text return buildQwen21EditKeepStanza(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 ') .replace(/\bthe (?:person|man|subject) in the (?:input )?(?:photo|picture|image)\b/gi, 'the person in ') .replace(/\bin the (?:input )?(?:photo|picture|image)\b/gi, 'in ') .replace(/\bthe (?:input )?(?:photo|picture|image)\b/gi, '') } /** * After PE-I2I: never let the PE chunk be the entire prompt. * Always lead with the keep/ 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 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 } }