// 首先加载 polyfills 以确保兼容性 import "../utils/polyfills"; import { GoogleGenAI } from "@google/genai"; import { RoleConfigLoader } from "./RoleConfigLoader"; import { ChatHistoryManager } from "../manager/ChatHistoryManager"; import { ApiConfig } from "./ApiConfigLoader"; import { ErrorHandler, ErrorType } from "../utils/ErrorHandler"; import { VideoEmotion } from "../../schema/schema"; import { ConfigManager } from "../manager/ConfigManager"; import LanguageUtils, { LanguageType } from "../../Main/Common/LanguageUtils"; import Utils from "../../Main/Common/Utils"; import { InnerMsgCode } from "../../Main/Config/InnerMsgCode"; /** * AI情绪分析服务类 * * 基于Google Gemini API实现的情绪分析系统,支持: * - 基于聊天历史的情绪状态分析 * - 多角色独立的情绪聊天实例管理 * - 返回标准的VideoEmotion枚举值 * - 自动初始化历史情绪分析 * * @example * ```typescript * const emotionService = EmotionAIService.Instance; * const emotion = await emotionService.analyzeEmotionalState(10001); * console.log(VideoEmotion[emotion]); // "calm_down" * ``` * * @author AI Chat System * @version 1.0.0 */ export class EmotionAIService { private static _instance: EmotionAIService; private emotionAI: GoogleGenAI; private emotionChatInstances: Map = new Map(); private currentEmotions: Map = new Map(); private constructor() { const emotionConfig = ApiConfig.Instance.getEmotionAIConfig(); try { this.emotionAI = new GoogleGenAI({ apiKey: emotionConfig.apiKey }); } catch (error) { ErrorHandler.Instance.handleError( error as Error, ErrorType.API_ERROR, { config: { ...emotionConfig, apiKey: "***" } }, // 隐藏API密钥 true ); throw error; } } /** * 获取EmotionAIService的单例实例 * * @returns {EmotionAIService} 情绪分析服务实例 * @static */ public static get Instance(): EmotionAIService { if (!this._instance) { this._instance = new EmotionAIService(); } return this._instance; } /** * 创建或获取指定角色的情绪聊天实例 * @param roleId 角色ID */ private createOrGetEmotionChat(roleId: number): any { if (!this.emotionChatInstances.has(roleId)) { const systemInstruction = RoleConfigLoader.getEmotionInstruction(roleId); const config = ApiConfig.Instance.getEmotionAIConfig(); const chat = this.emotionAI.chats.create({ model: config.model, config: { temperature: config.temperature, systemInstruction: systemInstruction, }, }); this.emotionChatInstances.set(roleId, chat); console.log(`Created new emotion chat instance for role ${roleId}`); // 异步分析历史情绪状态 this.initializeEmotionAnalysis(roleId); } return this.emotionChatInstances.get(roleId); } /** * 初始化情绪分析(检查是否有历史记录并进行分析) * @param roleId 角色ID */ private async initializeEmotionAnalysis(roleId: number): Promise { try { // 检查是否有聊天历史 const messageCount = ChatHistoryManager.Instance.getMessageCount(roleId); let emotionalState: VideoEmotion; if (messageCount > 0) { console.log( `Analyzing emotional state for role ${roleId} based on ${messageCount} messages` ); // 分析历史情绪状态 emotionalState = await this.analyzeEmotionalState(roleId); console.log( `Initial emotional state for role ${roleId}: ${VideoEmotion[emotionalState]}` ); } else { console.log( `No history found for role ${roleId}, using default emotion: calm_down` ); emotionalState = VideoEmotion.calm_down; } // 发送情绪初始化完成事件 Utils.sendInnerMsg(InnerMsgCode.Chat_EmotionInitialized, { roleId: roleId, emotion: emotionalState }); console.log(`Emotion initialization completed for role ${roleId}: ${VideoEmotion[emotionalState]}`); } catch (error) { console.error( `Failed to initialize emotion analysis for role ${roleId}:`, error ); // 即使发生错误,也发送默认情绪状态 Utils.sendInnerMsg(InnerMsgCode.Chat_EmotionInitialized, { roleId: roleId, emotion: VideoEmotion.calm_down }); } } /** * 格式化聊天历史用于情绪分析 * @param messages 聊天消息数组 * @returns 格式化后的分析提示 */ private formatHistoryForEmotionAnalysis(messages: any[]): string { if (messages.length === 0) { return "没有聊天历史,请返回默认情绪状态:calm_down"; } let conversationText = "请分析以下对话的情绪状态,返回对应的VideoEmotion枚举值:\n\n"; messages.forEach((message, index) => { const speaker = message.role === "user" ? "用户" : "AI"; const text = message.parts[0]?.text || ""; conversationText += `${speaker}: ${text}\n`; }); conversationText += "\n请返回以下枚举值之一:calm_down, arousal, desire, passion, orgasm"; return conversationText; } /** * 向指定角色的情绪AI发送消息并获取分析回复,返回VideoEmotion枚举值 * @param roleId 角色ID * @param message 用户发送的消息内容 * @returns Promise 情绪AI的分析结果,如果发生错误返回默认值calm_down */ public async sendEmotionMessage( roleId: number, message: string ): Promise { // 输入验证 if (!roleId || roleId <= 0) { ErrorHandler.Instance.handleValidationError( "roleId", "角色ID必须是正整数", roleId ); return VideoEmotion.calm_down; } if (!message || message.trim() === "") { ErrorHandler.Instance.handleValidationError( "message", "消息内容不能为空", message ); return VideoEmotion.calm_down; } try { const emotionChat = this.createOrGetEmotionChat(roleId); const response = await emotionChat.sendMessage({ message: message.trim(), }); if (response && response.text) { console.log(`Emotion analysis from role ${roleId}:`, response.text); return this.parseEmotionResponse(response.text); } else { const warningMsg = `情绪AI返回了空响应 (角色ID: ${roleId})`; ErrorHandler.Instance.handleError( new Error(warningMsg), ErrorType.API_ERROR, { roleId, message, response }, true ); return VideoEmotion.calm_down; } } catch (error) { ErrorHandler.Instance.handleApiError(error, "sendEmotionMessage", { roleId, message: message.substring(0, 100) + "...", }); return VideoEmotion.calm_down; } } /** * 分析指定角色的情绪状态(基于最近10条聊天记录) * @param roleId 角色ID * @returns Promise 情绪分析结果 */ public async analyzeEmotionalState(roleId: number): Promise { try { // 获取最近10条消息 const recentMessages = ChatHistoryManager.Instance.getRecentMessages( roleId, 10 ); // 格式化历史记录用于分析 const analysisPrompt = this.formatHistoryForEmotionAnalysis(recentMessages); // 发送给情绪AI进行分析 return await this.sendEmotionMessage(roleId, analysisPrompt); } catch (error) { ErrorHandler.Instance.handleError( error as Error, ErrorType.API_ERROR, { roleId }, false ); return VideoEmotion.calm_down; } } /** * 解析情绪AI返回的文本为VideoEmotion枚举值 * @param responseText AI返回的文本 * @returns VideoEmotion枚举值 */ private parseEmotionResponse(responseText: string): VideoEmotion { const text = responseText.toLowerCase().trim(); if (text.includes("arousal")) { return VideoEmotion.arousal; } else if (text.includes("desire")) { return VideoEmotion.desire; } else if (text.includes("passion")) { return VideoEmotion.passion; } else if (text.includes("orgasm")) { return VideoEmotion.orgasm; } else { return VideoEmotion.calm_down; // 默认值 } } /** * 确保指定角色的情绪聊天实例已创建 * @param roleId 角色ID */ public ensureEmotionChatExists(roleId: number): void { this.createOrGetEmotionChat(roleId); } /** * 清除指定角色的情绪聊天历史 * @param roleId 角色ID */ public clearEmotionHistory(roleId: number): void { if (this.emotionChatInstances.has(roleId)) { this.emotionChatInstances.delete(roleId); } console.log(`Cleared emotion chat history for role ${roleId}`); } /** * 清除所有情绪聊天历史 */ public clearAllEmotionHistory(): void { this.emotionChatInstances.clear(); console.log("Cleared all emotion chat histories"); } /** * 获取当前活跃的情绪聊天实例数量 */ public getActiveEmotionChatCount(): number { return this.emotionChatInstances.size; } /** * 获取指定角色的当前情绪状态 * @param roleId 角色ID * @returns VideoEmotion 当前情绪状态 */ public getCurrentEmotion(roleId: number): VideoEmotion { return this.currentEmotions.get(roleId) || VideoEmotion.calm_down; } /** * 设置指定角色的情绪状态 * @param roleId 角色ID * @param emotion 情绪状态 */ private setCurrentEmotion(roleId: number, emotion: VideoEmotion): void { this.currentEmotions.set(roleId, emotion); console.log(`Updated emotion for role ${roleId}: ${VideoEmotion[emotion]}`); } /** * 获取角色的英文名 * @param roleId 角色ID * @returns 角色英文名,如果未找到返回"AI" */ private getRoleEnglishName(roleId: number): string { try { if (ConfigManager.tables && ConfigManager.tables.TbGirls) { const girlData = ConfigManager.tables.TbGirls.get(roleId); if (girlData && girlData.nameKey) { return ( LanguageUtils.getTextByLanguage( girlData.nameKey, LanguageType.EN ) || "AI" ); } } } catch (error) { console.warn(`Failed to get role name for ${roleId}:`, error); } return "AI"; } /** * 根据聊天对话更新情绪状态 * @param roleId 角色ID * @param userMessage 用户消息 * @param aiResponse AI回复 * @returns Promise 更新后的情绪状态 */ public async updateEmotionFromChat( roleId: number, userMessage: string, aiResponse: string ): Promise { try { // 构建情绪分析提示,包含当前情绪上下文 const currentEmotion = this.getCurrentEmotion(roleId); const analysisPrompt = this.formatChatForEmotionAnalysis( roleId, userMessage, aiResponse, currentEmotion ); // 发送给情绪AI进行分析 const newEmotion = await this.sendEmotionMessage(roleId, analysisPrompt); // 更新并保存情绪状态 this.setCurrentEmotion(roleId, newEmotion); // 发送情绪更新完成事件 Utils.sendInnerMsg(InnerMsgCode.Chat_EmotionUpdated, { roleId: roleId, emotion: newEmotion }); return newEmotion; } catch (error) { ErrorHandler.Instance.handleError( error as Error, ErrorType.API_ERROR, { roleId, userMessage: userMessage.substring(0, 50) }, false ); return this.getCurrentEmotion(roleId); // 返回当前情绪状态 } } /** * 格式化聊天对话用于情绪分析 * @param roleId 角色ID * @param userMessage 用户消息 * @param aiResponse AI回复 * @param currentEmotion 当前情绪状态 * @returns 格式化后的分析提示 */ private formatChatForEmotionAnalysis( roleId: number, userMessage: string, aiResponse: string, currentEmotion: VideoEmotion ): string { const roleName = this.getRoleEnglishName(roleId); let analysisText = `当前情绪状态: ${VideoEmotion[currentEmotion]}\n\n`; analysisText += `请分析以下最新对话的情绪变化,返回对应的VideoEmotion枚举值:\n\n`; analysisText += `用户: ${userMessage}\n`; analysisText += `${roleName}: ${aiResponse}\n\n`; analysisText += `请基于对话内容和当前情绪状态,返回以下枚举值之一:calm_down, arousal, desire, passion, orgasm`; return analysisText; } }