/** Qwen PE refusal / empty-rewrite detection — fail open to promptRaw. */ const REFUSAL_RE = /i cannot|i can't|i['’]m not able|i am not able|cannot assist|can't assist|won['’]t create|will not create|against my|not appropriate|\bsafety\b|content policy|抱歉|无法|不能协助|不符合/i const STOP = new Set([ 'the', 'and', 'for', 'are', 'was', 'were', 'been', 'being', 'this', 'that', 'with', 'from', 'into', 'your', 'have', 'will', 'would', 'could', 'should', 'about', 'there', 'their', 'them', 'then', 'than', 'when', 'what', 'which', 'while', 'where', 'make', 'made', 'like', 'just', 'only', 'also', 'over', 'under', 'after', 'before', 'between', 'through', 'image', 'photo', 'picture', 'please', 'create', 'generate', 'show', 'want', 'need', 'scene', 'style', 'prompt', 'edit', 'change', 'keep', 'using', 'into', 'onto', 'her', 'him', 'his', 'she', 'they', 'them', ]) /** Concrete tokens from the user brief (latin words ≥3 or CJK runs). */ export function concreteTokens(text) { const raw = String(text || '').toLowerCase().match(/[a-z][a-z0-9-]{2,}|[\u4e00-\u9fff]{2,}/g) || [] return [...new Set(raw)].filter(w => !STOP.has(w)) } function isGutted(userPrompt, rewritten) { const user = String(userPrompt || '').trim() const out = String(rewritten || '').trim() if (!user || !out) return !out if (out.length >= user.length) return false const nouns = concreteTokens(user) if (!nouns.length) return false const lower = out.toLowerCase() return !nouns.some(n => lower.includes(n)) } /** * @returns {{ refused: boolean, reason: string }} */ export function qwen21PeRefusal(userPrompt, { positive_prompt, thinking, parse_ok } = {}) { const positive = String(positive_prompt || '').trim() const think = String(thinking || '').trim() if (!parse_ok && !positive) return { refused: true, reason: 'empty rewrite (parse_ok false)' } if (!positive) return { refused: true, reason: 'empty rewrite' } if (REFUSAL_RE.test(positive) || REFUSAL_RE.test(think)) return { refused: true, reason: 'model refused' } if (isGutted(userPrompt, positive)) return { refused: true, reason: 'gutted rewrite' } return { refused: false, reason: '' } }