@ppagent/memory 0.1.1 → 0.1.2
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/dist/index.d.ts +159 -117
- package/dist/index.js +326 -157
- package/llms.txt +845 -0
- package/package.json +10 -8
package/dist/index.js
CHANGED
|
@@ -25,9 +25,10 @@ function resolveConfig(config) {
|
|
|
25
25
|
detailMaxTokens: config.detailMaxTokens ?? 2048,
|
|
26
26
|
summaryMaxTokens: config.summaryMaxTokens ?? 512,
|
|
27
27
|
conciseMaxTokens: config.conciseMaxTokens ?? 128,
|
|
28
|
-
httpTimeoutMs: config.httpTimeoutMs ??
|
|
28
|
+
httpTimeoutMs: config.httpTimeoutMs ?? 6e4,
|
|
29
29
|
httpMaxRetries: config.httpMaxRetries ?? 2,
|
|
30
30
|
embeddingBatchSize: config.embeddingBatchSize ?? 20,
|
|
31
|
+
embeddingConcurrency: config.embeddingConcurrency ?? 2,
|
|
31
32
|
maxConcurrentCompressions: config.maxConcurrentCompressions ?? 3,
|
|
32
33
|
entitySimilarityThreshold: config.entitySimilarityThreshold ?? 0.92,
|
|
33
34
|
defaultSearchLimit: config.defaultSearchLimit ?? 10,
|
|
@@ -42,7 +43,12 @@ function resolveConfig(config) {
|
|
|
42
43
|
knowledgeGraphTriggerScore: config.knowledgeGraphTriggerScore ?? 0.78,
|
|
43
44
|
knowledgeGraphEntityTopK: config.knowledgeGraphEntityTopK ?? 10,
|
|
44
45
|
knowledgeGraphAnchorTopK: config.knowledgeGraphAnchorTopK ?? 3,
|
|
45
|
-
knowledgeGraphHopLimit: config.knowledgeGraphHopLimit ?? 10
|
|
46
|
+
knowledgeGraphHopLimit: config.knowledgeGraphHopLimit ?? 10,
|
|
47
|
+
graphExtractConcurrency: config.graphExtractConcurrency ?? 2,
|
|
48
|
+
graphBuildTimeoutMs: config.graphBuildTimeoutMs ?? 12e4,
|
|
49
|
+
autoOptimizeOnInit: config.autoOptimizeOnInit ?? true,
|
|
50
|
+
optimizeVersionRetentionMs: config.optimizeVersionRetentionMs ?? 0,
|
|
51
|
+
restoreConcurrency: config.restoreConcurrency ?? 8
|
|
46
52
|
};
|
|
47
53
|
}
|
|
48
54
|
|
|
@@ -162,6 +168,16 @@ import { GrafeoDB } from "@grafeo-db/js";
|
|
|
162
168
|
import { get_encoding } from "@dqbd/tiktoken";
|
|
163
169
|
|
|
164
170
|
// src/llm/http.ts
|
|
171
|
+
function normalizeFetchError(err, timeoutMs, errorLabel) {
|
|
172
|
+
if (isAbortError(err)) {
|
|
173
|
+
return new Error(`${errorLabel} request timed out after ${timeoutMs}ms`, { cause: err });
|
|
174
|
+
}
|
|
175
|
+
if (err instanceof Error) return err;
|
|
176
|
+
return new Error(String(err));
|
|
177
|
+
}
|
|
178
|
+
function isAbortError(err) {
|
|
179
|
+
return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
|
|
180
|
+
}
|
|
165
181
|
function backoffDelay(attempt) {
|
|
166
182
|
return Math.min(1e3 * 2 ** attempt, 8e3);
|
|
167
183
|
}
|
|
@@ -170,7 +186,9 @@ async function postJsonWithRetry(url, headers, body, opts, errorLabel = "HTTP")
|
|
|
170
186
|
let lastErr;
|
|
171
187
|
for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
|
|
172
188
|
const controller = new AbortController();
|
|
173
|
-
const timer = setTimeout(() =>
|
|
189
|
+
const timer = setTimeout(() => {
|
|
190
|
+
controller.abort(new Error(`${errorLabel} request timed out after ${opts.timeoutMs}ms`));
|
|
191
|
+
}, opts.timeoutMs);
|
|
174
192
|
let res;
|
|
175
193
|
try {
|
|
176
194
|
res = await fetch(url, {
|
|
@@ -181,12 +199,12 @@ async function postJsonWithRetry(url, headers, body, opts, errorLabel = "HTTP")
|
|
|
181
199
|
});
|
|
182
200
|
} catch (err) {
|
|
183
201
|
clearTimeout(timer);
|
|
184
|
-
lastErr = err;
|
|
202
|
+
lastErr = normalizeFetchError(err, opts.timeoutMs, errorLabel);
|
|
185
203
|
if (attempt < opts.maxRetries) {
|
|
186
204
|
await sleep(backoffDelay(attempt));
|
|
187
205
|
continue;
|
|
188
206
|
}
|
|
189
|
-
throw
|
|
207
|
+
throw lastErr;
|
|
190
208
|
}
|
|
191
209
|
clearTimeout(timer);
|
|
192
210
|
if (res.ok) {
|
|
@@ -226,27 +244,45 @@ var EmbedService = class {
|
|
|
226
244
|
const inputs = Array.isArray(texts) ? texts : [texts];
|
|
227
245
|
const url = `${this.config.embeddingBaseUrl.replace(/\/$/, "")}/embeddings`;
|
|
228
246
|
const batchSize = Math.max(1, this.config.embeddingBatchSize);
|
|
247
|
+
const concurrency = Math.max(1, this.config.embeddingConcurrency);
|
|
229
248
|
const retryOpts = { timeoutMs: this.config.httpTimeoutMs, maxRetries: this.config.httpMaxRetries };
|
|
230
|
-
const
|
|
249
|
+
const batches = [];
|
|
231
250
|
for (let i = 0; i < inputs.length; i += batchSize) {
|
|
232
|
-
|
|
251
|
+
batches.push({ index: batches.length, texts: inputs.slice(i, i + batchSize) });
|
|
252
|
+
}
|
|
253
|
+
const batchResults = await mapLimit(batches, concurrency, async (batch) => {
|
|
233
254
|
const data = await postJsonWithRetry(
|
|
234
255
|
url,
|
|
235
256
|
{ Authorization: `Bearer ${this.config.embeddingApiKey}` },
|
|
236
|
-
{ model: this.config.embeddingModel, input: batch },
|
|
257
|
+
{ model: this.config.embeddingModel, input: batch.texts },
|
|
237
258
|
retryOpts,
|
|
238
259
|
"Embedding API"
|
|
239
260
|
);
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
261
|
+
return {
|
|
262
|
+
index: batch.index,
|
|
263
|
+
vectors: data.data.sort((a, b) => a.index - b.index).map((d) => d.embedding)
|
|
264
|
+
};
|
|
265
|
+
});
|
|
266
|
+
return batchResults.sort((a, b) => a.index - b.index).flatMap((b) => b.vectors);
|
|
244
267
|
}
|
|
245
268
|
async embedOne(text) {
|
|
246
269
|
const result = await this.embed([text]);
|
|
247
270
|
return result[0];
|
|
248
271
|
}
|
|
249
272
|
};
|
|
273
|
+
async function mapLimit(items, limit, mapper) {
|
|
274
|
+
const results = new Array(items.length);
|
|
275
|
+
let next = 0;
|
|
276
|
+
const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
|
|
277
|
+
while (true) {
|
|
278
|
+
const index = next++;
|
|
279
|
+
if (index >= items.length) return;
|
|
280
|
+
results[index] = await mapper(items[index], index);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
await Promise.all(workers);
|
|
284
|
+
return results;
|
|
285
|
+
}
|
|
250
286
|
function cosineSimilarity(a, b) {
|
|
251
287
|
let dot = 0;
|
|
252
288
|
let normA = 0;
|
|
@@ -979,6 +1015,24 @@ function safeParseObject(s) {
|
|
|
979
1015
|
return {};
|
|
980
1016
|
}
|
|
981
1017
|
}
|
|
1018
|
+
var MESSAGES_INDEXES = [
|
|
1019
|
+
{ column: "message_id" },
|
|
1020
|
+
{ column: "session_id" },
|
|
1021
|
+
{ column: "content", fts: true },
|
|
1022
|
+
{ column: "metadata", fts: true }
|
|
1023
|
+
// 支持 metadata 内容全文检索
|
|
1024
|
+
];
|
|
1025
|
+
var TOPICS_INDEXES = [
|
|
1026
|
+
{ column: "chat_id" },
|
|
1027
|
+
{ column: "detail", fts: true }
|
|
1028
|
+
];
|
|
1029
|
+
var SESSIONS_INDEXES = [{ column: "session_id" }];
|
|
1030
|
+
var DOCUMENTS_INDEXES = [{ column: "doc_id" }];
|
|
1031
|
+
var CHUNKS_INDEXES = [
|
|
1032
|
+
{ column: "doc_id" },
|
|
1033
|
+
{ column: "content", fts: true }
|
|
1034
|
+
// 知识片段混合检索
|
|
1035
|
+
];
|
|
982
1036
|
var LanceService = class {
|
|
983
1037
|
config;
|
|
984
1038
|
conn;
|
|
@@ -991,6 +1045,7 @@ var LanceService = class {
|
|
|
991
1045
|
// Scalar indexes cannot be created on empty tables (LanceDB btree limitation).
|
|
992
1046
|
// These flags defer creation to the first insert.
|
|
993
1047
|
isNewMessagesTable = false;
|
|
1048
|
+
isNewTopicsTable = false;
|
|
994
1049
|
isNewSessionsTable = false;
|
|
995
1050
|
isNewDocumentsTable = false;
|
|
996
1051
|
isNewChunksTable = false;
|
|
@@ -1004,7 +1059,6 @@ var LanceService = class {
|
|
|
1004
1059
|
if (existingTables.includes(MESSAGES_TABLE)) {
|
|
1005
1060
|
this.messagesTable = await this.conn.openTable(MESSAGES_TABLE);
|
|
1006
1061
|
await this._ensurePartsColumn();
|
|
1007
|
-
await this.ensureScalarIndex(this.messagesTable, "message_id");
|
|
1008
1062
|
} else {
|
|
1009
1063
|
this.messagesTable = await this.conn.createEmptyTable(
|
|
1010
1064
|
MESSAGES_TABLE,
|
|
@@ -1020,6 +1074,7 @@ var LanceService = class {
|
|
|
1020
1074
|
TOPICS_TABLE,
|
|
1021
1075
|
topicsSchema(dim)
|
|
1022
1076
|
);
|
|
1077
|
+
this.isNewTopicsTable = true;
|
|
1023
1078
|
}
|
|
1024
1079
|
if (existingTables.includes(FACTS_TABLE)) {
|
|
1025
1080
|
this.factsTable = await this.conn.openTable(FACTS_TABLE);
|
|
@@ -1028,43 +1083,83 @@ var LanceService = class {
|
|
|
1028
1083
|
}
|
|
1029
1084
|
if (existingTables.includes(SESSIONS_TABLE)) {
|
|
1030
1085
|
this.sessionsTable = await this.conn.openTable(SESSIONS_TABLE);
|
|
1031
|
-
await this.ensureScalarIndex(this.sessionsTable, "session_id");
|
|
1032
1086
|
} else {
|
|
1033
1087
|
this.sessionsTable = await this.conn.createEmptyTable(SESSIONS_TABLE, sessionsSchema());
|
|
1034
1088
|
this.isNewSessionsTable = true;
|
|
1035
1089
|
}
|
|
1036
1090
|
if (existingTables.includes(DOCUMENTS_TABLE)) {
|
|
1037
1091
|
this.documentsTable = await this.conn.openTable(DOCUMENTS_TABLE);
|
|
1038
|
-
await this.ensureScalarIndex(this.documentsTable, "doc_id");
|
|
1039
1092
|
} else {
|
|
1040
1093
|
this.documentsTable = await this.conn.createEmptyTable(DOCUMENTS_TABLE, documentsSchema(dim));
|
|
1041
1094
|
this.isNewDocumentsTable = true;
|
|
1042
1095
|
}
|
|
1043
1096
|
if (existingTables.includes(CHUNKS_TABLE)) {
|
|
1044
1097
|
this.chunksTable = await this.conn.openTable(CHUNKS_TABLE);
|
|
1045
|
-
await this.ensureScalarIndex(this.chunksTable, "doc_id");
|
|
1046
1098
|
} else {
|
|
1047
1099
|
this.chunksTable = await this.conn.createEmptyTable(CHUNKS_TABLE, chunksSchema(dim));
|
|
1048
1100
|
this.isNewChunksTable = true;
|
|
1049
1101
|
}
|
|
1050
|
-
await this.
|
|
1102
|
+
await this.ensureIndexes(this.messagesTable, MESSAGES_INDEXES);
|
|
1103
|
+
await this.ensureIndexes(this.topicsTable, TOPICS_INDEXES);
|
|
1104
|
+
await this.ensureIndexes(this.sessionsTable, SESSIONS_INDEXES);
|
|
1105
|
+
await this.ensureIndexes(this.documentsTable, DOCUMENTS_INDEXES);
|
|
1106
|
+
await this.ensureIndexes(this.chunksTable, CHUNKS_INDEXES);
|
|
1051
1107
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1108
|
+
/**
|
|
1109
|
+
* 按需补齐索引:先经 listIndices 判存在,缺失的列才 createIndex(且 replace:false)。
|
|
1110
|
+
* createIndex 默认 replace:true 会在每次启动时全量重建索引并提交新表版本——
|
|
1111
|
+
* 这正是历史上「启动越来越慢 + _versions 目录膨胀」的根源,绝不可回退到无条件 createIndex。
|
|
1112
|
+
*/
|
|
1113
|
+
async ensureIndexes(table, specs) {
|
|
1114
|
+
let indexed;
|
|
1115
|
+
try {
|
|
1116
|
+
indexed = new Set((await table.listIndices()).flatMap((i) => i.columns));
|
|
1117
|
+
} catch {
|
|
1118
|
+
indexed = /* @__PURE__ */ new Set();
|
|
1119
|
+
}
|
|
1120
|
+
for (const { column, fts } of specs) {
|
|
1121
|
+
if (indexed.has(column)) continue;
|
|
1062
1122
|
try {
|
|
1063
|
-
await table.createIndex(
|
|
1123
|
+
await table.createIndex(
|
|
1124
|
+
column,
|
|
1125
|
+
fts ? { config: lancedb.Index.fts(), replace: false } : { replace: false }
|
|
1126
|
+
);
|
|
1064
1127
|
} catch {
|
|
1065
1128
|
}
|
|
1066
1129
|
}
|
|
1067
1130
|
}
|
|
1131
|
+
/**
|
|
1132
|
+
* 存储压实:逐表执行碎片合并 + 清理 retentionMs 之前的历史版本。
|
|
1133
|
+
* 嵌入式场景下 LanceDB 不会自动做这件事,长期运行后版本/碎片无限累积会显著拖慢启动与查询。
|
|
1134
|
+
*/
|
|
1135
|
+
async optimizeStorage(retentionMs = 0) {
|
|
1136
|
+
const cutoff = new Date(Date.now() - Math.max(0, retentionMs));
|
|
1137
|
+
const targets = [
|
|
1138
|
+
[MESSAGES_TABLE, this.messagesTable],
|
|
1139
|
+
[TOPICS_TABLE, this.topicsTable],
|
|
1140
|
+
[FACTS_TABLE, this.factsTable],
|
|
1141
|
+
[SESSIONS_TABLE, this.sessionsTable],
|
|
1142
|
+
[DOCUMENTS_TABLE, this.documentsTable],
|
|
1143
|
+
[CHUNKS_TABLE, this.chunksTable]
|
|
1144
|
+
];
|
|
1145
|
+
const results = [];
|
|
1146
|
+
for (const [name, table] of targets) {
|
|
1147
|
+
try {
|
|
1148
|
+
const stats = await table.optimize({ cleanupOlderThan: cutoff });
|
|
1149
|
+
results.push({
|
|
1150
|
+
table: name,
|
|
1151
|
+
fragmentsRemoved: stats.compaction.fragmentsRemoved,
|
|
1152
|
+
fragmentsAdded: stats.compaction.fragmentsAdded,
|
|
1153
|
+
filesRemoved: stats.compaction.filesRemoved,
|
|
1154
|
+
oldVersionsRemoved: stats.prune.oldVersionsRemoved,
|
|
1155
|
+
bytesRemoved: Number(stats.prune.bytesRemoved)
|
|
1156
|
+
});
|
|
1157
|
+
} catch (err) {
|
|
1158
|
+
console.warn(`[LanceService] optimize table "${name}" failed:`, err);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
return results;
|
|
1162
|
+
}
|
|
1068
1163
|
/**
|
|
1069
1164
|
* 为存量 messages 表添加 parts 列(如果缺失)。
|
|
1070
1165
|
* LanceDB 0.14+ 支持 addColumns;旧版本会 throw,此时 rowToMessage 的 ?? "[]" 兜底。
|
|
@@ -1101,24 +1196,20 @@ var LanceService = class {
|
|
|
1101
1196
|
} catch {
|
|
1102
1197
|
}
|
|
1103
1198
|
}
|
|
1104
|
-
// LanceDB btree scalar indexes require at least one row.
|
|
1105
|
-
// For existing tables (opened in init) this is safe; for new tables we defer via isNew*Table flags.
|
|
1106
|
-
async ensureScalarIndex(table, column) {
|
|
1107
|
-
try {
|
|
1108
|
-
await table.createIndex(column);
|
|
1109
|
-
} catch {
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
1199
|
async addMessages(messages) {
|
|
1113
1200
|
if (messages.length === 0) return;
|
|
1114
1201
|
await this.messagesTable.add(messages.map(messageToRow));
|
|
1115
1202
|
if (this.isNewMessagesTable) {
|
|
1116
|
-
await this.
|
|
1203
|
+
await this.ensureIndexes(this.messagesTable, MESSAGES_INDEXES);
|
|
1117
1204
|
this.isNewMessagesTable = false;
|
|
1118
1205
|
}
|
|
1119
1206
|
}
|
|
1120
1207
|
async addTopic(topic) {
|
|
1121
1208
|
await this.topicsTable.add([topicToRow(topic)]);
|
|
1209
|
+
if (this.isNewTopicsTable) {
|
|
1210
|
+
await this.ensureIndexes(this.topicsTable, TOPICS_INDEXES);
|
|
1211
|
+
this.isNewTopicsTable = false;
|
|
1212
|
+
}
|
|
1122
1213
|
}
|
|
1123
1214
|
async updateTopicRecallCount(summaryId, count) {
|
|
1124
1215
|
await this.topicsTable.update({
|
|
@@ -1251,7 +1342,7 @@ var LanceService = class {
|
|
|
1251
1342
|
async insertSession(session) {
|
|
1252
1343
|
await this.sessionsTable.add([sessionToRow(session)]);
|
|
1253
1344
|
if (this.isNewSessionsTable) {
|
|
1254
|
-
await this.
|
|
1345
|
+
await this.ensureIndexes(this.sessionsTable, SESSIONS_INDEXES);
|
|
1255
1346
|
this.isNewSessionsTable = false;
|
|
1256
1347
|
}
|
|
1257
1348
|
}
|
|
@@ -1357,7 +1448,7 @@ var LanceService = class {
|
|
|
1357
1448
|
async addDocument(doc) {
|
|
1358
1449
|
await this.documentsTable.add([documentToRow(doc)]);
|
|
1359
1450
|
if (this.isNewDocumentsTable) {
|
|
1360
|
-
await this.
|
|
1451
|
+
await this.ensureIndexes(this.documentsTable, DOCUMENTS_INDEXES);
|
|
1361
1452
|
this.isNewDocumentsTable = false;
|
|
1362
1453
|
}
|
|
1363
1454
|
}
|
|
@@ -1365,7 +1456,7 @@ var LanceService = class {
|
|
|
1365
1456
|
if (chunks.length === 0) return;
|
|
1366
1457
|
await this.chunksTable.add(chunks.map(chunkToRow));
|
|
1367
1458
|
if (this.isNewChunksTable) {
|
|
1368
|
-
await this.
|
|
1459
|
+
await this.ensureIndexes(this.chunksTable, CHUNKS_INDEXES);
|
|
1369
1460
|
this.isNewChunksTable = false;
|
|
1370
1461
|
}
|
|
1371
1462
|
}
|
|
@@ -1488,6 +1579,40 @@ var LanceService = class {
|
|
|
1488
1579
|
}
|
|
1489
1580
|
};
|
|
1490
1581
|
|
|
1582
|
+
// src/manager/semaphore.ts
|
|
1583
|
+
var Semaphore = class {
|
|
1584
|
+
count;
|
|
1585
|
+
queue = [];
|
|
1586
|
+
constructor(max) {
|
|
1587
|
+
this.count = max;
|
|
1588
|
+
}
|
|
1589
|
+
async run(fn) {
|
|
1590
|
+
await this.acquire();
|
|
1591
|
+
try {
|
|
1592
|
+
return await fn();
|
|
1593
|
+
} finally {
|
|
1594
|
+
this.release();
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
acquire() {
|
|
1598
|
+
if (this.count > 0) {
|
|
1599
|
+
this.count--;
|
|
1600
|
+
return Promise.resolve();
|
|
1601
|
+
}
|
|
1602
|
+
return new Promise((resolve) => {
|
|
1603
|
+
this.queue.push(resolve);
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
release() {
|
|
1607
|
+
const next = this.queue.shift();
|
|
1608
|
+
if (next) {
|
|
1609
|
+
next();
|
|
1610
|
+
} else {
|
|
1611
|
+
this.count++;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
};
|
|
1615
|
+
|
|
1491
1616
|
// src/llm/llm.service.ts
|
|
1492
1617
|
var ENTITY_EXTRACTOR_TOOL = {
|
|
1493
1618
|
type: "function",
|
|
@@ -1602,7 +1727,7 @@ var LlmService = class {
|
|
|
1602
1727
|
return data.choices[0].message;
|
|
1603
1728
|
}
|
|
1604
1729
|
// ── 第一步:生成三级摘要(JSON 输出)──────────────────────────────────────────
|
|
1605
|
-
async
|
|
1730
|
+
async summarizeMessages(messages) {
|
|
1606
1731
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1607
1732
|
const { detailMaxTokens, summaryMaxTokens, conciseMaxTokens } = this.config;
|
|
1608
1733
|
const systemPrompt = `\u4F60\u662F\u4E00\u4E2A\u5BF9\u8BDD\u8BB0\u5FC6\u7BA1\u7406\u52A9\u624B\u3002\u5F53\u524D\u7CFB\u7EDF\u65F6\u95F4\uFF1A${now}\u3002
|
|
@@ -1624,7 +1749,7 @@ ${formatted}`);
|
|
|
1624
1749
|
return { title: parsed.title ?? "", detail: parsed.detail, summary: parsed.summary, concise: parsed.concise };
|
|
1625
1750
|
}
|
|
1626
1751
|
// ── 第二步:通过工具调用提取实体和关系 ──────────────────────────────────────
|
|
1627
|
-
async
|
|
1752
|
+
async extractEntitiesFromMessages(messages) {
|
|
1628
1753
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1629
1754
|
const systemPrompt = `\u4F60\u662F\u4E00\u4E2A\u5B9E\u4F53\u5173\u7CFB\u62BD\u53D6\u52A9\u624B\u3002\u5F53\u524D\u7CFB\u7EDF\u65F6\u95F4\uFF1A${now}\u3002
|
|
1630
1755
|
\u68C0\u67E5\u7ED9\u5B9A\u7684\u5BF9\u8BDD\u5185\u5BB9\uFF0C\u5224\u65AD\u5176\u4E2D\u662F\u5426\u5B58\u5728\u503C\u5F97\u8BB0\u5F55\u7684\u547D\u540D\u5B9E\u4F53\uFF08\u4EBA\u7269\u3001\u7EC4\u7EC7\u3001\u9879\u76EE\u3001\u5730\u70B9\u3001\u6280\u672F\u7B49\uFF09\u548C\u5B83\u4EEC\u4E4B\u95F4\u7684\u5173\u7CFB\u3002
|
|
@@ -1652,15 +1777,15 @@ ${formatted}`
|
|
|
1652
1777
|
}
|
|
1653
1778
|
}
|
|
1654
1779
|
} catch (err) {
|
|
1655
|
-
console.error("[LlmService]
|
|
1780
|
+
console.error("[LlmService] extractEntitiesFromMessages failed:", err);
|
|
1656
1781
|
}
|
|
1657
1782
|
return { entities: [], relations: [] };
|
|
1658
1783
|
}
|
|
1659
1784
|
// ── 对外主接口:压缩 + 实体抽取(并发执行两步)────────────────────────────────
|
|
1660
1785
|
async compress(messages) {
|
|
1661
1786
|
const [summary, extraction] = await Promise.all([
|
|
1662
|
-
this.
|
|
1663
|
-
this.
|
|
1787
|
+
this.summarizeMessages(messages),
|
|
1788
|
+
this.extractEntitiesFromMessages(messages)
|
|
1664
1789
|
]);
|
|
1665
1790
|
return {
|
|
1666
1791
|
title: summary.title,
|
|
@@ -1758,42 +1883,6 @@ ${context}`, false);
|
|
|
1758
1883
|
|
|
1759
1884
|
// src/manager/compress.manager.ts
|
|
1760
1885
|
import { v4 as uuidv4 } from "uuid";
|
|
1761
|
-
|
|
1762
|
-
// src/manager/semaphore.ts
|
|
1763
|
-
var Semaphore = class {
|
|
1764
|
-
count;
|
|
1765
|
-
queue = [];
|
|
1766
|
-
constructor(max) {
|
|
1767
|
-
this.count = max;
|
|
1768
|
-
}
|
|
1769
|
-
async run(fn) {
|
|
1770
|
-
await this.acquire();
|
|
1771
|
-
try {
|
|
1772
|
-
return await fn();
|
|
1773
|
-
} finally {
|
|
1774
|
-
this.release();
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
acquire() {
|
|
1778
|
-
if (this.count > 0) {
|
|
1779
|
-
this.count--;
|
|
1780
|
-
return Promise.resolve();
|
|
1781
|
-
}
|
|
1782
|
-
return new Promise((resolve) => {
|
|
1783
|
-
this.queue.push(resolve);
|
|
1784
|
-
});
|
|
1785
|
-
}
|
|
1786
|
-
release() {
|
|
1787
|
-
const next = this.queue.shift();
|
|
1788
|
-
if (next) {
|
|
1789
|
-
next();
|
|
1790
|
-
} else {
|
|
1791
|
-
this.count++;
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
};
|
|
1795
|
-
|
|
1796
|
-
// src/manager/compress.manager.ts
|
|
1797
1886
|
var CompressManager = class {
|
|
1798
1887
|
config;
|
|
1799
1888
|
lance;
|
|
@@ -1812,17 +1901,17 @@ var CompressManager = class {
|
|
|
1812
1901
|
this.sessionCache = sessionCache;
|
|
1813
1902
|
this.semaphore = new Semaphore(config.maxConcurrentCompressions);
|
|
1814
1903
|
}
|
|
1815
|
-
triggerCompress(sessionId, force = false) {
|
|
1904
|
+
triggerCompress(sessionId, force = false, waitGraph = false) {
|
|
1816
1905
|
const chain = this.sessionChain.get(sessionId) ?? Promise.resolve();
|
|
1817
1906
|
const next = chain.then(
|
|
1818
|
-
() => this.semaphore.run(() => this.doCompress(sessionId, force))
|
|
1907
|
+
() => this.semaphore.run(() => this.doCompress(sessionId, force, waitGraph))
|
|
1819
1908
|
);
|
|
1820
1909
|
this.sessionChain.set(sessionId, next.catch(() => {
|
|
1821
1910
|
}));
|
|
1822
1911
|
return next;
|
|
1823
1912
|
}
|
|
1824
|
-
async doCompress(sessionId, force) {
|
|
1825
|
-
const messages = this.sessionCache.getSessionMessages(sessionId);
|
|
1913
|
+
async doCompress(sessionId, force, waitGraph) {
|
|
1914
|
+
const messages = this.sessionCache.getSessionMessages(sessionId).slice();
|
|
1826
1915
|
if (messages.length === 0) return;
|
|
1827
1916
|
const entry = this.sessionCache.getEntry(sessionId);
|
|
1828
1917
|
if (!entry) return;
|
|
@@ -1830,15 +1919,15 @@ var CompressManager = class {
|
|
|
1830
1919
|
const { chatId, userId } = entry.ids;
|
|
1831
1920
|
const startTime = messages[0].createdAt;
|
|
1832
1921
|
const endTime = messages[messages.length - 1].createdAt;
|
|
1833
|
-
let
|
|
1922
|
+
let summary;
|
|
1834
1923
|
try {
|
|
1835
|
-
|
|
1924
|
+
summary = await this.llm.summarizeMessages(messages);
|
|
1836
1925
|
} catch (err) {
|
|
1837
|
-
console.error(`[CompressManager] LLM compress failed for session ${sessionId}
|
|
1926
|
+
console.error(`[CompressManager] LLM compress failed for session ${sessionId}: ${formatError(err)}`);
|
|
1838
1927
|
return;
|
|
1839
1928
|
}
|
|
1840
|
-
const { title, detail, summary, concise
|
|
1841
|
-
const topicTextForEmbed = [detail,
|
|
1929
|
+
const { title, detail, summary: summaryText, concise } = summary;
|
|
1930
|
+
const topicTextForEmbed = [detail, summaryText, concise].find((t) => t.length > 0) ?? "";
|
|
1842
1931
|
let topicVector = [];
|
|
1843
1932
|
try {
|
|
1844
1933
|
topicVector = await this.embed.embedOne(topicTextForEmbed);
|
|
@@ -1852,7 +1941,7 @@ var CompressManager = class {
|
|
|
1852
1941
|
chatId,
|
|
1853
1942
|
title,
|
|
1854
1943
|
detail,
|
|
1855
|
-
summary,
|
|
1944
|
+
summary: summaryText,
|
|
1856
1945
|
concise,
|
|
1857
1946
|
startTime,
|
|
1858
1947
|
endTime,
|
|
@@ -1866,9 +1955,6 @@ var CompressManager = class {
|
|
|
1866
1955
|
} catch (err) {
|
|
1867
1956
|
console.error(`[CompressManager] Save topic failed:`, err);
|
|
1868
1957
|
}
|
|
1869
|
-
if (entities.length > 0 || relations.length > 0) {
|
|
1870
|
-
await this.persistGraph(entities, relations, sessionId, chatId, userId, endTime);
|
|
1871
|
-
}
|
|
1872
1958
|
this.sessionCache.clearMessages(sessionId, endTime);
|
|
1873
1959
|
const [n1, n2, n3] = this.config.topicRatio;
|
|
1874
1960
|
try {
|
|
@@ -1877,6 +1963,19 @@ var CompressManager = class {
|
|
|
1877
1963
|
} catch (err) {
|
|
1878
1964
|
console.error(`[CompressManager] Rebuild history window failed:`, err);
|
|
1879
1965
|
}
|
|
1966
|
+
const graphTask = this.extractAndPersistGraph(messages, sessionId, chatId, userId, endTime);
|
|
1967
|
+
if (waitGraph) {
|
|
1968
|
+
await withTimeout(graphTask, this.config.graphBuildTimeoutMs, "flushChat graph build");
|
|
1969
|
+
} else {
|
|
1970
|
+
graphTask.catch((err) => {
|
|
1971
|
+
console.error(`[CompressManager] Background graph persist failed:`, err);
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
async extractAndPersistGraph(messages, sessionId, chatId, userId, endTime) {
|
|
1976
|
+
const { entities, relations } = await this.llm.extractEntitiesFromMessages(messages);
|
|
1977
|
+
if (entities.length === 0 && relations.length === 0) return;
|
|
1978
|
+
await this.persistGraph(entities, relations, sessionId, chatId, userId, endTime);
|
|
1880
1979
|
}
|
|
1881
1980
|
async persistGraph(rawEntities, rawRelations, sessionId, chatId, userId, messageTime) {
|
|
1882
1981
|
const entities = rawEntities.map((e) => ({
|
|
@@ -1919,6 +2018,16 @@ var CompressManager = class {
|
|
|
1919
2018
|
}
|
|
1920
2019
|
}
|
|
1921
2020
|
};
|
|
2021
|
+
function formatError(err) {
|
|
2022
|
+
return err instanceof Error ? err.message : String(err);
|
|
2023
|
+
}
|
|
2024
|
+
function withTimeout(promise, timeoutMs, label) {
|
|
2025
|
+
let timer;
|
|
2026
|
+
const timeout = new Promise((_, reject) => {
|
|
2027
|
+
timer = setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
2028
|
+
});
|
|
2029
|
+
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
2030
|
+
}
|
|
1922
2031
|
|
|
1923
2032
|
// src/manager/fact.cache.ts
|
|
1924
2033
|
import { v4 as uuidv42 } from "uuid";
|
|
@@ -2178,20 +2287,29 @@ var KnowledgeManager = class {
|
|
|
2178
2287
|
updatedAt: now
|
|
2179
2288
|
};
|
|
2180
2289
|
await this.lance.addDocument(doc);
|
|
2181
|
-
const
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2290
|
+
const chunkIngest = this.semaphore.run(() => this.ingestChunks(doc, pieces));
|
|
2291
|
+
const graphBuild = chunkIngest.then((chunks) => {
|
|
2292
|
+
if (!shouldBuildGraph) return;
|
|
2293
|
+
return this.buildDocumentGraph(doc, chunks);
|
|
2294
|
+
});
|
|
2295
|
+
if (opts.wait || opts.waitGraph) {
|
|
2296
|
+
await chunkIngest;
|
|
2297
|
+
} else {
|
|
2298
|
+
chunkIngest.catch((err) => {
|
|
2299
|
+
console.error("[KnowledgeManager] background chunk ingest failed:", err);
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2302
|
+
if (opts.waitGraph) {
|
|
2303
|
+
await withTimeout2(graphBuild, this.config.graphBuildTimeoutMs, "document graph build");
|
|
2186
2304
|
} else {
|
|
2187
|
-
|
|
2188
|
-
console.error("[KnowledgeManager] background
|
|
2305
|
+
graphBuild.catch((err) => {
|
|
2306
|
+
console.error("[KnowledgeManager] background graph build failed:", err);
|
|
2189
2307
|
});
|
|
2190
2308
|
}
|
|
2191
2309
|
return { docId };
|
|
2192
2310
|
}
|
|
2193
|
-
async
|
|
2194
|
-
if (pieces.length === 0) return;
|
|
2311
|
+
async ingestChunks(doc, pieces) {
|
|
2312
|
+
if (pieces.length === 0) return [];
|
|
2195
2313
|
const redundant = this.config.chunkRedundantIds;
|
|
2196
2314
|
const embedInputs = pieces.map(
|
|
2197
2315
|
(p) => p.headingPath ? `${p.headingPath}
|
|
@@ -2202,7 +2320,7 @@ ${p.content}` : p.content
|
|
|
2202
2320
|
vectors = await this.embed.embed(embedInputs);
|
|
2203
2321
|
} catch (err) {
|
|
2204
2322
|
console.error("[KnowledgeManager] embed chunks failed:", err);
|
|
2205
|
-
return;
|
|
2323
|
+
return [];
|
|
2206
2324
|
}
|
|
2207
2325
|
const chunks = pieces.map((p, i) => ({
|
|
2208
2326
|
chunkId: uuidv43(),
|
|
@@ -2219,17 +2337,12 @@ ${p.content}` : p.content
|
|
|
2219
2337
|
createdAt: doc.createdAt
|
|
2220
2338
|
}));
|
|
2221
2339
|
await this.lance.addChunks(chunks);
|
|
2222
|
-
|
|
2223
|
-
await this.buildDocumentGraph(doc, chunks).catch((err) => {
|
|
2224
|
-
console.error("[KnowledgeManager] build graph failed:", err);
|
|
2225
|
-
});
|
|
2226
|
-
}
|
|
2340
|
+
return chunks;
|
|
2227
2341
|
}
|
|
2228
2342
|
async buildDocumentGraph(doc, chunks) {
|
|
2229
|
-
|
|
2230
|
-
for (const chunk of chunks) {
|
|
2343
|
+
const extracted = await mapLimit2(chunks, this.config.graphExtractConcurrency, async (chunk) => {
|
|
2231
2344
|
const { entities: rawEntities, relations: rawRelations } = await this.llm.extractEntitiesFromText(chunk.content);
|
|
2232
|
-
if (rawEntities.length === 0 && rawRelations.length === 0)
|
|
2345
|
+
if (rawEntities.length === 0 && rawRelations.length === 0) return null;
|
|
2233
2346
|
const meta = {
|
|
2234
2347
|
userId: doc.userId,
|
|
2235
2348
|
chatId: doc.chatId,
|
|
@@ -2238,46 +2351,47 @@ ${p.content}` : p.content
|
|
|
2238
2351
|
chunkId: chunk.chunkId,
|
|
2239
2352
|
messageTime: doc.createdAt
|
|
2240
2353
|
};
|
|
2241
|
-
const
|
|
2354
|
+
const entities2 = rawEntities.map((e) => ({
|
|
2242
2355
|
name: e.name,
|
|
2243
2356
|
type: e.type,
|
|
2244
2357
|
meta: { ...meta, ...e.meta }
|
|
2245
2358
|
}));
|
|
2246
|
-
const
|
|
2359
|
+
const relations2 = rawRelations.map((r) => ({
|
|
2247
2360
|
from: r.from,
|
|
2248
2361
|
to: r.to,
|
|
2249
2362
|
type: r.type,
|
|
2250
2363
|
happenedAt: r.happenedAt ?? void 0,
|
|
2251
2364
|
meta: { ...meta, ...r.meta }
|
|
2252
2365
|
}));
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
KIND_KNOWLEDGE
|
|
2273
|
-
);
|
|
2274
|
-
extractedAny = true;
|
|
2275
|
-
}
|
|
2276
|
-
if (extractedAny) {
|
|
2277
|
-
await this.lance.updateDocumentGraphFlag(doc.docId, true).catch((err) => {
|
|
2278
|
-
console.error(`[KnowledgeManager] Failed to update graph flag for doc ${doc.docId}:`, err);
|
|
2279
|
-
});
|
|
2366
|
+
return { entities: entities2, relations: relations2 };
|
|
2367
|
+
});
|
|
2368
|
+
const entities = extracted.flatMap((item) => item?.entities ?? []);
|
|
2369
|
+
const relations = extracted.flatMap((item) => item?.relations ?? []);
|
|
2370
|
+
if (entities.length === 0 && relations.length === 0) return;
|
|
2371
|
+
const allNames = [
|
|
2372
|
+
.../* @__PURE__ */ new Set([
|
|
2373
|
+
...entities.map((e) => e.name),
|
|
2374
|
+
...relations.flatMap((r) => [r.from, r.to])
|
|
2375
|
+
])
|
|
2376
|
+
];
|
|
2377
|
+
if (allNames.length === 0) return;
|
|
2378
|
+
const embeddings = /* @__PURE__ */ new Map();
|
|
2379
|
+
try {
|
|
2380
|
+
const vecs = await this.embed.embed(allNames);
|
|
2381
|
+
allNames.forEach((name, i) => embeddings.set(name, vecs[i]));
|
|
2382
|
+
} catch (err) {
|
|
2383
|
+
console.error("[KnowledgeManager] embed entity names failed:", err);
|
|
2384
|
+
return;
|
|
2280
2385
|
}
|
|
2386
|
+
await this.grafeo.upsertEntitiesAndRelations(
|
|
2387
|
+
entities,
|
|
2388
|
+
relations,
|
|
2389
|
+
embeddings,
|
|
2390
|
+
KIND_KNOWLEDGE
|
|
2391
|
+
);
|
|
2392
|
+
await this.lance.updateDocumentGraphFlag(doc.docId, true).catch((err) => {
|
|
2393
|
+
console.error(`[KnowledgeManager] Failed to update graph flag for doc ${doc.docId}:`, err);
|
|
2394
|
+
});
|
|
2281
2395
|
}
|
|
2282
2396
|
// ── 检索 ───────────────────────────────────────────────────────────────────────
|
|
2283
2397
|
async searchKnowledge(opts) {
|
|
@@ -2439,6 +2553,26 @@ function inferTitle(markdown) {
|
|
|
2439
2553
|
}
|
|
2440
2554
|
return void 0;
|
|
2441
2555
|
}
|
|
2556
|
+
async function mapLimit2(items, limit, mapper) {
|
|
2557
|
+
const results = new Array(items.length);
|
|
2558
|
+
let next = 0;
|
|
2559
|
+
const workers = Array.from({ length: Math.min(Math.max(1, limit), items.length) }, async () => {
|
|
2560
|
+
while (true) {
|
|
2561
|
+
const index = next++;
|
|
2562
|
+
if (index >= items.length) return;
|
|
2563
|
+
results[index] = await mapper(items[index], index);
|
|
2564
|
+
}
|
|
2565
|
+
});
|
|
2566
|
+
await Promise.all(workers);
|
|
2567
|
+
return results;
|
|
2568
|
+
}
|
|
2569
|
+
function withTimeout2(promise, timeoutMs, label) {
|
|
2570
|
+
let timer;
|
|
2571
|
+
const timeout = new Promise((_, reject) => {
|
|
2572
|
+
timer = setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
2573
|
+
});
|
|
2574
|
+
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
2575
|
+
}
|
|
2442
2576
|
|
|
2443
2577
|
// src/manager/session.cache.ts
|
|
2444
2578
|
var SessionCache = class {
|
|
@@ -2558,27 +2692,62 @@ var MemoryManager = class {
|
|
|
2558
2692
|
this.sessionMap.set(s.sessionId, this.deserializeSession(s));
|
|
2559
2693
|
}
|
|
2560
2694
|
await this.restoreFromStorage();
|
|
2695
|
+
if (this.config.autoOptimizeOnInit) {
|
|
2696
|
+
void this.optimizeStorage().then((results) => {
|
|
2697
|
+
const versions = results.reduce((s, r) => s + r.oldVersionsRemoved, 0);
|
|
2698
|
+
const fragments = results.reduce((s, r) => s + r.fragmentsRemoved, 0);
|
|
2699
|
+
if (versions > 0 || fragments > 0) {
|
|
2700
|
+
console.info(
|
|
2701
|
+
`[MemoryManager] \u540E\u53F0\u5B58\u50A8\u538B\u5B9E\u5B8C\u6210\uFF1A\u6E05\u7406\u5386\u53F2\u7248\u672C ${versions} \u4E2A\uFF0C\u5408\u5E76\u788E\u7247 ${fragments} \u4E2A`
|
|
2702
|
+
);
|
|
2703
|
+
}
|
|
2704
|
+
}).catch((err) => {
|
|
2705
|
+
console.warn("[MemoryManager] \u540E\u53F0\u5B58\u50A8\u538B\u5B9E\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u670D\u52A1\uFF09:", err);
|
|
2706
|
+
});
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
/**
|
|
2710
|
+
* 手动触发存储压实与历史版本清理(init 后台会自动执行一次;上层维护任务也可调用)。
|
|
2711
|
+
* @param retentionMs 保留多久内的历史版本,默认取配置 optimizeVersionRetentionMs
|
|
2712
|
+
*/
|
|
2713
|
+
async optimizeStorage(retentionMs) {
|
|
2714
|
+
return this.lance.optimizeStorage(retentionMs ?? this.config.optimizeVersionRetentionMs);
|
|
2561
2715
|
}
|
|
2562
2716
|
async restoreFromStorage() {
|
|
2563
2717
|
const [n1, n2, n3] = this.config.topicRatio;
|
|
2564
2718
|
const sessionIds = await this.lance.getAllSessionIds();
|
|
2565
2719
|
if (sessionIds.length === 0) return;
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
if (rawMessages.length > 0) {
|
|
2574
|
-
this.sessionCache.addMessages(sessionId, rawMessages, chatId, userId);
|
|
2720
|
+
const topicsCache = /* @__PURE__ */ new Map();
|
|
2721
|
+
const getTopics = (chatId, userId) => {
|
|
2722
|
+
const key = `${chatId}\0${userId}`;
|
|
2723
|
+
let cached = topicsCache.get(key);
|
|
2724
|
+
if (!cached) {
|
|
2725
|
+
cached = this.lance.getRecentTopics(chatId, userId, n1, n2, n3);
|
|
2726
|
+
topicsCache.set(key, cached);
|
|
2575
2727
|
}
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2728
|
+
return cached;
|
|
2729
|
+
};
|
|
2730
|
+
const sem = new Semaphore(this.config.restoreConcurrency);
|
|
2731
|
+
await Promise.all(
|
|
2732
|
+
sessionIds.map(
|
|
2733
|
+
(sessionId) => sem.run(async () => {
|
|
2734
|
+
const recentMessages = await this.lance.getLatestMessages(sessionId, 1);
|
|
2735
|
+
if (recentMessages.length === 0) return;
|
|
2736
|
+
const { chatId, userId } = recentMessages[0];
|
|
2737
|
+
const topicGroups = await getTopics(chatId, userId);
|
|
2738
|
+
const n1EndTime = topicGroups.detail.length > 0 ? topicGroups.detail[topicGroups.detail.length - 1].endTime : 0;
|
|
2739
|
+
const rawMessages = n1EndTime > 0 ? await this.lance.getMessagesSince(sessionId, n1EndTime) : await this.lance.getLatestMessages(sessionId, 100);
|
|
2740
|
+
if (rawMessages.length > 0) {
|
|
2741
|
+
this.sessionCache.addMessages(sessionId, rawMessages, chatId, userId);
|
|
2742
|
+
}
|
|
2743
|
+
if (topicGroups.detail.length + topicGroups.summary.length + topicGroups.concise.length > 0) {
|
|
2744
|
+
this.sessionCache.buildHistoryWindow(sessionId, topicGroups);
|
|
2745
|
+
} else {
|
|
2746
|
+
this.sessionCache.setHistoryWindow(sessionId, "");
|
|
2747
|
+
}
|
|
2748
|
+
})
|
|
2749
|
+
)
|
|
2750
|
+
);
|
|
2582
2751
|
}
|
|
2583
2752
|
async updateChat(messages, opts) {
|
|
2584
2753
|
if (messages.length === 0) return;
|
|
@@ -2653,7 +2822,7 @@ var MemoryManager = class {
|
|
|
2653
2822
|
}
|
|
2654
2823
|
async flushChat(sessionId, opts) {
|
|
2655
2824
|
const sid = sessionId ?? DEFAULT_SESSION_ID;
|
|
2656
|
-
const promise = this.compressManager.triggerCompress(sid, true);
|
|
2825
|
+
const promise = this.compressManager.triggerCompress(sid, true, opts?.waitGraph === true);
|
|
2657
2826
|
if (opts?.wait) await promise;
|
|
2658
2827
|
}
|
|
2659
2828
|
async updateFacts(content, level, userId, chatId, sessionId) {
|