Files
services/routes/ai.js
T
董海洋 79d34f17c5 AI
2026-05-24 11:04:24 +08:00

146 lines
4.5 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.
const Router = require('koa-router');
const fetch = require('node-fetch');
const router = new Router();
// NVIDIA API 配置(NVCF 云 API
const NVIDIA_API_KEY = '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';
const NVIDIA_API_URL = 'https://integrate.api.nvidia.com/v1/chat/completions';
// Mock 数据(用于测试或网络不可用时)
const mockProducts = [
{ name: '可口可乐', category: '饮料', description: '经典碳酸饮料,清爽解渴,330ml罐装', suggestedPrice: 3.5 },
{ name: '乐事薯片', category: '零食', description: '香脆可口,原味薯片,75g包装', suggestedPrice: 8.9 },
{ name: '维达纸巾', category: '日用品', description: '三层加厚,柔软亲肤,10包家庭装', suggestedPrice: 25.9 },
{ name: '康师傅方便面', category: '食品', description: '红烧牛肉面口味,12桶整箱装', suggestedPrice: 39.9 },
{ name: '农夫山泉', category: '饮料', description: '天然矿泉水,550ml瓶装,12瓶/箱', suggestedPrice: 24.0 },
{ name: '奥利奥饼干', category: '零食', description: '夹心饼干,巧克力口味,388g家庭装', suggestedPrice: 18.9 },
];
// 获取随机 Mock 数据
function getMockProduct(keywords) {
let product = mockProducts[Math.floor(Math.random() * mockProducts.length)];
if (keywords) {
const keyword = keywords.toLowerCase();
const matched = mockProducts.find(p =>
p.name.toLowerCase().includes(keyword) ||
p.category.toLowerCase().includes(keyword)
);
if (matched) product = matched;
}
return { ...product };
}
// 生成商品信息的 API
router.post('/generate-product', async (ctx) => {
try {
const { imageUrl, keywords } = ctx.request.body;
if (!imageUrl && !keywords) {
ctx.status = 400;
ctx.body = {
code: 400,
message: '请提供商品图片或关键词'
};
return;
}
// 构建提示词
let prompt = '你是一个专业的便利店商品管理助手。';
if (imageUrl) {
prompt += `\n请分析这张商品图片:${imageUrl}`;
}
if (keywords) {
prompt += `\n关键词:${keywords}`;
}
prompt += `
请生成商品的详细信息,返回JSON格式,不要包含其他内容:
{
"name": "商品名称(简洁明了,2-10字)",
"category": "商品分类(请从以下选择:饮料,零食,日用品,食品,烟酒,其他)",
"description": "商品详细描述(50-100字,突出产品特点)",
"suggestedPrice": 建议售价(数字)
}`;
let productInfo;
let useMock = false;
try {
// 调用 NVIDIA NVCF 云 APIOpenAI 兼容格式)
const response = await fetch(NVIDIA_API_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${NVIDIA_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-ai/deepseek-v4-pro',
messages: [
{
role: 'user',
content: prompt
}
],
temperature: 0.7,
max_tokens: 500
}),
timeout: 60000 // 60秒超时
});
if (!response.ok) {
const errorText = await response.text();
console.error('NVIDIA API Error:', response.status, errorText);
throw new Error(`API 调用失败: ${response.status}`);
}
const data = await response.json();
const aiResponse = data.choices?.[0]?.message?.content;
if (!aiResponse) {
console.log('API 返回为空');
throw new Error('API 返回为空');
}
// 解析 JSON 响应
const jsonMatch = aiResponse.match(/\{[\s\S]*\}/);
if (jsonMatch) {
productInfo = JSON.parse(jsonMatch[0]);
} else {
console.log('无法解析 JSON');
throw new Error('无法解析 AI 响应');
}
} catch (apiError) {
console.error('NVIDIA API 调用异常:', apiError.message);
console.log('切换到 Mock 模式');
useMock = true;
}
// 使用 Mock 数据作为备用
if (useMock) {
productInfo = getMockProduct(keywords);
}
ctx.body = {
code: 200,
message: useMock ? '使用模拟数据生成' : '生成成功',
data: productInfo
};
} catch (error) {
console.error('生成商品信息失败:', error);
ctx.status = 500;
ctx.body = {
code: 500,
message: error.message || '生成失败,请稍后重试'
};
}
});
module.exports = router.routes();