Files
amz-pic-flow/lib/vision-extractor.js

199 lines
6.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Vision产品特征提取器
* 支持超时重试 + 缓存
*/
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const API_KEY = 'G9rXx3Ag2Xfa7Gs8zou6t6HqeZ';
const API_BASE = 'https://api.wuyinkeji.com/api';
// Vision提取Prompt
const VISION_EXTRACT_PROMPT = `Analyze this pet product image in EXTREME detail.
Output ONLY a valid JSON object (no markdown, no explanation, no thinking):
{
"color": {
"primary": "<hex code like #C3E6E8>",
"name": "<descriptive name like 'ice blue', 'mint green'>",
"secondary": "<accent colors>"
},
"shape": {
"type": "<flower/fan/cone/donut>",
"petal_count": <number of segments>,
"opening": "<C-shaped/full-circle/adjustable>",
"description": "<detailed shape description>"
},
"material": {
"type": "<PU/nylon/polyester/fabric>",
"finish": "<glossy/matte/satin>",
"texture": "<smooth/quilted/padded>"
},
"edge_binding": {
"color": "<color of inner neck edge>",
"material": "<ribbed elastic/fabric>"
},
"closure": {
"type": "<velcro/button/snap>",
"color": "<white/matching>",
"position": "<location description>"
},
"logo": {
"text": "<brand name>",
"style": "<embroidered/printed/tag>",
"position": "<location>"
},
"unique_features": ["<list distinctive features>"],
"overall_description": "<2-3 sentence summary for image generation>"
}`;
/**
* 调用Vision API提取产品特征
* @param {string} imageUrl - 图片URL
* @param {object} options - 配置选项
* @returns {object|null} - 提取的产品特征JSON
*/
async function extractProductFeatures(imageUrl, options = {}) {
const {
maxRetries = 3,
timeout = 120000, // 120秒
retryDelay = 5000,
cacheDir = null,
cacheKey = null
} = options;
// 检查缓存
if (cacheDir && cacheKey) {
const cachePath = path.join(cacheDir, `vision-cache-${cacheKey}.json`);
if (fs.existsSync(cachePath)) {
try {
const cached = JSON.parse(fs.readFileSync(cachePath, 'utf-8'));
if (cached && cached.color) {
console.log(' 📦 使用缓存的Vision结果');
return cached;
}
} catch (e) {
// 缓存无效,继续请求
}
}
}
let lastError = null;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
console.log(` 🔍 Vision分析 (尝试 ${attempt}/${maxRetries})...`);
try {
const response = await axios.post(`${API_BASE}/chat/index`, {
key: API_KEY,
model: 'gemini-3-pro',
content: VISION_EXTRACT_PROMPT,
image_url: imageUrl
}, { timeout });
const content = response.data.data?.choices?.[0]?.message?.content ||
response.data.data?.content;
if (!content) {
throw new Error('Vision响应为空');
}
// 提取JSON跳过thinking部分
let jsonStr = content;
// 如果有<think>标签,跳过它
const thinkEnd = content.indexOf('</think>');
if (thinkEnd !== -1) {
jsonStr = content.substring(thinkEnd + 8);
}
// 提取JSON
const jsonMatch = jsonStr.match(/\{[\s\S]*\}/);
if (jsonMatch) {
const parsed = JSON.parse(jsonMatch[0]);
// 验证必要字段
if (parsed.color && parsed.shape) {
console.log(' ✓ Vision提取成功');
// 保存缓存
if (cacheDir && cacheKey) {
const cachePath = path.join(cacheDir, `vision-cache-${cacheKey}.json`);
fs.writeFileSync(cachePath, JSON.stringify(parsed, null, 2));
}
return parsed;
}
}
throw new Error('无法解析有效的JSON');
} catch (error) {
lastError = error;
console.log(` ⚠️ 尝试 ${attempt} 失败: ${error.message}`);
if (attempt < maxRetries) {
console.log(`${retryDelay/1000}秒后重试...`);
await new Promise(r => setTimeout(r, retryDelay));
}
}
}
console.error(` ❌ Vision提取最终失败: ${lastError?.message}`);
return null;
}
/**
* 将Vision结果转换为Golden Description
*/
function buildGoldenDescription(visionResult, productType = 'pet recovery cone') {
if (!visionResult) {
return `
PRODUCT: ${productType}
- Shape: Soft flower/petal shape with C-shaped opening
- Material: Soft waterproof fabric
- Closure: Velcro strap
- Comfortable design for pets
CRITICAL: Follow the reference image EXACTLY for product shape and color.
`;
}
const r = visionResult;
return `
EXACT PRODUCT APPEARANCE (MUST MATCH REFERENCE IMAGE):
- Shape: ${r.shape?.petal_count || '7-8'}-PETAL ${(r.shape?.type || 'flower').toUpperCase()} shape
- Opening: ${r.shape?.opening || 'C-shaped'} (NOT a full circle)
- Color: ${(r.color?.name || 'ice blue').toUpperCase()} (${r.color?.primary || '#C3E6E8'})
- Material: ${r.material?.finish || 'Soft'} ${r.material?.type || 'waterproof fabric'} with ${r.material?.texture || 'padded'} texture
- Edge binding: ${r.edge_binding?.color || 'Matching color'} ${r.edge_binding?.material || 'ribbed elastic'} around inner neck hole
- Closure: ${r.closure?.color || 'White'} ${r.closure?.type || 'velcro'} on ${r.closure?.position || 'one segment'}
- Logo: "${r.logo?.text || 'TOUCHDOG'}" ${r.logo?.style || 'embroidered'} on ${r.logo?.position || 'one petal'}
UNIQUE FEATURES:
${(r.unique_features || ['Scalloped petal edges', 'Radial stitching', 'Soft padded construction']).map(f => `- ${f}`).join('\n')}
OVERALL: ${r.overall_description || 'A soft, comfortable pet recovery cone with flower-petal design.'}
CRITICAL PROHIBITIONS:
- ❌ NO printed colorful patterns (solid color only unless reference shows otherwise)
- ❌ NO hard plastic transparent cones
- ❌ NO fully circular/closed shapes (must match reference C-opening)
- ❌ NO random brand logos
- ❌ MUST match reference image product EXACTLY
`.trim();
}
module.exports = {
extractProductFeatures,
buildGoldenDescription,
VISION_EXTRACT_PROMPT
};