96 lines
5.0 KiB
JavaScript
96 lines
5.0 KiB
JavaScript
import {
|
||
IMAGE_GENERATION_MODE,
|
||
resolveImageGenerationDecision,
|
||
} from '../chat-intent-router.mjs';
|
||
|
||
export const WECHAT_PAGE_THUMBNAIL_MODE = {
|
||
AUTO: 'auto',
|
||
REQUIRED_FRESH: 'required_fresh',
|
||
};
|
||
|
||
const INLINE_IMAGE_REQUIRED_PATTERNS = [
|
||
/(?:正文|内容|段落|章节).{0,12}(?:配图|插图|图片)/u,
|
||
/(?:每段|每章|文中|文章中).{0,12}(?:配图|插图|图片)/u,
|
||
/(?:多张|若干张|几张).{0,12}(?:配图|插图|图片)/u,
|
||
];
|
||
|
||
export function resolveWechatImageGenerationPolicy({
|
||
text,
|
||
isPageGenerate = false,
|
||
requireFreshPageThumbnail = false,
|
||
} = {}) {
|
||
const normalizedText = String(text ?? '').trim();
|
||
const requested = resolveImageGenerationDecision({ text: normalizedText });
|
||
const pageThumbnailMode = isPageGenerate && requireFreshPageThumbnail
|
||
? WECHAT_PAGE_THUMBNAIL_MODE.REQUIRED_FRESH
|
||
: WECHAT_PAGE_THUMBNAIL_MODE.AUTO;
|
||
const inlineImageMode = requested.mode === IMAGE_GENERATION_MODE.DISABLED
|
||
? IMAGE_GENERATION_MODE.DISABLED
|
||
: INLINE_IMAGE_REQUIRED_PATTERNS.some((pattern) => pattern.test(normalizedText))
|
||
? IMAGE_GENERATION_MODE.REQUIRED
|
||
: IMAGE_GENERATION_MODE.AUTO;
|
||
const standaloneImageMode = isPageGenerate
|
||
? IMAGE_GENERATION_MODE.AUTO
|
||
: requested.mode;
|
||
|
||
return {
|
||
channel: 'wechat_mp',
|
||
imageGenerationMode: pageThumbnailMode === WECHAT_PAGE_THUMBNAIL_MODE.REQUIRED_FRESH
|
||
? IMAGE_GENERATION_MODE.REQUIRED
|
||
: standaloneImageMode,
|
||
pageThumbnailMode,
|
||
inlineImageMode,
|
||
standaloneImageMode,
|
||
requested,
|
||
};
|
||
}
|
||
|
||
export function buildWechatImageRunMetadata(policy) {
|
||
if (!policy) return { channel: 'wechat_mp' };
|
||
return {
|
||
channel: 'wechat_mp',
|
||
imageGenerationMode: policy.imageGenerationMode,
|
||
pageThumbnailMode: policy.pageThumbnailMode,
|
||
inlineImageMode: policy.inlineImageMode,
|
||
};
|
||
}
|
||
|
||
export function buildWechatStandaloneImageInstruction(policy, { sourceMessageId = '' } = {}) {
|
||
if (!policy || policy.standaloneImageMode === IMAGE_GENERATION_MODE.AUTO) return '';
|
||
if (policy.standaloneImageMode === IMAGE_GENERATION_MODE.DISABLED) {
|
||
return '【图片策略】用户明确不要生成图片。本轮禁止调用 generate_image,只返回文字。';
|
||
}
|
||
const idempotencyKey = sourceMessageId
|
||
? `wechat-${String(sourceMessageId).replace(/[^a-zA-Z0-9._-]+/g, '-').slice(0, 80)}-image`
|
||
: '';
|
||
return [
|
||
'【微信服务号生图任务】用户明确要求生成一张新图片。',
|
||
'必须先调用 `load_skill` → `image-generation`,再调用一次 `sandbox-fs__generate_image`,purpose=`inline_image`。',
|
||
'必须使用本轮新返回的 PNG/JPEG/WebP 资产;禁止复用历史图片、SVG、CSS 绘图、占位图或网页图片。',
|
||
idempotencyKey ? `调用 generate_image 时传入 idempotency_key=${idempotencyKey}。` : '',
|
||
'成功后简短说明结果,不要输出内部工具步骤;失败时如实报告,禁止声称已经生成。',
|
||
].filter(Boolean).join('\n');
|
||
}
|
||
|
||
export function buildWechatPageImageInstruction(policy, { sourceMessageId = '' } = {}) {
|
||
if (policy?.pageThumbnailMode !== WECHAT_PAGE_THUMBNAIL_MODE.REQUIRED_FRESH) return '';
|
||
const keyPrefix = sourceMessageId
|
||
? `wechat-${String(sourceMessageId).replace(/[^a-zA-Z0-9._-]+/g, '-').slice(0, 72)}-page`
|
||
: 'wechat-page';
|
||
const inlineInstruction = policy.inlineImageMode === IMAGE_GENERATION_MODE.DISABLED
|
||
? '用户明确不要正文图片:页面正文和 Hero 不展示图片,但缩略图源图仍必须生成并只写入 mindspace-cover.cover。'
|
||
: policy.inlineImageMode === IMAGE_GENERATION_MODE.REQUIRED
|
||
? '用户明确要求正文配图:缩略图源图之外,只有正文确实需要不同画面时才可按需调用 inline_image。'
|
||
: '正文独立插图按意图判断;没有明确必要时不要额外调用 inline_image。';
|
||
return [
|
||
'【服务号页面新缩略图硬要求】每个本轮交付给用户的正式内容页面都必须使用本轮新生成、与主题一致的位图作为缩略图源图。',
|
||
'写 HTML 前先调用 `load_skill` → `image-generation`,再为每个正式内容页面各调用一次 `sandbox-fs__generate_image`,优先 purpose=`hero`。',
|
||
`幂等键使用 ${keyPrefix}-<页面序号>-thumbnail;不同正式页面不得共用同一张图。`,
|
||
'同一正式页面在 Agent 层只调用一次 generate_image;工具内部会完成语义重试。返回失败后禁止自行更换提示词并重复调用,交由服务号交付层统一重试。',
|
||
'生成成功后,把返回的 asset.htmlSrc 原样写入对应页面的 mindspace-cover.cover。页面需要主视觉时复用同一张图作为 Hero,禁止再调用 card_cover/feed_cover。',
|
||
'管理后台、密码查看、重定向、下载包装和错误辅助页不属于正式内容页面,不要求单独生图;这类页面必须在 head 写 `<meta name="mindspace-page-role" content="auxiliary">` 供交付守卫识别。',
|
||
inlineInstruction,
|
||
'当前轮没有新的成功 generate_image、没有新 jobId,或正式页面未引用本轮资产时,禁止交付页面链接或声称完成。',
|
||
].join('\n');
|
||
}
|