Initial commit of akmon project

This commit is contained in:
2026-01-20 08:04:15 +08:00
commit 77a2bab985
1309 changed files with 343305 additions and 0 deletions

View File

@@ -0,0 +1,562 @@
// AI翻译服务
// filepath: h:\blews\akmon\uni_modules\ak-ai-news\services\ai-translation-service.uts
import { AkReq } from '@/uni_modules/ak-req/index.uts'
import type {
TranslationResult,
TranslationOptions,
AIProvider,
AIResponse,
AIServiceConfig
} from '../types/ai-types.uts'
export class AITranslationService {
private config: AIServiceConfig
private req: AkReq
private cache: Map<string, TranslationResult> = new Map()
constructor(config: AIServiceConfig) {
this.config = config
this.req = new AkReq()
}
/**
* 翻译文本 - 智能选择最佳AI服务
*/
async translateText(
text: string,
targetLang: string,
sourceLang?: string,
options?: TranslationOptions
): Promise<AIResponse<TranslationResult>> {
try {
// 检查缓存
const cacheKey = this.generateCacheKey(text, targetLang, sourceLang)
const cached = this.cache.get(cacheKey)
if (cached && this.isCacheValid(cached)) {
return {
success: true,
data: cached,
processingTimeMs: 0,
costUSD: 0
}
}
// 智能选择提供商
const provider = this.selectOptimalProvider(text, targetLang, options)
let result: TranslationResult
const startTime = Date.now()
switch (provider) {
case 'openai':
result = await this.translateWithOpenAI(text, targetLang, sourceLang, options)
break
case 'google':
result = await this.translateWithGoogle(text, targetLang, sourceLang, options)
break
case 'baidu':
result = await this.translateWithBaidu(text, targetLang, sourceLang, options)
break
default:
throw new Error(`不支持的AI提供商: ${provider}`)
}
result.processingTimeMs = Date.now() - startTime
// 质量检查
if (result.qualityScore < (options?.qualityThreshold ?? 0.7)) {
// 尝试使用备用提供商
const fallbackProvider = this.getFallbackProvider(provider)
if (fallbackProvider) {
const fallbackResult = await this.translateWithProvider(
text, targetLang, sourceLang, fallbackProvider, options
)
if (fallbackResult.qualityScore > result.qualityScore) {
result = fallbackResult
}
}
}
// 缓存结果
this.cache.set(cacheKey, result)
// 记录使用统计
await this.recordUsage(result)
return {
success: true,
data: result,
processingTimeMs: result.processingTimeMs,
costUSD: result.costUSD,
provider: result.provider
}
} catch (error) {
console.error('翻译失败:', error)
return {
success: false,
error: error instanceof Error ? error.message : '翻译服务异常',
errorCode: 'TRANSLATION_FAILED'
}
}
}
/**
* 批量翻译
*/
async batchTranslate(
texts: string[],
targetLang: string,
sourceLang?: string,
options?: TranslationOptions
): Promise<AIResponse<TranslationResult[]>> {
try {
const results: TranslationResult[] = []
const batchSize = 10 // 批处理大小
for (let i = 0; i < texts.length; i += batchSize) {
const batch = texts.slice(i, i + batchSize)
const batchPromises = batch.map(text =>
this.translateText(text, targetLang, sourceLang, options)
)
const batchResults = await Promise.all(batchPromises)
for (const result of batchResults) {
if (result.success && result.data) {
results.push(result.data)
}
}
// 避免API限流
if (i + batchSize < texts.length) {
await this.delay(100)
}
}
return {
success: true,
data: results,
processingTimeMs: results.reduce((sum, r) => sum + r.processingTimeMs, 0),
costUSD: results.reduce((sum, r) => sum + r.costUSD, 0)
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : '批量翻译失败',
errorCode: 'BATCH_TRANSLATION_FAILED'
}
}
}
/**
* OpenAI翻译实现
*/
private async translateWithOpenAI(
text: string,
targetLang: string,
sourceLang?: string,
options?: TranslationOptions
): Promise<TranslationResult> {
const openaiConfig = this.config.openai
if (!openaiConfig) {
throw new Error('OpenAI配置未找到')
}
const prompt = this.buildOpenAIPrompt(text, targetLang, sourceLang, options)
const response = await this.req.post<any>({
url: `${openaiConfig.baseURL || 'https://api.openai.com'}/v1/chat/completions`,
headers: {
'Authorization': `Bearer ${openaiConfig.apiKey}`,
'Content-Type': 'application/json'
},
data: {
model: options?.model || openaiConfig.model,
messages: [
{
role: 'system',
content: '你是一个专业的翻译助手,能够提供高质量的多语言翻译服务。'
},
{
role: 'user',
content: prompt
}
],
max_tokens: options?.maxTokens || openaiConfig.maxTokens,
temperature: options?.temperature || openaiConfig.temperature
}
})
if (!response.success || !response.data) {
throw new Error('OpenAI API调用失败')
}
const choice = response.data.choices?.[0]
if (!choice) {
throw new Error('OpenAI响应格式错误')
}
return this.parseOpenAIResponse(
choice.message.content,
text,
targetLang,
sourceLang || 'auto',
response.data.usage
)
}
/**
* Google翻译实现
*/
private async translateWithGoogle(
text: string,
targetLang: string,
sourceLang?: string,
options?: TranslationOptions
): Promise<TranslationResult> {
const googleConfig = this.config.google
if (!googleConfig) {
throw new Error('Google翻译配置未找到')
}
const startTime = Date.now()
const response = await this.req.post<any>({
url: 'https://translation.googleapis.com/language/translate/v2',
headers: {
'Content-Type': 'application/json'
},
data: {
key: googleConfig.apiKey,
q: text,
target: this.convertLanguageCode(targetLang, 'google'),
source: sourceLang ? this.convertLanguageCode(sourceLang, 'google') : undefined,
format: 'text'
}
})
if (!response.success || !response.data) {
throw new Error('Google翻译API调用失败')
}
const translation = response.data.data?.translations?.[0]
if (!translation) {
throw new Error('Google翻译响应格式错误')
}
return {
translatedText: translation.translatedText,
originalText: text,
sourceLang: translation.detectedSourceLanguage || sourceLang || 'auto',
targetLang,
confidence: 0.9, // Google翻译通常质量较高
qualityScore: 0.85,
provider: 'google',
tokensUsed: Math.ceil(text.length / 4), // 估算token数
processingTimeMs: Date.now() - startTime,
costUSD: this.calculateGoogleCost(text.length)
}
}
/**
* 百度翻译实现
*/
private async translateWithBaidu(
text: string,
targetLang: string,
sourceLang?: string,
options?: TranslationOptions
): Promise<TranslationResult> {
const baiduConfig = this.config.baidu
if (!baiduConfig) {
throw new Error('百度翻译配置未找到')
}
const startTime = Date.now()
const salt = Date.now().toString()
const sign = this.generateBaiduSign(text, salt, baiduConfig.apiKey, baiduConfig.secretKey)
const response = await this.req.post<any>({
url: 'https://fanyi-api.baidu.com/api/trans/vip/translate',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {
q: text,
from: sourceLang ? this.convertLanguageCode(sourceLang, 'baidu') : 'auto',
to: this.convertLanguageCode(targetLang, 'baidu'),
appid: baiduConfig.apiKey,
salt: salt,
sign: sign
}
})
if (!response.success || !response.data) {
throw new Error('百度翻译API调用失败')
}
const result = response.data.trans_result?.[0]
if (!result) {
throw new Error('百度翻译响应格式错误')
}
return {
translatedText: result.dst,
originalText: text,
sourceLang: response.data.from || sourceLang || 'auto',
targetLang,
confidence: 0.85,
qualityScore: 0.8,
provider: 'baidu',
tokensUsed: Math.ceil(text.length / 4),
processingTimeMs: Date.now() - startTime,
costUSD: this.calculateBaiduCost(text.length)
}
}
/**
* 选择最优提供商
*/
private selectOptimalProvider(
text: string,
targetLang: string,
options?: TranslationOptions
): AIProvider {
if (options?.provider) {
return options.provider
}
// 根据文本长度和语言选择最佳提供商
const textLength = text.length
const isChineseTarget = targetLang.startsWith('zh')
const isChineseSource = /[\u4e00-\u9fff]/.test(text)
// 中文相关翻译优先使用百度
if (isChineseTarget || isChineseSource) {
return 'baidu'
}
// 长文本使用OpenAI更好的上下文理解
if (textLength > 1000) {
return 'openai'
}
// 短文本使用Google速度快成本低
return 'google'
}
/**
* 获取备用提供商
*/
private getFallbackProvider(primary: AIProvider): AIProvider | null {
switch (primary) {
case 'openai':
return 'google'
case 'google':
return 'baidu'
case 'baidu':
return 'google'
default:
return null
}
}
/**
* 生成缓存键
*/
private generateCacheKey(text: string, targetLang: string, sourceLang?: string): string {
const source = sourceLang || 'auto'
const hash = this.simpleHash(text)
return `${source}-${targetLang}-${hash}`
}
/**
* 检查缓存是否有效
*/
private isCacheValid(result: TranslationResult): boolean {
// 简单的缓存有效性检查,可以根据需要扩展
return result.qualityScore > 0.7
}
/**
* 构建OpenAI提示词
*/
private buildOpenAIPrompt(
text: string,
targetLang: string,
sourceLang?: string,
options?: TranslationOptions
): string {
let prompt = `请将以下文本翻译成${this.getLanguageName(targetLang)}\n\n${text}\n\n`
if (options?.culturalAdaptation) {
prompt += '请考虑文化差异,进行适当的本地化调整。\n'
}
if (options?.preserveFormatting) {
prompt += '请保持原文的格式和结构。\n'
}
prompt += '只返回翻译结果,不要包含其他解释。'
return prompt
}
/**
* 解析OpenAI响应
*/
private parseOpenAIResponse(
content: string,
originalText: string,
targetLang: string,
sourceLang: string,
usage: any
): TranslationResult {
return {
translatedText: content.trim(),
originalText,
sourceLang,
targetLang,
confidence: 0.9,
qualityScore: 0.9,
provider: 'openai',
tokensUsed: usage?.total_tokens || 0,
processingTimeMs: 0,
costUSD: this.calculateOpenAICost(usage?.total_tokens || 0)
}
}
/**
* 转换语言代码
*/
private convertLanguageCode(code: string, provider: AIProvider): string {
const codeMap: Record<AIProvider, Record<string, string>> = {
openai: {
'zh-CN': 'Chinese',
'en-US': 'English',
'ja-JP': 'Japanese',
'ko-KR': 'Korean'
},
google: {
'zh-CN': 'zh',
'en-US': 'en',
'ja-JP': 'ja',
'ko-KR': 'ko'
},
baidu: {
'zh-CN': 'zh',
'en-US': 'en',
'ja-JP': 'jp',
'ko-KR': 'kor'
},
custom: {}
}
return codeMap[provider]?.[code] || code
}
/**
* 获取语言名称
*/
private getLanguageName(code: string): string {
const nameMap: Record<string, string> = {
'zh-CN': '中文',
'en-US': 'English',
'ja-JP': '日语',
'ko-KR': '韩语',
'es-ES': 'Spanish',
'fr-FR': 'French',
'de-DE': 'German'
}
return nameMap[code] || code
}
/**
* 计算成本
*/
private calculateOpenAICost(tokens: number): number {
// GPT-4 pricing: $0.03 per 1K tokens (input + output)
return (tokens / 1000) * 0.03
}
private calculateGoogleCost(textLength: number): number {
// Google Translate: $20 per 1M characters
return (textLength / 1000000) * 20
}
private calculateBaiduCost(textLength: number): number {
// 百度翻译:较低成本,假设$5 per 1M characters
return (textLength / 1000000) * 5
}
/**
* 生成百度签名
*/
private generateBaiduSign(text: string, salt: string, appid: string, key: string): string {
// 简化的签名生成实际应用中需要使用MD5
const str = appid + text + salt + key
return this.simpleHash(str)
}
/**
* 简单哈希函数
*/
private simpleHash(str: string): string {
let hash = 0
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i)
hash = ((hash << 5) - hash) + char
hash = hash & hash // Convert to 32-bit integer
}
return Math.abs(hash).toString(36)
}
/**
* 延迟函数
*/
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}
/**
* 记录使用统计
*/
private async recordUsage(result: TranslationResult): Promise<void> {
try {
// 这里可以将使用统计发送到数据库
console.log('翻译使用统计:', {
provider: result.provider,
tokensUsed: result.tokensUsed,
costUSD: result.costUSD,
processingTimeMs: result.processingTimeMs
})
} catch (error) {
console.error('记录使用统计失败:', error)
}
}
/**
* 根据提供商翻译
*/
private async translateWithProvider(
text: string,
targetLang: string,
sourceLang: string | undefined,
provider: AIProvider,
options?: TranslationOptions
): Promise<TranslationResult> {
switch (provider) {
case 'openai':
return await this.translateWithOpenAI(text, targetLang, sourceLang, options)
case 'google':
return await this.translateWithGoogle(text, targetLang, sourceLang, options)
case 'baidu':
return await this.translateWithBaidu(text, targetLang, sourceLang, options)
default:
throw new Error(`不支持的提供商: ${provider}`)
}
}
}