Upload latest code and optimized prompts (v6)

This commit is contained in:
tony
2025-12-14 19:52:54 +08:00
commit f5cb1042ae
42 changed files with 15302 additions and 0 deletions

369
lib/constraint-injector.js Normal file
View File

@@ -0,0 +1,369 @@
/**
* 约束注入器
* 自动将各类约束追加到AI生图Prompt中
*/
const fs = require('fs');
const path = require('path');
// 读取约束模板
const constraintsDir = path.join(__dirname, '../prompts/constraints');
/**
* 简单的模板引擎,替换 {{variable}} 占位符
* @param {string} template - 模板字符串
* @param {object} data - 数据对象
* @returns {string} 替换后的字符串
*/
function renderTemplate(template, data) {
return template.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
const keys = key.trim().split('.');
let value = data;
for (const k of keys) {
if (value && typeof value === 'object') {
// 处理数组索引 selling_points[0]
const arrayMatch = k.match(/^(\w+)\[(\d+)\]$/);
if (arrayMatch) {
value = value[arrayMatch[1]]?.[parseInt(arrayMatch[2])];
} else {
value = value[k];
}
} else {
return match; // 保留原占位符
}
}
if (Array.isArray(value)) {
return value.join(', ');
}
return value !== undefined ? String(value) : match;
});
}
/**
* 处理Handlebars风格的循环 {{#each array}}...{{/each}}
* @param {string} template - 模板字符串
* @param {object} data - 数据对象
* @returns {string} 处理后的字符串
*/
function processEachBlocks(template, data) {
const eachRegex = /\{\{#each\s+(\w+)\}\}([\s\S]*?)\{\{\/each\}\}/g;
return template.replace(eachRegex, (match, arrayName, content) => {
const array = data[arrayName];
if (!Array.isArray(array)) {
return '';
}
return array.map(item => {
// 处理 {{#if field}} 条件
let processed = content.replace(/\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (m, field, ifContent) => {
return item[field] ? ifContent : '';
});
// 替换当前项的字段
processed = processed.replace(/\{\{(\w+)\}\}/g, (m, field) => {
return item[field] !== undefined ? String(item[field]) : m;
});
return processed;
}).join('\n');
});
}
/**
* 生成产品一致性约束
* @param {object} sku - SKU数据
* @returns {string} 约束文本
*/
function generateProductConsistencyConstraint(sku) {
const template = `
=== PRODUCT CONSISTENCY REQUIREMENTS (CRITICAL - DO NOT IGNORE) ===
The product shown in this image must EXACTLY match the reference images provided.
PRODUCT SPECIFICATIONS:
- Product Type: ${sku.product_name}
- Color: ${sku.color.hex} (${sku.color.name})
- Color Series: ${sku.color.series || 'Standard'}
- Edge/Binding Color: ${sku.color.edge_color || sku.color.hex}
STRUCTURAL REQUIREMENTS:
- Shape: Soft cone collar with petal-like segments (NOT rigid plastic cone)
- Segments: Multiple soft fabric petals radiating from center opening
- Opening: Circular neck opening with ribbed cotton binding
- Closure: Velcro strap closure system
BRAND LOGO ON PRODUCT:
- Text: "TOUCHDOG®" (UPPERCASE with ® symbol)
- Position: On one petal segment, typically right side (2-3 o'clock position when viewed from front)
- Style: Embroidered/stitched into fabric, slightly recessed
- Color: Darker or contrasting shade matching the product color
TEXTURE & MATERIAL APPEARANCE:
- Outer Layer: Smooth, slightly glossy waterproof PU fabric
- Inner Layer: Visible soft cotton lining at edges
- Stitching: Reinforced seams, visible quilted pattern on petals
- Edge Binding: Ribbed cotton binding in ${sku.color.edge_color || 'matching color'}
CRITICAL PROHIBITIONS:
- DO NOT alter the product shape or structure
- DO NOT change the color from specified hex value
- DO NOT move or resize the embroidered logo
- DO NOT add any elements not present in reference images
- DO NOT stylize, reimagine, or "improve" the product design
- DO NOT generate rigid plastic cone - must be SOFT fabric cone
=== END PRODUCT CONSISTENCY REQUIREMENTS ===
`;
return template.trim();
}
/**
* 生成卖点合规约束
* @param {object} sku - SKU数据
* @returns {string} 约束文本
*/
function generateSellingPointConstraint(sku) {
const sellingPointsList = sku.selling_points.map(sp => {
let line = `- ${sp.title_en}`;
if (sp.value) line += ` (Value: ${sp.value})`;
if (sp.description_en) line += `: ${sp.description_en}`;
return line;
}).join('\n');
const visualPromptsList = sku.selling_points
.filter(sp => sp.visual_prompt)
.map(sp => `- ${sp.key}: ${sp.visual_prompt}`)
.join('\n');
const useCasesList = sku.use_cases
.map(uc => `- ${uc.name_en}`)
.join('\n');
const template = `
=== SELLING POINTS COMPLIANCE (LEGAL REQUIREMENT) ===
ALL product claims, features, and specifications shown in this image must come EXACTLY from the provided data.
APPROVED SELLING POINTS (use ONLY these):
${sellingPointsList}
APPROVED SPECIFICATIONS (use EXACT values):
- Weight: ${sku.specs.weight}
- Depth: ${sku.specs.depth_cm}cm
- Available Sizes: ${sku.specs.sizes.join(', ')}
- Outer Material: ${sku.specs.materials?.outer || 'Waterproof PU'}
- Inner Material: ${sku.specs.materials?.inner || 'Cotton blend'}
APPROVED USE CASES:
${useCasesList}
PROHIBITED ACTIONS:
- DO NOT invent new features or benefits not listed above
- DO NOT modify numerical values (no rounding, no approximation)
- DO NOT use superlatives: "best", "first", "only", "most", "#1", "leading"
- DO NOT make comparative claims without data: "better than", "superior to"
- DO NOT add health claims not approved by regulations
- DO NOT promise specific outcomes: "guaranteed", "100% effective"
VISUAL REPRESENTATION OF SELLING POINTS:
${visualPromptsList}
=== END SELLING POINTS COMPLIANCE ===
`;
return template.trim();
}
/**
* 生成竞品合规约束(仅用于竞品对比图)
* @param {object} sku - SKU数据
* @returns {string} 约束文本
*/
function generateCompetitorConstraint(sku) {
const productCategory = sku.product_name.includes('Cone') ? 'Plastic Cone' : 'Product';
const template = `
=== COMPETITOR COMPARISON COMPLIANCE (LEGAL RED LINE) ===
This is a product comparison image. You MUST follow these legal requirements:
OUR PRODUCT SIDE (Left/Colored):
- Show the Touchdog ${sku.product_name} in FULL COLOR
- Product color: ${sku.color.hex} (${sku.color.name})
- Use green checkmark icon (✓) to indicate positive
- Label as "OUR" or "TOUCHDOG"
- List ONLY the approved selling points from provided data
- Product must match reference images exactly
COMPETITOR SIDE (Right/Grayscale):
- Label as "OTHER" or "Traditional ${productCategory}" ONLY
- DO NOT use any competitor brand names
- DO NOT use any competitor logos or trademarks
- DO NOT show any identifiable competitor product designs
- Show a GENERIC, UNBRANDED version of traditional product
- Apply GRAYSCALE filter to entire competitor side
- Use red X icon to indicate negative
APPROVED COMPETITOR DISADVANTAGES (generic industry facts only):
- "Heavy & Bulky"
- "Blocks Vision & Movement"
- "Hard to Store"
- "Uncomfortable for Extended Wear"
- "Difficult to Clean"
- "Rigid and Inflexible"
VISUAL TREATMENT:
- Our side: Vibrant, colorful, positive imagery
- Competitor side: Muted, grayscale, neutral imagery
- Clear visual contrast between the two sides
- Center divider or clear separation between sides
STRICTLY PROHIBITED:
- ❌ Any specific competitor brand name
- ❌ Any competitor logo or trademark symbol
- ❌ Any identifiable competitor product photo
- ❌ Claims like "better than [Brand]"
- ❌ Using competitor's actual product images
=== END COMPETITOR COMPARISON COMPLIANCE ===
`;
return template.trim();
}
/**
* 生成品牌VI约束
* @param {object} sku - SKU数据
* @param {object} logoPlacement - Logo放置信息
* @returns {string} 约束文本
*/
function generateBrandVIConstraint(sku, logoPlacement = {}) {
const position = logoPlacement.position || 'bottom-right';
const logoType = logoPlacement.type || 'combined';
const logoColor = logoPlacement.color || 'red';
const template = `
=== BRAND VI REQUIREMENTS ===
EMBROIDERED LOGO ON PRODUCT:
- Text: "TOUCHDOG®" (UPPERCASE with ® symbol)
- Position: Right petal segment, 2-3 o'clock position
- Style: Embroidered, stitched into fabric
- Color: Slightly darker shade of product color (${sku.color.hex})
E-COMMERCE IMAGE BRAND LOGO:
- Position: ${position}
- Type: ${logoType === 'combined' ? 'Graphic icon + "touchdog®" text + "wow pretty"' : logoType}
- Color: ${logoColor === 'red' ? 'Red (#E60012)' : 'White'}
LOGO SPECIFICATIONS:
- Clear space: Minimum 1/4 of logo height on all sides
- Minimum size: Combined logo ≥ 46px height
- Tagline: "wow pretty" (for international markets)
PROHIBITED LOGO MODIFICATIONS:
- ❌ NO tilting or rotation
- ❌ NO outlines or strokes
- ❌ NO gradient fills
- ❌ NO drop shadows
- ❌ NO proportion changes
- ❌ NO underlines
BRAND TYPOGRAPHY:
- Headlines: Clean sans-serif, bold weight
- Body text: Clean sans-serif, medium weight
- Style: Clean, modern, professional
=== END BRAND VI REQUIREMENTS ===
`;
return template.trim();
}
/**
* 注入所有约束到Prompt
* @param {string} originalPrompt - 原始Prompt
* @param {object} sku - SKU数据
* @param {object} options - 选项
* @param {boolean} options.isComparison - 是否是竞品对比图
* @param {object} options.logoPlacement - Logo放置信息
* @returns {string} 注入约束后的完整Prompt
*/
function injectConstraints(originalPrompt, sku, options = {}) {
const { isComparison = false, logoPlacement = {} } = options;
const constraints = [];
// 始终添加产品一致性约束
constraints.push(generateProductConsistencyConstraint(sku));
// 始终添加卖点合规约束
constraints.push(generateSellingPointConstraint(sku));
// 如果是竞品对比图,添加竞品合规约束
if (isComparison) {
constraints.push(generateCompetitorConstraint(sku));
}
// 始终添加品牌VI约束
constraints.push(generateBrandVIConstraint(sku, logoPlacement));
// 添加参考图提示
if (sku.ref_images && sku.ref_images.length > 0) {
constraints.push(`
=== REFERENCE IMAGES ===
The following reference images must be used to ensure product consistency:
${sku.ref_images.map((url, i) => `- Reference ${i + 1}: ${url}`).join('\n')}
Product in generated image must match these reference images exactly.
=== END REFERENCE IMAGES ===
`);
}
// 组合最终Prompt
return `${originalPrompt}
${constraints.join('\n\n')}`;
}
/**
* 提取图片的宽高比参数
* @param {string} imageId - 图片ID (Main_01 或 APlus_01)
* @returns {string} 宽高比参数
*/
function getAspectRatio(imageId) {
if (imageId.startsWith('Main_')) {
return '1:1';
} else if (imageId.startsWith('APlus_')) {
return '3:2';
}
return '1:1';
}
/**
* 获取图片尺寸
* @param {string} imageId - 图片ID
* @returns {object} 尺寸信息
*/
function getImageSize(imageId) {
if (imageId.startsWith('Main_')) {
return { width: 1600, height: 1600, label: '1600x1600' };
} else if (imageId.startsWith('APlus_')) {
return { width: 970, height: 600, label: '970x600' };
}
return { width: 1600, height: 1600, label: '1600x1600' };
}
module.exports = {
injectConstraints,
generateProductConsistencyConstraint,
generateSellingPointConstraint,
generateCompetitorConstraint,
generateBrandVIConstraint,
getAspectRatio,
getImageSize,
renderTemplate,
processEachBlocks
};