@joshuaswarren/openclaw-engram 8.3.0 → 8.3.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.js +138 -15
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +5 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -339,6 +339,7 @@ function parseConfig(raw) {
|
|
|
339
339
|
queryAwareIndexingMaxCandidates: typeof cfg.queryAwareIndexingMaxCandidates === "number" ? Math.max(0, cfg.queryAwareIndexingMaxCandidates) : 200,
|
|
340
340
|
// v8.2: Multi-graph memory (PR 18)
|
|
341
341
|
multiGraphMemoryEnabled: cfg.multiGraphMemoryEnabled === true,
|
|
342
|
+
graphRecallEnabled: cfg.graphRecallEnabled === true,
|
|
342
343
|
entityGraphEnabled: cfg.entityGraphEnabled !== false,
|
|
343
344
|
timeGraphEnabled: cfg.timeGraphEnabled !== false,
|
|
344
345
|
causalGraphEnabled: cfg.causalGraphEnabled !== false,
|
|
@@ -9729,6 +9730,13 @@ function computeArtifactRecallLimit(recallMode, recallResultLimit, verbatimArtif
|
|
|
9729
9730
|
}
|
|
9730
9731
|
return base;
|
|
9731
9732
|
}
|
|
9733
|
+
function resolveEffectiveRecallMode(options) {
|
|
9734
|
+
const plannedMode = options.plannerEnabled ? planRecallMode(options.prompt) : "full";
|
|
9735
|
+
if (plannedMode === "graph_mode" && (!options.graphRecallEnabled || !options.multiGraphMemoryEnabled)) {
|
|
9736
|
+
return "full";
|
|
9737
|
+
}
|
|
9738
|
+
return plannedMode;
|
|
9739
|
+
}
|
|
9732
9740
|
function computeArtifactCandidateFetchLimit(targetCount) {
|
|
9733
9741
|
const cappedTarget = Math.max(0, targetCount);
|
|
9734
9742
|
if (cappedTarget === 0) return 0;
|
|
@@ -9742,6 +9750,32 @@ function computeQmdHybridFetchLimit(recallFetchLimit, artifactsEnabled, maxArtif
|
|
|
9742
9750
|
const artifactHeadroom = Math.max(20, Math.max(0, maxArtifactRecall) * 8);
|
|
9743
9751
|
return Math.min(400, cappedRecallLimit + artifactHeadroom);
|
|
9744
9752
|
}
|
|
9753
|
+
function mergeGraphExpandedResults(primary, expanded) {
|
|
9754
|
+
const mergedByPath = /* @__PURE__ */ new Map();
|
|
9755
|
+
for (const item of [...primary, ...expanded]) {
|
|
9756
|
+
const prev = mergedByPath.get(item.path);
|
|
9757
|
+
if (!prev) {
|
|
9758
|
+
mergedByPath.set(item.path, item);
|
|
9759
|
+
continue;
|
|
9760
|
+
}
|
|
9761
|
+
const better = item.score > prev.score ? item : prev;
|
|
9762
|
+
const snippet = prev.snippet || item.snippet;
|
|
9763
|
+
mergedByPath.set(item.path, { ...better, snippet });
|
|
9764
|
+
}
|
|
9765
|
+
return Array.from(mergedByPath.values());
|
|
9766
|
+
}
|
|
9767
|
+
function graphPathRelativeToStorage(storageDir, candidatePath) {
|
|
9768
|
+
const absolutePath = path22.isAbsolute(candidatePath) ? candidatePath : path22.resolve(storageDir, candidatePath);
|
|
9769
|
+
const rel = path22.relative(storageDir, absolutePath);
|
|
9770
|
+
if (!rel || rel === ".") return null;
|
|
9771
|
+
if (rel.startsWith("..")) return null;
|
|
9772
|
+
return rel.split(path22.sep).join("/");
|
|
9773
|
+
}
|
|
9774
|
+
function graphActivationScoreToRecallScore(score) {
|
|
9775
|
+
const bounded = Number.isFinite(score) && score > 0 ? score : 0;
|
|
9776
|
+
const normalized = bounded / (1 + bounded);
|
|
9777
|
+
return Math.max(0.05, Math.min(0.95, normalized));
|
|
9778
|
+
}
|
|
9745
9779
|
function mergeArtifactRecallCandidates(candidatesByNamespace, limit) {
|
|
9746
9780
|
const cappedLimit = Math.max(0, limit);
|
|
9747
9781
|
if (cappedLimit === 0) return [];
|
|
@@ -10378,11 +10412,93 @@ var Orchestrator = class _Orchestrator {
|
|
|
10378
10412
|
}
|
|
10379
10413
|
return bestFiltered;
|
|
10380
10414
|
}
|
|
10415
|
+
async expandResultsViaGraph(options) {
|
|
10416
|
+
const byNamespace = /* @__PURE__ */ new Map();
|
|
10417
|
+
for (const result of options.memoryResults) {
|
|
10418
|
+
const ns = this.namespaceFromPath(result.path);
|
|
10419
|
+
if (!options.recallNamespaces.includes(ns)) continue;
|
|
10420
|
+
const existing = byNamespace.get(ns);
|
|
10421
|
+
if (existing) {
|
|
10422
|
+
existing.push(result);
|
|
10423
|
+
} else {
|
|
10424
|
+
byNamespace.set(ns, [result]);
|
|
10425
|
+
}
|
|
10426
|
+
}
|
|
10427
|
+
const perNamespaceSeedCap = Math.max(3, options.recallResultLimit);
|
|
10428
|
+
const perNamespaceExpandedCap = Math.max(8, options.recallResultLimit * 2);
|
|
10429
|
+
const seedPaths = [];
|
|
10430
|
+
const expandedPaths = [];
|
|
10431
|
+
const expandedResults = [];
|
|
10432
|
+
for (const [namespace, nsResults] of byNamespace.entries()) {
|
|
10433
|
+
const storage = await this.storageRouter.storageFor(namespace);
|
|
10434
|
+
const seedRelativePaths = nsResults.slice(0, perNamespaceSeedCap).map((result) => graphPathRelativeToStorage(storage.dir, result.path)).filter((value) => typeof value === "string" && value.length > 0);
|
|
10435
|
+
if (seedRelativePaths.length === 0) continue;
|
|
10436
|
+
seedPaths.push(...seedRelativePaths.map((rel) => path22.join(storage.dir, rel)));
|
|
10437
|
+
const seedSet = new Set(seedRelativePaths);
|
|
10438
|
+
const expanded = await this.graphIndexFor(storage).spreadingActivation(
|
|
10439
|
+
seedRelativePaths,
|
|
10440
|
+
this.config.maxGraphTraversalSteps
|
|
10441
|
+
);
|
|
10442
|
+
if (expanded.length === 0) continue;
|
|
10443
|
+
for (const candidate of expanded.slice(0, perNamespaceExpandedCap)) {
|
|
10444
|
+
if (seedSet.has(candidate.path)) continue;
|
|
10445
|
+
const memoryPath = path22.resolve(storage.dir, candidate.path);
|
|
10446
|
+
const memory = await storage.readMemoryByPath(memoryPath);
|
|
10447
|
+
if (!memory) continue;
|
|
10448
|
+
if (isArtifactMemoryPath(memory.path)) continue;
|
|
10449
|
+
if (memory.frontmatter.status && memory.frontmatter.status !== "active") continue;
|
|
10450
|
+
const snippet = memory.content.slice(0, 400);
|
|
10451
|
+
const score = graphActivationScoreToRecallScore(candidate.score);
|
|
10452
|
+
expandedResults.push({
|
|
10453
|
+
docid: memory.frontmatter.id,
|
|
10454
|
+
path: memory.path,
|
|
10455
|
+
snippet,
|
|
10456
|
+
score
|
|
10457
|
+
});
|
|
10458
|
+
expandedPaths.push({
|
|
10459
|
+
path: memory.path,
|
|
10460
|
+
score,
|
|
10461
|
+
namespace
|
|
10462
|
+
});
|
|
10463
|
+
}
|
|
10464
|
+
}
|
|
10465
|
+
return {
|
|
10466
|
+
merged: mergeGraphExpandedResults(options.memoryResults, expandedResults),
|
|
10467
|
+
seedPaths,
|
|
10468
|
+
expandedPaths
|
|
10469
|
+
};
|
|
10470
|
+
}
|
|
10471
|
+
async recordLastGraphRecallSnapshot(options) {
|
|
10472
|
+
try {
|
|
10473
|
+
const snapshotPath = path22.join(options.storage.dir, "state", "last_graph_recall.json");
|
|
10474
|
+
await mkdir16(path22.dirname(snapshotPath), { recursive: true });
|
|
10475
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10476
|
+
const payload = {
|
|
10477
|
+
recordedAt: now,
|
|
10478
|
+
mode: options.recallMode,
|
|
10479
|
+
queryHash: createHash4("sha256").update(options.prompt).digest("hex"),
|
|
10480
|
+
queryLength: options.prompt.length,
|
|
10481
|
+
namespaces: options.recallNamespaces,
|
|
10482
|
+
seedCount: options.seedPaths.length,
|
|
10483
|
+
expandedCount: options.expandedPaths.length,
|
|
10484
|
+
seeds: options.seedPaths,
|
|
10485
|
+
expanded: options.expandedPaths
|
|
10486
|
+
};
|
|
10487
|
+
await writeFile15(snapshotPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
10488
|
+
} catch (err) {
|
|
10489
|
+
log.debug(`last graph recall write failed: ${err}`);
|
|
10490
|
+
}
|
|
10491
|
+
}
|
|
10381
10492
|
async recallInternal(prompt, sessionKey) {
|
|
10382
10493
|
const recallStart = Date.now();
|
|
10383
10494
|
const timings = {};
|
|
10384
10495
|
const sections = [];
|
|
10385
|
-
const recallMode =
|
|
10496
|
+
const recallMode = resolveEffectiveRecallMode({
|
|
10497
|
+
plannerEnabled: this.config.recallPlannerEnabled,
|
|
10498
|
+
graphRecallEnabled: this.config.graphRecallEnabled,
|
|
10499
|
+
multiGraphMemoryEnabled: this.config.multiGraphMemoryEnabled,
|
|
10500
|
+
prompt
|
|
10501
|
+
});
|
|
10386
10502
|
timings.recallPlan = recallMode;
|
|
10387
10503
|
const recallResultLimit = recallMode === "no_recall" ? 0 : recallMode === "minimal" ? Math.max(0, Math.min(this.config.qmdMaxResults, this.config.recallPlannerMaxQmdResultsMinimal)) : this.config.qmdMaxResults;
|
|
10388
10504
|
const recallHeadroom = this.config.verbatimArtifactsEnabled ? Math.max(12, this.config.verbatimArtifactsMaxRecall * 4) : 12;
|
|
@@ -10530,20 +10646,7 @@ ${tmtNode.summary}`);
|
|
|
10530
10646
|
if (qmdResult) {
|
|
10531
10647
|
const t0 = Date.now();
|
|
10532
10648
|
const { memoryResultsLists, globalResults } = qmdResult;
|
|
10533
|
-
const
|
|
10534
|
-
for (const list of memoryResultsLists) {
|
|
10535
|
-
for (const r of list) {
|
|
10536
|
-
const prev = mergedByPath.get(r.path);
|
|
10537
|
-
if (!prev) {
|
|
10538
|
-
mergedByPath.set(r.path, r);
|
|
10539
|
-
continue;
|
|
10540
|
-
}
|
|
10541
|
-
const better = r.score > prev.score ? r : prev;
|
|
10542
|
-
const snippet = prev.snippet || r.snippet;
|
|
10543
|
-
mergedByPath.set(r.path, { ...better, snippet });
|
|
10544
|
-
}
|
|
10545
|
-
}
|
|
10546
|
-
const memoryResultsRaw = Array.from(mergedByPath.values());
|
|
10649
|
+
const memoryResultsRaw = mergeGraphExpandedResults(memoryResultsLists.flat(), []);
|
|
10547
10650
|
let memoryResults = memoryResultsRaw;
|
|
10548
10651
|
if (this.config.namespacesEnabled) {
|
|
10549
10652
|
memoryResults = memoryResults.filter(
|
|
@@ -10551,6 +10654,26 @@ ${tmtNode.summary}`);
|
|
|
10551
10654
|
);
|
|
10552
10655
|
}
|
|
10553
10656
|
memoryResults = memoryResults.filter((r) => !isArtifactMemoryPath(r.path));
|
|
10657
|
+
if (recallMode === "graph_mode") {
|
|
10658
|
+
const {
|
|
10659
|
+
merged,
|
|
10660
|
+
seedPaths,
|
|
10661
|
+
expandedPaths
|
|
10662
|
+
} = await this.expandResultsViaGraph({
|
|
10663
|
+
memoryResults,
|
|
10664
|
+
recallNamespaces,
|
|
10665
|
+
recallResultLimit
|
|
10666
|
+
});
|
|
10667
|
+
memoryResults = merged;
|
|
10668
|
+
await this.recordLastGraphRecallSnapshot({
|
|
10669
|
+
storage: profileStorage,
|
|
10670
|
+
prompt,
|
|
10671
|
+
recallMode,
|
|
10672
|
+
recallNamespaces,
|
|
10673
|
+
seedPaths,
|
|
10674
|
+
expandedPaths
|
|
10675
|
+
});
|
|
10676
|
+
}
|
|
10554
10677
|
memoryResults = await this.boostSearchResults(memoryResults, recallNamespaces, prompt);
|
|
10555
10678
|
if (this.config.rerankEnabled && this.config.rerankProvider === "local") {
|
|
10556
10679
|
const ranked = await rerankLocalOrNoop({
|