Files
akmon/uni_modules/ak-ai-news/services/AIServiceManager.uts
2026-01-20 08:04:15 +08:00

564 lines
16 KiB
Plaintext
Raw Permalink 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.
// AI Service Manager - Unified coordinator for all AI services
import {
AIServiceConfig,
AIProvider,
AIResponse,
AIServiceError,
UsageStatistics,
CacheOptions
} from '../types/ai-types.uts'
import { AITranslationService } from './AITranslationService.uts'
import { AIContentAnalysisService } from './AIContentAnalysisService.uts'
import { AIChatService } from './AIChatService.uts'
import { AIRecommendationService } from './AIRecommendationService.uts'
import { ContentProcessingPipeline } from './ContentProcessingPipeline.uts'
// 服务状态枚举
type ServiceStatus = 'initializing' | 'ready' | 'busy' | 'error' | 'maintenance'
// 服务健康状态
type ServiceHealth = {
status: ServiceStatus
lastChecked: number
responseTime: number
errorRate: number
uptime: number
version: string
capabilities: string[]
}
// 负载均衡策略
type LoadBalanceStrategy = 'round_robin' | 'least_connections' | 'weighted' | 'random'
// 服务监控配置
type MonitoringConfig = {
healthCheckInterval: number // 健康检查间隔(毫秒)
maxErrorRate: number // 最大错误率
maxResponseTime: number // 最大响应时间(毫秒)
alertThresholds: {
errorRate: number
responseTime: number
dailyCost: number
}
}
// 成本控制配置
type CostControlConfig = {
dailyLimit: number // 每日成本限制USD
monthlyLimit: number // 每月成本限制USD
perRequestLimit: number // 单次请求成本限制USD
alertThresholds: {
daily: number // 每日预警阈值
monthly: number // 每月预警阈值
}
}
// 管理器统计
type ManagerStats = {
totalRequests: number
successfulRequests: number
failedRequests: number
totalCost: number
avgResponseTime: number
servicesHealth: Record<string, ServiceHealth>
dailyUsage: UsageStatistics[]
costBreakdown: Record<AIProvider, number>
lastReset: number
}
/**
* AI服务管理器
* 统一管理所有AI服务提供负载均衡、监控、成本控制等功能
*/
export class AIServiceManager {
private config: AIServiceConfig
private monitoringConfig: MonitoringConfig
private costControlConfig: CostControlConfig
private cacheOptions: CacheOptions
// 服务实例
private translationService: AITranslationService
private analysisService: AIContentAnalysisService
private chatService: AIChatService
private recommendationService: AIRecommendationService
private processingPipeline: ContentProcessingPipeline
// 状态管理
private servicesHealth: Map<string, ServiceHealth> = new Map()
private loadBalanceState: Map<AIProvider, number> = new Map()
private stats: ManagerStats
private healthCheckInterval: any
private isInitialized: boolean = false
constructor(
config: AIServiceConfig,
monitoringConfig: Partial<MonitoringConfig> = {},
costControlConfig: Partial<CostControlConfig> = {},
cacheOptions: Partial<CacheOptions> = {}
) {
this.config = config
this.monitoringConfig = this.createDefaultMonitoringConfig(monitoringConfig)
this.costControlConfig = this.createDefaultCostControlConfig(costControlConfig)
this.cacheOptions = this.createDefaultCacheOptions(cacheOptions)
this.stats = this.initializeStats()
this.initializeServices()
}
/**
* 初始化所有服务
*/
async initialize(): Promise<AIResponse<boolean>> {
try {
console.log('Initializing AI Service Manager...')
// 初始化各个服务
this.translationService = new AITranslationService(this.config, this.cacheOptions)
this.analysisService = new AIContentAnalysisService(this.config)
this.chatService = new AIChatService(this.config)
this.recommendationService = new AIRecommendationService(this.config)
this.processingPipeline = new ContentProcessingPipeline(this.config)
// 初始化服务健康状态
await this.initializeHealthStatus()
// 启动健康检查
this.startHealthMonitoring()
// 初始化负载均衡状态
this.initializeLoadBalancing()
this.isInitialized = true
console.log('AI Service Manager initialized successfully')
return { success: true, data: true }
} catch (error) {
console.error('Failed to initialize AI Service Manager:', error)
return {
success: false,
error: error.message || 'Initialization failed'
}
}
}
/**
* 获取翻译服务
*/
getTranslationService(): AITranslationService {
this.ensureInitialized()
return this.translationService
}
/**
* 获取内容分析服务
*/
getAnalysisService(): AIContentAnalysisService {
this.ensureInitialized()
return this.analysisService
}
/**
* 获取聊天服务
*/
getChatService(): AIChatService {
this.ensureInitialized()
return this.chatService
}
/**
* 获取推荐服务
*/
getRecommendationService(): AIRecommendationService {
this.ensureInitialized()
return this.recommendationService
}
/**
* 获取内容处理管道
*/
getProcessingPipeline(): ContentProcessingPipeline {
this.ensureInitialized()
return this.processingPipeline
}
/**
* 选择最佳提供商
* @param serviceType 服务类型
*/
selectBestProvider(serviceType: string = 'general'): AIProvider {
const availableProviders = this.getAvailableProviders()
if (availableProviders.length === 0) {
return 'openai' // 默认提供商
}
// 基于健康状态和负载均衡策略选择
return this.applyLoadBalancing(availableProviders, serviceType)
}
/**
* 检查成本限制
* @param estimatedCost 预估成本
*/
checkCostLimits(estimatedCost: number): boolean {
const now = new Date()
const today = now.toISOString().split('T')[0]
const currentMonth = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`
// 检查每日限制
const dailyCost = this.getDailyCost(today)
if (dailyCost + estimatedCost > this.costControlConfig.dailyLimit) {
console.warn(`Daily cost limit exceeded: ${dailyCost + estimatedCost} > ${this.costControlConfig.dailyLimit}`)
return false
}
// 检查每月限制
const monthlyCost = this.getMonthlyCost(currentMonth)
if (monthlyCost + estimatedCost > this.costControlConfig.monthlyLimit) {
console.warn(`Monthly cost limit exceeded: ${monthlyCost + estimatedCost} > ${this.costControlConfig.monthlyLimit}`)
return false
}
// 检查单次请求限制
if (estimatedCost > this.costControlConfig.perRequestLimit) {
console.warn(`Per-request cost limit exceeded: ${estimatedCost} > ${this.costControlConfig.perRequestLimit}`)
return false
}
return true
}
/**
* 记录使用统计
* @param provider 提供商
* @param serviceType 服务类型
* @param stats 统计信息
*/
recordUsage(provider: AIProvider, serviceType: string, stats: Partial<UsageStatistics>): void {
this.stats.totalRequests++
if (stats.requestsCount && stats.requestsCount > 0) {
this.stats.successfulRequests++
} else {
this.stats.failedRequests++
}
this.stats.totalCost += stats.costUSD || 0
this.stats.costBreakdown[provider] = (this.stats.costBreakdown[provider] || 0) + (stats.costUSD || 0)
if (stats.avgResponseTimeMs) {
this.stats.avgResponseTime = (
this.stats.avgResponseTime * (this.stats.totalRequests - 1) + stats.avgResponseTimeMs
) / this.stats.totalRequests
}
// 记录每日使用情况
const today = new Date().toISOString().split('T')[0]
const hour = new Date().getHours()
const dailyStats: UsageStatistics = {
provider,
serviceType,
tokensUsed: stats.tokensUsed || 0,
requestsCount: stats.requestsCount || 0,
costUSD: stats.costUSD || 0,
successCount: stats.successCount || 0,
errorCount: stats.errorCount || 0,
avgResponseTimeMs: stats.avgResponseTimeMs || 0,
date: today,
hour
}
this.stats.dailyUsage.push(dailyStats)
// 保持最近30天的数据
const cutoffDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]
this.stats.dailyUsage = this.stats.dailyUsage.filter(usage => usage.date >= cutoffDate)
}
/**
* 获取服务健康状态
*/
getServicesHealth(): Record<string, ServiceHealth> {
const health: Record<string, ServiceHealth> = {}
for (const [serviceName, serviceHealth] of this.servicesHealth.entries()) {
health[serviceName] = { ...serviceHealth }
}
return health
}
/**
* 获取管理器统计
*/
getManagerStatistics(): ManagerStats {
return {
...this.stats,
servicesHealth: this.getServicesHealth()
}
}
/**
* 重置统计数据
*/
resetStatistics(): void {
this.stats = this.initializeStats()
}
/**
* 停止所有服务
*/
async shutdown(): Promise<void> {
console.log('Shutting down AI Service Manager...')
// 停止健康检查
if (this.healthCheckInterval) {
clearInterval(this.healthCheckInterval)
}
// 清理缓存
if (this.translationService) {
this.translationService.clearCache()
}
this.isInitialized = false
console.log('AI Service Manager shut down completed')
}
// Private methods
private initializeServices(): void {
// 初始化负载均衡状态
const providers: AIProvider[] = ['openai', 'google', 'baidu', 'custom']
providers.forEach(provider => {
this.loadBalanceState.set(provider, 0)
})
}
private async initializeHealthStatus(): Promise<void> {
const services = ['translation', 'analysis', 'chat', 'recommendation', 'pipeline']
for (const serviceName of services) {
const health: ServiceHealth = {
status: 'ready',
lastChecked: Date.now(),
responseTime: 0,
errorRate: 0,
uptime: Date.now(),
version: '1.0.0',
capabilities: this.getServiceCapabilities(serviceName)
}
this.servicesHealth.set(serviceName, health)
}
}
private getServiceCapabilities(serviceName: string): string[] {
const capabilities: Record<string, string[]> = {
translation: ['text_translation', 'language_detection', 'batch_translation'],
analysis: ['sentiment_analysis', 'entity_extraction', 'content_classification', 'quality_assessment'],
chat: ['conversation', 'multilingual_support', 'context_awareness'],
recommendation: ['personalized_recommendations', 'trending_content', 'similarity_matching'],
pipeline: ['automated_processing', 'batch_processing', 'workflow_management']
}
return capabilities[serviceName] || []
}
private startHealthMonitoring(): void {
this.healthCheckInterval = setInterval(() => {
this.performHealthCheck()
}, this.monitoringConfig.healthCheckInterval)
}
private async performHealthCheck(): Promise<void> {
for (const [serviceName, health] of this.servicesHealth.entries()) {
try {
const startTime = Date.now()
// 执行简单的健康检查
await this.checkServiceHealth(serviceName)
const responseTime = Date.now() - startTime
// 更新健康状态
health.lastChecked = Date.now()
health.responseTime = responseTime
health.status = responseTime > this.monitoringConfig.maxResponseTime ? 'error' : 'ready'
// 检查错误率
if (health.errorRate > this.monitoringConfig.maxErrorRate) {
health.status = 'error'
}
} catch (error) {
console.error(`Health check failed for ${serviceName}:`, error)
this.servicesHealth.get(serviceName)!.status = 'error'
}
}
}
private async checkServiceHealth(serviceName: string): Promise<void> {
// 简单的健康检查实现
switch (serviceName) {
case 'translation':
// 可以测试一个简单的翻译
break
case 'analysis':
// 可以测试一个简单的分析
break
case 'chat':
// 可以检查会话状态
break
case 'recommendation':
// 可以检查推荐算法状态
break
case 'pipeline':
// 可以检查处理管道状态
break
}
// 模拟健康检查延迟
await this.delay(Math.random() * 100 + 50)
}
private initializeLoadBalancing(): void {
const providers = this.getAvailableProviders()
providers.forEach(provider => {
this.loadBalanceState.set(provider, 0)
})
}
private getAvailableProviders(): AIProvider[] {
const providers: AIProvider[] = []
if (this.config.openai?.apiKey) providers.push('openai')
if (this.config.google?.apiKey) providers.push('google')
if (this.config.baidu?.apiKey) providers.push('baidu')
return providers
}
private applyLoadBalancing(providers: AIProvider[], serviceType: string): AIProvider {
// 过滤健康的提供商
const healthyProviders = providers.filter(provider => {
const serviceName = this.getServiceNameForProvider(provider, serviceType)
const health = this.servicesHealth.get(serviceName)
return health && health.status === 'ready'
})
if (healthyProviders.length === 0) {
return providers[0] // 回退到第一个可用提供商
}
// 轮询策略
const providerCounts = healthyProviders.map(provider => ({
provider,
count: this.loadBalanceState.get(provider) || 0
}))
// 选择使用次数最少的提供商
const selectedProvider = providerCounts.reduce((min, current) =>
current.count < min.count ? current : min
).provider
// 更新计数
this.loadBalanceState.set(selectedProvider, (this.loadBalanceState.get(selectedProvider) || 0) + 1)
return selectedProvider
}
private getServiceNameForProvider(provider: AIProvider, serviceType: string): string {
// 根据提供商和服务类型映射到内部服务名称
const serviceMap: Record<string, string> = {
'translation': 'translation',
'analysis': 'analysis',
'chat': 'chat',
'recommendation': 'recommendation'
}
return serviceMap[serviceType] || 'translation'
}
private getDailyCost(date: string): number {
return this.stats.dailyUsage
.filter(usage => usage.date === date)
.reduce((total, usage) => total + usage.costUSD, 0)
}
private getMonthlyCost(month: string): number {
return this.stats.dailyUsage
.filter(usage => usage.date.startsWith(month))
.reduce((total, usage) => total + usage.costUSD, 0)
}
private ensureInitialized(): void {
if (!this.isInitialized) {
throw new Error('AI Service Manager not initialized. Call initialize() first.')
}
}
private createDefaultMonitoringConfig(overrides: Partial<MonitoringConfig>): MonitoringConfig {
return {
healthCheckInterval: 60000, // 1分钟
maxErrorRate: 0.1, // 10%
maxResponseTime: 5000, // 5秒
alertThresholds: {
errorRate: 0.05, // 5%
responseTime: 3000, // 3秒
dailyCost: 100 // $100
},
...overrides
}
}
private createDefaultCostControlConfig(overrides: Partial<CostControlConfig>): CostControlConfig {
return {
dailyLimit: 200, // $200
monthlyLimit: 5000, // $5000
perRequestLimit: 10, // $10
alertThresholds: {
daily: 150, // $150
monthly: 4000 // $4000
},
...overrides
}
}
private createDefaultCacheOptions(overrides: Partial<CacheOptions>): CacheOptions {
return {
enabled: true,
ttlHours: 24,
maxSize: 10000,
strategy: 'lru',
...overrides
}
}
private initializeStats(): ManagerStats {
const providers: AIProvider[] = ['openai', 'google', 'baidu', 'custom']
const costBreakdown: Record<AIProvider, number> = {} as Record<AIProvider, number>
providers.forEach(provider => {
costBreakdown[provider] = 0
})
return {
totalRequests: 0,
successfulRequests: 0,
failedRequests: 0,
totalCost: 0,
avgResponseTime: 0,
servicesHealth: {},
dailyUsage: [],
costBreakdown,
lastReset: Date.now()
}
}
private async delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}
}