inspiration-agent 0.0.1

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.
@@ -0,0 +1,486 @@
1
+ const logger = require("../utils/logger");
2
+ const tiktoken = require("tiktoken");
3
+
4
+ const MEMORY_EXPIRY_DAYS = 30;
5
+ const MAX_LONG_TERM_MEMORIES = 1000;
6
+ const MIN_IMPORTANCE_THRESHOLD = 1;
7
+
8
+ let encoder = null;
9
+
10
+ function getEncoder() {
11
+ if (!encoder) {
12
+ encoder = tiktoken.get_encoding("cl100k_base");
13
+ }
14
+ return encoder;
15
+ }
16
+
17
+ class MemoryManager {
18
+ constructor(dbService, shortTermLimit = 20) {
19
+ this.dbService = dbService;
20
+ this.shortTermMemory = [];
21
+ this.shortTermLimit = shortTermLimit;
22
+ this.maxContextTokens = 100000;
23
+ this._archiveQueue = [];
24
+ this._isArchiving = false;
25
+ }
26
+
27
+ async _processArchiveQueue() {
28
+ if (this._isArchiving) return;
29
+ this._isArchiving = true;
30
+
31
+ while (this._archiveQueue.length > 0) {
32
+ const message = this._archiveQueue.shift();
33
+ try {
34
+ const memoryType = this.determineMemoryType(message);
35
+
36
+ const validTypes = ["important", "identity", "preference"];
37
+ if (validTypes.includes(memoryType)) {
38
+ await this.dbService.saveLongTermMemory({
39
+ type: memoryType,
40
+ content: message.content,
41
+ keywords: this.extractKeywords(message.content),
42
+ importance: this.calculateImportance(message),
43
+ });
44
+ logger.debug(`已归档记忆: ${memoryType}`);
45
+ }
46
+ } catch (error) {
47
+ logger.error(`归档记忆失败: ${error.message}`);
48
+ }
49
+ }
50
+
51
+ this._isArchiving = false;
52
+ }
53
+
54
+ async cleanupExpiredMemories() {
55
+ try {
56
+ const expiryDate = new Date();
57
+ expiryDate.setDate(expiryDate.getDate() - MEMORY_EXPIRY_DAYS);
58
+
59
+ const sql = `
60
+ DELETE FROM long_term_memory
61
+ WHERE importance < ?
62
+ AND last_accessed < datetime(?)
63
+ `;
64
+
65
+ const result = await new Promise((resolve, reject) => {
66
+ this.dbService.db.run(
67
+ sql,
68
+ [MIN_IMPORTANCE_THRESHOLD, expiryDate.toISOString()],
69
+ function (err) {
70
+ if (err) reject(err);
71
+ else resolve({ deleted: this.changes });
72
+ }
73
+ );
74
+ });
75
+
76
+ if (result.deleted > 0) {
77
+ logger.info(`已清理 ${result.deleted} 条过期记忆`);
78
+ }
79
+
80
+ return result;
81
+ } catch (error) {
82
+ logger.error(`清理过期记忆失败: ${error.message}`);
83
+ return { deleted: 0 };
84
+ }
85
+ }
86
+
87
+ async pruneMemoriesIfNecessary() {
88
+ try {
89
+ const countResult = await new Promise((resolve, reject) => {
90
+ this.dbService.db.get("SELECT COUNT(*) as count FROM long_term_memory", [], (err, row) => {
91
+ if (err) reject(err);
92
+ else resolve(row);
93
+ });
94
+ });
95
+
96
+ if (countResult.count > MAX_LONG_TERM_MEMORIES) {
97
+ const deleteCount = countResult.count - MAX_LONG_TERM_MEMORIES;
98
+
99
+ const sql = `
100
+ DELETE FROM long_term_memory
101
+ WHERE id IN (
102
+ SELECT id FROM long_term_memory
103
+ ORDER BY importance ASC, last_accessed ASC
104
+ LIMIT ?
105
+ )
106
+ `;
107
+
108
+ const result = await new Promise((resolve, reject) => {
109
+ this.dbService.db.run(sql, [deleteCount], function (err) {
110
+ if (err) reject(err);
111
+ else resolve({ deleted: this.changes });
112
+ });
113
+ });
114
+
115
+ logger.info(`已修剪 ${result.deleted} 条低优先级记忆`);
116
+ return result;
117
+ }
118
+
119
+ return { deleted: 0 };
120
+ } catch (error) {
121
+ logger.error(`修剪记忆失败: ${error.message}`);
122
+ return { deleted: 0 };
123
+ }
124
+ }
125
+
126
+ /**
127
+ * 估算文本的 token 数量
128
+ * 使用更保守的估算:中文字符约 2 tokens,英文单词约 1.5 tokens
129
+ * @param {string} text - 要估算的文本
130
+ * @returns {number} 估算的 token 数量
131
+ */
132
+ estimateTokens(text) {
133
+ if (!text) return 0;
134
+ try {
135
+ const enc = getEncoder();
136
+ return enc.encode(text).length;
137
+ } catch (error) {
138
+ logger.warn(`tiktoken 编码失败,使用备用估算: ${error.message}`);
139
+ return Math.ceil(text.length / 4);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * 计算消息数组的总 token 数量
145
+ * @param {Array} messages - 消息数组
146
+ * @returns {number} 总 token 数量
147
+ */
148
+ calculateTotalTokens(messages) {
149
+ let total = 0;
150
+ for (const msg of messages) {
151
+ total += this.estimateTokens(msg.content || "");
152
+ if (msg.role) {
153
+ total += 5;
154
+ }
155
+ }
156
+ return total;
157
+ }
158
+
159
+ limitContextLength(messages) {
160
+ let totalTokens = this.calculateTotalTokens(messages);
161
+
162
+ if (totalTokens <= this.maxContextTokens) {
163
+ return messages;
164
+ }
165
+
166
+ const limitedMessages = [];
167
+ const droppedMessages = [];
168
+ let currentTokens = 0;
169
+
170
+ for (let i = messages.length - 1; i >= 0; i--) {
171
+ const msg = messages[i];
172
+ const msgTokens = this.estimateTokens(msg.content || "") + (msg.role ? 5 : 0);
173
+
174
+ if (currentTokens + msgTokens > this.maxContextTokens) {
175
+ droppedMessages.unshift(msg);
176
+ continue;
177
+ }
178
+
179
+ limitedMessages.unshift(msg);
180
+ currentTokens += msgTokens;
181
+ }
182
+
183
+ if (droppedMessages.length > 0) {
184
+ logger.debug(`丢弃 ${droppedMessages.length} 条超出上下文限制的消息`);
185
+ }
186
+
187
+ logger.debug(`\n========== 上下文长度限制 ==========`);
188
+ logger.debug(`原始 token 数: ${totalTokens}`);
189
+ logger.debug(`限制后 token 数: ${currentTokens}`);
190
+ logger.debug(`最大 token 限制: ${this.maxContextTokens}`);
191
+ logger.debug(`消息数量: ${messages.length} -> ${limitedMessages.length}`);
192
+ logger.debug(`已归档消息: ${droppedMessages.length}`);
193
+ logger.debug(`===================================\n`);
194
+
195
+ return limitedMessages;
196
+ }
197
+
198
+ addMessage(role, content) {
199
+ this.shortTermMemory.push({ role, content, timestamp: Date.now() });
200
+
201
+ while (this.shortTermMemory.length > this.shortTermLimit) {
202
+ this.shortTermMemory.shift();
203
+ }
204
+ }
205
+
206
+ async archiveToLongTerm(message) {
207
+ this._archiveQueue.push(message);
208
+ await this._processArchiveQueue();
209
+ }
210
+
211
+ async archiveAllShortTerm() {
212
+ for (const msg of this.shortTermMemory) {
213
+ this._archiveQueue.push(msg);
214
+ }
215
+ await this._processArchiveQueue();
216
+ }
217
+
218
+ determineMemoryType(message) {
219
+ const content = message.content.toLowerCase();
220
+
221
+ if (
222
+ content.includes("记住") ||
223
+ content.includes("记得") ||
224
+ content.includes("重要") ||
225
+ content.includes("关键") ||
226
+ content.includes("别忘了") ||
227
+ content.includes("不要忘记") ||
228
+ content.includes("帮我记") ||
229
+ content.includes("记录一下") ||
230
+ content.includes("记下来")
231
+ ) {
232
+ return "important";
233
+ }
234
+
235
+ if (
236
+ content.includes("偏好") ||
237
+ content.includes("喜欢") ||
238
+ content.includes("不喜欢") ||
239
+ content.includes("习惯") ||
240
+ content.includes("我通常") ||
241
+ content.includes("我一般")
242
+ ) {
243
+ return "preference";
244
+ }
245
+
246
+ if (content.includes("任务") || content.includes("计划") || content.includes("目标")) {
247
+ return "task";
248
+ }
249
+
250
+ if (
251
+ content.includes("名字") ||
252
+ content.includes("身份") ||
253
+ content.includes("职业") ||
254
+ content.includes("我是") ||
255
+ content.includes("我叫") ||
256
+ content.includes("我的名字")
257
+ ) {
258
+ return "identity";
259
+ }
260
+
261
+ if (content.includes("文件") || content.includes("路径") || content.includes("目录")) {
262
+ return "file_info";
263
+ }
264
+
265
+ return "conversation";
266
+ }
267
+
268
+ calculateImportance(message) {
269
+ let importance = 5;
270
+
271
+ const content = message.content.toLowerCase();
272
+
273
+ if (content.includes("重要") || content.includes("关键")) {
274
+ importance += 5;
275
+ }
276
+
277
+ if (
278
+ content.includes("记住") ||
279
+ content.includes("记得") ||
280
+ content.includes("别忘了") ||
281
+ content.includes("不要忘记")
282
+ ) {
283
+ importance += 4;
284
+ }
285
+
286
+ if (content.includes("我是") || content.includes("我叫") || content.includes("我的名字")) {
287
+ importance += 3;
288
+ }
289
+
290
+ if (content.includes("喜欢") || content.includes("偏好") || content.includes("习惯")) {
291
+ importance += 2;
292
+ }
293
+
294
+ if (message.role === "user") {
295
+ importance += 1;
296
+ }
297
+
298
+ return Math.min(importance, 10);
299
+ }
300
+
301
+ extractKeywords(content) {
302
+ const keywords = [];
303
+
304
+ const patterns = [
305
+ /文件[路径]*[::]\s*([^\s,,。!?]+)/g,
306
+ /名字[是]*[::]\s*([^\s,,。!?]+)/g,
307
+ /偏好[是]*[::]\s*([^\s,,。!?]+)/g,
308
+ /任务[是]*[::]\s*([^\s,,。!?]+)/g,
309
+ /记住[::]?\s*([^\s,,。!?]+)/g,
310
+ /记得[::]?\s*([^\s,,。!?]+)/g,
311
+ /我是([^\s,,。!?]+)/g,
312
+ /我叫([^\s,,。!?]+)/g,
313
+ /我的名字[是]*([^\s,,。!?]+)/g,
314
+ ];
315
+
316
+ patterns.forEach((pattern) => {
317
+ const matches = content.match(pattern);
318
+ if (matches) {
319
+ matches.forEach((match) => {
320
+ const keyword = match
321
+ .replace(
322
+ /文件[路径]*[::]\s*|名字[是]*[::]\s*|偏好[是]*[::]\s*|任务[是]*[::]\s*|记住[::]?\s*|记得[::]?\s*|我是|我叫|我的名字[是]*/g,
323
+ ""
324
+ )
325
+ .trim();
326
+ if (keyword && keyword.length > 0 && keyword.length < 50) {
327
+ keywords.push(keyword);
328
+ }
329
+ });
330
+ }
331
+ });
332
+
333
+ const importantWords = content.match(/[\u4e00-\u9fa5]{2,8}/g) || [];
334
+ importantWords.slice(0, 5).forEach((word) => {
335
+ if (
336
+ !keywords.includes(word) &&
337
+ !["这是", "那个", "这个", "什么", "怎么", "如何"].includes(word)
338
+ ) {
339
+ keywords.push(word);
340
+ }
341
+ });
342
+
343
+ return keywords;
344
+ }
345
+
346
+ /**
347
+ * 搜索相关的长期记忆
348
+ * @param {string} query - 搜索关键词
349
+ * @param {number} limit - 返回结果数量限制,默认 5
350
+ * @returns {Promise<Array>} 相关记忆数组
351
+ */
352
+ async searchRelevantMemories(query, limit = 5) {
353
+ try {
354
+ const memories = await this.dbService.searchLongTermMemories(query, limit);
355
+
356
+ memories.forEach((memory) => {
357
+ this.dbService.updateMemoryAccess(memory.id);
358
+ });
359
+
360
+ return memories;
361
+ } catch (error) {
362
+ logger.error(`搜索记忆失败: ${error.message}`);
363
+ return [];
364
+ }
365
+ }
366
+
367
+ /**
368
+ * 获取最近的长期记忆
369
+ * @param {string|null} type - 记忆类型过滤,null 表示不过滤
370
+ * @param {number} limit - 返回结果数量限制,默认 10
371
+ * @returns {Promise<Array>} 记忆数组
372
+ */
373
+ async getRecentMemories(type = null, limit = 10) {
374
+ try {
375
+ return await this.dbService.getLongTermMemories(type, limit);
376
+ } catch (error) {
377
+ logger.error(`获取记忆失败: ${error.message}`);
378
+ return [];
379
+ }
380
+ }
381
+
382
+ /**
383
+ * 获取短期记忆上下文
384
+ * @returns {Array} 短期记忆数组(不含 timestamp)
385
+ */
386
+ getShortTermContext() {
387
+ return this.shortTermMemory.map((msg) => ({
388
+ role: msg.role,
389
+ content: msg.content,
390
+ }));
391
+ }
392
+
393
+ async buildFullContext(userMessage) {
394
+ const shortTerm = this.getShortTermContext();
395
+
396
+ const importantMemories = await this.getImportantMemories();
397
+
398
+ const keywords = this.extractKeywords(userMessage);
399
+
400
+ let relevantMemories = [];
401
+ if (keywords.length > 0) {
402
+ relevantMemories = await this.searchRelevantMemories(keywords.join(" "), 3);
403
+ }
404
+
405
+ const allMemories = [...importantMemories];
406
+ for (const mem of relevantMemories) {
407
+ if (!allMemories.find((m) => m.id === mem.id)) {
408
+ allMemories.push(mem);
409
+ }
410
+ }
411
+
412
+ const memoryContext = allMemories.map((mem) => ({
413
+ role: "system",
414
+ content: `[长期记忆] ${mem.type}: ${mem.content}`,
415
+ }));
416
+
417
+ const fullContext = [...memoryContext, ...shortTerm];
418
+
419
+ return this.limitContextLength(fullContext);
420
+ }
421
+
422
+ async getImportantMemories() {
423
+ try {
424
+ const importantTypes = ["important", "identity", "preference"];
425
+ const memories = [];
426
+
427
+ for (const type of importantTypes) {
428
+ const typeMemories = await this.dbService.getLongTermMemories(type, 5);
429
+ for (const mem of typeMemories) {
430
+ if (!memories.find((m) => m.id === mem.id)) {
431
+ memories.push(mem);
432
+ }
433
+ }
434
+ }
435
+
436
+ return memories;
437
+ } catch (error) {
438
+ logger.error(`获取重要记忆失败: ${error.message}`);
439
+ return [];
440
+ }
441
+ }
442
+
443
+ clearShortTerm() {
444
+ this.shortTermMemory = [];
445
+ logger.info("短期记忆已清空");
446
+ }
447
+
448
+ async clearShortTermAsync() {
449
+ this.shortTermMemory = [];
450
+ logger.info("短期记忆已清空");
451
+ }
452
+
453
+ async getStats() {
454
+ let longTermCount = 0;
455
+ try {
456
+ const result = await new Promise((resolve, reject) => {
457
+ this.dbService.db.get("SELECT COUNT(*) as count FROM long_term_memory", [], (err, row) => {
458
+ if (err) reject(err);
459
+ else resolve(row);
460
+ });
461
+ });
462
+ longTermCount = result.count;
463
+ } catch (error) {
464
+ logger.error(`获取长期记忆统计失败: ${error.message}`);
465
+ }
466
+
467
+ return {
468
+ shortTermCount: this.shortTermMemory?.length || 0,
469
+ shortTermLimit: this.shortTermLimit || 20,
470
+ longTermCount: longTermCount || 0,
471
+ maxLongTermMemories: MAX_LONG_TERM_MEMORIES,
472
+ memoryExpiryDays: MEMORY_EXPIRY_DAYS,
473
+ archiveQueueLength: this._archiveQueue?.length || 0,
474
+ };
475
+ }
476
+
477
+ async performMaintenance() {
478
+ logger.info("开始记忆维护...");
479
+ const expired = await this.cleanupExpiredMemories();
480
+ const pruned = await this.pruneMemoriesIfNecessary();
481
+ logger.info(`记忆维护完成: 清理 ${expired.deleted} 条过期, 修剪 ${pruned.deleted} 条低优先级`);
482
+ return { expired: expired.deleted, pruned: pruned.deleted };
483
+ }
484
+ }
485
+
486
+ module.exports = MemoryManager;
@@ -0,0 +1,157 @@
1
+ const logger = require("../utils/logger");
2
+
3
+ class MessageQueue {
4
+ constructor() {
5
+ this.queue = [];
6
+ this.isProcessing = false;
7
+ this.currentMessage = null;
8
+ this.messageHandlers = new Map();
9
+ this.maxQueueSize = 100;
10
+ this.stats = {
11
+ totalReceived: 0,
12
+ totalProcessed: 0,
13
+ totalDropped: 0,
14
+ };
15
+ }
16
+
17
+ enqueue(message, source = "unknown", metadata = {}) {
18
+ this.stats.totalReceived++;
19
+
20
+ if (this.queue.length >= this.maxQueueSize) {
21
+ this.stats.totalDropped++;
22
+ logger.warn(`消息队列已满,丢弃消息: ${source}`);
23
+ return false;
24
+ }
25
+
26
+ const queueItem = {
27
+ id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
28
+ message,
29
+ source,
30
+ metadata,
31
+ enqueuedAt: Date.now(),
32
+ };
33
+
34
+ this.queue.push(queueItem);
35
+ logger.debug(`消息入队: ${queueItem.id} (来源: ${source}, 队列长度: ${this.queue.length})`);
36
+
37
+ if (!this.isProcessing) {
38
+ this.processNext();
39
+ }
40
+
41
+ return true;
42
+ }
43
+
44
+ async processNext() {
45
+ if (this.isProcessing || this.queue.length === 0) {
46
+ return;
47
+ }
48
+
49
+ this.isProcessing = true;
50
+ const item = this.queue.shift();
51
+ this.currentMessage = item;
52
+
53
+ const waitTime = Date.now() - item.enqueuedAt;
54
+ logger.info(`开始处理消息: ${item.id} (等待时间: ${waitTime}ms, 来源: ${item.source})`);
55
+
56
+ try {
57
+ const handler = this.messageHandlers.get(item.source);
58
+ if (handler) {
59
+ const result = await handler(item.message, item.metadata);
60
+ logger.info(`消息处理完成: ${item.id}`);
61
+ this.stats.totalProcessed++;
62
+
63
+ if (item.metadata.onComplete) {
64
+ logger.debug(`执行消息完成回调: ${item.id}`);
65
+ try {
66
+ await item.metadata.onComplete(result);
67
+ logger.debug(`消息完成回调执行完毕: ${item.id}`);
68
+ } catch (callbackError) {
69
+ logger.error(`消息完成回调执行失败: ${item.id}`, callbackError);
70
+ }
71
+ }
72
+
73
+ return result;
74
+ } else {
75
+ logger.warn(`未找到消息处理器: ${item.source}`);
76
+ }
77
+ } catch (error) {
78
+ logger.error(`消息处理失败: ${item.id} - ${error.message}`);
79
+ } finally {
80
+ this.currentMessage = null;
81
+ this.isProcessing = false;
82
+
83
+ if (this.queue.length > 0) {
84
+ setImmediate(() => this.processNext());
85
+ }
86
+ }
87
+ }
88
+
89
+ registerHandler(source, handler) {
90
+ this.messageHandlers.set(source, handler);
91
+ logger.debug(`注册消息处理器: ${source}`);
92
+ }
93
+
94
+ unregisterHandler(source) {
95
+ this.messageHandlers.delete(source);
96
+ logger.debug(`注销消息处理器: ${source}`);
97
+ }
98
+
99
+ getStatus() {
100
+ return {
101
+ queueLength: this.queue.length,
102
+ isProcessing: this.isProcessing,
103
+ currentMessageId: this.currentMessage?.id || null,
104
+ maxQueueSize: this.maxQueueSize,
105
+ stats: { ...this.stats },
106
+ handlers: Array.from(this.messageHandlers.keys()),
107
+ };
108
+ }
109
+
110
+ clear() {
111
+ const dropped = this.queue.length;
112
+ this.queue = [];
113
+ this.stats.totalDropped += dropped;
114
+ logger.info(`消息队列已清空,丢弃 ${dropped} 条消息`);
115
+ return dropped;
116
+ }
117
+
118
+ peek() {
119
+ return this.queue.length > 0 ? this.queue[0] : null;
120
+ }
121
+
122
+ getQueueLength() {
123
+ return this.queue.length;
124
+ }
125
+
126
+ isEmpty() {
127
+ return this.queue.length === 0;
128
+ }
129
+
130
+ isIdle() {
131
+ return !this.isProcessing && this.queue.length === 0;
132
+ }
133
+
134
+ async waitForIdle(timeout = 300000) {
135
+ const startTime = Date.now();
136
+
137
+ return new Promise((resolve) => {
138
+ const checkIdle = () => {
139
+ if (this.isIdle()) {
140
+ resolve(true);
141
+ return;
142
+ }
143
+
144
+ if (Date.now() - startTime > timeout) {
145
+ resolve(false);
146
+ return;
147
+ }
148
+
149
+ setTimeout(checkIdle, 100);
150
+ };
151
+
152
+ checkIdle();
153
+ });
154
+ }
155
+ }
156
+
157
+ module.exports = MessageQueue;