代码整理,ai相关配置转luban,聊天气泡缓存

This commit is contained in:
2025-08-13 11:31:21 +08:00
parent b511412833
commit db0b040fd8
78 changed files with 913 additions and 5189 deletions
@@ -0,0 +1,89 @@
/**
* API配置管理
* 统一管理所有API相关的配置信息
*/
import ConfigManager from "../manager/ConfigManager";
export interface AIConfig {
/** API密钥 */
apiKey: string;
/** 模型名称 */
model: string;
/** 生成温度参数 */
temperature: number;
/** 最大令牌数 */
maxTokens?: number;
/** 请求超时时间(毫秒) */
timeout?: number;
}
/**
* API配置管理器
*/
export class ApiConfig {
private static _instance: ApiConfig;
private config: AIConfig;
private constructor() {
this.initConfig();
}
public static get Instance(): ApiConfig {
if (!this._instance) {
this._instance = new ApiConfig();
}
return this._instance;
}
/**
* 初始化配置
* TODO: 应该从环境变量或安全配置文件中读取
*/
private initConfig(): void {
const config = ConfigManager.tables.TbGlobalConfig;
this.config = {
// 警告: API密钥不应该硬编码在代码中
// 生产环境中应该从环境变量或安全配置文件中读取
apiKey: config.ApiKey,
model: config.Model,
temperature: config.Temperature,
maxTokens: config.MaxTokens,
timeout: config.Timeout, // 30秒
};
}
/**
* 获取AI配置
*/
public getAIConfig(): AIConfig {
return { ...this.config };
}
/**
* 更新生成参数
* @param temperature 温度参数
*/
public updateTemperature(temperature: number): void {
if (temperature >= 0 && temperature <= 2) {
this.config.temperature = temperature;
} else {
console.warn("Temperature should be between 0 and 2");
}
}
/**
* 验证配置是否有效
*/
public validateConfig(): boolean {
if (!this.config.apiKey || this.config.apiKey.trim() === "") {
console.error("API key is missing");
return false;
}
if (!this.config.model || this.config.model.trim() === "") {
console.error("Model name is missing");
return false;
}
return true;
}
}
@@ -2,7 +2,7 @@
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "ef7e0614-de5a-4407-b6bf-35a8854324b8",
"uuid": "e7ea0d42-895c-4a18-a177-782ddb6ddefe",
"files": [],
"subMetas": {},
"userData": {}
+239 -239
View File
@@ -1,269 +1,269 @@
// 首先加载 polyfills 以确保兼容性
import "../utils/polyfills";
import { GoogleGenAI } from "@google/genai";
import { RoleConfig } from "./RoleConfig";
import { ChatHistoryManager } from "./ChatHistoryManager";
import { ApiConfig } from "../config/ApiConfig";
import { RoleConfigLoader } from "./RoleConfigLoader";
import { ChatHistoryManager } from "../manager/ChatHistoryManager";
import { ApiConfig } from "./ApiConfigLoader";
import { ErrorHandler, ErrorType } from "../utils/ErrorHandler";
/**
* AI聊天服务类
*
*
* 基于Google Gemini API实现的多角色聊天系统,支持:
* - 多个独立的聊天实例管理
* - 每个角色拥有独立的对话上下文和历史记录
* - 本地聊天历史存储和加载
* - 动态角色配置和切换
*
*
* @example
* ```typescript
* const chatService = ChatAIService.Instance;
* chatService.setCurrentRole(10001);
* const response = await chatService.sendMessage(10001, "Hello");
* ```
*
*
* @author AI Chat System
* @version 2.0.0
*/
export class ChatAIService {
private static _instance: ChatAIService;
private ai: GoogleGenAI;
private chatInstances: Map<number, any> = new Map();
private currentRoleId: number | null = null;
private static _instance: ChatAIService;
private ai: GoogleGenAI;
private chatInstances: Map<number, any> = new Map();
private currentRoleId: number | null = null;
private constructor() {
const config = ApiConfig.Instance.getAIConfig();
// 验证配置
if (!ApiConfig.Instance.validateConfig()) {
ErrorHandler.Instance.handleError(
new Error("AI配置验证失败"),
ErrorType.CONFIG_ERROR,
{ config },
true
);
throw new Error("AI服务初始化失败:配置无效");
}
try {
this.ai = new GoogleGenAI({ apiKey: config.apiKey });
} catch (error) {
ErrorHandler.Instance.handleError(
error as Error,
ErrorType.API_ERROR,
{ config: { ...config, apiKey: "***" } }, // 隐藏API密钥
true
);
throw error;
}
private constructor() {
const config = ApiConfig.Instance.getAIConfig();
// 验证配置
if (!ApiConfig.Instance.validateConfig()) {
ErrorHandler.Instance.handleError(
new Error("AI配置验证失败"),
ErrorType.CONFIG_ERROR,
{ config },
true
);
throw new Error("AI服务初始化失败:配置无效");
}
/**
* 获取ChatAIService的单例实例
*
* @returns {ChatAIService} 聊天服务实例
* @static
*/
public static get Instance(): ChatAIService {
if (!this._instance) {
this._instance = new ChatAIService();
}
return this._instance;
try {
this.ai = new GoogleGenAI({ apiKey: config.apiKey });
} catch (error) {
ErrorHandler.Instance.handleError(
error as Error,
ErrorType.API_ERROR,
{ config: { ...config, apiKey: "***" } }, // 隐藏API密钥
true
);
throw error;
}
}
/**
* 获取ChatAIService的单例实例
*
* @returns {ChatAIService} 聊天服务实例
* @static
*/
public static get Instance(): ChatAIService {
if (!this._instance) {
this._instance = new ChatAIService();
}
return this._instance;
}
/**
* 创建或获取指定角色的聊天实例
* @param roleId 角色ID
*/
private createOrGetChat(roleId: number): any {
if (!this.chatInstances.has(roleId)) {
const systemInstruction = RoleConfigLoader.getRoleInstruction(roleId);
const config = ApiConfig.Instance.getAIConfig();
// 从本地加载历史记录
const savedHistory = ChatHistoryManager.Instance.loadHistory(roleId);
let chat;
if (savedHistory && savedHistory.length > 0) {
chat = this.ai.chats.create({
model: config.model,
config: {
temperature: config.temperature,
systemInstruction: systemInstruction,
},
history: savedHistory,
});
} else {
chat = this.ai.chats.create({
model: config.model,
config: {
temperature: config.temperature,
systemInstruction: systemInstruction,
},
});
}
this.chatInstances.set(roleId, chat);
if (savedHistory.length > 0) {
console.log(
`Loaded ${savedHistory.length} history messages for role ${roleId}`
);
} else {
console.log(`Created new chat instance for role ${roleId}`);
}
}
return this.chatInstances.get(roleId);
}
/**
* 设置当前活动的角色ID
* @param roleId 角色ID
*/
public setCurrentRole(roleId: number): void {
this.currentRoleId = roleId;
// 预创建聊天实例
this.createOrGetChat(roleId);
}
/**
* 获取当前角色ID
*/
public getCurrentRoleId(): number | null {
return this.currentRoleId;
}
/**
* 向指定角色发送消息并获取AI回复
*
* @param {number} roleId - 角色ID,用于区分不同的聊天实例
* @param {string} message - 用户发送的消息内容
* @returns {Promise<string>} AI的回复消息,如果发生错误返回null
*
* @example
* ```typescript
* const response = await chatService.sendMessage(10001, "你好");
* console.log(response); // AI的回复
* ```
*/
public async sendMessage(roleId: number, message: string): Promise<string> {
// 输入验证
if (!roleId || roleId <= 0) {
ErrorHandler.Instance.handleValidationError(
"roleId",
"角色ID必须是正整数",
roleId
);
return null;
}
/**
* 创建或获取指定角色的聊天实例
* @param roleId 角色ID
*/
private createOrGetChat(roleId: number): any {
if (!this.chatInstances.has(roleId)) {
const systemInstruction = RoleConfig.getRoleInstruction(roleId);
const config = ApiConfig.Instance.getAIConfig();
// 从本地加载历史记录
const savedHistory = ChatHistoryManager.Instance.loadHistory(roleId);
let chat;
if(savedHistory && savedHistory.length > 0) {
chat = this.ai.chats.create({
model: config.model,
config: {
temperature: config.temperature,
systemInstruction: systemInstruction
},
history: savedHistory
});
}else{
chat = this.ai.chats.create({
model: config.model,
config: {
temperature: config.temperature,
systemInstruction: systemInstruction
}
});
}
this.chatInstances.set(roleId, chat);
if (savedHistory.length > 0) {
console.log(`Loaded ${savedHistory.length} history messages for role ${roleId}`);
} else {
console.log(`Created new chat instance for role ${roleId}`);
}
}
return this.chatInstances.get(roleId);
if (!message || message.trim() === "") {
ErrorHandler.Instance.handleValidationError(
"message",
"消息内容不能为空",
message
);
return null;
}
/**
* 设置当前活动的角色ID
* @param roleId 角色ID
*/
public setCurrentRole(roleId: number): void {
this.currentRoleId = roleId;
// 预创建聊天实例
this.createOrGetChat(roleId);
}
try {
const chat = this.createOrGetChat(roleId);
const response = await chat.sendMessage({
message: message.trim(),
});
/**
* 获取当前角色ID
*/
public getCurrentRoleId(): number | null {
return this.currentRoleId;
}
/**
* 向指定角色发送消息并获取AI回复
*
* @param {number} roleId - 角色ID,用于区分不同的聊天实例
* @param {string} message - 用户发送的消息内容
* @returns {Promise<string>} AI的回复消息,如果发生错误返回null
*
* @example
* ```typescript
* const response = await chatService.sendMessage(10001, "你好");
* console.log(response); // AI的回复
* ```
*/
public async sendMessage(roleId: number, message: string): Promise<string> {
// 输入验证
if (!roleId || roleId <= 0) {
ErrorHandler.Instance.handleValidationError(
"roleId",
"角色ID必须是正整数",
roleId
);
return null;
}
if (!message || message.trim() === "") {
ErrorHandler.Instance.handleValidationError(
"message",
"消息内容不能为空",
message
);
return null;
}
if (response && response.text) {
console.log(`Response from role ${roleId}:`, response.text);
try {
const chat = this.createOrGetChat(roleId);
const response = await chat.sendMessage({
message: message.trim()
});
// 保存用户消息
ChatHistoryManager.Instance.appendMessage(roleId, {
role: "user",
parts: [{ text: message }],
});
if (response && response.text) {
console.log(`Response from role ${roleId}:`, response.text);
try {
// 保存用户消息
ChatHistoryManager.Instance.appendMessage(roleId, {
role: "user",
parts: [{ text: message }]
});
// 保存AI回复
ChatHistoryManager.Instance.appendMessage(roleId, {
role: "model",
parts: [{ text: response.text }]
});
} catch (storageError) {
ErrorHandler.Instance.handleError(
storageError as Error,
ErrorType.STORAGE_ERROR,
{ roleId, message: message.substring(0, 100) },
false
);
// 即使存储失败,也返回AI回复
}
return response.text;
} else {
const warningMsg = `AI返回了空响应 (角色ID: ${roleId})`;
ErrorHandler.Instance.handleError(
new Error(warningMsg),
ErrorType.API_ERROR,
{ roleId, message, response },
true
);
return null;
}
} catch (error) {
ErrorHandler.Instance.handleApiError(
error,
"sendMessage",
{ roleId, message: message.substring(0, 100) + "..." }
);
return null;
// 保存AI回复
ChatHistoryManager.Instance.appendMessage(roleId, {
role: "model",
parts: [{ text: response.text }],
});
} catch (storageError) {
ErrorHandler.Instance.handleError(
storageError as Error,
ErrorType.STORAGE_ERROR,
{ roleId, message: message.substring(0, 100) },
false
);
// 即使存储失败,也返回AI回复
}
}
/**
* 清除指定角色的聊天历史
* @param roleId 角色ID
*/
public clearChatHistory(roleId: number): void {
if (this.chatInstances.has(roleId)) {
this.chatInstances.delete(roleId);
}
// 清除本地存储的历史
ChatHistoryManager.Instance.clearHistory(roleId);
console.log(`Cleared chat history for role ${roleId}`);
return response.text;
} else {
const warningMsg = `AI返回了空响应 (角色ID: ${roleId})`;
ErrorHandler.Instance.handleError(
new Error(warningMsg),
ErrorType.API_ERROR,
{ roleId, message, response },
true
);
return null;
}
} catch (error) {
ErrorHandler.Instance.handleApiError(error, "sendMessage", {
roleId,
message: message.substring(0, 100) + "...",
});
return null;
}
}
/**
* 清除所有聊天历史
*/
public clearAllChatHistory(): void {
this.chatInstances.clear();
// 清除本地存储的所有历史
ChatHistoryManager.Instance.clearAllHistory();
console.log("Cleared all chat histories");
/**
* 清除指定角色的聊天历史
* @param roleId 角色ID
*/
public clearChatHistory(roleId: number): void {
if (this.chatInstances.has(roleId)) {
this.chatInstances.delete(roleId);
}
// 清除本地存储的历史
ChatHistoryManager.Instance.clearHistory(roleId);
console.log(`Cleared chat history for role ${roleId}`);
}
/**
* 获取当前活跃的聊天实例数量
*/
public getActiveChatCount(): number {
return this.chatInstances.size;
}
/**
* 清除所有聊天历史
*/
public clearAllChatHistory(): void {
this.chatInstances.clear();
// 清除本地存储的所有历史
ChatHistoryManager.Instance.clearAllHistory();
console.log("Cleared all chat histories");
}
/**
* 兼容旧接口的Post方法
* @deprecated 请使用sendMessage方法,此方法将在下个版本中移除
*/
public async Post(data: GPTRequest): Promise<string> {
console.warn("Post方法已废弃,请使用sendMessage方法");
const roleId = this.currentRoleId || 10001; // 默认使用第一个角色
const message = data.messages[0]?.content || "";
return this.sendMessage(roleId, message);
}
/**
* 获取当前活跃的聊天实例数量
*/
public getActiveChatCount(): number {
return this.chatInstances.size;
}
/**
* 兼容旧接口的Post方法
* @deprecated 请使用sendMessage方法,此方法将在下个版本中移除
*/
public async Post(data: GPTRequest): Promise<string> {
console.warn("Post方法已废弃,请使用sendMessage方法");
const roleId = this.currentRoleId || 10001; // 默认使用第一个角色
const message = data.messages[0]?.content || "";
return this.sendMessage(roleId, message);
}
}
// 请求数据类型定义
export interface GPTRequest {
model: string;
messages: { role: string; content: string }[];
temperature: number;
id: string;
model: string;
messages: { role: string; content: string }[];
temperature: number;
id: string;
}
// 兼容旧名称(已废弃,建议使用 GPTRequest
@@ -273,21 +273,21 @@ export type GPTResquest = GPTRequest;
// 响应数据类型定义(当前未使用,预留用于未来API调用统计)
/** @deprecated 当前未使用,考虑移除或实现API统计功能时使用 */
export interface GPTResult {
id: string;
object: string;
created: number;
model: string;
usage: {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
id: string;
object: string;
created: number;
model: string;
usage: {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
};
choices: {
message: {
role: string;
content: string;
};
choices: {
message: {
role: string;
content: string;
};
finish_reason: string;
index: number;
}[];
}
finish_reason: string;
index: number;
}[];
}
@@ -1,212 +0,0 @@
import { sys } from "cc";
export interface ChatMessage {
role: "user" | "model";
parts: { text: string }[];
timestamp?: number;
}
export interface ChatHistory {
roleId: number;
messages: ChatMessage[];
createdAt: number;
updatedAt: number;
}
/**
* 聊天历史管理器
* 负责聊天记录的本地存储、加载和管理
*/
export class ChatHistoryManager {
private static _instance: ChatHistoryManager;
public static get Instance(): ChatHistoryManager {
if (!this._instance) {
this._instance = new ChatHistoryManager();
}
return this._instance;
}
private constructor() {}
/**
* 保存聊天历史到本地
* @param roleId 角色ID
* @param messages 消息列表
*/
public saveHistory(roleId: number, messages: ChatMessage[]): void {
const key = `chat_history_${roleId}`;
try {
const history: ChatHistory = {
roleId: roleId,
messages: messages,
createdAt: Date.now(),
updatedAt: Date.now()
};
sys.localStorage.setItem(key, JSON.stringify(history));
console.log(`Saved chat history for role ${roleId}, ${messages.length} messages`);
} catch (error) {
console.error(`Failed to save chat history for role ${roleId}:`, error);
}
}
/**
* 从本地加载聊天历史
* @param roleId 角色ID
* @returns 消息列表,如果没有历史记录则返回空数组
*/
public loadHistory(roleId: number): ChatMessage[] {
const key = `chat_history_${roleId}`;
try {
const data = sys.localStorage.getItem(key);
if (data) {
const history: ChatHistory = JSON.parse(data);
console.log(`Loaded chat history for role ${roleId}: ${history.messages.length} messages`);
return history.messages;
}
} catch (error) {
console.error(`Failed to load chat history for role ${roleId}:`, error);
// 如果数据损坏,清除错误的数据
this.clearHistory(roleId);
}
return [];
}
/**
* 清除指定角色的聊天历史
* @param roleId 角色ID
*/
public clearHistory(roleId: number): void {
const key = `chat_history_${roleId}`;
sys.localStorage.removeItem(key);
console.log(`Cleared chat history for role ${roleId}`);
}
/**
* 追加消息到历史记录
* @param roleId 角色ID
* @param message 消息对象
*/
public appendMessage(roleId: number, message: ChatMessage): void {
const history = this.loadHistory(roleId);
// 添加时间戳
message.timestamp = Date.now();
history.push(message);
// 限制历史长度,保留最近100条消息
if (history.length > 100) {
history.splice(0, history.length - 100);
console.log(`Trimmed chat history for role ${roleId} to 100 messages`);
}
this.saveHistory(roleId, history);
}
/**
* 获取指定角色的消息数量
* @param roleId 角色ID
* @returns 消息数量
*/
public getMessageCount(roleId: number): number {
const history = this.loadHistory(roleId);
return history.length;
}
/**
* 获取最近的N条消息
* @param roleId 角色ID
* @param count 消息数量
* @returns 最近的消息列表
*/
public getRecentMessages(roleId: number, count: number = 10): ChatMessage[] {
const history = this.loadHistory(roleId);
return history.slice(-count);
}
/**
* 清除所有聊天历史
*/
public clearAllHistory(): void {
// 查找所有chat_history_开头的key
const keysToRemove: string[] = [];
for (let i = 0; i < sys.localStorage.length; i++) {
const key = sys.localStorage.key(i);
if (key && key.startsWith('chat_history_')) {
keysToRemove.push(key);
}
}
// 删除找到的所有聊天历史
keysToRemove.forEach(key => {
sys.localStorage.removeItem(key);
});
console.log(`Cleared all chat histories, ${keysToRemove.length} records removed`);
}
/**
* 获取所有有历史记录的角色ID列表
* @returns 角色ID数组
*/
public getAllHistoryRoleIds(): number[] {
const roleIds: number[] = [];
for (let i = 0; i < sys.localStorage.length; i++) {
const key = sys.localStorage.key(i);
if (key && key.startsWith('chat_history_')) {
const roleId = parseInt(key.replace('chat_history_', ''));
if (!isNaN(roleId)) {
roleIds.push(roleId);
}
}
}
return roleIds;
}
/**
* 准备上传到远程服务器(预留接口)
* @param roleId 角色ID
*/
public async syncToRemote(roleId: number): Promise<void> {
const history = this.loadHistory(roleId);
if (history.length === 0) {
console.log(`No history to sync for role ${roleId}`);
return;
}
try {
// TODO: 调用 HttpUnit.ins.api 上传到服务器
// await HttpUnit.ins.api("chat/save_history", {
// role_id: roleId,
// messages: history
// }, "POST");
console.log(`Ready to sync ${history.length} messages for role ${roleId} to remote server`);
} catch (error) {
console.error(`Failed to sync history for role ${roleId}:`, error);
}
}
/**
* 从远程服务器下载历史(预留接口)
* @param roleId 角色ID
*/
public async syncFromRemote(roleId: number): Promise<void> {
try {
// TODO: 调用 HttpUnit.ins.api 从服务器获取历史
// const response = await HttpUnit.ins.api("chat/get_history", {
// role_id: roleId
// }, "GET");
// if (response && response.messages) {
// this.saveHistory(roleId, response.messages);
// console.log(`Synced ${response.messages.length} messages from remote for role ${roleId}`);
// }
console.log(`Ready to sync history from remote server for role ${roleId}`);
} catch (error) {
console.error(`Failed to sync from remote for role ${roleId}:`, error);
}
}
}
-46
View File
@@ -1,46 +0,0 @@
import System_Instruction from "../config/SystemPrompts";
/**
* 角色配置映射
* 将角色ID映射到对应的System Instruction
*/
export class RoleConfig {
private static roleMap: Map<number, string> = new Map([
[10001, System_Instruction.Role_1], // Anaya Kapoor - 成熟魅惑型
[10002, System_Instruction.Role_2], // Meher Joshi - 猫咪性格
[10003, System_Instruction.Role_3], // Sana Reddy - 小狗性格
]);
/**
* 根据角色ID获取对应的System Instruction
* @param roleId 角色ID
* @returns System Instruction字符串,如果未找到则返回默认Role_1
*/
public static getRoleInstruction(roleId: number): string {
return this.roleMap.get(roleId) || System_Instruction.Role_1;
}
/**
* 添加或更新角色配置
* @param roleId 角色ID
* @param instruction System Instruction内容
*/
public static setRoleInstruction(roleId: number, instruction: string): void {
this.roleMap.set(roleId, instruction);
}
/**
* 检查角色是否存在配置
* @param roleId 角色ID
*/
public static hasRole(roleId: number): boolean {
return this.roleMap.has(roleId);
}
/**
* 获取所有配置的角色ID
*/
public static getAllRoleIds(): number[] {
return Array.from(this.roleMap.keys());
}
}
@@ -0,0 +1,36 @@
import { AiCharacter } from "../../schema/schema";
import ConfigManager from "../manager/ConfigManager";
/**
* 角色配置映射
* 将角色ID映射到对应的System Instruction
*/
export class RoleConfigLoader {
/**
* 根据角色ID获取对应的System Instruction
* @param roleId 角色ID
* @returns System Instruction字符串,如果未找到则返回默认Role_1
*/
public static getRoleInstruction(roleId: number): string {
const AiCharacter = ConfigManager.tables.TbAiCharacters.get(roleId);
return AiCharacter
? AiCharacter.systemInstruction
: ConfigManager.tables.TbAiCharacters.get(10001).systemInstruction;
}
/**
* 检查角色是否存在配置
* @param roleId 角色ID
*/
public static hasRole(roleId: number): boolean {
return ConfigManager.tables.TbAiCharacters.get(roleId) != null;
}
/**
* 获取所有配置的角色ID
*/
public static getAllRoles(): AiCharacter[] {
return ConfigManager.tables.TbAiCharacters.getDataList();
}
}
@@ -2,7 +2,7 @@
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "635087af-dfed-4857-bb82-9f7b296b3ee1",
"uuid": "93d02d5d-af10-49ca-98ce-7d9e09568de7",
"files": [],
"subMetas": {},
"userData": {}