diff --git a/apps/ai.js b/apps/ai.js index 6a013cf..9b65037 100644 --- a/apps/ai.js +++ b/apps/ai.js @@ -82,6 +82,8 @@ async function index(e) { if (!userMessage) { return; } + const adapter = await YunzaiUtils.getAdapter(e); + await Message.emojiLike(e,e.message_id,128064,e.group_id,adapter);//👀 const result = await processMessage(userMessage, e, aiConfig); if (result && result.length > 0) { // TODO 优化流式输出 @@ -200,7 +202,7 @@ async function handleMixMode(userMessage, e, aiConfig) { async function callAiForResponse(userMessage, e, aiConfig) { try { //创建session - const session = SessionManager.createOrGetSession(e.group_id, e.user_id); + const session = SessionManager.createOrGetSession(e.group_id, e.user_id,e); if (!session) { logger.info( `[crystelf-ai] 群${e.group_id} , 用户${e.user_id}无法创建session,请检查是否聊天频繁` @@ -234,6 +236,7 @@ async function callAiForResponse(userMessage, e, aiConfig) { { role: 'assistant', content: aiResult.response }, ]; SessionManager.updateChatHistory(e.group_id, newChatHistory); + SessionManager.deactivateSession(e.group_id,e.user_id); return processedResponse; } catch (error) { logger.error(`[crystelf-ai] AI调用失败: ${error.message}`); diff --git a/lib/ai/sessionManager.js b/lib/ai/sessionManager.js index 165a1a4..6d5b4e3 100644 --- a/lib/ai/sessionManager.js +++ b/lib/ai/sessionManager.js @@ -1,124 +1,208 @@ import ConfigControl from "../config/configControl.js"; -/** - * Session管理器 - */ +//会话管理 class SessionManager { constructor() { - this.sessions = new Map(); // 存储群聊ID到session的映射 - this.maxSessions = 10; // 默认最大sessions数量 - this.userSessions = new Map(); // 存储用户ID到群聊ID的映射,确保一个群只有一个用户聊天 + this.sessions = new Map(); + this.groupHistories = new Map(); + this.maxSessions = 10; } - // TODO 优化session处理逻辑,主人不清理session等 + async init() { try { - const config = await ConfigControl.get('ai'); + const config = await ConfigControl.get("ai"); this.maxSessions = config?.maxSessions || 10; } catch (error) { - logger.error(`[crystelf-ai] 初始化失败: ${error.message}`); + logger.error(`[crystelf-ai] SessionManager 初始化失败: ${error.message}`); } } /** - * 创建/获取session - * @param groupId 群聊id - * @param userId 用户id - * @returns {{groupId, userId, chatHistory: *[], memory: *[], createdAt: number, lastActive: number}|any|null} + * 创建或获取会话 + * @param groupId + * @param userId + * @param e + * @returns {*|{groupId, userId, isMaster: boolean, chatHistory: null, memory: *[], createdAt: number, lastActive: number, active: boolean}|null} */ - createOrGetSession(groupId, userId) { - //是否已有该群聊的session - if (this.sessions.has(groupId)) { - const session = this.sessions.get(groupId); - //当前用户不是session的拥有者,返回null - if (session.userId !== userId) { + createOrGetSession(groupId, userId, e) { + let groupSessions = this.sessions.get(groupId); + if (!groupSessions) { + groupSessions = new Map(); + this.sessions.set(groupId, groupSessions); + } + + let activeSession = null; + for (const s of groupSessions.values()) { + if (s.active) { + activeSession = s; + break; + } + } + + if (activeSession) { + if (activeSession.userId === userId) { + activeSession.lastActive = Date.now(); + return activeSession; + } + if (!e?.isMaster) { + logger.info(`[crystelf-ai] 群${groupId}存在活跃session(${activeSession.userId}),拒绝${userId}创建新会话`); return null; } - //更新最后活动时间 - session.lastActive = Date.now(); - return session; + activeSession.active = false; } - // 检查是否达到最大sessions数量 - if (this.sessions.size >= this.maxSessions) { - this.cleanOldestSession(); - } - const session = { - groupId, - userId, - chatHistory: [], - memory: [], - createdAt: Date.now(), - lastActive: Date.now() - }; - this.sessions.set(groupId, session); - logger.info(`[crystelf-ai] 创建新session: 群${groupId}, 用户${userId}`); - return session; - } - - /** - * 清理最旧的session - */ - cleanOldestSession() { - let oldestSession = null; - let oldestTime = Date.now(); - for (const [groupId, session] of this.sessions) { - if (session.lastActive < oldestTime) { - oldestTime = session.lastActive; - oldestSession = groupId; + if (this.totalActiveSessionCount() >= this.maxSessions) { + if (e.isMaster) { + this.cleanOldestActiveSession(); + } else { + logger.info('[crystelf-ai] 全局活跃session达上限..'); + return null; } } - if (oldestSession) { - this.sessions.delete(oldestSession); - logger.info(`[crystelf-ai] 清理最旧session: 群${oldestSession}`); + + let userSession = groupSessions.get(userId); + if (!userSession) { + userSession = { + groupId, + userId, + isMaster: !!e?.isMaster, + chatHistory: null, + memory: [], + createdAt: Date.now(), + lastActive: Date.now(), + active: true, + }; + groupSessions.set(userId, userSession); + logger.info(`[crystelf-ai] 创建新session: 群${groupId}, 用户${userId}${userSession.isMaster ? "(master)" : ""}`); + } else { + userSession.active = true; + userSession.lastActive = Date.now(); + logger.info(`[crystelf-ai] 重新激活session: 群${groupId}, 用户${userId}`); + } + + for (const s of groupSessions.values()) { + if (s.userId !== userId) s.active = false; + } + if (!this.groupHistories.has(groupId)) { + this.groupHistories.set(groupId, []); + } + userSession.chatHistory = this.groupHistories.get(groupId); + + return userSession; + } + + /** + * 标记一个会话为不活跃 + * @param groupId + * @param userId + */ + deactivateSession(groupId, userId) { + const session = this.sessions.get(groupId)?.get(userId); + if (session) { + session.active = false; + logger.debug(`[crystelf-ai] 标记session不活跃: 群${groupId}, 用户${userId}`); } } /** - * 获取session + * 清理最老会话 + */ + cleanOldestActiveSession() { + let oldest = null; + let oldestTime = Date.now(); + for (const [groupId, groupSessions] of this.sessions) { + for (const [userId, session] of groupSessions) { + if (!session.active || session.isMaster) continue; + if (session.lastActive < oldestTime) { + oldestTime = session.lastActive; + oldest = { groupId, userId }; + } + } + } + + if (oldest) { + const groupSessions = this.sessions.get(oldest.groupId); + groupSessions?.delete(oldest.userId); + logger.info(`[crystelf-ai] 清理最旧活跃session: 群${oldest.groupId}, 用户${oldest.userId}`); + } + } + + /** + * 获取会话 * @param groupId - * @returns {any|null} + * @returns {{active}|any|null} */ getSession(groupId) { - return this.sessions.get(groupId) || null; + const groupSessions = this.sessions.get(groupId); + if (!groupSessions) return null; + for (const s of groupSessions.values()) { + if (s.active) return s; + } + return null; } - /** - * 删除session - * @param groupId - */ - removeSession(groupId) { - if (this.sessions.has(groupId)) { + removeSession(groupId, e) { + const groupSessions = this.sessions.get(groupId); + if (!groupSessions) return; + for (const [userId, session] of groupSessions) { + if (session.isMaster && !e?.isMaster) continue; + groupSessions.delete(userId); + logger.info(`[crystelf-ai] 删除session: 群${groupId}, 用户${userId}`); + } + if (groupSessions.size === 0) { this.sessions.delete(groupId); - logger.info(`[crystelf-ai] 删除session: 群${groupId}`); + this.groupHistories.delete(groupId); } } /** - * 更新聊天历史 + * 更新聊天记录 * @param groupId * @param chatHistory */ updateChatHistory(groupId, chatHistory) { - const session = this.sessions.get(groupId); + if (this.groupHistories.has(groupId)) { + this.groupHistories.set(groupId, chatHistory); + } else { + this.groupHistories.set(groupId, chatHistory); + } + const session = this.getSession(groupId); if (session) { - session.chatHistory = chatHistory; session.lastActive = Date.now(); + session.chatHistory = this.groupHistories.get(groupId); } } - /** - * 清理超时的sessions - * @param {number} timeout 超时时间(毫秒) - */ - cleanTimeoutSessions(timeout = 30 * 60 * 1000) { // 默认30分钟 + cleanTimeoutSessions(timeout = 30 * 60 * 1000) { const now = Date.now(); - for (const [groupId, session] of this.sessions) { - if (now - session.lastActive > timeout) { + for (const [groupId, groupSessions] of this.sessions) { + for (const [userId, session] of groupSessions) { + if (session.isMaster) continue; + if (now - session.lastActive > timeout) { + groupSessions.delete(userId); + logger.info(`[crystelf-ai] 清理超时session: 群${groupId}, 用户${userId}`); + } + } + if (groupSessions.size === 0) { this.sessions.delete(groupId); - logger.info(`[crystelf-ai] 清理超时session: 群${groupId}`); + this.groupHistories.delete(groupId); } } } + + totalSessionCount() { + let count = 0; + for (const g of this.sessions.values()) count += g.size; + return count; + } + + totalActiveSessionCount() { + let count = 0; + for (const g of this.sessions.values()) { + for (const s of g.values()) if (s.active) count++; + } + return count; + } } export default new SessionManager(); diff --git a/lib/core/meme.js b/lib/core/meme.js index 6e334b3..879f319 100644 --- a/lib/core/meme.js +++ b/lib/core/meme.js @@ -12,7 +12,7 @@ const Meme = { const coreUrl = coreConfig?.coreUrl; const token = coreConfig?.token; //logger.info(`${coreUrl}/api/meme`); - return `${coreUrl}/api/meme?token=${token}?character=${character}&status=${status}`; + return `${coreUrl}/api/meme?token=${token}&character=${character}&status=${status}`; }, };