Summary: - Fix Prompt list API response schema missing activeVersion and draftVersion fields - Fastify was filtering out undefined schema fields, causing version columns to show empty - Add detailed diagnostic logging for Prompt debug mode troubleshooting - Verify debug mode works correctly (DRAFT version is used when debug enabled) Changes: - backend/src/common/prompt/prompt.routes.ts: Add activeVersion and draftVersion to response schema - backend/src/common/prompt/prompt.service.ts: Add diagnostic logs for setDebugMode and get methods - PKB module: Various authentication and document handling fixes from previous session Tested: Debug mode verified working - v2 DRAFT version correctly loaded when debug enabled
188 lines
4.2 KiB
TypeScript
188 lines
4.2 KiB
TypeScript
/**
|
||
* SessionMemory - 会话记忆管理器(内存版)
|
||
*
|
||
* 功能:
|
||
* - 存储用户最近3轮对话
|
||
* - 提供上下文查询
|
||
* - 自动清理过期会话(1小时)
|
||
*
|
||
* 设计原则:
|
||
* - Node.js内存存储(MVP阶段,无需数据库)
|
||
* - 单例模式(全局共享)
|
||
* - 轻量级(<100行代码)
|
||
*/
|
||
|
||
import { logger } from '../../../common/logging/index.js';
|
||
|
||
/**
|
||
* 对话消息
|
||
*/
|
||
export interface ConversationMessage {
|
||
role: 'user' | 'assistant';
|
||
content: string;
|
||
timestamp: Date;
|
||
}
|
||
|
||
/**
|
||
* 对话历史
|
||
*/
|
||
export interface ConversationHistory {
|
||
userId: string;
|
||
messages: ConversationMessage[];
|
||
createdAt: Date;
|
||
updatedAt: Date;
|
||
}
|
||
|
||
/**
|
||
* 会话记忆管理器
|
||
*/
|
||
export class SessionMemory {
|
||
// 内存存储:{ userId: ConversationHistory }
|
||
private sessions: Map<string, ConversationHistory> = new Map();
|
||
private readonly MAX_HISTORY = 3; // 只保留最近3轮(6条消息)
|
||
private readonly SESSION_TIMEOUT = 3600000; // 1小时(毫秒)
|
||
|
||
/**
|
||
* 添加对话记录
|
||
*/
|
||
addMessage(userId: string, role: 'user' | 'assistant', content: string): void {
|
||
if (!this.sessions.has(userId)) {
|
||
this.sessions.set(userId, {
|
||
userId,
|
||
messages: [],
|
||
createdAt: new Date(),
|
||
updatedAt: new Date(),
|
||
});
|
||
logger.debug('[SessionMemory] 创建新会话', { userId });
|
||
}
|
||
|
||
const session = this.sessions.get(userId)!;
|
||
session.messages.push({
|
||
role,
|
||
content,
|
||
timestamp: new Date(),
|
||
});
|
||
|
||
// 只保留最近3轮(6条消息:3个user + 3个assistant)
|
||
if (session.messages.length > this.MAX_HISTORY * 2) {
|
||
const removed = session.messages.length - this.MAX_HISTORY * 2;
|
||
session.messages = session.messages.slice(-this.MAX_HISTORY * 2);
|
||
logger.debug('[SessionMemory] 清理历史消息', { userId, removedCount: removed });
|
||
}
|
||
|
||
session.updatedAt = new Date();
|
||
logger.debug('[SessionMemory] 添加消息', {
|
||
userId,
|
||
role,
|
||
messageLength: content.length,
|
||
totalMessages: session.messages.length,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取用户对话历史(最近N轮)
|
||
*/
|
||
getHistory(userId: string, maxTurns: number = 3): ConversationMessage[] {
|
||
const session = this.sessions.get(userId);
|
||
if (!session) {
|
||
return [];
|
||
}
|
||
|
||
// 返回最近N轮(2N条消息)
|
||
const maxMessages = maxTurns * 2;
|
||
return session.messages.length > maxMessages
|
||
? session.messages.slice(-maxMessages)
|
||
: session.messages;
|
||
}
|
||
|
||
/**
|
||
* 获取用户上下文(格式化为字符串,用于LLM Prompt)
|
||
*/
|
||
getContext(userId: string): string {
|
||
const history = this.getHistory(userId, 2); // 只取最近2轮
|
||
if (history.length === 0) {
|
||
return '';
|
||
}
|
||
|
||
return history
|
||
.map((m) => `${m.role === 'user' ? 'PI' : 'Assistant'}: ${m.content}`)
|
||
.join('\n');
|
||
}
|
||
|
||
/**
|
||
* 清除用户会话
|
||
*/
|
||
clearSession(userId: string): void {
|
||
const existed = this.sessions.delete(userId);
|
||
if (existed) {
|
||
logger.info('[SessionMemory] 清除会话', { userId });
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清理过期会话(超过1小时未使用)
|
||
*/
|
||
cleanupExpiredSessions(): void {
|
||
const now = Date.now();
|
||
let cleanedCount = 0;
|
||
|
||
for (const [userId, session] of this.sessions.entries()) {
|
||
if (now - session.updatedAt.getTime() > this.SESSION_TIMEOUT) {
|
||
this.sessions.delete(userId);
|
||
cleanedCount++;
|
||
}
|
||
}
|
||
|
||
if (cleanedCount > 0) {
|
||
logger.info('[SessionMemory] 清理过期会话', { cleanedCount });
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取统计信息(用于监控)
|
||
*/
|
||
getStats() {
|
||
return {
|
||
totalSessions: this.sessions.size,
|
||
sessions: Array.from(this.sessions.entries()).map(([userId, session]) => ({
|
||
userId,
|
||
messageCount: session.messages.length,
|
||
createdAt: session.createdAt,
|
||
updatedAt: session.updatedAt,
|
||
})),
|
||
};
|
||
}
|
||
}
|
||
|
||
// 全局单例
|
||
export const sessionMemory = new SessionMemory();
|
||
|
||
// 定时清理过期会话(每小时)
|
||
setInterval(() => {
|
||
sessionMemory.cleanupExpiredSessions();
|
||
}, 3600000);
|
||
|
||
logger.info('[SessionMemory] 会话记忆管理器已启动', {
|
||
maxHistory: 3,
|
||
timeout: '1小时',
|
||
});
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|