@mingxy/cerebro 1.15.14 → 1.16.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/package.json +1 -1
- package/schema.json +0 -7
- package/src/client.ts +0 -1
- package/src/config.ts +0 -3
- package/src/hooks.ts +41 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mingxy/cerebro",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "Cerebro persistent memory plugin for OpenCode — auto-recall, auto-capture, 9 memory tools with clustering, project-scoped memory isolation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
package/schema.json
CHANGED
|
@@ -144,13 +144,6 @@
|
|
|
144
144
|
"description": "LLM refinement strategy: strict (high only), balanced (high+medium), loose (keep all)",
|
|
145
145
|
"enum": ["strict", "balanced", "loose"],
|
|
146
146
|
"default": "balanced"
|
|
147
|
-
},
|
|
148
|
-
"refineMediumChars": {
|
|
149
|
-
"type": "number",
|
|
150
|
-
"description": "Character limit for medium-relevance content truncation",
|
|
151
|
-
"default": 200,
|
|
152
|
-
"minimum": 50,
|
|
153
|
-
"maximum": 2000
|
|
154
147
|
}
|
|
155
148
|
},
|
|
156
149
|
"additionalProperties": false
|
package/src/client.ts
CHANGED
package/src/config.ts
CHANGED
|
@@ -29,7 +29,6 @@ export interface OmemPluginConfig {
|
|
|
29
29
|
phase2Multiplier: number;
|
|
30
30
|
llmMaxEval: number;
|
|
31
31
|
refineStrategy: "strict" | "balanced" | "loose";
|
|
32
|
-
refineMediumChars: number;
|
|
33
32
|
};
|
|
34
33
|
logging: {
|
|
35
34
|
logEnabled: boolean;
|
|
@@ -73,7 +72,6 @@ const DEFAULTS: OmemPluginConfig = {
|
|
|
73
72
|
phase2Multiplier: 2,
|
|
74
73
|
llmMaxEval: 15,
|
|
75
74
|
refineStrategy: "balanced",
|
|
76
|
-
refineMediumChars: 200,
|
|
77
75
|
},
|
|
78
76
|
logging: {
|
|
79
77
|
logEnabled: true,
|
|
@@ -140,7 +138,6 @@ function migrateFlatToNested(flat: FlatConfig): OmemPluginConfig {
|
|
|
140
138
|
phase2Multiplier: DEFAULTS.recall.phase2Multiplier,
|
|
141
139
|
llmMaxEval: DEFAULTS.recall.llmMaxEval,
|
|
142
140
|
refineStrategy: DEFAULTS.recall.refineStrategy,
|
|
143
|
-
refineMediumChars: DEFAULTS.recall.refineMediumChars,
|
|
144
141
|
},
|
|
145
142
|
logging: {
|
|
146
143
|
logEnabled: flat.logEnabled ?? DEFAULTS.logging.logEnabled,
|
package/src/hooks.ts
CHANGED
|
@@ -243,14 +243,30 @@ const FETCH_POLICY = [
|
|
|
243
243
|
"</cerebro-fetch-policy>",
|
|
244
244
|
].join("\n");
|
|
245
245
|
|
|
246
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Score-weighted budget allocation: high-score memories get more chars.
|
|
248
|
+
* Falls back to uniform distribution when totalScore === 0 or all scores equal.
|
|
249
|
+
*/
|
|
250
|
+
function buildContextBlock(
|
|
251
|
+
results: SearchResult[],
|
|
252
|
+
budget: number,
|
|
253
|
+
maxContentLength: number = 500,
|
|
254
|
+
minItemChars: number = MIN_ITEM_CONTENT_CHARS,
|
|
255
|
+
): string {
|
|
247
256
|
if (results.length === 0) return "";
|
|
248
257
|
|
|
258
|
+
const totalScore = results.reduce((sum, r) => sum + r.score, 0);
|
|
259
|
+
|
|
249
260
|
const grouped = categorize(results);
|
|
250
261
|
const sections: string[] = [];
|
|
251
262
|
|
|
252
263
|
for (const [label, items] of grouped) {
|
|
253
|
-
const lines = items.map((r) =>
|
|
264
|
+
const lines = items.map((r) => {
|
|
265
|
+
const itemMaxLen = totalScore > 0
|
|
266
|
+
? Math.min(maxContentLength, Math.max(minItemChars, Math.floor((r.score / totalScore) * budget)))
|
|
267
|
+
: Math.min(maxContentLength, Math.max(minItemChars, Math.floor(budget / results.length)));
|
|
268
|
+
return formatMemoryLine(r, itemMaxLen);
|
|
269
|
+
});
|
|
254
270
|
sections.push(`[${label}]\n${lines.join("\n")}`);
|
|
255
271
|
}
|
|
256
272
|
|
|
@@ -262,13 +278,23 @@ function buildContextBlock(results: SearchResult[], maxContentLength: number = 5
|
|
|
262
278
|
].join("\n");
|
|
263
279
|
}
|
|
264
280
|
|
|
265
|
-
function buildClusteredContextBlock(
|
|
281
|
+
function buildClusteredContextBlock(
|
|
282
|
+
clustered: import("./client.js").ClusteredRecallResult,
|
|
283
|
+
budget: number,
|
|
284
|
+
maxContentLength: number = 500,
|
|
285
|
+
minItemChars: number = MIN_ITEM_CONTENT_CHARS,
|
|
286
|
+
): string {
|
|
266
287
|
const sections: string[] = [];
|
|
267
288
|
|
|
268
289
|
if (clustered.cluster_summaries.length > 0) {
|
|
290
|
+
const totalClusterScore = clustered.cluster_summaries.reduce((sum, cs) => sum + cs.relevance_score, 0);
|
|
291
|
+
|
|
269
292
|
sections.push("## 📋 主题簇(聚合记忆)");
|
|
270
293
|
for (const cs of clustered.cluster_summaries) {
|
|
271
294
|
const scoreIndicator = cs.relevance_score >= 0.8 ? "★★★" : cs.relevance_score >= 0.6 ? "★★" : "★";
|
|
295
|
+
const clusterMaxLen = totalClusterScore > 0
|
|
296
|
+
? Math.min(maxContentLength, Math.max(minItemChars, Math.floor((cs.relevance_score / totalClusterScore) * budget)))
|
|
297
|
+
: Math.min(maxContentLength, Math.max(minItemChars, Math.floor(budget / clustered.cluster_summaries.length)));
|
|
272
298
|
sections.push(`\n### ${cs.title} (整合自${cs.member_count}条记忆) ${scoreIndicator}`);
|
|
273
299
|
sections.push(`> ${cs.summary}`);
|
|
274
300
|
if (cs.key_memories.length > 0) {
|
|
@@ -279,7 +305,7 @@ function buildClusteredContextBlock(clustered: import("./client.js").ClusteredRe
|
|
|
279
305
|
? ` [rel:${mem.relations.map((rel) => rel.target_id).join(",")}]`
|
|
280
306
|
: "";
|
|
281
307
|
const importanceBar = mem.importance >= 0.7 ? "●" : mem.importance >= 0.4 ? "◐" : "○";
|
|
282
|
-
const content = truncate(mem.content,
|
|
308
|
+
const content = truncate(mem.content, clusterMaxLen);
|
|
283
309
|
sections.push(`- ${importanceBar}${idTag}${relTag} ${content}`);
|
|
284
310
|
}
|
|
285
311
|
}
|
|
@@ -287,13 +313,18 @@ function buildClusteredContextBlock(clustered: import("./client.js").ClusteredRe
|
|
|
287
313
|
}
|
|
288
314
|
|
|
289
315
|
if (clustered.standalone_memories.length > 0) {
|
|
316
|
+
const standaloneBudget = clustered.cluster_summaries.length === 0
|
|
317
|
+
? budget
|
|
318
|
+
: Math.floor(budget * 0.3);
|
|
319
|
+
const standaloneMaxLen = Math.min(maxContentLength, Math.max(minItemChars, Math.floor(standaloneBudget / clustered.standalone_memories.length)));
|
|
320
|
+
|
|
290
321
|
sections.push("\n## 📌 补充信息");
|
|
291
322
|
for (const mem of clustered.standalone_memories) {
|
|
292
323
|
const idTag = mem.id ? ` [id:${mem.id}]` : "";
|
|
293
324
|
const relTag = mem.relations && mem.relations.length > 0
|
|
294
325
|
? ` [rel:${mem.relations.map((rel) => rel.target_id).join(",")}]`
|
|
295
326
|
: "";
|
|
296
|
-
const content = truncate(mem.content,
|
|
327
|
+
const content = truncate(mem.content, standaloneMaxLen);
|
|
297
328
|
sections.push(`-${idTag}${relTag} ${content}`);
|
|
298
329
|
}
|
|
299
330
|
}
|
|
@@ -316,7 +347,6 @@ export function autoRecallHook(client: CerebroClient, containerTags: string[], t
|
|
|
316
347
|
const phase2Multiplier = config.recall?.phase2Multiplier ?? 2;
|
|
317
348
|
const llmMaxEval = config.recall?.llmMaxEval ?? 15;
|
|
318
349
|
const refineStrategy = config.recall?.refineStrategy ?? "balanced";
|
|
319
|
-
const refineMediumChars = config.recall?.refineMediumChars ?? 200;
|
|
320
350
|
const maxContentLength = Math.max(MIN_CONTENT_LENGTH, config.content?.maxContentLength ?? 500);
|
|
321
351
|
const maxContentChars = Math.max(MIN_CONTENT_CHARS, config.content?.maxContentChars ?? 30000);
|
|
322
352
|
const toastDelayMs = config.ui?.toastDelayMs ?? 7000;
|
|
@@ -333,7 +363,7 @@ export function autoRecallHook(client: CerebroClient, containerTags: string[], t
|
|
|
333
363
|
if (policy === "none") return;
|
|
334
364
|
|
|
335
365
|
try {
|
|
336
|
-
logDebug("autoRecallHook start", { sessionId: input.sessionID, agentId, policy, similarityThreshold, maxRecallResults, fetchMultiplier, topkCapMultiplier, mmrJaccardThreshold, mmrPenaltyFactor, phase2Multiplier, llmMaxEval, refineStrategy
|
|
366
|
+
logDebug("autoRecallHook start", { sessionId: input.sessionID, agentId, policy, similarityThreshold, maxRecallResults, fetchMultiplier, topkCapMultiplier, mmrJaccardThreshold, mmrPenaltyFactor, phase2Multiplier, llmMaxEval, refineStrategy });
|
|
337
367
|
const messages = sessionMessages.get(input.sessionID) ?? [];
|
|
338
368
|
const userMessages = messages.filter((m) => m.role === "user");
|
|
339
369
|
|
|
@@ -399,7 +429,6 @@ export function autoRecallHook(client: CerebroClient, containerTags: string[], t
|
|
|
399
429
|
phase2_multiplier: phase2Multiplier,
|
|
400
430
|
llm_max_eval: llmMaxEval,
|
|
401
431
|
refine_strategy: refineStrategy,
|
|
402
|
-
refine_medium_chars: refineMediumChars,
|
|
403
432
|
},
|
|
404
433
|
directory || process.env.OMEM_PROJECT_DIR,
|
|
405
434
|
);
|
|
@@ -494,20 +523,14 @@ export function autoRecallHook(client: CerebroClient, containerTags: string[], t
|
|
|
494
523
|
if (budgetRemaining < 0) {
|
|
495
524
|
logDebug("autoRecallHook budget overflow", { profileChars, maxContentChars, deficit: -budgetRemaining });
|
|
496
525
|
}
|
|
497
|
-
const itemCount = clustered
|
|
498
|
-
? (clustered.cluster_summaries.length + clustered.standalone_memories.length)
|
|
499
|
-
: newResults.length;
|
|
500
|
-
const dynamicMaxContentLength = itemCount > 0
|
|
501
|
-
? Math.min(maxContentLength, Math.max(MIN_ITEM_CONTENT_CHARS, Math.floor(budgetRemaining / itemCount)))
|
|
502
|
-
: maxContentLength;
|
|
503
526
|
logDebug("autoRecallHook budget", {
|
|
504
|
-
maxContentChars, profileChars, budgetRemaining,
|
|
505
|
-
configuredMax: maxContentLength,
|
|
527
|
+
maxContentChars, profileChars, budgetRemaining,
|
|
528
|
+
configuredMax: maxContentLength,
|
|
506
529
|
});
|
|
507
530
|
|
|
508
531
|
const block = clustered
|
|
509
|
-
? buildClusteredContextBlock(clustered,
|
|
510
|
-
: buildContextBlock(newResults,
|
|
532
|
+
? buildClusteredContextBlock(clustered, budgetRemaining, maxContentLength, MIN_ITEM_CONTENT_CHARS)
|
|
533
|
+
: buildContextBlock(newResults, budgetRemaining, maxContentLength, MIN_ITEM_CONTENT_CHARS);
|
|
511
534
|
if (block) {
|
|
512
535
|
appendToSystem(output.system, block);
|
|
513
536
|
appendToSystem(output.system, FETCH_POLICY);
|