@zhin.js/agent 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/lib/agent.d.ts +4 -129
- package/lib/agent.d.ts.map +1 -1
- package/lib/agent.js +3 -733
- package/lib/agent.js.map +1 -1
- package/lib/compaction.d.ts +3 -129
- package/lib/compaction.d.ts.map +1 -1
- package/lib/compaction.js +2 -367
- package/lib/compaction.js.map +1 -1
- package/lib/context-manager.d.ts +3 -210
- package/lib/context-manager.d.ts.map +1 -1
- package/lib/context-manager.js +2 -310
- package/lib/context-manager.js.map +1 -1
- package/lib/conversation-memory.d.ts +3 -189
- package/lib/conversation-memory.d.ts.map +1 -1
- package/lib/conversation-memory.js +2 -616
- package/lib/conversation-memory.js.map +1 -1
- package/lib/init/create-zhin-agent.d.ts.map +1 -1
- package/lib/init/create-zhin-agent.js +1 -3
- package/lib/init/create-zhin-agent.js.map +1 -1
- package/lib/init/register-management-tools.js +3 -3
- package/lib/output.d.ts +3 -90
- package/lib/output.d.ts.map +1 -1
- package/lib/output.js +2 -173
- package/lib/output.js.map +1 -1
- package/lib/rate-limiter.d.ts +3 -35
- package/lib/rate-limiter.d.ts.map +1 -1
- package/lib/rate-limiter.js +2 -83
- package/lib/rate-limiter.js.map +1 -1
- package/lib/session.d.ts +3 -190
- package/lib/session.d.ts.map +1 -1
- package/lib/session.js +2 -462
- package/lib/session.js.map +1 -1
- package/lib/storage.d.ts +3 -65
- package/lib/storage.d.ts.map +1 -1
- package/lib/storage.js +2 -102
- package/lib/storage.js.map +1 -1
- package/lib/tone-detector.d.ts +3 -16
- package/lib/tone-detector.d.ts.map +1 -1
- package/lib/tone-detector.js +2 -69
- package/lib/tone-detector.js.map +1 -1
- package/package.json +3 -2
- package/src/agent.ts +4 -852
- package/src/compaction.ts +27 -528
- package/src/context-manager.ts +14 -439
- package/src/conversation-memory.ts +3 -814
- package/src/init/create-zhin-agent.ts +1 -3
- package/src/init/register-management-tools.ts +3 -3
- package/src/output.ts +14 -260
- package/src/rate-limiter.ts +3 -127
- package/src/session.ts +12 -565
- package/src/storage.ts +8 -134
- package/src/tone-detector.ts +3 -87
- package/tests/ai/setup.ts +20 -84
- package/tests/ai/agent.test.ts +0 -565
- package/tests/ai/context-manager.test.ts +0 -413
- package/tests/ai/conversation-memory.test.ts +0 -128
- package/tests/ai/output.test.ts +0 -128
- package/tests/ai/rate-limiter.test.ts +0 -108
- package/tests/ai/session.test.ts +0 -334
- package/tests/ai/tone-detector.test.ts +0 -80
package/lib/session.d.ts
CHANGED
|
@@ -1,193 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @zhin.js/ai
|
|
3
|
-
* 会话管理器,支持上下文记忆和数据库持久化
|
|
4
|
-
*
|
|
5
|
-
* 特性:
|
|
6
|
-
* - 数据库持久化存储(使用 Zhin 的数据库服务)
|
|
7
|
-
* - 内存缓存加速读取
|
|
8
|
-
* - 自动过期清理
|
|
9
|
-
* - 更长的上下文记忆能力
|
|
2
|
+
* Re-export from @zhin.js/ai for backward compatibility.
|
|
10
3
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* 数据库模型定义
|
|
14
|
-
*/
|
|
15
|
-
export declare const AI_SESSION_MODEL: {
|
|
16
|
-
session_id: {
|
|
17
|
-
type: "text";
|
|
18
|
-
nullable: boolean;
|
|
19
|
-
};
|
|
20
|
-
messages: {
|
|
21
|
-
type: "json";
|
|
22
|
-
default: never[];
|
|
23
|
-
};
|
|
24
|
-
config: {
|
|
25
|
-
type: "json";
|
|
26
|
-
default: {};
|
|
27
|
-
};
|
|
28
|
-
created_at: {
|
|
29
|
-
type: "integer";
|
|
30
|
-
default: number;
|
|
31
|
-
};
|
|
32
|
-
updated_at: {
|
|
33
|
-
type: "integer";
|
|
34
|
-
default: number;
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* 会话管理器接口
|
|
39
|
-
*/
|
|
40
|
-
export interface ISessionManager {
|
|
41
|
-
get(sessionId: string, config?: SessionConfig): Session | Promise<Session>;
|
|
42
|
-
has(sessionId: string): boolean | Promise<boolean>;
|
|
43
|
-
addMessage(sessionId: string, message: ChatMessage): void | Promise<void>;
|
|
44
|
-
getMessages(sessionId: string): ChatMessage[] | Promise<ChatMessage[]>;
|
|
45
|
-
setSystemPrompt(sessionId: string, prompt: string): void | Promise<void>;
|
|
46
|
-
clear(sessionId: string): boolean | Promise<boolean>;
|
|
47
|
-
reset(sessionId: string): void | Promise<void>;
|
|
48
|
-
listSessions(): string[] | Promise<string[]>;
|
|
49
|
-
getStats(): {
|
|
50
|
-
total: number;
|
|
51
|
-
active: number;
|
|
52
|
-
expired: number;
|
|
53
|
-
} | Promise<{
|
|
54
|
-
total: number;
|
|
55
|
-
active: number;
|
|
56
|
-
expired: number;
|
|
57
|
-
}>;
|
|
58
|
-
cleanup(): number | Promise<number>;
|
|
59
|
-
dispose(): void | Promise<void>;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* 内存会话管理器(回退方案)
|
|
63
|
-
*/
|
|
64
|
-
export declare class MemorySessionManager implements ISessionManager {
|
|
65
|
-
private sessions;
|
|
66
|
-
private config;
|
|
67
|
-
private cleanupTimer?;
|
|
68
|
-
constructor(config?: {
|
|
69
|
-
maxHistory?: number;
|
|
70
|
-
expireMs?: number;
|
|
71
|
-
});
|
|
72
|
-
get(sessionId: string, config?: SessionConfig): Session;
|
|
73
|
-
has(sessionId: string): boolean;
|
|
74
|
-
addMessage(sessionId: string, message: ChatMessage): void;
|
|
75
|
-
private trimMessages;
|
|
76
|
-
getMessages(sessionId: string): ChatMessage[];
|
|
77
|
-
setSystemPrompt(sessionId: string, prompt: string): void;
|
|
78
|
-
clear(sessionId: string): boolean;
|
|
79
|
-
reset(sessionId: string): void;
|
|
80
|
-
listSessions(): string[];
|
|
81
|
-
getStats(): {
|
|
82
|
-
total: number;
|
|
83
|
-
active: number;
|
|
84
|
-
expired: number;
|
|
85
|
-
};
|
|
86
|
-
cleanup(): number;
|
|
87
|
-
dispose(): void;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* 数据库会话管理器
|
|
91
|
-
* 使用 Zhin 的数据库服务进行持久化存储
|
|
92
|
-
*/
|
|
93
|
-
export declare class DatabaseSessionManager implements ISessionManager {
|
|
94
|
-
private cache;
|
|
95
|
-
private config;
|
|
96
|
-
private cleanupTimer?;
|
|
97
|
-
private saveQueue;
|
|
98
|
-
private saveTimer?;
|
|
99
|
-
private model;
|
|
100
|
-
/** 内存缓存上限,超出时淘汰最久未访问的条目 */
|
|
101
|
-
private static readonly MAX_CACHE_SIZE;
|
|
102
|
-
constructor(model: any, config?: {
|
|
103
|
-
maxHistory?: number;
|
|
104
|
-
expireMs?: number;
|
|
105
|
-
});
|
|
106
|
-
/**
|
|
107
|
-
* 从数据库加载会话
|
|
108
|
-
*/
|
|
109
|
-
private loadSession;
|
|
110
|
-
/**
|
|
111
|
-
* 保存会话到数据库(防抖)
|
|
112
|
-
*/
|
|
113
|
-
private schedulesSave;
|
|
114
|
-
/**
|
|
115
|
-
* 批量保存队列中的会话
|
|
116
|
-
*/
|
|
117
|
-
private flushSaveQueue;
|
|
118
|
-
get(sessionId: string, config?: SessionConfig): Promise<Session>;
|
|
119
|
-
/**
|
|
120
|
-
* 当缓存超过上限时,淘汰最久未访问的条目。
|
|
121
|
-
* 利用 Map 的插入顺序(最旧的在前)。
|
|
122
|
-
*/
|
|
123
|
-
private evictCacheIfNeeded;
|
|
124
|
-
has(sessionId: string): Promise<boolean>;
|
|
125
|
-
addMessage(sessionId: string, message: ChatMessage): Promise<void>;
|
|
126
|
-
private trimMessages;
|
|
127
|
-
getMessages(sessionId: string): Promise<ChatMessage[]>;
|
|
128
|
-
setSystemPrompt(sessionId: string, prompt: string): Promise<void>;
|
|
129
|
-
clear(sessionId: string): Promise<boolean>;
|
|
130
|
-
reset(sessionId: string): Promise<void>;
|
|
131
|
-
listSessions(): Promise<string[]>;
|
|
132
|
-
getStats(): Promise<{
|
|
133
|
-
total: number;
|
|
134
|
-
active: number;
|
|
135
|
-
expired: number;
|
|
136
|
-
}>;
|
|
137
|
-
cleanup(): Promise<number>;
|
|
138
|
-
dispose(): Promise<void>;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* 会话管理器包装器
|
|
142
|
-
* 支持同步和异步接口的统一使用
|
|
143
|
-
*/
|
|
144
|
-
export declare class SessionManager implements ISessionManager {
|
|
145
|
-
private manager;
|
|
146
|
-
constructor(manager: ISessionManager);
|
|
147
|
-
/**
|
|
148
|
-
* 生成会话 ID
|
|
149
|
-
*/
|
|
150
|
-
static generateId(platform: string, userId: string, channelId?: string): string;
|
|
151
|
-
get(sessionId: string, config?: SessionConfig): Session | Promise<Session>;
|
|
152
|
-
has(sessionId: string): boolean | Promise<boolean>;
|
|
153
|
-
addMessage(sessionId: string, message: ChatMessage): void | Promise<void>;
|
|
154
|
-
getMessages(sessionId: string): ChatMessage[] | Promise<ChatMessage[]>;
|
|
155
|
-
setSystemPrompt(sessionId: string, prompt: string): void | Promise<void>;
|
|
156
|
-
clear(sessionId: string): boolean | Promise<boolean>;
|
|
157
|
-
reset(sessionId: string): void | Promise<void>;
|
|
158
|
-
listSessions(): string[] | Promise<string[]>;
|
|
159
|
-
getStats(): {
|
|
160
|
-
total: number;
|
|
161
|
-
active: number;
|
|
162
|
-
expired: number;
|
|
163
|
-
} | Promise<{
|
|
164
|
-
total: number;
|
|
165
|
-
active: number;
|
|
166
|
-
expired: number;
|
|
167
|
-
}>;
|
|
168
|
-
cleanup(): number | Promise<number>;
|
|
169
|
-
dispose(): void | Promise<void>;
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* 创建内存会话管理器(回退方案)
|
|
173
|
-
*/
|
|
174
|
-
export declare function createMemorySessionManager(config?: {
|
|
175
|
-
maxHistory?: number;
|
|
176
|
-
expireMs?: number;
|
|
177
|
-
}): SessionManager;
|
|
178
|
-
/**
|
|
179
|
-
* 创建数据库会话管理器
|
|
180
|
-
*/
|
|
181
|
-
export declare function createDatabaseSessionManager(model: any, config?: {
|
|
182
|
-
maxHistory?: number;
|
|
183
|
-
expireMs?: number;
|
|
184
|
-
}): SessionManager;
|
|
185
|
-
/**
|
|
186
|
-
* 创建会话管理器(向后兼容)
|
|
187
|
-
* @deprecated 使用 createMemorySessionManager 或 createDatabaseSessionManager
|
|
188
|
-
*/
|
|
189
|
-
export declare function createSessionManager(config?: {
|
|
190
|
-
maxHistory?: number;
|
|
191
|
-
expireMs?: number;
|
|
192
|
-
}): SessionManager;
|
|
4
|
+
export { SessionManager, MemorySessionManager, DatabaseSessionManager, createSessionManager, createMemorySessionManager, createDatabaseSessionManager, AI_SESSION_MODEL, } from '@zhin.js/ai';
|
|
5
|
+
export type { ISessionManager } from '@zhin.js/ai';
|
|
193
6
|
//# sourceMappingURL=session.d.ts.map
|
package/lib/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,0BAA0B,EAC1B,4BAA4B,EAC5B,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/lib/session.js
CHANGED
|
@@ -1,465 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @zhin.js/ai
|
|
3
|
-
* 会话管理器,支持上下文记忆和数据库持久化
|
|
4
|
-
*
|
|
5
|
-
* 特性:
|
|
6
|
-
* - 数据库持久化存储(使用 Zhin 的数据库服务)
|
|
7
|
-
* - 内存缓存加速读取
|
|
8
|
-
* - 自动过期清理
|
|
9
|
-
* - 更长的上下文记忆能力
|
|
2
|
+
* Re-export from @zhin.js/ai for backward compatibility.
|
|
10
3
|
*/
|
|
11
|
-
|
|
12
|
-
const logger = new Logger(null, 'AI-Session');
|
|
13
|
-
/**
|
|
14
|
-
* 数据库模型定义
|
|
15
|
-
*/
|
|
16
|
-
export const AI_SESSION_MODEL = {
|
|
17
|
-
session_id: { type: 'text', nullable: false },
|
|
18
|
-
messages: { type: 'json', default: [] },
|
|
19
|
-
config: { type: 'json', default: {} },
|
|
20
|
-
created_at: { type: 'integer', default: 0 },
|
|
21
|
-
updated_at: { type: 'integer', default: 0 },
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* 内存会话管理器(回退方案)
|
|
25
|
-
*/
|
|
26
|
-
export class MemorySessionManager {
|
|
27
|
-
sessions = new Map();
|
|
28
|
-
config;
|
|
29
|
-
cleanupTimer;
|
|
30
|
-
constructor(config = {}) {
|
|
31
|
-
this.config = {
|
|
32
|
-
maxHistory: config.maxHistory ?? 100,
|
|
33
|
-
expireMs: config.expireMs ?? 24 * 60 * 60 * 1000, // 24 小时
|
|
34
|
-
};
|
|
35
|
-
// 定期清理过期会话
|
|
36
|
-
this.cleanupTimer = setInterval(() => this.cleanup(), 5 * 60 * 1000);
|
|
37
|
-
}
|
|
38
|
-
get(sessionId, config) {
|
|
39
|
-
let session = this.sessions.get(sessionId);
|
|
40
|
-
if (!session) {
|
|
41
|
-
session = {
|
|
42
|
-
id: sessionId,
|
|
43
|
-
config: config || { provider: 'openai' },
|
|
44
|
-
messages: [],
|
|
45
|
-
createdAt: Date.now(),
|
|
46
|
-
updatedAt: Date.now(),
|
|
47
|
-
};
|
|
48
|
-
this.sessions.set(sessionId, session);
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
session.updatedAt = Date.now();
|
|
52
|
-
}
|
|
53
|
-
return session;
|
|
54
|
-
}
|
|
55
|
-
has(sessionId) {
|
|
56
|
-
return this.sessions.has(sessionId);
|
|
57
|
-
}
|
|
58
|
-
addMessage(sessionId, message) {
|
|
59
|
-
const session = this.get(sessionId);
|
|
60
|
-
session.messages.push(message);
|
|
61
|
-
session.updatedAt = Date.now();
|
|
62
|
-
this.trimMessages(session);
|
|
63
|
-
}
|
|
64
|
-
trimMessages(session) {
|
|
65
|
-
const maxHistory = session.config.maxHistory ?? this.config.maxHistory;
|
|
66
|
-
if (session.messages.length > maxHistory) {
|
|
67
|
-
const systemMessages = session.messages.filter((m) => m.role === 'system');
|
|
68
|
-
const otherMessages = session.messages.filter((m) => m.role !== 'system');
|
|
69
|
-
const keepCount = maxHistory - systemMessages.length;
|
|
70
|
-
session.messages = [...systemMessages, ...otherMessages.slice(-keepCount)];
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
getMessages(sessionId) {
|
|
74
|
-
return this.sessions.get(sessionId)?.messages || [];
|
|
75
|
-
}
|
|
76
|
-
setSystemPrompt(sessionId, prompt) {
|
|
77
|
-
const session = this.get(sessionId);
|
|
78
|
-
session.messages = session.messages.filter((m) => m.role !== 'system');
|
|
79
|
-
session.messages.unshift({ role: 'system', content: prompt });
|
|
80
|
-
session.updatedAt = Date.now();
|
|
81
|
-
}
|
|
82
|
-
clear(sessionId) {
|
|
83
|
-
return this.sessions.delete(sessionId);
|
|
84
|
-
}
|
|
85
|
-
reset(sessionId) {
|
|
86
|
-
const session = this.sessions.get(sessionId);
|
|
87
|
-
if (session) {
|
|
88
|
-
const systemMessages = session.messages.filter((m) => m.role === 'system');
|
|
89
|
-
session.messages = systemMessages;
|
|
90
|
-
session.updatedAt = Date.now();
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
listSessions() {
|
|
94
|
-
return Array.from(this.sessions.keys());
|
|
95
|
-
}
|
|
96
|
-
getStats() {
|
|
97
|
-
const now = Date.now();
|
|
98
|
-
let active = 0;
|
|
99
|
-
let expired = 0;
|
|
100
|
-
for (const session of this.sessions.values()) {
|
|
101
|
-
if (now - session.updatedAt > this.config.expireMs) {
|
|
102
|
-
expired++;
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
active++;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return { total: this.sessions.size, active, expired };
|
|
109
|
-
}
|
|
110
|
-
cleanup() {
|
|
111
|
-
const now = Date.now();
|
|
112
|
-
let cleaned = 0;
|
|
113
|
-
for (const [id, session] of this.sessions) {
|
|
114
|
-
const expireMs = session.config.expireMs ?? this.config.expireMs;
|
|
115
|
-
if (now - session.updatedAt > expireMs) {
|
|
116
|
-
this.sessions.delete(id);
|
|
117
|
-
cleaned++;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return cleaned;
|
|
121
|
-
}
|
|
122
|
-
dispose() {
|
|
123
|
-
if (this.cleanupTimer) {
|
|
124
|
-
clearInterval(this.cleanupTimer);
|
|
125
|
-
this.cleanupTimer = undefined;
|
|
126
|
-
}
|
|
127
|
-
this.sessions.clear();
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* 数据库会话管理器
|
|
132
|
-
* 使用 Zhin 的数据库服务进行持久化存储
|
|
133
|
-
*/
|
|
134
|
-
export class DatabaseSessionManager {
|
|
135
|
-
cache = new Map();
|
|
136
|
-
config;
|
|
137
|
-
cleanupTimer;
|
|
138
|
-
saveQueue = new Map();
|
|
139
|
-
saveTimer;
|
|
140
|
-
model; // 数据库模型
|
|
141
|
-
/** 内存缓存上限,超出时淘汰最久未访问的条目 */
|
|
142
|
-
static MAX_CACHE_SIZE = 2000;
|
|
143
|
-
constructor(model, config = {}) {
|
|
144
|
-
this.model = model;
|
|
145
|
-
this.config = {
|
|
146
|
-
maxHistory: config.maxHistory ?? 200,
|
|
147
|
-
expireMs: config.expireMs ?? 7 * 24 * 60 * 60 * 1000,
|
|
148
|
-
};
|
|
149
|
-
// 定期清理过期会话(每小时)
|
|
150
|
-
this.cleanupTimer = setInterval(() => this.cleanup(), 60 * 60 * 1000);
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* 从数据库加载会话
|
|
154
|
-
*/
|
|
155
|
-
async loadSession(sessionId) {
|
|
156
|
-
try {
|
|
157
|
-
const records = await this.model.select().where({ session_id: sessionId });
|
|
158
|
-
if (records && records.length > 0) {
|
|
159
|
-
const record = records[0];
|
|
160
|
-
// SQLite 中 json 类型存储为 TEXT,读回时需要解析
|
|
161
|
-
const messages = typeof record.messages === 'string'
|
|
162
|
-
? JSON.parse(record.messages)
|
|
163
|
-
: (record.messages || []);
|
|
164
|
-
const config = typeof record.config === 'string'
|
|
165
|
-
? JSON.parse(record.config)
|
|
166
|
-
: (record.config || { provider: 'openai' });
|
|
167
|
-
return {
|
|
168
|
-
id: record.session_id,
|
|
169
|
-
config: Array.isArray(config) ? { provider: 'openai' } : config,
|
|
170
|
-
messages: Array.isArray(messages) ? messages : [],
|
|
171
|
-
createdAt: record.created_at,
|
|
172
|
-
updatedAt: record.updated_at,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
catch (error) {
|
|
177
|
-
logger.debug('加载会话失败:', error);
|
|
178
|
-
}
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* 保存会话到数据库(防抖)
|
|
183
|
-
*/
|
|
184
|
-
schedulesSave(session) {
|
|
185
|
-
this.saveQueue.set(session.id, session);
|
|
186
|
-
if (!this.saveTimer) {
|
|
187
|
-
this.saveTimer = setTimeout(() => this.flushSaveQueue(), 1000);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* 批量保存队列中的会话
|
|
192
|
-
*/
|
|
193
|
-
async flushSaveQueue() {
|
|
194
|
-
this.saveTimer = undefined;
|
|
195
|
-
const sessions = Array.from(this.saveQueue.values());
|
|
196
|
-
this.saveQueue.clear();
|
|
197
|
-
for (const session of sessions) {
|
|
198
|
-
try {
|
|
199
|
-
const existing = await this.model.select().where({ session_id: session.id });
|
|
200
|
-
const record = {
|
|
201
|
-
session_id: session.id,
|
|
202
|
-
messages: session.messages,
|
|
203
|
-
config: session.config,
|
|
204
|
-
updated_at: session.updatedAt,
|
|
205
|
-
};
|
|
206
|
-
if (existing && existing.length > 0) {
|
|
207
|
-
await this.model.update(record).where({ session_id: session.id });
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
record.created_at = session.createdAt;
|
|
211
|
-
await this.model.create(record);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
catch (error) {
|
|
215
|
-
logger.debug(`保存会话 ${session.id} 失败:`, error);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
async get(sessionId, config) {
|
|
220
|
-
// 先检查缓存
|
|
221
|
-
let session = this.cache.get(sessionId);
|
|
222
|
-
if (!session) {
|
|
223
|
-
// 从数据库加载
|
|
224
|
-
session = await this.loadSession(sessionId) ?? undefined;
|
|
225
|
-
if (!session) {
|
|
226
|
-
session = {
|
|
227
|
-
id: sessionId,
|
|
228
|
-
config: config || { provider: 'openai' },
|
|
229
|
-
messages: [],
|
|
230
|
-
createdAt: Date.now(),
|
|
231
|
-
updatedAt: Date.now(),
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
this.evictCacheIfNeeded();
|
|
235
|
-
this.cache.set(sessionId, session);
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
// LRU: 重新插入以更新 Map 的迭代顺序
|
|
239
|
-
this.cache.delete(sessionId);
|
|
240
|
-
this.cache.set(sessionId, session);
|
|
241
|
-
}
|
|
242
|
-
session.updatedAt = Date.now();
|
|
243
|
-
this.schedulesSave(session);
|
|
244
|
-
return session;
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* 当缓存超过上限时,淘汰最久未访问的条目。
|
|
248
|
-
* 利用 Map 的插入顺序(最旧的在前)。
|
|
249
|
-
*/
|
|
250
|
-
evictCacheIfNeeded() {
|
|
251
|
-
while (this.cache.size >= DatabaseSessionManager.MAX_CACHE_SIZE) {
|
|
252
|
-
const oldest = this.cache.keys().next().value;
|
|
253
|
-
if (oldest !== undefined) {
|
|
254
|
-
this.cache.delete(oldest);
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
break;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
async has(sessionId) {
|
|
262
|
-
if (this.cache.has(sessionId)) {
|
|
263
|
-
return true;
|
|
264
|
-
}
|
|
265
|
-
try {
|
|
266
|
-
const records = await this.model.select().where({ session_id: sessionId });
|
|
267
|
-
return records && records.length > 0;
|
|
268
|
-
}
|
|
269
|
-
catch {
|
|
270
|
-
return false;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
async addMessage(sessionId, message) {
|
|
274
|
-
const session = await this.get(sessionId);
|
|
275
|
-
session.messages.push(message);
|
|
276
|
-
session.updatedAt = Date.now();
|
|
277
|
-
this.trimMessages(session);
|
|
278
|
-
this.schedulesSave(session);
|
|
279
|
-
}
|
|
280
|
-
trimMessages(session) {
|
|
281
|
-
const maxHistory = session.config.maxHistory ?? this.config.maxHistory;
|
|
282
|
-
if (session.messages.length > maxHistory) {
|
|
283
|
-
const systemMessages = session.messages.filter((m) => m.role === 'system');
|
|
284
|
-
const otherMessages = session.messages.filter((m) => m.role !== 'system');
|
|
285
|
-
const keepCount = maxHistory - systemMessages.length;
|
|
286
|
-
session.messages = [...systemMessages, ...otherMessages.slice(-keepCount)];
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
async getMessages(sessionId) {
|
|
290
|
-
const session = await this.get(sessionId);
|
|
291
|
-
return session.messages;
|
|
292
|
-
}
|
|
293
|
-
async setSystemPrompt(sessionId, prompt) {
|
|
294
|
-
const session = await this.get(sessionId);
|
|
295
|
-
session.messages = session.messages.filter((m) => m.role !== 'system');
|
|
296
|
-
session.messages.unshift({ role: 'system', content: prompt });
|
|
297
|
-
session.updatedAt = Date.now();
|
|
298
|
-
this.schedulesSave(session);
|
|
299
|
-
}
|
|
300
|
-
async clear(sessionId) {
|
|
301
|
-
this.cache.delete(sessionId);
|
|
302
|
-
this.saveQueue.delete(sessionId);
|
|
303
|
-
try {
|
|
304
|
-
await this.model.delete({ session_id: sessionId });
|
|
305
|
-
return true;
|
|
306
|
-
}
|
|
307
|
-
catch (error) {
|
|
308
|
-
logger.debug(`删除会话 ${sessionId} 失败:`, error);
|
|
309
|
-
return false;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
async reset(sessionId) {
|
|
313
|
-
const session = await this.get(sessionId);
|
|
314
|
-
const systemMessages = session.messages.filter((m) => m.role === 'system');
|
|
315
|
-
session.messages = systemMessages;
|
|
316
|
-
session.updatedAt = Date.now();
|
|
317
|
-
this.schedulesSave(session);
|
|
318
|
-
}
|
|
319
|
-
async listSessions() {
|
|
320
|
-
try {
|
|
321
|
-
const records = await this.model.select();
|
|
322
|
-
return records.map(r => r.session_id);
|
|
323
|
-
}
|
|
324
|
-
catch (error) {
|
|
325
|
-
logger.debug('列出会话失败:', error);
|
|
326
|
-
return Array.from(this.cache.keys());
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
async getStats() {
|
|
330
|
-
const now = Date.now();
|
|
331
|
-
let total = 0;
|
|
332
|
-
let active = 0;
|
|
333
|
-
let expired = 0;
|
|
334
|
-
try {
|
|
335
|
-
const records = await this.model.select();
|
|
336
|
-
total = records.length;
|
|
337
|
-
for (const record of records) {
|
|
338
|
-
const expireMs = record.config?.expireMs ?? this.config.expireMs;
|
|
339
|
-
if (now - record.updated_at > expireMs) {
|
|
340
|
-
expired++;
|
|
341
|
-
}
|
|
342
|
-
else {
|
|
343
|
-
active++;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
catch (error) {
|
|
348
|
-
logger.debug('获取统计失败:', error);
|
|
349
|
-
// 回退到缓存统计
|
|
350
|
-
total = this.cache.size;
|
|
351
|
-
for (const session of this.cache.values()) {
|
|
352
|
-
if (now - session.updatedAt > this.config.expireMs) {
|
|
353
|
-
expired++;
|
|
354
|
-
}
|
|
355
|
-
else {
|
|
356
|
-
active++;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
return { total, active, expired };
|
|
361
|
-
}
|
|
362
|
-
async cleanup() {
|
|
363
|
-
const now = Date.now();
|
|
364
|
-
let cleaned = 0;
|
|
365
|
-
try {
|
|
366
|
-
const records = await this.model.select();
|
|
367
|
-
for (const record of records) {
|
|
368
|
-
const expireMs = record.config?.expireMs ?? this.config.expireMs;
|
|
369
|
-
if (now - record.updated_at > expireMs) {
|
|
370
|
-
await this.model.delete({ session_id: record.session_id });
|
|
371
|
-
this.cache.delete(record.session_id);
|
|
372
|
-
cleaned++;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
catch (error) {
|
|
377
|
-
logger.debug('清理会话失败:', error);
|
|
378
|
-
}
|
|
379
|
-
return cleaned;
|
|
380
|
-
}
|
|
381
|
-
async dispose() {
|
|
382
|
-
// 保存所有待保存的会话
|
|
383
|
-
if (this.saveTimer) {
|
|
384
|
-
clearTimeout(this.saveTimer);
|
|
385
|
-
this.saveTimer = undefined;
|
|
386
|
-
}
|
|
387
|
-
await this.flushSaveQueue();
|
|
388
|
-
if (this.cleanupTimer) {
|
|
389
|
-
clearInterval(this.cleanupTimer);
|
|
390
|
-
this.cleanupTimer = undefined;
|
|
391
|
-
}
|
|
392
|
-
this.cache.clear();
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* 会话管理器包装器
|
|
397
|
-
* 支持同步和异步接口的统一使用
|
|
398
|
-
*/
|
|
399
|
-
export class SessionManager {
|
|
400
|
-
manager;
|
|
401
|
-
constructor(manager) {
|
|
402
|
-
this.manager = manager;
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* 生成会话 ID
|
|
406
|
-
*/
|
|
407
|
-
static generateId(platform, userId, channelId) {
|
|
408
|
-
return channelId
|
|
409
|
-
? `${platform}:${channelId}:${userId}`
|
|
410
|
-
: `${platform}:${userId}`;
|
|
411
|
-
}
|
|
412
|
-
get(sessionId, config) {
|
|
413
|
-
return this.manager.get(sessionId, config);
|
|
414
|
-
}
|
|
415
|
-
has(sessionId) {
|
|
416
|
-
return this.manager.has(sessionId);
|
|
417
|
-
}
|
|
418
|
-
addMessage(sessionId, message) {
|
|
419
|
-
return this.manager.addMessage(sessionId, message);
|
|
420
|
-
}
|
|
421
|
-
getMessages(sessionId) {
|
|
422
|
-
return this.manager.getMessages(sessionId);
|
|
423
|
-
}
|
|
424
|
-
setSystemPrompt(sessionId, prompt) {
|
|
425
|
-
return this.manager.setSystemPrompt(sessionId, prompt);
|
|
426
|
-
}
|
|
427
|
-
clear(sessionId) {
|
|
428
|
-
return this.manager.clear(sessionId);
|
|
429
|
-
}
|
|
430
|
-
reset(sessionId) {
|
|
431
|
-
return this.manager.reset(sessionId);
|
|
432
|
-
}
|
|
433
|
-
listSessions() {
|
|
434
|
-
return this.manager.listSessions();
|
|
435
|
-
}
|
|
436
|
-
getStats() {
|
|
437
|
-
return this.manager.getStats();
|
|
438
|
-
}
|
|
439
|
-
cleanup() {
|
|
440
|
-
return this.manager.cleanup();
|
|
441
|
-
}
|
|
442
|
-
dispose() {
|
|
443
|
-
return this.manager.dispose();
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* 创建内存会话管理器(回退方案)
|
|
448
|
-
*/
|
|
449
|
-
export function createMemorySessionManager(config) {
|
|
450
|
-
return new SessionManager(new MemorySessionManager(config));
|
|
451
|
-
}
|
|
452
|
-
/**
|
|
453
|
-
* 创建数据库会话管理器
|
|
454
|
-
*/
|
|
455
|
-
export function createDatabaseSessionManager(model, config) {
|
|
456
|
-
return new SessionManager(new DatabaseSessionManager(model, config));
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* 创建会话管理器(向后兼容)
|
|
460
|
-
* @deprecated 使用 createMemorySessionManager 或 createDatabaseSessionManager
|
|
461
|
-
*/
|
|
462
|
-
export function createSessionManager(config) {
|
|
463
|
-
return createMemorySessionManager(config);
|
|
464
|
-
}
|
|
4
|
+
export { SessionManager, MemorySessionManager, DatabaseSessionManager, createSessionManager, createMemorySessionManager, createDatabaseSessionManager, AI_SESSION_MODEL, } from '@zhin.js/ai';
|
|
465
5
|
//# sourceMappingURL=session.js.map
|