feat:优化会话管理

This commit is contained in:
Jerry 2025-10-18 10:03:34 +08:00
parent 358b663388
commit 356233263c
3 changed files with 162 additions and 75 deletions

View File

@ -82,6 +82,8 @@ async function index(e) {
if (!userMessage) { if (!userMessage) {
return; 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); const result = await processMessage(userMessage, e, aiConfig);
if (result && result.length > 0) { if (result && result.length > 0) {
// TODO 优化流式输出 // TODO 优化流式输出
@ -200,7 +202,7 @@ async function handleMixMode(userMessage, e, aiConfig) {
async function callAiForResponse(userMessage, e, aiConfig) { async function callAiForResponse(userMessage, e, aiConfig) {
try { try {
//创建session //创建session
const session = SessionManager.createOrGetSession(e.group_id, e.user_id); const session = SessionManager.createOrGetSession(e.group_id, e.user_id,e);
if (!session) { if (!session) {
logger.info( logger.info(
`[crystelf-ai] 群${e.group_id} , 用户${e.user_id}无法创建session,请检查是否聊天频繁` `[crystelf-ai] 群${e.group_id} , 用户${e.user_id}无法创建session,请检查是否聊天频繁`
@ -234,6 +236,7 @@ async function callAiForResponse(userMessage, e, aiConfig) {
{ role: 'assistant', content: aiResult.response }, { role: 'assistant', content: aiResult.response },
]; ];
SessionManager.updateChatHistory(e.group_id, newChatHistory); SessionManager.updateChatHistory(e.group_id, newChatHistory);
SessionManager.deactivateSession(e.group_id,e.user_id);
return processedResponse; return processedResponse;
} catch (error) { } catch (error) {
logger.error(`[crystelf-ai] AI调用失败: ${error.message}`); logger.error(`[crystelf-ai] AI调用失败: ${error.message}`);

View File

@ -1,124 +1,208 @@
import ConfigControl from "../config/configControl.js"; import ConfigControl from "../config/configControl.js";
/** //会话管理
* Session管理器
*/
class SessionManager { class SessionManager {
constructor() { constructor() {
this.sessions = new Map(); // 存储群聊ID到session的映射 this.sessions = new Map();
this.maxSessions = 10; // 默认最大sessions数量 this.groupHistories = new Map();
this.userSessions = new Map(); // 存储用户ID到群聊ID的映射,确保一个群只有一个用户聊天 this.maxSessions = 10;
} }
// TODO 优化session处理逻辑,主人不清理session等
async init() { async init() {
try { try {
const config = await ConfigControl.get('ai'); const config = await ConfigControl.get("ai");
this.maxSessions = config?.maxSessions || 10; this.maxSessions = config?.maxSessions || 10;
} catch (error) { } catch (error) {
logger.error(`[crystelf-ai] 初始化失败: ${error.message}`); logger.error(`[crystelf-ai] SessionManager 初始化失败: ${error.message}`);
} }
} }
/** /**
* 创建/获取session * 创建或获取会话
* @param groupId 群聊id * @param groupId
* @param userId 用户id * @param userId
* @returns {{groupId, userId, chatHistory: *[], memory: *[], createdAt: number, lastActive: number}|any|null} * @param e
* @returns {*|{groupId, userId, isMaster: boolean, chatHistory: null, memory: *[], createdAt: number, lastActive: number, active: boolean}|null}
*/ */
createOrGetSession(groupId, userId) { createOrGetSession(groupId, userId, e) {
//是否已有该群聊的session let groupSessions = this.sessions.get(groupId);
if (this.sessions.has(groupId)) { if (!groupSessions) {
const session = this.sessions.get(groupId); groupSessions = new Map();
//当前用户不是session的拥有者,返回null this.sessions.set(groupId, groupSessions);
if (session.userId !== userId) { }
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; return null;
} }
//更新最后活动时间 activeSession.active = false;
session.lastActive = Date.now();
return session;
} }
// 检查是否达到最大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); if (this.totalActiveSessionCount() >= this.maxSessions) {
logger.info(`[crystelf-ai] 创建新session: 群${groupId}, 用户${userId}`); if (e.isMaster) {
return session; this.cleanOldestActiveSession();
} } else {
logger.info('[crystelf-ai] 全局活跃session达上限..');
/** return null;
* 清理最旧的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 (oldestSession) {
this.sessions.delete(oldestSession); let userSession = groupSessions.get(userId);
logger.info(`[crystelf-ai] 清理最旧session: 群${oldestSession}`); 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 * @param groupId
* @returns {any|null} * @returns {{active}|any|null}
*/ */
getSession(groupId) { 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;
} }
/** removeSession(groupId, e) {
* 删除session const groupSessions = this.sessions.get(groupId);
* @param groupId if (!groupSessions) return;
*/ for (const [userId, session] of groupSessions) {
removeSession(groupId) { if (session.isMaster && !e?.isMaster) continue;
if (this.sessions.has(groupId)) { groupSessions.delete(userId);
logger.info(`[crystelf-ai] 删除session: 群${groupId}, 用户${userId}`);
}
if (groupSessions.size === 0) {
this.sessions.delete(groupId); this.sessions.delete(groupId);
logger.info(`[crystelf-ai] 删除session: 群${groupId}`); this.groupHistories.delete(groupId);
} }
} }
/** /**
* 更新聊天历史 * 更新聊天记录
* @param groupId * @param groupId
* @param chatHistory * @param chatHistory
*/ */
updateChatHistory(groupId, 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) { if (session) {
session.chatHistory = chatHistory;
session.lastActive = Date.now(); session.lastActive = Date.now();
session.chatHistory = this.groupHistories.get(groupId);
} }
} }
/** cleanTimeoutSessions(timeout = 30 * 60 * 1000) {
* 清理超时的sessions
* @param {number} timeout 超时时间(毫秒)
*/
cleanTimeoutSessions(timeout = 30 * 60 * 1000) { // 默认30分钟
const now = Date.now(); const now = Date.now();
for (const [groupId, session] of this.sessions) { for (const [groupId, groupSessions] of this.sessions) {
if (now - session.lastActive > timeout) { 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); 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(); export default new SessionManager();

View File

@ -12,7 +12,7 @@ const Meme = {
const coreUrl = coreConfig?.coreUrl; const coreUrl = coreConfig?.coreUrl;
const token = coreConfig?.token; const token = coreConfig?.token;
//logger.info(`${coreUrl}/api/meme`); //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}`;
}, },
}; };