guanghulab/dingtalk-bot/conversation-manager.js
Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

140 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 对话上下文管理器 · conversation-manager.js · v1.0
// HoloLake · M-DINGTALK Phase 8
// DEV-004 之之 × 秋秋
// 功能:对话历史存储 · 上下文窗口管理 · 多用户隔离
//
var fs = require('fs');
var path = require('path');
// 对话存储目录
var CONVERSATION_DIR = path.join(__dirname, 'conversations');
if (!fs.existsSync(CONVERSATION_DIR)) {
fs.mkdirSync(CONVERSATION_DIR, { recursive: true });
}
// 配置
var MAX_HISTORY = 20; // 每个用户最多保留20轮对话
var MAX_CONTEXT_TOKENS = 3000; // 上下文窗口大小(估算字符数)
var EXPIRE_HOURS = 24; // 对话24小时后过期
// ===== 获取用户对话文件路径 =====
function getConversationPath(userId) {
var safeId = (userId || 'anonymous').replace(/[^a-zA-Z0-9_-]/g, '_');
return path.join(CONVERSATION_DIR, safeId + '.json');
}
// ===== 加载对话历史 =====
function loadHistory(userId) {
var filePath = getConversationPath(userId);
try {
if (!fs.existsSync(filePath)) return [];
var data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
// 过滤过期消息
var now = Date.now();
var expireMs = EXPIRE_HOURS * 60 * 60 * 1000;
var valid = (data.messages || []).filter(function(msg) {
return (now - msg.timestamp) < expireMs;
});
return valid;
} catch (err) {
console.error('[ConvMgr] 加载历史失败:', err.message);
return [];
}
}
// ===== 保存对话历史 =====
function saveHistory(userId, messages) {
var filePath = getConversationPath(userId);
try {
// 只保留最近N轮
var trimmed = messages.slice(-MAX_HISTORY * 2);
fs.writeFileSync(filePath, JSON.stringify({
userid: userId,
updatedAt: new Date().toISOString(),
messages: trimmed
}, null, 2), 'utf8');
} catch (err) {
console.error('[ConvMgr] 保存历史失败:', err.message);
}
}
// ====== 添加消息 ======
function addMessage(userId, role, content) {
var messages = loadHistory(userId);
messages.push({
role: role,
content: content,
timestamp: Date.now()
});
saveHistory(userId, messages);
return messages;
}
// ====== 构建上下文窗口给LLM用 ======
function buildContext(userId, currentMessage) {
var history = loadHistory(userId);
var contextMessages = [];
var totalChars = 0;
// 从最新的开始往回取,直到超过上下文窗口
for (var i = history.length - 1; i >= 0; i--) {
var msg = history[i];
var msgChars = (msg.content || '').length;
if (totalChars + msgChars > MAX_CONTEXT_TOKENS) break;
contextMessages.unshift({
role: msg.role,
content: msg.content
});
totalChars += msgChars;
}
// 添加当前消息
contextMessages.push({
role: 'user',
content: currentMessage
});
return contextMessages;
}
// ===== 清除用户对话历史 =====
function clearHistory(userId) {
var filePath = getConversationPath(userId);
try {
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
return true;
} catch (err) {
return false;
}
}
// ===== 获取活跃对话统计 =====
function getStats() {
try {
var files = fs.readdirSync(CONVERSATION_DIR).filter(function(f) {
return f.endsWith('.json');
});
return {
activeUsers: files.length,
conversationDir: CONVERSATION_DIR
};
} catch (err) {
return {
activeUsers: 0,
conversationDir: CONVERSATION_DIR
};
}
}
module.exports = {
loadHistory: loadHistory,
saveHistory: saveHistory,
addMessage: addMessage,
buildContext: buildContext,
clearHistory: clearHistory,
getStats: getStats
};