修复AI翻译功能
This commit is contained in:
+52
-112
@@ -1,139 +1,79 @@
|
|||||||
import OpenAI from "openai";
|
import OpenAI from "openai";
|
||||||
|
import {getConfig} from "@/lib/config";
|
||||||
|
|
||||||
// AI 翻译配置
|
|
||||||
interface AIConfig {
|
|
||||||
apiKey: string;
|
|
||||||
baseUrl?: string;
|
|
||||||
model?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 默认配置(从环境变量)
|
|
||||||
function getDefaultConfig(): AIConfig {
|
|
||||||
return {
|
|
||||||
apiKey: process.env.AI_API_KEY || "",
|
|
||||||
baseUrl: process.env.AI_BASE_URL || undefined,
|
|
||||||
model: process.env.AI_MODEL || "gpt-3.5-turbo",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模块级配置存储
|
|
||||||
let savedConfig: AIConfig | null = null;
|
|
||||||
|
|
||||||
// 设置保存的配置
|
|
||||||
export function setAIConfig(config: AIConfig) {
|
|
||||||
savedConfig = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取配置(优先使用保存的配置)
|
|
||||||
export function getAIConfig(): AIConfig {
|
|
||||||
return savedConfig || getDefaultConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析翻译结果
|
|
||||||
function parseTranslation(content: string): string | null {
|
function parseTranslation(content: string): string | null {
|
||||||
const patterns = [
|
// 第一步:清理内容,移除 Markdown 代码块、换行、多余空格
|
||||||
/english\s*:\s*['"]([^'"]+)['"]/i,
|
const cleanContent = content
|
||||||
/english\s*[:\s]+([^,'"\n]+)/i,
|
.replace(/```json|```/g, '') // 移除 ```json 和 ```
|
||||||
/['"]([^'"]+)['"]\s*[,}].*english/i,
|
.replace(/\n/g, ' ') // 把换行换成空格
|
||||||
/^([^,]+)\s*,\s*english\s*:\s*['"]([^'"]+)['"]/i,
|
.trim();
|
||||||
];
|
|
||||||
|
|
||||||
for (const pattern of patterns) {
|
// 精准匹配 english 字段(支持任何格式、带空格)
|
||||||
const match = content.match(pattern);
|
const regex = /"english"\s*:\s*["']([^"']+)["']/i;
|
||||||
if (match) {
|
const match = cleanContent.match(regex);
|
||||||
return match[1]?.trim() || null;
|
|
||||||
|
if (match && match[1]) {
|
||||||
|
return match[1].trim();
|
||||||
}
|
}
|
||||||
}
|
return null;
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 翻译单个词组(带重试)
|
// 翻译单个词组(带重试)
|
||||||
export async function translateWithAI(
|
export async function translateWithAI(word: string): Promise<string | null> {
|
||||||
word: string,
|
// 获取配置
|
||||||
config?: AIConfig,
|
const aiConfig = getConfig().ai
|
||||||
maxRetries: number = 3
|
|
||||||
): Promise<string | null> {
|
|
||||||
const aiConfig = config || getAIConfig();
|
|
||||||
|
|
||||||
if (!aiConfig.apiKey) {
|
if (!aiConfig.apiKey) {
|
||||||
throw new Error("AI API Key 未配置");
|
throw new Error("AI API Key 未配置");
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new OpenAI({
|
const client = new OpenAI({
|
||||||
apiKey: aiConfig.apiKey,
|
apiKey: aiConfig.apiKey,
|
||||||
baseURL: aiConfig.baseUrl,
|
baseURL: aiConfig.baseUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
const prompt = `你是一个专业的中文到英文翻译助手。
|
|
||||||
|
|
||||||
|
const prompt = `你是一个专业的中文到英文翻译助手。
|
||||||
任务:将给定的中文词组翻译成英文。
|
任务:将给定的中文词组翻译成英文。
|
||||||
|
|
||||||
严格要求:
|
严格要求:
|
||||||
1. 必须严格返回 JSON 格式:{"word":"中文原文","english":"英文翻译"}
|
1. 必须严格返回 JSON 格式:{"word":"中文原文","english":"英文翻译"}
|
||||||
2. 不要返回任何其他内容,不要解释
|
2. 不要返回任何其他内容,不要解释
|
||||||
3. 只返回这一行 JSON
|
3. 只返回这一行 JSON,不要加引号,不要加代码块
|
||||||
4. 翻译要简洁、准确
|
中文词组:${word}`;
|
||||||
|
|
||||||
中文词组:${word}
|
|
||||||
|
|
||||||
请立即返回 JSON 结果:`;
|
|
||||||
|
|
||||||
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
||||||
try {
|
try {
|
||||||
const completion = await client.chat.completions.create({
|
const completion = await client.chat.completions.create({
|
||||||
model: aiConfig.model || "gpt-3.5-turbo",
|
model: aiConfig.model,
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
role: "system",
|
role: "system",
|
||||||
content: "你是一个严格遵循指令的翻译助手。必须只返回要求的 JSON 格式,不要返回任何其他内容。",
|
content: "你是一个严格遵循指令的翻译助手。必须只返回一行纯 JSON,不要加任何其他文字、符号、代码块。",
|
||||||
},
|
},
|
||||||
{ role: "user", content: prompt },
|
{role: "user", content: prompt},
|
||||||
],
|
]
|
||||||
temperature: 0,
|
});
|
||||||
max_tokens: 50,
|
const content = completion.choices[0]?.message?.content || "";
|
||||||
});
|
return parseTranslation(content);
|
||||||
|
|
||||||
const content = completion.choices[0]?.message?.content || "";
|
|
||||||
const translation = parseTranslation(content);
|
|
||||||
|
|
||||||
if (translation && translation.length > 0) {
|
|
||||||
return translation;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: 提取英文单词
|
|
||||||
const fallbackMatch = content.match(/[a-zA-Z][a-zA-Z\s-]*/);
|
|
||||||
if (fallbackMatch) {
|
|
||||||
const fallback = fallbackMatch[0].trim();
|
|
||||||
if (fallback.length > 1) {
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Attempt ${attempt + 1} error:`, error);
|
console.error("翻译出错:", error);
|
||||||
}
|
}
|
||||||
}
|
return null;
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量翻译词组
|
// 批量翻译词组
|
||||||
export async function batchTranslateWithAI(
|
export async function batchTranslateWithAI(
|
||||||
words: string[],
|
words: string[]
|
||||||
config?: AIConfig
|
|
||||||
): Promise<Map<string, string>> {
|
): Promise<Map<string, string>> {
|
||||||
const results = new Map<string, string>();
|
const results = new Map<string, string>();
|
||||||
|
|
||||||
for (const word of words) {
|
for (const word of words) {
|
||||||
try {
|
try {
|
||||||
const translation = await translateWithAI(word, config);
|
const translation = await translateWithAI(word);
|
||||||
if (translation) {
|
if (translation) {
|
||||||
results.set(word, translation);
|
results.set(word, translation);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to translate "${word}":`, error);
|
console.error(`Failed to translate "${word}":`, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,16 +14,6 @@ const pool = mysql.createPool({
|
|||||||
connectionLimit: 10,
|
connectionLimit: 10,
|
||||||
queueLimit: 0,
|
queueLimit: 0,
|
||||||
});
|
});
|
||||||
console.log({
|
|
||||||
host: dataBaseConfig.host || "127.0.0.1",
|
|
||||||
port: dataBaseConfig.port || 3306,
|
|
||||||
user: dataBaseConfig.user || "root",
|
|
||||||
password: dataBaseConfig.password || "123456",
|
|
||||||
database: dataBaseConfig.database || "recipe_tools",
|
|
||||||
waitForConnections: true,
|
|
||||||
connectionLimit: 10,
|
|
||||||
queueLimit: 0,
|
|
||||||
})
|
|
||||||
export default pool;
|
export default pool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user