@cup319/mmpl 2.5.0
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/README.md +187 -0
- package/dist/auto-context-NUQFETSY.js +248 -0
- package/dist/auto-context-NUQFETSY.js.map +1 -0
- package/dist/checkpoint-ABRNCBCA.js +68 -0
- package/dist/checkpoint-ABRNCBCA.js.map +1 -0
- package/dist/chunk-2H7UOFLK.js +11 -0
- package/dist/chunk-2H7UOFLK.js.map +1 -0
- package/dist/chunk-CHVFXR24.js +19 -0
- package/dist/chunk-CHVFXR24.js.map +1 -0
- package/dist/chunk-JL66S7P7.js +151 -0
- package/dist/chunk-JL66S7P7.js.map +1 -0
- package/dist/chunk-SSDURI3I.js +46 -0
- package/dist/chunk-SSDURI3I.js.map +1 -0
- package/dist/index.js +21841 -0
- package/dist/index.js.map +1 -0
- package/dist/mermaid-injector-ROQAAM7B.js +171 -0
- package/dist/mermaid-injector-ROQAAM7B.js.map +1 -0
- package/dist/observability-6AMQYVCF.js +100 -0
- package/dist/observability-6AMQYVCF.js.map +1 -0
- package/dist/offload-engine-BF5IXXBH.js +10 -0
- package/dist/offload-engine-BF5IXXBH.js.map +1 -0
- package/dist/persona-generator-NZQVIVDA.js +228 -0
- package/dist/persona-generator-NZQVIVDA.js.map +1 -0
- package/dist/pipeline-manager-IJ2JLUMK.js +485 -0
- package/dist/pipeline-manager-IJ2JLUMK.js.map +1 -0
- package/dist/scene-builder-TLVG23BJ.js +296 -0
- package/dist/scene-builder-TLVG23BJ.js.map +1 -0
- package/dist/scene-index-SU6GMVDL.js +178 -0
- package/dist/scene-index-SU6GMVDL.js.map +1 -0
- package/dist/symbolic-A4XAGBBB.js +502 -0
- package/dist/symbolic-A4XAGBBB.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "./chunk-2H7UOFLK.js";
|
|
3
|
+
|
|
4
|
+
// src/pipeline-manager.ts
|
|
5
|
+
import * as crypto from "crypto";
|
|
6
|
+
var PipelineManager = class {
|
|
7
|
+
opts;
|
|
8
|
+
timer = null;
|
|
9
|
+
running = false;
|
|
10
|
+
started = false;
|
|
11
|
+
lastL1Time = 0;
|
|
12
|
+
lastL2Time = 0;
|
|
13
|
+
lastL3Time = 0;
|
|
14
|
+
lastTickAt = 0;
|
|
15
|
+
l1Pending = 0;
|
|
16
|
+
lastError = null;
|
|
17
|
+
/** 压缩调度状态 */
|
|
18
|
+
lastCompressTime = 0;
|
|
19
|
+
/** 整理调度状态 */
|
|
20
|
+
lastConsolidateTime = 0;
|
|
21
|
+
/** LLM 是否可用(analyzerApiUrl 非空) */
|
|
22
|
+
llmEnabled;
|
|
23
|
+
constructor(opts) {
|
|
24
|
+
const pollIntervalMs = opts.pollIntervalMs ?? 6e4;
|
|
25
|
+
this.opts = {
|
|
26
|
+
storage: opts.storage,
|
|
27
|
+
autoEvolver: opts.autoEvolver,
|
|
28
|
+
sceneBuilder: opts.sceneBuilder,
|
|
29
|
+
personaGen: opts.personaGen,
|
|
30
|
+
embeddingService: opts.embeddingService,
|
|
31
|
+
config: opts.config,
|
|
32
|
+
compressionService: opts.compressionService,
|
|
33
|
+
reinforcement: opts.reinforcement,
|
|
34
|
+
l1TriggerCount: opts.l1TriggerCount ?? 5,
|
|
35
|
+
l1IdleTimeoutMs: opts.l1IdleTimeoutMs ?? 3e5,
|
|
36
|
+
l2DelayAfterL1Ms: opts.l2DelayAfterL1Ms ?? 1e4,
|
|
37
|
+
l3DelayAfterL2Ms: opts.l3DelayAfterL2Ms ?? 1e4,
|
|
38
|
+
pollIntervalMs,
|
|
39
|
+
l2MinIntervalMs: opts.l2MinIntervalMs ?? 6e4,
|
|
40
|
+
l2MaxScenes: opts.l2MaxScenes ?? 15,
|
|
41
|
+
l3Mode: opts.l3Mode ?? "incremental",
|
|
42
|
+
compressIntervalMs: opts.compressIntervalMs ?? 36e5,
|
|
43
|
+
consolidateIntervalMs: opts.consolidateIntervalMs ?? 36e5
|
|
44
|
+
};
|
|
45
|
+
this.llmEnabled = !!opts.config?.analyzerApiUrl;
|
|
46
|
+
}
|
|
47
|
+
// ===== 生命周期 =====
|
|
48
|
+
/** 启动管道(幂等) */
|
|
49
|
+
start() {
|
|
50
|
+
if (this.timer) return;
|
|
51
|
+
const interval = Math.max(1e3, this.opts.pollIntervalMs);
|
|
52
|
+
this.timer = setInterval(() => {
|
|
53
|
+
this.tick().catch((err) => {
|
|
54
|
+
console.error("[pipeline] uncaught tick error:", err);
|
|
55
|
+
});
|
|
56
|
+
}, interval);
|
|
57
|
+
if (typeof this.timer.unref === "function") {
|
|
58
|
+
this.timer.unref();
|
|
59
|
+
}
|
|
60
|
+
this.started = true;
|
|
61
|
+
console.error(
|
|
62
|
+
`[pipeline] started (poll=${interval}ms, l1Trigger=${this.opts.l1TriggerCount}, llm=${this.llmEnabled ? "\u2705" : "\u26A0\uFE0F off"})`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
/** 停止管道(幂等) */
|
|
66
|
+
stop() {
|
|
67
|
+
if (this.timer) {
|
|
68
|
+
clearInterval(this.timer);
|
|
69
|
+
this.timer = null;
|
|
70
|
+
}
|
|
71
|
+
this.started = false;
|
|
72
|
+
}
|
|
73
|
+
/** 立即手动触发一次(不影响定时器;仍受 running 保护) */
|
|
74
|
+
async runOnce() {
|
|
75
|
+
await this.tick();
|
|
76
|
+
}
|
|
77
|
+
// ===== 核心调度 =====
|
|
78
|
+
/** 核心 tick:顺序检查 L1→L2→L3,running 标志防止重叠 */
|
|
79
|
+
async tick() {
|
|
80
|
+
if (this.running) return;
|
|
81
|
+
this.running = true;
|
|
82
|
+
this.lastTickAt = Date.now();
|
|
83
|
+
try {
|
|
84
|
+
await this.checkL1();
|
|
85
|
+
await this.checkL2();
|
|
86
|
+
await this.checkL3();
|
|
87
|
+
await this.checkCompress();
|
|
88
|
+
await this.checkConsolidate();
|
|
89
|
+
this.lastError = null;
|
|
90
|
+
} catch (err) {
|
|
91
|
+
this.lastError = String(err);
|
|
92
|
+
console.error("[pipeline] tick error:", err);
|
|
93
|
+
} finally {
|
|
94
|
+
this.running = false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// ===== L1: L0 → L1 提取 =====
|
|
98
|
+
async checkL1() {
|
|
99
|
+
if (!this.llmEnabled) return;
|
|
100
|
+
if (!this.opts.embeddingService?.available) return;
|
|
101
|
+
let unconsumed = [];
|
|
102
|
+
try {
|
|
103
|
+
unconsumed = await this.opts.storage.getUnconsumedRaw(100, "conversation");
|
|
104
|
+
} catch (err) {
|
|
105
|
+
console.warn("[pipeline] L1 \u8BFB\u53D6 L0 \u5931\u8D25:", err);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this.l1Pending = unconsumed.length;
|
|
109
|
+
const now = Date.now();
|
|
110
|
+
const shouldTrigger = unconsumed.length >= this.opts.l1TriggerCount || unconsumed.length > 0 && now - this.lastL1Time > this.opts.l1IdleTimeoutMs;
|
|
111
|
+
if (!shouldTrigger) return;
|
|
112
|
+
try {
|
|
113
|
+
await this.runL1Extraction(unconsumed);
|
|
114
|
+
this.lastL1Time = Date.now();
|
|
115
|
+
} catch (err) {
|
|
116
|
+
console.warn("[pipeline] L1 \u63D0\u53D6\u5931\u8D25:", err);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* L1 提取执行 —— 与 index.ts 的 memory_extract_from_raw 逻辑一致:
|
|
121
|
+
* 1. autoEvolver.extract
|
|
122
|
+
* 2. 向量去重 (cosineSim > 0.92)
|
|
123
|
+
* 3. 写入 memories + drill_chain(L1→L0) + markRawConsumed
|
|
124
|
+
*/
|
|
125
|
+
async runL1Extraction(rawRecords) {
|
|
126
|
+
if (rawRecords.length === 0) return;
|
|
127
|
+
const { storage, autoEvolver, embeddingService } = this.opts;
|
|
128
|
+
const conversations = rawRecords.map((r) => r.content);
|
|
129
|
+
let extracted;
|
|
130
|
+
try {
|
|
131
|
+
extracted = await autoEvolver.extract(conversations);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.warn("[pipeline] L1 autoEvolver.extract \u5931\u8D25:", err);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (!Array.isArray(extracted) || extracted.length === 0) {
|
|
137
|
+
await this.markConsumed(rawRecords, `extracted_${Date.now()}`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const written = [];
|
|
141
|
+
for (const item of extracted) {
|
|
142
|
+
try {
|
|
143
|
+
if (!embeddingService.available) break;
|
|
144
|
+
const emb = await embeddingService.embed(item.content);
|
|
145
|
+
const isDup = await storage.findDuplicates(emb);
|
|
146
|
+
if (isDup) continue;
|
|
147
|
+
const id = `mem_${Date.now()}_${crypto.randomUUID().split("-")[0]}`;
|
|
148
|
+
const now = Date.now();
|
|
149
|
+
await storage.createMemory({
|
|
150
|
+
id,
|
|
151
|
+
content: item.content,
|
|
152
|
+
type: item.type || "fact",
|
|
153
|
+
entities: item.entities || [],
|
|
154
|
+
importance: item.importance || 0.5,
|
|
155
|
+
sentiment: 0.5,
|
|
156
|
+
tags: item.tags || [],
|
|
157
|
+
temporal: {},
|
|
158
|
+
embedding: emb,
|
|
159
|
+
access_count: 0,
|
|
160
|
+
last_accessed: null,
|
|
161
|
+
created_at: now,
|
|
162
|
+
strength: item.importance || 0.5,
|
|
163
|
+
review_history: [],
|
|
164
|
+
status: "active",
|
|
165
|
+
infrequent_since: null,
|
|
166
|
+
last_deep_recall: null,
|
|
167
|
+
permanent: false,
|
|
168
|
+
consecutive_correct: 0,
|
|
169
|
+
promotion_asked: false,
|
|
170
|
+
promotion_rejected_at: null,
|
|
171
|
+
compressed: false,
|
|
172
|
+
source_ids: [],
|
|
173
|
+
compression_level: 0,
|
|
174
|
+
anchor_id: null
|
|
175
|
+
});
|
|
176
|
+
for (const r of rawRecords) {
|
|
177
|
+
try {
|
|
178
|
+
await storage.addDrillChain({ l1_id: id, l0_id: r.id, created_at: now });
|
|
179
|
+
} catch {
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
written.push({ id });
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.warn("[pipeline] L1 \u5199\u5165\u5931\u8D25:", err);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const firstWrittenId = written.length > 0 ? written[0].id : `extracted_${Date.now()}`;
|
|
188
|
+
await this.markConsumed(rawRecords, firstWrittenId);
|
|
189
|
+
console.error(
|
|
190
|
+
`[pipeline] L1 done: raw=${rawRecords.length} extracted=${extracted.length} written=${written.length}`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
async markConsumed(rawRecords, l1Id) {
|
|
194
|
+
for (const r of rawRecords) {
|
|
195
|
+
try {
|
|
196
|
+
await this.opts.storage.markRawConsumed(r.id, l1Id);
|
|
197
|
+
} catch {
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// ===== L2: L1 → L2 场景构建 =====
|
|
202
|
+
async checkL2() {
|
|
203
|
+
if (!this.llmEnabled) return;
|
|
204
|
+
if (this.lastL1Time === 0) return;
|
|
205
|
+
const now = Date.now();
|
|
206
|
+
const elapsedSinceL1 = now - this.lastL1Time;
|
|
207
|
+
const sinceLastL2 = now - this.lastL2Time;
|
|
208
|
+
if (elapsedSinceL1 >= this.opts.l2DelayAfterL1Ms && sinceLastL2 > this.opts.l2MinIntervalMs) {
|
|
209
|
+
try {
|
|
210
|
+
await this.runL2SceneBuild();
|
|
211
|
+
this.lastL2Time = Date.now();
|
|
212
|
+
} catch (err) {
|
|
213
|
+
console.warn("[pipeline] L2 \u573A\u666F\u6784\u5EFA\u5931\u8D25:", err);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* L2 场景构建 —— 与 index.ts 的 memory_scene_build 逻辑一致:
|
|
219
|
+
* 1. 获取 active memories
|
|
220
|
+
* 2. sceneBuilder.buildScenes
|
|
221
|
+
* 3. writeSceneMarkdown + storage.createScene + drill_chain(L2→L1)
|
|
222
|
+
*/
|
|
223
|
+
async runL2SceneBuild() {
|
|
224
|
+
const { storage, sceneBuilder } = this.opts;
|
|
225
|
+
let memories;
|
|
226
|
+
try {
|
|
227
|
+
if (typeof storage.getActiveMemories === "function") {
|
|
228
|
+
memories = await storage.getActiveMemories();
|
|
229
|
+
} else {
|
|
230
|
+
const all = await storage.getAllMemories();
|
|
231
|
+
memories = all.filter((m) => m.status === "active");
|
|
232
|
+
}
|
|
233
|
+
} catch (err) {
|
|
234
|
+
console.warn("[pipeline] L2 \u8BFB\u53D6 active memories \u5931\u8D25:", err);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (!memories || memories.length === 0) {
|
|
238
|
+
console.error("[pipeline] L2 \u8DF3\u8FC7\uFF1A\u65E0 active \u8BB0\u5FC6");
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
let scenes;
|
|
242
|
+
try {
|
|
243
|
+
scenes = await sceneBuilder.buildScenes({
|
|
244
|
+
memories,
|
|
245
|
+
maxScenes: this.opts.l2MaxScenes
|
|
246
|
+
});
|
|
247
|
+
} catch (err) {
|
|
248
|
+
console.warn("[pipeline] L2 buildScenes \u5931\u8D25:", err);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (!Array.isArray(scenes) || scenes.length === 0) {
|
|
252
|
+
console.error("[pipeline] L2 \u8DF3\u8FC7\uFF1A\u672A\u751F\u6210\u573A\u666F");
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
let persisted = 0;
|
|
256
|
+
for (const scene of scenes) {
|
|
257
|
+
try {
|
|
258
|
+
if (typeof sceneBuilder.writeSceneMarkdown === "function") {
|
|
259
|
+
await sceneBuilder.writeSceneMarkdown(scene, memories);
|
|
260
|
+
}
|
|
261
|
+
await storage.createScene({
|
|
262
|
+
id: scene.id,
|
|
263
|
+
label: scene.label,
|
|
264
|
+
summary: scene.summary,
|
|
265
|
+
memory_ids: scene.memory_ids,
|
|
266
|
+
file_path: scene.file_path,
|
|
267
|
+
created_at: scene.created_at,
|
|
268
|
+
updated_at: scene.updated_at,
|
|
269
|
+
embedding: scene.embedding || []
|
|
270
|
+
});
|
|
271
|
+
for (const mid of scene.memory_ids || []) {
|
|
272
|
+
try {
|
|
273
|
+
await storage.addDrillChain({ l2_id: scene.id, l1_id: mid, created_at: Date.now() });
|
|
274
|
+
} catch {
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
persisted++;
|
|
278
|
+
} catch (err) {
|
|
279
|
+
console.warn(`[pipeline] L2 \u6301\u4E45\u5316\u573A\u666F\u5931\u8D25 (${scene.id}):`, err);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
console.error(`[pipeline] L2 done: scenes=${scenes.length} persisted=${persisted}`);
|
|
283
|
+
}
|
|
284
|
+
// ===== L3: L2 → L3 画像生成 =====
|
|
285
|
+
async checkL3() {
|
|
286
|
+
if (!this.llmEnabled) return;
|
|
287
|
+
if (this.lastL2Time === 0) return;
|
|
288
|
+
const now = Date.now();
|
|
289
|
+
const elapsedSinceL2 = now - this.lastL2Time;
|
|
290
|
+
if (elapsedSinceL2 >= this.opts.l3DelayAfterL2Ms) {
|
|
291
|
+
try {
|
|
292
|
+
await this.runL3Persona();
|
|
293
|
+
this.lastL3Time = Date.now();
|
|
294
|
+
} catch (err) {
|
|
295
|
+
console.warn("[pipeline] L3 \u753B\u50CF\u751F\u6210\u5931\u8D25:", err);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* L3 画像生成 —— 与 index.ts 的 memory_persona_update 逻辑一致:
|
|
301
|
+
* 1. 获取 scenes
|
|
302
|
+
* 2. personaGen.generate
|
|
303
|
+
* 3. storage.createPersona + drill_chain(L3→L2)
|
|
304
|
+
*/
|
|
305
|
+
async runL3Persona() {
|
|
306
|
+
const { storage, personaGen } = this.opts;
|
|
307
|
+
let sceneRows;
|
|
308
|
+
try {
|
|
309
|
+
sceneRows = await storage.getAllScenes();
|
|
310
|
+
} catch (err) {
|
|
311
|
+
console.warn("[pipeline] L3 \u8BFB\u53D6 scenes \u5931\u8D25:", err);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (!sceneRows || sceneRows.length === 0) {
|
|
315
|
+
console.error("[pipeline] L3 \u8DF3\u8FC7\uFF1A\u65E0\u53EF\u7528\u573A\u666F");
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const scenes = sceneRows.map((r) => ({
|
|
319
|
+
id: r.id,
|
|
320
|
+
label: r.label,
|
|
321
|
+
summary: r.summary || "",
|
|
322
|
+
memory_ids: typeof r.memory_ids === "string" ? safeParse(r.memory_ids) || [] : r.memory_ids || [],
|
|
323
|
+
file_path: r.file_path,
|
|
324
|
+
created_at: r.created_at,
|
|
325
|
+
updated_at: r.updated_at,
|
|
326
|
+
embedding: []
|
|
327
|
+
}));
|
|
328
|
+
let existing = null;
|
|
329
|
+
try {
|
|
330
|
+
existing = await storage.getLatestPersona();
|
|
331
|
+
} catch (err) {
|
|
332
|
+
console.warn("[pipeline] L3 \u8BFB\u53D6\u73B0\u6709 persona \u5931\u8D25:", err);
|
|
333
|
+
}
|
|
334
|
+
let persona;
|
|
335
|
+
try {
|
|
336
|
+
persona = await personaGen.generate({
|
|
337
|
+
scenes,
|
|
338
|
+
mode: this.opts.l3Mode,
|
|
339
|
+
existingPersona: existing
|
|
340
|
+
});
|
|
341
|
+
} catch (err) {
|
|
342
|
+
console.warn("[pipeline] L3 personaGen.generate \u5931\u8D25:", err);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (!persona) {
|
|
346
|
+
console.error("[pipeline] L3 \u8DF3\u8FC7\uFF1A\u672A\u751F\u6210\u753B\u50CF");
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
await storage.createPersona({
|
|
351
|
+
id: persona.id,
|
|
352
|
+
content: persona.content,
|
|
353
|
+
file_path: persona.file_path,
|
|
354
|
+
scene_ids: persona.scene_ids,
|
|
355
|
+
version: persona.version,
|
|
356
|
+
created_at: persona.created_at,
|
|
357
|
+
updated_at: persona.updated_at
|
|
358
|
+
});
|
|
359
|
+
for (const sid of persona.scene_ids || []) {
|
|
360
|
+
try {
|
|
361
|
+
await storage.addDrillChain({ l3_id: persona.id, l2_id: sid, created_at: Date.now() });
|
|
362
|
+
} catch {
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
console.error(
|
|
366
|
+
`[pipeline] L3 done: id=${persona.id} version=${persona.version} len=${persona.content?.length ?? 0}`
|
|
367
|
+
);
|
|
368
|
+
} catch (err) {
|
|
369
|
+
console.warn("[pipeline] L3 createPersona \u5931\u8D25:", err);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
// ===== 压缩调度 =====
|
|
373
|
+
/**
|
|
374
|
+
* 检查是否到了自动压缩的时间,如果是则调用 CompressionService.autoCompress()
|
|
375
|
+
* 默认间隔 1 小时,需要 compressionService 已注入且 LLM 可用
|
|
376
|
+
*/
|
|
377
|
+
async checkCompress() {
|
|
378
|
+
if (!this.opts.compressionService) return;
|
|
379
|
+
if (!this.llmEnabled) return;
|
|
380
|
+
const now = Date.now();
|
|
381
|
+
if (now - this.lastCompressTime < this.opts.compressIntervalMs) return;
|
|
382
|
+
try {
|
|
383
|
+
this.lastCompressTime = now;
|
|
384
|
+
const results = await this.opts.compressionService.autoCompress();
|
|
385
|
+
if (results.length > 0) {
|
|
386
|
+
console.error(
|
|
387
|
+
`[pipeline] auto-compress done: anchors=${results.length} levels=${results.map((r) => `L${r.level}`).join(",")}`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
} catch (err) {
|
|
391
|
+
console.warn("[pipeline] auto-compress \u5931\u8D25:", err);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// ===== 整理调度 =====
|
|
395
|
+
/**
|
|
396
|
+
* 定期运行记忆整理(衰减 -> 降级 -> 归档 -> 删除)
|
|
397
|
+
* 默认间隔 1 小时
|
|
398
|
+
*/
|
|
399
|
+
async checkConsolidate() {
|
|
400
|
+
if (!this.opts.reinforcement) return;
|
|
401
|
+
const now = Date.now();
|
|
402
|
+
if (now - this.lastConsolidateTime < this.opts.consolidateIntervalMs) return;
|
|
403
|
+
try {
|
|
404
|
+
this.lastConsolidateTime = now;
|
|
405
|
+
const { storage, reinforcement } = this.opts;
|
|
406
|
+
const active = await storage.getActiveMemories();
|
|
407
|
+
const infrequent = await storage.getInfrequentMemories();
|
|
408
|
+
const memories = [...active, ...infrequent].filter(
|
|
409
|
+
(m) => !m.permanent
|
|
410
|
+
);
|
|
411
|
+
if (memories.length === 0) return;
|
|
412
|
+
const decayResults = reinforcement.computeDecayBatch(memories);
|
|
413
|
+
let downgraded = 0;
|
|
414
|
+
let archived = 0;
|
|
415
|
+
let deleted = 0;
|
|
416
|
+
let updated = 0;
|
|
417
|
+
for (const result of decayResults) {
|
|
418
|
+
switch (result.action) {
|
|
419
|
+
case "delete":
|
|
420
|
+
await storage.deleteMemory(result.id);
|
|
421
|
+
deleted++;
|
|
422
|
+
break;
|
|
423
|
+
case "archive":
|
|
424
|
+
await storage.updateMemory(result.id, {
|
|
425
|
+
strength: result.newStrength,
|
|
426
|
+
status: "archived"
|
|
427
|
+
});
|
|
428
|
+
archived++;
|
|
429
|
+
break;
|
|
430
|
+
case "downgrade_to_infrequent":
|
|
431
|
+
await storage.updateMemory(result.id, {
|
|
432
|
+
strength: result.newStrength,
|
|
433
|
+
status: "infrequent",
|
|
434
|
+
infrequent_since: Date.now()
|
|
435
|
+
});
|
|
436
|
+
downgraded++;
|
|
437
|
+
break;
|
|
438
|
+
case "none":
|
|
439
|
+
default:
|
|
440
|
+
if (result.newStrength !== void 0) {
|
|
441
|
+
await storage.updateMemory(result.id, {
|
|
442
|
+
strength: result.newStrength
|
|
443
|
+
});
|
|
444
|
+
updated++;
|
|
445
|
+
}
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
console.error(
|
|
450
|
+
`[pipeline] auto-consolidate done: processed=${decayResults.length} updated=${updated} downgraded=${downgraded} archived=${archived} deleted=${deleted}`
|
|
451
|
+
);
|
|
452
|
+
} catch (err) {
|
|
453
|
+
console.warn("[pipeline] auto-consolidate \u5931\u8D25:", err);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
// ===== Observability =====
|
|
457
|
+
/** 获取管道状态(供 observability 用) */
|
|
458
|
+
getStatus() {
|
|
459
|
+
const now = Date.now();
|
|
460
|
+
const nextPollIn = this.timer && this.lastTickAt > 0 ? Math.max(0, this.opts.pollIntervalMs - (now - this.lastTickAt)) : this.timer ? this.opts.pollIntervalMs : 0;
|
|
461
|
+
return {
|
|
462
|
+
running: this.running,
|
|
463
|
+
llmEnabled: this.llmEnabled,
|
|
464
|
+
started: this.started,
|
|
465
|
+
l1Pending: this.l1Pending,
|
|
466
|
+
lastL1Time: this.lastL1Time,
|
|
467
|
+
lastL2Time: this.lastL2Time,
|
|
468
|
+
lastL3Time: this.lastL3Time,
|
|
469
|
+
lastTickAt: this.lastTickAt,
|
|
470
|
+
nextPollIn,
|
|
471
|
+
lastError: this.lastError
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
function safeParse(s) {
|
|
476
|
+
try {
|
|
477
|
+
return JSON.parse(s);
|
|
478
|
+
} catch {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
export {
|
|
483
|
+
PipelineManager
|
|
484
|
+
};
|
|
485
|
+
//# sourceMappingURL=pipeline-manager-IJ2JLUMK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pipeline-manager.ts"],"sourcesContent":["/**\n * PipelineManager — 后台定时自动触发 L0→L1→L2→L3 记忆处理管道。\n *\n * 进程内(长驻 MCP Server)的轮询调度器:\n * - L1: 未消费的 L0 记录达到阈值或空闲超时 → 调用 autoEvolver 提取原子事实并去重写入\n * - L2: L1 完成延迟后 → 用 sceneBuilder 聚类 active memories 为场景块并持久化\n * - L3: L2 完成延迟后 → 用 personaGen 合成/更新用户画像并持久化\n *\n * 设计约束:\n * - 所有 LLM/IO 调用 try-catch,失败 console.warn 后继续(不崩溃管道)\n * - 无 analyzerApiUrl(无 LLM)时跳过三级处理,仅保持轮询\n * - tick() 用 running 标志防止重叠执行\n * - start()/stop() 幂等\n */\nimport * as crypto from \"crypto\";\n\nexport interface PipelineManagerOptions {\n storage: any;\n autoEvolver: any;\n sceneBuilder: any;\n personaGen: any;\n embeddingService: any;\n /** config 对象,用于读取 analyzerApiUrl 判定 LLM 是否可用 */\n config?: any;\n /** 压缩服务,用于 autoCompress 定时压缩 */\n compressionService?: any;\n /** 强化引擎,用于 auto-consolidation 定时整理 */\n reinforcement?: any;\n // ===== 管道参数 =====\n /** L0 积累多少条触发 L1 提取,默认 5 */\n l1TriggerCount?: number;\n /** L0 空闲多久触发 L1,默认 300000 (5分钟) */\n l1IdleTimeoutMs?: number;\n /** L1 完成后多久触发 L2,默认 10000 (10秒) */\n l2DelayAfterL1Ms?: number;\n /** L2 完成后多久触发 L3,默认 10000 (10秒) */\n l3DelayAfterL2Ms?: number;\n /** 轮询间隔,默认 60000 (1分钟) */\n pollIntervalMs?: number;\n /** 两次 L2 之间的最小间隔(防��),默认 60000 */\n l2MinIntervalMs?: number;\n /** buildScenes 的 maxScenes 参数,默认 15 */\n l2MaxScenes?: number;\n /** L3 画像模式,默认 incremental */\n l3Mode?: \"first\" | \"incremental\";\n /** 自动压缩检查间隔(ms),默认 3600000 (1小时) */\n compressIntervalMs?: number;\n /** 自动整理检查间隔(ms),默认 3600000 (1小时) */\n consolidateIntervalMs?: number;\n}\n\nexport interface PipelineStatus {\n running: boolean;\n llmEnabled: boolean;\n started: boolean;\n l1Pending: number;\n lastL1Time: number;\n lastL2Time: number;\n lastL3Time: number;\n lastTickAt: number;\n nextPollIn: number;\n lastError: string | null;\n}\n\nexport class PipelineManager {\n private opts: Required<Omit<PipelineManagerOptions, \"storage\" | \"autoEvolver\" | \"sceneBuilder\" | \"personaGen\" | \"embeddingService\" | \"config\" | \"compressionService\" | \"reinforcement\">> & {\n storage: any;\n autoEvolver: any;\n sceneBuilder: any;\n personaGen: any;\n embeddingService: any;\n config: any;\n compressionService: any;\n reinforcement: any;\n };\n\n private timer: NodeJS.Timeout | null = null;\n private running: boolean = false;\n private started: boolean = false;\n\n private lastL1Time: number = 0;\n private lastL2Time: number = 0;\n private lastL3Time: number = 0;\n private lastTickAt: number = 0;\n private l1Pending: number = 0;\n private lastError: string | null = null;\n\n /** 压缩调度状态 */\n private lastCompressTime: number = 0;\n /** 整理调度状态 */\n private lastConsolidateTime: number = 0;\n\n /** LLM 是否可用(analyzerApiUrl 非空) */\n readonly llmEnabled: boolean;\n\n constructor(opts: PipelineManagerOptions) {\n const pollIntervalMs = opts.pollIntervalMs ?? 60000;\n this.opts = {\n storage: opts.storage,\n autoEvolver: opts.autoEvolver,\n sceneBuilder: opts.sceneBuilder,\n personaGen: opts.personaGen,\n embeddingService: opts.embeddingService,\n config: opts.config,\n compressionService: opts.compressionService,\n reinforcement: opts.reinforcement,\n l1TriggerCount: opts.l1TriggerCount ?? 5,\n l1IdleTimeoutMs: opts.l1IdleTimeoutMs ?? 300000,\n l2DelayAfterL1Ms: opts.l2DelayAfterL1Ms ?? 10000,\n l3DelayAfterL2Ms: opts.l3DelayAfterL2Ms ?? 10000,\n pollIntervalMs,\n l2MinIntervalMs: opts.l2MinIntervalMs ?? 60000,\n l2MaxScenes: opts.l2MaxScenes ?? 15,\n l3Mode: opts.l3Mode ?? \"incremental\",\n compressIntervalMs: opts.compressIntervalMs ?? 3600000,\n consolidateIntervalMs: opts.consolidateIntervalMs ?? 3600000,\n };\n this.llmEnabled = !!opts.config?.analyzerApiUrl;\n }\n\n // ===== 生命周期 =====\n\n /** 启动管道(幂等) */\n start(): void {\n if (this.timer) return; // 已启动,幂等\n const interval = Math.max(1000, this.opts.pollIntervalMs);\n this.timer = setInterval(() => {\n // tick 内部自带 running 防重入,且异常自消化,不会冒泡到 setInterval\n this.tick().catch((err) => {\n console.error(\"[pipeline] uncaught tick error:\", err);\n });\n }, interval);\n // 防止 timer 阻止进程退出\n if (typeof (this.timer as any).unref === \"function\") {\n (this.timer as any).unref();\n }\n this.started = true;\n console.error(\n `[pipeline] started (poll=${interval}ms, l1Trigger=${this.opts.l1TriggerCount}, llm=${this.llmEnabled ? \"✅\" : \"⚠️ off\"})`\n );\n }\n\n /** 停止管道(幂等) */\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.started = false;\n }\n\n /** 立即手动触发一次(不影响定时器;仍受 running 保护) */\n async runOnce(): Promise<void> {\n await this.tick();\n }\n\n // ===== 核心调度 =====\n\n /** 核心 tick:顺序检查 L1→L2→L3,running 标志防止重叠 */\n private async tick(): Promise<void> {\n if (this.running) return;\n this.running = true;\n this.lastTickAt = Date.now();\n try {\n await this.checkL1();\n await this.checkL2();\n await this.checkL3();\n await this.checkCompress();\n await this.checkConsolidate();\n this.lastError = null;\n } catch (err) {\n this.lastError = String(err);\n console.error(\"[pipeline] tick error:\", err);\n } finally {\n this.running = false;\n }\n }\n\n // ===== L1: L0 → L1 提取 =====\n\n private async checkL1(): Promise<void> {\n // 无 LLM 配置则跳过,但继续轮询\n if (!this.llmEnabled) return;\n if (!this.opts.embeddingService?.available) return; // 提取依赖向量去重\n\n let unconsumed: any[] = [];\n try {\n unconsumed = await this.opts.storage.getUnconsumedRaw(100, \"conversation\");\n } catch (err) {\n console.warn(\"[pipeline] L1 读取 L0 失败:\", err);\n return;\n }\n this.l1Pending = unconsumed.length;\n\n const now = Date.now();\n // 触发条件:达到数量阈值 OR (有积压 AND 空闲超时)\n const shouldTrigger =\n unconsumed.length >= this.opts.l1TriggerCount ||\n (unconsumed.length > 0 && now - this.lastL1Time > this.opts.l1IdleTimeoutMs);\n if (!shouldTrigger) return;\n\n try {\n await this.runL1Extraction(unconsumed);\n this.lastL1Time = Date.now();\n } catch (err) {\n console.warn(\"[pipeline] L1 提取失败:\", err);\n }\n }\n\n /**\n * L1 提取执行 —— 与 index.ts 的 memory_extract_from_raw 逻辑一致:\n * 1. autoEvolver.extract\n * 2. 向量去重 (cosineSim > 0.92)\n * 3. 写入 memories + drill_chain(L1→L0) + markRawConsumed\n */\n private async runL1Extraction(rawRecords: any[]): Promise<void> {\n if (rawRecords.length === 0) return;\n const { storage, autoEvolver, embeddingService } = this.opts;\n\n // 1. LLM 提取\n const conversations = rawRecords.map((r: any) => r.content);\n let extracted: any[];\n try {\n extracted = await autoEvolver.extract(conversations);\n } catch (err) {\n console.warn(\"[pipeline] L1 autoEvolver.extract 失败:\", err);\n return;\n }\n if (!Array.isArray(extracted) || extracted.length === 0) {\n // 提取为空也标记为已消费,避免反复喂给 LLM 同样的无意义内容\n await this.markConsumed(rawRecords, `extracted_${Date.now()}`);\n return;\n }\n\n // 2. 去重 + 写入 — DuckDB server-side array_cosine_similarity()\n const written: { id: string }[] = [];\n for (const item of extracted) {\n try {\n if (!embeddingService.available) break;\n const emb = await embeddingService.embed(item.content);\n\n // 向量去重:DuckDB SQL 替代 O(N) JS 循环\n const isDup = await storage.findDuplicates(emb);\n if (isDup) continue;\n\n // 写入 L1 memory\n const id = `mem_${Date.now()}_${crypto.randomUUID().split(\"-\")[0]}`;\n const now = Date.now();\n await storage.createMemory({\n id,\n content: item.content,\n type: item.type || \"fact\",\n entities: item.entities || [],\n importance: item.importance || 0.5,\n sentiment: 0.5,\n tags: item.tags || [],\n temporal: {},\n embedding: emb,\n access_count: 0,\n last_accessed: null,\n created_at: now,\n strength: item.importance || 0.5,\n review_history: [],\n status: \"active\",\n infrequent_since: null,\n last_deep_recall: null,\n permanent: false,\n consecutive_correct: 0,\n promotion_asked: false,\n promotion_rejected_at: null,\n compressed: false,\n source_ids: [],\n compression_level: 0,\n anchor_id: null,\n });\n\n // 钻取链 L1→L0\n for (const r of rawRecords) {\n try {\n await storage.addDrillChain({ l1_id: id, l0_id: r.id, created_at: now });\n } catch {}\n }\n written.push({ id });\n } catch (err) {\n console.warn(\"[pipeline] L1 写入失败:\", err);\n }\n }\n\n // 3. 标记 L0 已消费\n const firstWrittenId = written.length > 0 ? written[0].id : `extracted_${Date.now()}`;\n await this.markConsumed(rawRecords, firstWrittenId);\n\n console.error(\n `[pipeline] L1 done: raw=${rawRecords.length} extracted=${extracted.length} written=${written.length}`\n );\n }\n\n private async markConsumed(rawRecords: any[], l1Id: string): Promise<void> {\n for (const r of rawRecords) {\n try {\n await this.opts.storage.markRawConsumed(r.id, l1Id);\n } catch {}\n }\n }\n\n // ===== L2: L1 → L2 场景构建 =====\n\n private async checkL2(): Promise<void> {\n if (!this.llmEnabled) return;\n if (this.lastL1Time === 0) return; // 从未跑过 L1,不触发 L2\n\n const now = Date.now();\n const elapsedSinceL1 = now - this.lastL1Time;\n const sinceLastL2 = now - this.lastL2Time;\n\n // 条件:L1 后过了 delay 时间 AND 距上次 L2 至少 l2MinIntervalMs\n if (\n elapsedSinceL1 >= this.opts.l2DelayAfterL1Ms &&\n sinceLastL2 > this.opts.l2MinIntervalMs\n ) {\n try {\n await this.runL2SceneBuild();\n this.lastL2Time = Date.now();\n } catch (err) {\n console.warn(\"[pipeline] L2 场景构建失败:\", err);\n }\n }\n }\n\n /**\n * L2 场景构建 —— 与 index.ts 的 memory_scene_build 逻辑一致:\n * 1. 获取 active memories\n * 2. sceneBuilder.buildScenes\n * 3. writeSceneMarkdown + storage.createScene + drill_chain(L2→L1)\n */\n private async runL2SceneBuild(): Promise<void> {\n const { storage, sceneBuilder } = this.opts;\n\n let memories: any[];\n try {\n // 优先用 getActiveMemories(更高效),降级到 getAllMemories 过滤\n if (typeof storage.getActiveMemories === \"function\") {\n memories = await storage.getActiveMemories();\n } else {\n const all = await storage.getAllMemories();\n memories = all.filter((m: any) => m.status === \"active\");\n }\n } catch (err) {\n console.warn(\"[pipeline] L2 读取 active memories 失败:\", err);\n return;\n }\n if (!memories || memories.length === 0) {\n console.error(\"[pipeline] L2 跳过:无 active 记忆\");\n return;\n }\n\n let scenes: any[];\n try {\n scenes = await sceneBuilder.buildScenes({\n memories,\n maxScenes: this.opts.l2MaxScenes,\n });\n } catch (err) {\n console.warn(\"[pipeline] L2 buildScenes 失败:\", err);\n return;\n }\n if (!Array.isArray(scenes) || scenes.length === 0) {\n console.error(\"[pipeline] L2 跳过:未生成场景\");\n return;\n }\n\n let persisted = 0;\n for (const scene of scenes) {\n try {\n // 写 Markdown 并回填 file_path\n if (typeof sceneBuilder.writeSceneMarkdown === \"function\") {\n await sceneBuilder.writeSceneMarkdown(scene, memories);\n }\n // 写入 DuckDB\n await storage.createScene({\n id: scene.id,\n label: scene.label,\n summary: scene.summary,\n memory_ids: scene.memory_ids,\n file_path: scene.file_path,\n created_at: scene.created_at,\n updated_at: scene.updated_at,\n embedding: scene.embedding || [],\n });\n // 钻取链 L2→L1\n for (const mid of scene.memory_ids || []) {\n try {\n await storage.addDrillChain({ l2_id: scene.id, l1_id: mid, created_at: Date.now() });\n } catch {}\n }\n persisted++;\n } catch (err) {\n console.warn(`[pipeline] L2 持久化场景失败 (${scene.id}):`, err);\n }\n }\n console.error(`[pipeline] L2 done: scenes=${scenes.length} persisted=${persisted}`);\n }\n\n // ===== L3: L2 → L3 画像生成 =====\n\n private async checkL3(): Promise<void> {\n if (!this.llmEnabled) return;\n if (this.lastL2Time === 0) return; // 从未跑过 L2,不触发 L3\n\n const now = Date.now();\n const elapsedSinceL2 = now - this.lastL2Time;\n if (elapsedSinceL2 >= this.opts.l3DelayAfterL2Ms) {\n try {\n await this.runL3Persona();\n this.lastL3Time = Date.now();\n } catch (err) {\n console.warn(\"[pipeline] L3 画像生成失败:\", err);\n }\n }\n }\n\n /**\n * L3 画像生成 —— 与 index.ts 的 memory_persona_update 逻辑一致:\n * 1. 获取 scenes\n * 2. personaGen.generate\n * 3. storage.createPersona + drill_chain(L3→L2)\n */\n private async runL3Persona(): Promise<void> {\n const { storage, personaGen } = this.opts;\n\n let sceneRows: any[];\n try {\n sceneRows = await storage.getAllScenes();\n } catch (err) {\n console.warn(\"[pipeline] L3 读取 scenes 失败:\", err);\n return;\n }\n if (!sceneRows || sceneRows.length === 0) {\n console.error(\"[pipeline] L3 跳过:无可用场景\");\n return;\n }\n\n // 还原 scene 结构(与 index.ts 一致)\n const scenes = sceneRows.map((r: any) => ({\n id: r.id,\n label: r.label,\n summary: r.summary || \"\",\n memory_ids: typeof r.memory_ids === \"string\" ? safeParse(r.memory_ids) || [] : (r.memory_ids || []),\n file_path: r.file_path,\n created_at: r.created_at,\n updated_at: r.updated_at,\n embedding: [],\n }));\n\n let existing: any = null;\n try {\n existing = await storage.getLatestPersona();\n } catch (err) {\n console.warn(\"[pipeline] L3 读取现有 persona 失败:\", err);\n }\n\n let persona: any;\n try {\n persona = await personaGen.generate({\n scenes,\n mode: this.opts.l3Mode,\n existingPersona: existing,\n });\n } catch (err) {\n console.warn(\"[pipeline] L3 personaGen.generate 失败:\", err);\n return;\n }\n if (!persona) {\n console.error(\"[pipeline] L3 跳过:未生成画像\");\n return;\n }\n\n try {\n await storage.createPersona({\n id: persona.id,\n content: persona.content,\n file_path: persona.file_path,\n scene_ids: persona.scene_ids,\n version: persona.version,\n created_at: persona.created_at,\n updated_at: persona.updated_at,\n });\n // 钻取链 L3→L2\n for (const sid of persona.scene_ids || []) {\n try {\n await storage.addDrillChain({ l3_id: persona.id, l2_id: sid, created_at: Date.now() });\n } catch {}\n }\n console.error(\n `[pipeline] L3 done: id=${persona.id} version=${persona.version} len=${persona.content?.length ?? 0}`\n );\n } catch (err) {\n console.warn(\"[pipeline] L3 createPersona 失败:\", err);\n }\n }\n\n // ===== 压缩调度 =====\n\n /**\n * 检查是否到了自动压缩的时间,如果是则调用 CompressionService.autoCompress()\n * 默认间隔 1 小时,需要 compressionService 已注入且 LLM 可用\n */\n private async checkCompress(): Promise<void> {\n if (!this.opts.compressionService) return;\n // 压缩依赖 LLM 生成高质量压缩内容\n if (!this.llmEnabled) return;\n\n const now = Date.now();\n if (now - this.lastCompressTime < this.opts.compressIntervalMs) return;\n\n try {\n this.lastCompressTime = now;\n const results = await this.opts.compressionService.autoCompress();\n if (results.length > 0) {\n console.error(\n `[pipeline] auto-compress done: anchors=${results.length} ` +\n `levels=${results.map((r: any) => `L${r.level}`).join(\",\")}`\n );\n }\n } catch (err) {\n console.warn(\"[pipeline] auto-compress 失败:\", err);\n }\n }\n\n // ===== 整理调度 =====\n\n /**\n * 定期运行记忆整理(衰减 -> 降级 -> 归档 -> 删除)\n * 默认间隔 1 小时\n */\n private async checkConsolidate(): Promise<void> {\n if (!this.opts.reinforcement) return;\n\n const now = Date.now();\n if (now - this.lastConsolidateTime < this.opts.consolidateIntervalMs) return;\n\n try {\n this.lastConsolidateTime = now;\n const { storage, reinforcement } = this.opts;\n\n // 获取 active + infrequent 的非永久记忆\n const active = await storage.getActiveMemories();\n const infrequent = await storage.getInfrequentMemories();\n const memories = [...active, ...infrequent].filter(\n (m: any) => !m.permanent\n );\n\n if (memories.length === 0) return;\n\n const decayResults = reinforcement.computeDecayBatch(memories);\n\n let downgraded = 0;\n let archived = 0;\n let deleted = 0;\n let updated = 0;\n\n for (const result of decayResults) {\n switch (result.action) {\n case \"delete\":\n await storage.deleteMemory(result.id);\n deleted++;\n break;\n\n case \"archive\":\n await storage.updateMemory(result.id, {\n strength: result.newStrength,\n status: \"archived\",\n });\n archived++;\n break;\n\n case \"downgrade_to_infrequent\":\n await storage.updateMemory(result.id, {\n strength: result.newStrength,\n status: \"infrequent\",\n infrequent_since: Date.now(),\n });\n downgraded++;\n break;\n\n case \"none\":\n default:\n if (result.newStrength !== undefined) {\n await storage.updateMemory(result.id, {\n strength: result.newStrength,\n });\n updated++;\n }\n break;\n }\n }\n\n console.error(\n `[pipeline] auto-consolidate done: processed=${decayResults.length} ` +\n `updated=${updated} downgraded=${downgraded} archived=${archived} deleted=${deleted}`\n );\n } catch (err) {\n console.warn(\"[pipeline] auto-consolidate 失败:\", err);\n }\n }\n\n // ===== Observability =====\n\n /** 获取管道状态(供 observability 用) */\n getStatus(): PipelineStatus {\n const now = Date.now();\n const nextPollIn =\n this.timer && this.lastTickAt > 0\n ? Math.max(0, this.opts.pollIntervalMs - (now - this.lastTickAt))\n : this.timer\n ? this.opts.pollIntervalMs\n : 0;\n return {\n running: this.running,\n llmEnabled: this.llmEnabled,\n started: this.started,\n l1Pending: this.l1Pending,\n lastL1Time: this.lastL1Time,\n lastL2Time: this.lastL2Time,\n lastL3Time: this.lastL3Time,\n lastTickAt: this.lastTickAt,\n nextPollIn,\n lastError: this.lastError,\n };\n }\n}\n\n/** 安全的 JSON.parse embedding */\nfunction safeParse(s: string): any {\n try {\n return JSON.parse(s);\n } catch {\n return null;\n }\n}\n"],"mappings":"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","names":[]}
|