mirror of
https://github.com/Jerryplusy/crystelf-plugin.git
synced 2025-12-05 15:41:56 +00:00
feat:优化会话管理
This commit is contained in:
parent
358b663388
commit
356233263c
@ -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}`);
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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}`;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user