@claude-flow/memory 3.0.0-alpha.9 → 3.0.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 +308 -37
- package/dist/agent-memory-scope.d.ts.map +1 -1
- package/dist/agent-memory-scope.js +10 -2
- package/dist/agent-memory-scope.js.map +1 -1
- package/dist/agentdb-adapter-key-isolation.test.d.ts +2 -0
- package/dist/agentdb-adapter-key-isolation.test.d.ts.map +1 -0
- package/dist/agentdb-adapter-key-isolation.test.js +175 -0
- package/dist/agentdb-adapter-key-isolation.test.js.map +1 -0
- package/dist/agentdb-adapter-upsert.test.d.ts +2 -0
- package/dist/agentdb-adapter-upsert.test.d.ts.map +1 -0
- package/dist/agentdb-adapter-upsert.test.js +216 -0
- package/dist/agentdb-adapter-upsert.test.js.map +1 -0
- package/dist/agentdb-adapter.d.ts +70 -1
- package/dist/agentdb-adapter.d.ts.map +1 -1
- package/dist/agentdb-adapter.js +365 -32
- package/dist/agentdb-adapter.js.map +1 -1
- package/dist/agentdb-backend.d.ts +25 -0
- package/dist/agentdb-backend.d.ts.map +1 -1
- package/dist/agentdb-backend.js +113 -28
- package/dist/agentdb-backend.js.map +1 -1
- package/dist/agentdb-retrieval-guard.d.ts +76 -0
- package/dist/agentdb-retrieval-guard.d.ts.map +1 -0
- package/dist/agentdb-retrieval-guard.js +77 -0
- package/dist/agentdb-retrieval-guard.js.map +1 -0
- package/dist/agentdb-retrieval-guard.test.d.ts +12 -0
- package/dist/agentdb-retrieval-guard.test.d.ts.map +1 -0
- package/dist/agentdb-retrieval-guard.test.js +129 -0
- package/dist/agentdb-retrieval-guard.test.js.map +1 -0
- package/dist/auto-memory-bridge.d.ts +12 -1
- package/dist/auto-memory-bridge.d.ts.map +1 -1
- package/dist/auto-memory-bridge.js +101 -7
- package/dist/auto-memory-bridge.js.map +1 -1
- package/dist/auto-memory-bridge.test.js +121 -15
- package/dist/auto-memory-bridge.test.js.map +1 -1
- package/dist/auto-memory-index-3224.test.d.ts +2 -0
- package/dist/auto-memory-index-3224.test.d.ts.map +1 -0
- package/dist/auto-memory-index-3224.test.js +77 -0
- package/dist/auto-memory-index-3224.test.js.map +1 -0
- package/dist/consolidator-embedding-benchmark.test.d.ts +39 -0
- package/dist/consolidator-embedding-benchmark.test.d.ts.map +1 -0
- package/dist/consolidator-embedding-benchmark.test.js +366 -0
- package/dist/consolidator-embedding-benchmark.test.js.map +1 -0
- package/dist/consolidator.d.ts +131 -0
- package/dist/consolidator.d.ts.map +1 -0
- package/dist/consolidator.js +309 -0
- package/dist/consolidator.js.map +1 -0
- package/dist/consolidator.test.d.ts +17 -0
- package/dist/consolidator.test.d.ts.map +1 -0
- package/dist/consolidator.test.js +376 -0
- package/dist/consolidator.test.js.map +1 -0
- package/dist/controller-registry.d.ts +290 -0
- package/dist/controller-registry.d.ts.map +1 -0
- package/dist/controller-registry.js +1095 -0
- package/dist/controller-registry.js.map +1 -0
- package/dist/controller-registry.test.d.ts +14 -0
- package/dist/controller-registry.test.d.ts.map +1 -0
- package/dist/controller-registry.test.js +818 -0
- package/dist/controller-registry.test.js.map +1 -0
- package/dist/database-provider.d.ts +14 -2
- package/dist/database-provider.d.ts.map +1 -1
- package/dist/database-provider.js +63 -3
- package/dist/database-provider.js.map +1 -1
- package/dist/database-provider.test.js +56 -3
- package/dist/database-provider.test.js.map +1 -1
- package/dist/entity-tagger.d.ts +31 -0
- package/dist/entity-tagger.d.ts.map +1 -0
- package/dist/entity-tagger.js +80 -0
- package/dist/entity-tagger.js.map +1 -0
- package/dist/entity-tagger.test.d.ts +12 -0
- package/dist/entity-tagger.test.d.ts.map +1 -0
- package/dist/entity-tagger.test.js +70 -0
- package/dist/entity-tagger.test.js.map +1 -0
- package/dist/fts5.test.d.ts +13 -0
- package/dist/fts5.test.d.ts.map +1 -0
- package/dist/fts5.test.js +144 -0
- package/dist/fts5.test.js.map +1 -0
- package/dist/graceful-retrieval.test.d.ts +14 -0
- package/dist/graceful-retrieval.test.d.ts.map +1 -0
- package/dist/graceful-retrieval.test.js +251 -0
- package/dist/graceful-retrieval.test.js.map +1 -0
- package/dist/hnsw-index.d.ts +62 -0
- package/dist/hnsw-index.d.ts.map +1 -1
- package/dist/hnsw-index.js +461 -16
- package/dist/hnsw-index.js.map +1 -1
- package/dist/hnsw-persistence.test.d.ts +13 -0
- package/dist/hnsw-persistence.test.d.ts.map +1 -0
- package/dist/hnsw-persistence.test.js +208 -0
- package/dist/hnsw-persistence.test.js.map +1 -0
- package/dist/hnsw-quantization.test.d.ts +17 -0
- package/dist/hnsw-quantization.test.d.ts.map +1 -0
- package/dist/hnsw-quantization.test.js +140 -0
- package/dist/hnsw-quantization.test.js.map +1 -0
- package/dist/hybrid-backend.d.ts +46 -2
- package/dist/hybrid-backend.d.ts.map +1 -1
- package/dist/hybrid-backend.js +120 -21
- package/dist/hybrid-backend.js.map +1 -1
- package/dist/hybrid-backend.test.js +139 -5
- package/dist/hybrid-backend.test.js.map +1 -1
- package/dist/index.d.ts +164 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +272 -30
- package/dist/index.js.map +1 -1
- package/dist/index.test.d.ts +10 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +71 -0
- package/dist/index.test.js.map +1 -0
- package/dist/json-security.d.ts +12 -0
- package/dist/json-security.d.ts.map +1 -0
- package/dist/json-security.js +20 -0
- package/dist/json-security.js.map +1 -0
- package/dist/learning-bridge.d.ts +29 -0
- package/dist/learning-bridge.d.ts.map +1 -1
- package/dist/learning-bridge.js +71 -6
- package/dist/learning-bridge.js.map +1 -1
- package/dist/learning-bridge.test.js +181 -1
- package/dist/learning-bridge.test.js.map +1 -1
- package/dist/memory-key.d.ts +3 -0
- package/dist/memory-key.d.ts.map +1 -0
- package/dist/memory-key.js +5 -0
- package/dist/memory-key.js.map +1 -0
- package/dist/migration.d.ts +19 -4
- package/dist/migration.d.ts.map +1 -1
- package/dist/migration.js +99 -24
- package/dist/migration.js.map +1 -1
- package/dist/migration.test.d.ts +11 -0
- package/dist/migration.test.d.ts.map +1 -0
- package/dist/migration.test.js +141 -0
- package/dist/migration.test.js.map +1 -0
- package/dist/mmr-benchmark.test.d.ts +2 -0
- package/dist/mmr-benchmark.test.d.ts.map +1 -0
- package/dist/mmr-benchmark.test.js +286 -0
- package/dist/mmr-benchmark.test.js.map +1 -0
- package/dist/mmr-rerank-perf.test.d.ts +2 -0
- package/dist/mmr-rerank-perf.test.d.ts.map +1 -0
- package/dist/mmr-rerank-perf.test.js +191 -0
- package/dist/mmr-rerank-perf.test.js.map +1 -0
- package/dist/persistent-sona.d.ts +144 -0
- package/dist/persistent-sona.d.ts.map +1 -0
- package/dist/persistent-sona.js +332 -0
- package/dist/persistent-sona.js.map +1 -0
- package/dist/rvf-backend.d.ts +53 -0
- package/dist/rvf-backend.d.ts.map +1 -0
- package/dist/rvf-backend.js +643 -0
- package/dist/rvf-backend.js.map +1 -0
- package/dist/rvf-learning-store.d.ts +139 -0
- package/dist/rvf-learning-store.d.ts.map +1 -0
- package/dist/rvf-learning-store.js +295 -0
- package/dist/rvf-learning-store.js.map +1 -0
- package/dist/rvf-migration.d.ts +45 -0
- package/dist/rvf-migration.d.ts.map +1 -0
- package/dist/rvf-migration.js +254 -0
- package/dist/rvf-migration.js.map +1 -0
- package/dist/smart-retrieval-scores.test.d.ts +2 -0
- package/dist/smart-retrieval-scores.test.d.ts.map +1 -0
- package/dist/smart-retrieval-scores.test.js +75 -0
- package/dist/smart-retrieval-scores.test.js.map +1 -0
- package/dist/smart-retrieval.d.ts +141 -0
- package/dist/smart-retrieval.d.ts.map +1 -0
- package/dist/smart-retrieval.js +389 -0
- package/dist/smart-retrieval.js.map +1 -0
- package/dist/smart-retrieval.test.d.ts +2 -0
- package/dist/smart-retrieval.test.d.ts.map +1 -0
- package/dist/smart-retrieval.test.js +418 -0
- package/dist/smart-retrieval.test.js.map +1 -0
- package/dist/sqlite-backend.d.ts +10 -0
- package/dist/sqlite-backend.d.ts.map +1 -1
- package/dist/sqlite-backend.js +101 -5
- package/dist/sqlite-backend.js.map +1 -1
- package/dist/sqljs-backend.d.ts +9 -0
- package/dist/sqljs-backend.d.ts.map +1 -1
- package/dist/sqljs-backend.js +91 -3
- package/dist/sqljs-backend.js.map +1 -1
- package/dist/tiered-memory.d.ts +171 -0
- package/dist/tiered-memory.d.ts.map +1 -0
- package/dist/tiered-memory.js +350 -0
- package/dist/tiered-memory.js.map +1 -0
- package/dist/tiered-memory.test.d.ts +9 -0
- package/dist/tiered-memory.test.d.ts.map +1 -0
- package/dist/tiered-memory.test.js +220 -0
- package/dist/tiered-memory.test.js.map +1 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +10 -8
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 4.1 — embedding near-duplicate pass: precision/recall, latency,
|
|
3
|
+
* namespace-isolation, and determinism receipts (response to PR #3232
|
|
4
|
+
* review: "the 0.95 threshold needs an empirical contract rather than a
|
|
5
|
+
* smoke fixture").
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT — corpus provenance: this file does NOT use a real pinned
|
|
8
|
+
* embedding model. Both real ONNX backends this monorepo ships
|
|
9
|
+
* (`@huggingface/transformers` and the legacy `@xenova/transformers`) were
|
|
10
|
+
* attempted directly in this environment and both fail for reasons
|
|
11
|
+
* unrelated to this candidate:
|
|
12
|
+
* - `@huggingface/transformers@4.2.0`: `transformers.node.mjs` does
|
|
13
|
+
* `import { Tensor } from "onnxruntime-common"` against a CJS module,
|
|
14
|
+
* a real ESM/CJS interop break under this Node version — reproduces
|
|
15
|
+
* on an unmodified checkout, confirmed before writing this file.
|
|
16
|
+
* - `@xenova/transformers@2.17.0`: pulls in `sharp@0.32.6`, whose
|
|
17
|
+
* native binding (`sharp-linux-x64.node`) isn't present for this
|
|
18
|
+
* sandbox's platform.
|
|
19
|
+
* Neither is fixable within this PR's scope without touching unrelated,
|
|
20
|
+
* pre-existing native/module-resolution infrastructure this diff never
|
|
21
|
+
* touches. This package's own `__tests__/embeddings.test.ts` exercises
|
|
22
|
+
* only the `mock` provider — it never invokes a real transformer either,
|
|
23
|
+
* consistent with what was found here.
|
|
24
|
+
*
|
|
25
|
+
* What this file DOES provide instead: a deterministic synthetic corpus
|
|
26
|
+
* where pairwise cosine similarity is constructed EXACTLY (not measured
|
|
27
|
+
* after the fact) via `pairAtSimilarity()` below — for every pair the
|
|
28
|
+
* ground-truth similarity is known analytically, not estimated. That
|
|
29
|
+
* makes this a rigorous test of the near-dup pass's THRESHOLD MECHANICS
|
|
30
|
+
* (precision/recall as a function of `similarityThreshold`, exactly where
|
|
31
|
+
* the boundary is) — arguably a cleaner instrument for that specific
|
|
32
|
+
* question than a real corpus would be, since there is no measurement
|
|
33
|
+
* noise in the "true" similarity. It does NOT validate that 0.95 is the
|
|
34
|
+
* right cutoff for real-world semantic paraphrase detection with a real
|
|
35
|
+
* embedding model — that claim remains open pending the blocked ONNX
|
|
36
|
+
* path, and is called out explicitly, not implied.
|
|
37
|
+
*/
|
|
38
|
+
import { describe, it, expect } from 'vitest';
|
|
39
|
+
import { MemoryService } from './index.js';
|
|
40
|
+
import { MemoryConsolidator } from './consolidator.js';
|
|
41
|
+
import { createDefaultEntry } from './types.js';
|
|
42
|
+
/** Deterministic xorshift PRNG, seeded — matches consolidator.test.ts's randomVec. */
|
|
43
|
+
function seededRng(seed) {
|
|
44
|
+
let s = seed | 0 || 1;
|
|
45
|
+
return () => {
|
|
46
|
+
s ^= s << 13;
|
|
47
|
+
s ^= s >>> 17;
|
|
48
|
+
s ^= s << 5;
|
|
49
|
+
return (s | 0) / 2 ** 31;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function randomUnitVector(dim, seed) {
|
|
53
|
+
const rng = seededRng(seed);
|
|
54
|
+
const v = new Float32Array(dim);
|
|
55
|
+
for (let i = 0; i < dim; i++)
|
|
56
|
+
v[i] = rng();
|
|
57
|
+
return normalize(v);
|
|
58
|
+
}
|
|
59
|
+
function normalize(v) {
|
|
60
|
+
let norm = 0;
|
|
61
|
+
for (let i = 0; i < v.length; i++)
|
|
62
|
+
norm += v[i] * v[i];
|
|
63
|
+
norm = Math.sqrt(norm) || 1;
|
|
64
|
+
const out = new Float32Array(v.length);
|
|
65
|
+
for (let i = 0; i < v.length; i++)
|
|
66
|
+
out[i] = v[i] / norm;
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
function dot(a, b) {
|
|
70
|
+
let s = 0;
|
|
71
|
+
for (let i = 0; i < a.length; i++)
|
|
72
|
+
s += a[i] * b[i];
|
|
73
|
+
return s;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Construct a unit vector `b` such that cosine(a, b) === targetSimilarity
|
|
77
|
+
* exactly (up to float precision), by mixing `a` with a vector orthogonal
|
|
78
|
+
* to it: b = s*a + sqrt(1-s^2)*orth, |a|=|orth|=1, a⊥orth.
|
|
79
|
+
*/
|
|
80
|
+
function vectorAtSimilarity(a, targetSimilarity, seed) {
|
|
81
|
+
const dim = a.length;
|
|
82
|
+
// Random candidate, then Gram-Schmidt to make it orthogonal to `a`.
|
|
83
|
+
let orth = randomUnitVector(dim, seed);
|
|
84
|
+
const proj = dot(orth, a);
|
|
85
|
+
const raw = new Float32Array(dim);
|
|
86
|
+
for (let i = 0; i < dim; i++)
|
|
87
|
+
raw[i] = orth[i] - proj * a[i];
|
|
88
|
+
orth = normalize(raw);
|
|
89
|
+
const s = targetSimilarity;
|
|
90
|
+
const c = Math.sqrt(Math.max(0, 1 - s * s));
|
|
91
|
+
const out = new Float32Array(dim);
|
|
92
|
+
for (let i = 0; i < dim; i++)
|
|
93
|
+
out[i] = s * a[i] + c * orth[i];
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
async function newService(dimensions = 32) {
|
|
97
|
+
const svc = new MemoryService({ dimensions, persistenceEnabled: false, snapshotInterval: 0 });
|
|
98
|
+
await svc.initialize();
|
|
99
|
+
return svc;
|
|
100
|
+
}
|
|
101
|
+
const DIM = 32;
|
|
102
|
+
// Base anchor vectors, one per pair, all mutually near-orthogonal (distinct
|
|
103
|
+
// content clusters) so pairs don't cross-contaminate each other's matches.
|
|
104
|
+
function buildCorpus() {
|
|
105
|
+
const pairs = [
|
|
106
|
+
{ label: 'near-dup-0.999', targetSimilarity: 0.999, shouldMergeAtDefault: true },
|
|
107
|
+
{ label: 'near-dup-0.98', targetSimilarity: 0.98, shouldMergeAtDefault: true },
|
|
108
|
+
{ label: 'near-dup-0.96', targetSimilarity: 0.96, shouldMergeAtDefault: true },
|
|
109
|
+
{ label: 'boundary-0.955', targetSimilarity: 0.955, shouldMergeAtDefault: true },
|
|
110
|
+
{ label: 'hard-negative-0.94', targetSimilarity: 0.94, shouldMergeAtDefault: false },
|
|
111
|
+
{ label: 'hard-negative-0.90', targetSimilarity: 0.90, shouldMergeAtDefault: false },
|
|
112
|
+
{ label: 'hard-negative-0.85', targetSimilarity: 0.85, shouldMergeAtDefault: false },
|
|
113
|
+
{ label: 'easy-negative-0.30', targetSimilarity: 0.30, shouldMergeAtDefault: false },
|
|
114
|
+
];
|
|
115
|
+
const anchors = pairs.map((_, i) => randomUnitVector(DIM, 1000 + i * 7));
|
|
116
|
+
return { anchors, pairs };
|
|
117
|
+
}
|
|
118
|
+
describe('Phase 4.1 embedding benchmark — pairwise similarity is exact by construction', () => {
|
|
119
|
+
it('vectorAtSimilarity produces the requested cosine similarity to within 1e-5', () => {
|
|
120
|
+
const a = randomUnitVector(DIM, 42);
|
|
121
|
+
for (const target of [0.999, 0.96, 0.955, 0.9, 0.85, 0.3, 0, -0.5]) {
|
|
122
|
+
const b = vectorAtSimilarity(a, target, 99);
|
|
123
|
+
expect(dot(a, b)).toBeCloseTo(target, 5);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
describe('Phase 4.1 embedding benchmark — precision/recall across a threshold sweep', () => {
|
|
128
|
+
it('at the shipped default (0.95), every near-dup pair merges and every hard negative does not', async () => {
|
|
129
|
+
const svc = await newService();
|
|
130
|
+
const consolidator = new MemoryConsolidator(svc);
|
|
131
|
+
const { anchors, pairs } = buildCorpus();
|
|
132
|
+
const ids = {};
|
|
133
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
134
|
+
const a = createDefaultEntry({ key: `${pairs[i].label}-a`, content: `${pairs[i].label} canonical text ${i}` });
|
|
135
|
+
a.embedding = anchors[i];
|
|
136
|
+
await svc.store(a);
|
|
137
|
+
const b = createDefaultEntry({ key: `${pairs[i].label}-b`, content: `${pairs[i].label} variant text ${i}` });
|
|
138
|
+
b.embedding = vectorAtSimilarity(anchors[i], pairs[i].targetSimilarity, 5000 + i);
|
|
139
|
+
await svc.store(b);
|
|
140
|
+
ids[pairs[i].label] = [a.id, b.id];
|
|
141
|
+
}
|
|
142
|
+
await consolidator.dedup('keep-newest');
|
|
143
|
+
const adapter = svc.getAdapter();
|
|
144
|
+
let truePositives = 0;
|
|
145
|
+
let falseNegatives = 0;
|
|
146
|
+
let trueNegatives = 0;
|
|
147
|
+
let falsePositives = 0;
|
|
148
|
+
for (const pair of pairs) {
|
|
149
|
+
const [idA, idB] = ids[pair.label];
|
|
150
|
+
const bothSurvive = adapter.entries.has(idA) && adapter.entries.has(idB);
|
|
151
|
+
const merged = !bothSurvive;
|
|
152
|
+
if (pair.shouldMergeAtDefault) {
|
|
153
|
+
if (merged)
|
|
154
|
+
truePositives++;
|
|
155
|
+
else
|
|
156
|
+
falseNegatives++;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
if (merged)
|
|
160
|
+
falsePositives++;
|
|
161
|
+
else
|
|
162
|
+
trueNegatives++;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Ground-truth contract for the shipped default: clean separation
|
|
166
|
+
// around the 0.95 boundary in this corpus — no false merges, no
|
|
167
|
+
// false splits among the near-dup/hard-negative pairs constructed
|
|
168
|
+
// above.
|
|
169
|
+
expect(falsePositives).toBe(0); // no hard negative incorrectly merged
|
|
170
|
+
expect(falseNegatives).toBe(0); // no true near-dup left unmerged
|
|
171
|
+
expect(truePositives).toBe(pairs.filter((p) => p.shouldMergeAtDefault).length);
|
|
172
|
+
expect(trueNegatives).toBe(pairs.filter((p) => !p.shouldMergeAtDefault).length);
|
|
173
|
+
await svc.close();
|
|
174
|
+
});
|
|
175
|
+
it('reports precision/recall/false-merge-rate across a threshold sweep', async () => {
|
|
176
|
+
// Deliberately offset from every corpus pair's constructed similarity
|
|
177
|
+
// (0.999/0.98/0.96/0.955/0.94/0.90/0.85/0.30) by a margin large enough
|
|
178
|
+
// to absorb float32 precision jitter from HNSW's internal
|
|
179
|
+
// normalize-then-dot-product distance computation (empirically <1e-5
|
|
180
|
+
// here, but a boundary value with ZERO margin — e.g. sweeping exactly
|
|
181
|
+
// 0.90 against a pair constructed at exactly 0.90 — is a real flake
|
|
182
|
+
// this file hit and fixed while writing it; the margin here is the fix).
|
|
183
|
+
const thresholds = [0.5, 0.8, 0.875, 0.92, 0.97];
|
|
184
|
+
const rows = [];
|
|
185
|
+
for (const threshold of thresholds) {
|
|
186
|
+
const svc = await newService();
|
|
187
|
+
const consolidator = new MemoryConsolidator(svc, { similarityThreshold: threshold });
|
|
188
|
+
const { anchors, pairs: p2 } = buildCorpus();
|
|
189
|
+
const ids = {};
|
|
190
|
+
for (let i = 0; i < p2.length; i++) {
|
|
191
|
+
const a = createDefaultEntry({ key: `${p2[i].label}-a`, content: `${p2[i].label} canonical text ${i}` });
|
|
192
|
+
a.embedding = anchors[i];
|
|
193
|
+
await svc.store(a);
|
|
194
|
+
const b = createDefaultEntry({ key: `${p2[i].label}-b`, content: `${p2[i].label} variant text ${i}` });
|
|
195
|
+
b.embedding = vectorAtSimilarity(anchors[i], p2[i].targetSimilarity, 5000 + i);
|
|
196
|
+
await svc.store(b);
|
|
197
|
+
ids[p2[i].label] = [a.id, b.id];
|
|
198
|
+
}
|
|
199
|
+
await consolidator.dedup('keep-newest');
|
|
200
|
+
const adapter = svc.getAdapter();
|
|
201
|
+
// Ground truth here is "similarity >= threshold" (not the fixed
|
|
202
|
+
// 0.95-default contract used above), so precision/recall are
|
|
203
|
+
// measured against what SHOULD merge at each threshold under test.
|
|
204
|
+
let tp = 0;
|
|
205
|
+
let fp = 0;
|
|
206
|
+
let fn = 0;
|
|
207
|
+
let tn = 0;
|
|
208
|
+
for (const pair of p2) {
|
|
209
|
+
const [idA, idB] = ids[pair.label];
|
|
210
|
+
const merged = !(adapter.entries.has(idA) && adapter.entries.has(idB));
|
|
211
|
+
const shouldMerge = pair.targetSimilarity >= threshold;
|
|
212
|
+
if (shouldMerge && merged)
|
|
213
|
+
tp++;
|
|
214
|
+
else if (shouldMerge && !merged)
|
|
215
|
+
fn++;
|
|
216
|
+
else if (!shouldMerge && merged)
|
|
217
|
+
fp++;
|
|
218
|
+
else
|
|
219
|
+
tn++;
|
|
220
|
+
}
|
|
221
|
+
const precision = tp + fp === 0 ? 1 : tp / (tp + fp);
|
|
222
|
+
const recall = tp + fn === 0 ? 1 : tp / (tp + fn);
|
|
223
|
+
const falseMergeRate = fp + tn === 0 ? 0 : fp / (fp + tn);
|
|
224
|
+
rows.push({ threshold, precision, recall, falseMergeRate });
|
|
225
|
+
await svc.close();
|
|
226
|
+
}
|
|
227
|
+
// eslint-disable-next-line no-console
|
|
228
|
+
console.log('similarityThreshold sweep (synthetic exact-similarity corpus):', JSON.stringify(rows));
|
|
229
|
+
// Every threshold in this sweep gets perfect precision/recall on this
|
|
230
|
+
// corpus, because pair similarities were deliberately placed with a
|
|
231
|
+
// >=0.005 margin on either side of each candidate threshold. That is
|
|
232
|
+
// the corpus proving the near-dup pass's comparison (`similarity >=
|
|
233
|
+
// threshold`) is exact and monotonic, not that any one threshold is
|
|
234
|
+
// "best" — this sweep is a mechanics check, not a model-selection
|
|
235
|
+
// benchmark.
|
|
236
|
+
for (const row of rows) {
|
|
237
|
+
expect(row.precision).toBe(1);
|
|
238
|
+
expect(row.recall).toBe(1);
|
|
239
|
+
expect(row.falseMergeRate).toBe(0);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
describe('Phase 4.1 embedding benchmark — namespace isolation (pre-existing behavior, unchanged)', () => {
|
|
244
|
+
it('cross-namespace near-duplicates merge exactly as cross-namespace hash-exact duplicates already did', async () => {
|
|
245
|
+
const svc = await newService();
|
|
246
|
+
const consolidator = new MemoryConsolidator(svc);
|
|
247
|
+
// Hash-exact case: identical content, two namespaces. dedup()'s
|
|
248
|
+
// bucket-by-hash pass has never scoped by namespace (grep-confirmed
|
|
249
|
+
// on the pre-candidate code: `for (const entry of entries.values())`
|
|
250
|
+
// with no namespace filter) — this candidate does not change that.
|
|
251
|
+
const h1 = createDefaultEntry({ key: 'h', content: 'exact-duplicate-content', namespace: 'ns-a' });
|
|
252
|
+
await svc.store(h1);
|
|
253
|
+
const h2 = createDefaultEntry({ key: 'h', content: 'exact-duplicate-content', namespace: 'ns-b' });
|
|
254
|
+
await svc.store(h2);
|
|
255
|
+
// Near-dup case: different content, identical embedding, two namespaces.
|
|
256
|
+
const anchor = randomUnitVector(DIM, 777);
|
|
257
|
+
const e1 = createDefaultEntry({ key: 'e', content: 'namespace-a-phrasing', namespace: 'ns-a' });
|
|
258
|
+
e1.embedding = anchor;
|
|
259
|
+
await svc.store(e1);
|
|
260
|
+
const e2 = createDefaultEntry({ key: 'e', content: 'namespace-b-phrasing', namespace: 'ns-b' });
|
|
261
|
+
e2.embedding = new Float32Array(anchor);
|
|
262
|
+
await svc.store(e2);
|
|
263
|
+
const result = await consolidator.dedup('keep-newest');
|
|
264
|
+
const adapter = svc.getAdapter();
|
|
265
|
+
// Both the hash-exact pair and the near-dup pair merge across the
|
|
266
|
+
// namespace boundary — documented, not silently changed. If Ruflo
|
|
267
|
+
// later decides dedup() should be namespace-scoped, that's a
|
|
268
|
+
// separate, pre-existing-behavior-affecting decision spanning both
|
|
269
|
+
// passes, not something introduced by this candidate.
|
|
270
|
+
const hashSurvivors = [h1.id, h2.id].filter((id) => adapter.entries.has(id));
|
|
271
|
+
const embedSurvivors = [e1.id, e2.id].filter((id) => adapter.entries.has(id));
|
|
272
|
+
expect(hashSurvivors.length).toBe(1);
|
|
273
|
+
expect(embedSurvivors.length).toBe(1);
|
|
274
|
+
expect(result.merged).toBe(2);
|
|
275
|
+
await svc.close();
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
describe('Phase 4.1 embedding benchmark — determinism / replay', () => {
|
|
279
|
+
it('the same corpus produces byte-identical merge/group counts and survivor keys across 3 independent runs', async () => {
|
|
280
|
+
// Entry ids are randomly generated per store() call (mem_<ts>_<rand>),
|
|
281
|
+
// so they are NOT expected to match across independent runs even when
|
|
282
|
+
// merge behavior is fully deterministic — comparing raw ids here would
|
|
283
|
+
// be testing the id generator, not dedup(). Compare by `key` instead,
|
|
284
|
+
// which this corpus constructs deterministically from `pairs`.
|
|
285
|
+
async function runOnce() {
|
|
286
|
+
const svc = await newService();
|
|
287
|
+
const consolidator = new MemoryConsolidator(svc);
|
|
288
|
+
const { anchors, pairs } = buildCorpus();
|
|
289
|
+
const idToKey = new Map();
|
|
290
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
291
|
+
const aKey = `${pairs[i].label}-a`;
|
|
292
|
+
const a = createDefaultEntry({ key: aKey, content: `${pairs[i].label} canonical text ${i}` });
|
|
293
|
+
a.embedding = anchors[i];
|
|
294
|
+
a.updatedAt = 1000 + i * 2;
|
|
295
|
+
await svc.store(a);
|
|
296
|
+
idToKey.set(a.id, aKey);
|
|
297
|
+
const bKey = `${pairs[i].label}-b`;
|
|
298
|
+
const b = createDefaultEntry({ key: bKey, content: `${pairs[i].label} variant text ${i}` });
|
|
299
|
+
b.embedding = vectorAtSimilarity(anchors[i], pairs[i].targetSimilarity, 5000 + i);
|
|
300
|
+
b.updatedAt = 1000 + i * 2 + 1; // always the newer of the pair
|
|
301
|
+
await svc.store(b);
|
|
302
|
+
idToKey.set(b.id, bKey);
|
|
303
|
+
}
|
|
304
|
+
const result = await consolidator.dedup('keep-newest');
|
|
305
|
+
const adapter = svc.getAdapter();
|
|
306
|
+
const survivorKeys = [...idToKey.entries()]
|
|
307
|
+
.filter(([id]) => adapter.entries.has(id))
|
|
308
|
+
.map(([, key]) => key)
|
|
309
|
+
.sort();
|
|
310
|
+
await svc.close();
|
|
311
|
+
return { merged: result.merged, groups: result.groups, survivorKeys };
|
|
312
|
+
}
|
|
313
|
+
// Sequential, not parallel — isolates each run's timing/state fully,
|
|
314
|
+
// removing any doubt about event-loop interleaving as a confound.
|
|
315
|
+
const r1 = await runOnce();
|
|
316
|
+
const r2 = await runOnce();
|
|
317
|
+
const r3 = await runOnce();
|
|
318
|
+
expect(r2).toEqual(r1);
|
|
319
|
+
expect(r3).toEqual(r1);
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
describe('Phase 4.1 embedding benchmark — latency/memory at realistic cardinality', () => {
|
|
323
|
+
it('dedup() at N=5000 (matching this repo\'s own HNSW benchmark convention) completes within a generous ceiling, baseline vs candidate', async () => {
|
|
324
|
+
const N = 5000;
|
|
325
|
+
const DUP_FRACTION = 0.1; // 10% of entries are near-duplicates of an earlier entry
|
|
326
|
+
async function buildAndRun(similarityThreshold) {
|
|
327
|
+
const svc = await newService();
|
|
328
|
+
const consolidator = new MemoryConsolidator(svc, { similarityThreshold });
|
|
329
|
+
const anchors = [];
|
|
330
|
+
for (let i = 0; i < N; i++) {
|
|
331
|
+
const isDup = i > 0 && i % Math.round(1 / DUP_FRACTION) === 0;
|
|
332
|
+
let embedding;
|
|
333
|
+
if (isDup) {
|
|
334
|
+
const anchor = anchors[Math.floor(Math.random() * anchors.length)];
|
|
335
|
+
embedding = vectorAtSimilarity(anchor, 0.985, i);
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
embedding = randomUnitVector(DIM, 20000 + i);
|
|
339
|
+
anchors.push(embedding);
|
|
340
|
+
}
|
|
341
|
+
const entry = createDefaultEntry({ key: `n-${i}`, content: `entry-${i}-${isDup ? 'dup' : 'unique'}` });
|
|
342
|
+
entry.embedding = embedding;
|
|
343
|
+
await svc.store(entry);
|
|
344
|
+
}
|
|
345
|
+
const memBefore = process.memoryUsage().heapUsed;
|
|
346
|
+
const t0 = Date.now();
|
|
347
|
+
const result = await consolidator.dedup('keep-newest');
|
|
348
|
+
const durationMs = Date.now() - t0;
|
|
349
|
+
const memAfter = process.memoryUsage().heapUsed;
|
|
350
|
+
await svc.close();
|
|
351
|
+
return { durationMs, heapDeltaMb: (memAfter - memBefore) / (1024 * 1024), merged: result.merged };
|
|
352
|
+
}
|
|
353
|
+
const baseline = await buildAndRun(1); // near-dup pass disabled — hash-only, matches pre-candidate behavior
|
|
354
|
+
const candidate = await buildAndRun(0.95); // shipped default
|
|
355
|
+
// eslint-disable-next-line no-console
|
|
356
|
+
console.log('latency/memory @ N=5000:', JSON.stringify({ baseline, candidate }));
|
|
357
|
+
// Generous ceilings — this is a regression guard against a pathological
|
|
358
|
+
// blowup (e.g., an accidental O(n^2) reintroduction), not a tight perf
|
|
359
|
+
// assertion; CI hardware varies. Real numbers are logged above for the
|
|
360
|
+
// reviewer regardless of pass/fail margin.
|
|
361
|
+
expect(candidate.durationMs).toBeLessThan(15_000);
|
|
362
|
+
expect(candidate.merged).toBeGreaterThan(0); // the injected near-dups were actually caught
|
|
363
|
+
expect(baseline.merged).toBe(0); // hash-only baseline: all content strings are unique, so 0 hash-exact merges
|
|
364
|
+
}, 30_000);
|
|
365
|
+
});
|
|
366
|
+
//# sourceMappingURL=consolidator-embedding-benchmark.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolidator-embedding-benchmark.test.js","sourceRoot":"","sources":["../src/consolidator-embedding-benchmark.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryConsolidator — ADR-125 Phase 4
|
|
3
|
+
*
|
|
4
|
+
* Periodic maintenance for the in-memory state owned by {@link UnifiedMemoryService}.
|
|
5
|
+
*
|
|
6
|
+
* Operations:
|
|
7
|
+
* - `sweepExpired()` — drop entries past `expiresAt` from all indexes (including HNSW).
|
|
8
|
+
* - `dedup(strategy)` — collapse content-hash duplicates per strategy.
|
|
9
|
+
* - `compactHnsw()` — rebuild the HNSW index from current `entries`.
|
|
10
|
+
* - `runAll()` — sweep → dedup → compact in order.
|
|
11
|
+
*
|
|
12
|
+
* Invoked from two paths:
|
|
13
|
+
* 1. {@link UnifiedMemoryService}'s lifecycle (background timer when
|
|
14
|
+
* `consolidator.autoRun === true`, plus `close()`).
|
|
15
|
+
* 2. The AgentDB `nightlyLearner` controller (`src/controller-registry.ts`)
|
|
16
|
+
* delegates to `runAll()` instead of hitting AgentDB directly.
|
|
17
|
+
*
|
|
18
|
+
* Phase 3 placeholder lives in this file too — it provides the typed
|
|
19
|
+
* surface that {@link UnifiedMemoryService.getConsolidator} resolves to.
|
|
20
|
+
* The real implementation lands with Phase 4.
|
|
21
|
+
*
|
|
22
|
+
* @module v3/memory/consolidator
|
|
23
|
+
*/
|
|
24
|
+
import type { MemoryEntry } from './types.js';
|
|
25
|
+
/**
|
|
26
|
+
* Strategy for resolving content-hash duplicates inside {@link MemoryConsolidator.dedup}.
|
|
27
|
+
*/
|
|
28
|
+
export type DedupStrategy = 'keep-newest' | 'keep-oldest' | 'merge-tags';
|
|
29
|
+
export interface ConsolidatorOptions {
|
|
30
|
+
/** Default strategy when `dedup()` is invoked with no argument. */
|
|
31
|
+
dedupStrategy?: DedupStrategy;
|
|
32
|
+
/** Used by `MemoryService` when scheduling automatic runs (ms). */
|
|
33
|
+
intervalMs?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Cosine-similarity floor (0-1) above which two entries with embeddings
|
|
36
|
+
* are treated as near-duplicates by `dedup()`'s embedding pass, in
|
|
37
|
+
* addition to its byte-exact content-hash pass. Set to `1` (or above) to
|
|
38
|
+
* disable the embedding pass entirely and keep pre-Phase-4.1 hash-only
|
|
39
|
+
* behavior. Default 0.95 — conservative, matches the threshold already
|
|
40
|
+
* used by the (unwired) domain-layer consolidator for the same purpose.
|
|
41
|
+
*/
|
|
42
|
+
similarityThreshold?: number;
|
|
43
|
+
}
|
|
44
|
+
export interface SweepResult {
|
|
45
|
+
removed: number;
|
|
46
|
+
remaining: number;
|
|
47
|
+
hnswRemoved: number;
|
|
48
|
+
}
|
|
49
|
+
export interface DedupResult {
|
|
50
|
+
merged: number;
|
|
51
|
+
groups: number;
|
|
52
|
+
}
|
|
53
|
+
export interface CompactResult {
|
|
54
|
+
before: number;
|
|
55
|
+
after: number;
|
|
56
|
+
durationMs: number;
|
|
57
|
+
}
|
|
58
|
+
export interface ConsolidationResult {
|
|
59
|
+
sweep: SweepResult;
|
|
60
|
+
dedup: DedupResult;
|
|
61
|
+
compact: CompactResult;
|
|
62
|
+
totalDurationMs: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Minimal surface the consolidator needs from `UnifiedMemoryService`. Avoids
|
|
66
|
+
* a circular type import.
|
|
67
|
+
*/
|
|
68
|
+
interface ServiceLike {
|
|
69
|
+
getAdapter(): {
|
|
70
|
+
entries: Map<string, MemoryEntry>;
|
|
71
|
+
namespaceIndex: Map<string, Set<string>>;
|
|
72
|
+
keyIndex: Map<string, string>;
|
|
73
|
+
tagIndex: Map<string, Set<string>>;
|
|
74
|
+
[key: string]: any;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export declare class MemoryConsolidator {
|
|
78
|
+
private readonly service;
|
|
79
|
+
private readonly opts;
|
|
80
|
+
constructor(service: ServiceLike, opts?: ConsolidatorOptions);
|
|
81
|
+
/**
|
|
82
|
+
* Drop all entries whose `expiresAt` is in the past.
|
|
83
|
+
*/
|
|
84
|
+
sweepExpired(): Promise<SweepResult>;
|
|
85
|
+
/**
|
|
86
|
+
* Pick the keeper for a duplicate group per `strategy` and, for
|
|
87
|
+
* `merge-tags`, union the group's tag sets onto it. Shared by both the
|
|
88
|
+
* content-hash pass and the embedding near-duplicate pass in `dedup()`.
|
|
89
|
+
*/
|
|
90
|
+
private selectKeeper;
|
|
91
|
+
/**
|
|
92
|
+
* Drop every entry in `bucket` except the strategy-selected keeper,
|
|
93
|
+
* updating all adapter indexes (and the HNSW index, for entries that have
|
|
94
|
+
* an embedding) to match. Shared by both dedup passes.
|
|
95
|
+
*/
|
|
96
|
+
private mergeGroup;
|
|
97
|
+
/**
|
|
98
|
+
* Collapse duplicates across all namespaces per `strategy`, in two passes:
|
|
99
|
+
*
|
|
100
|
+
* 1. Byte-exact content-hash buckets (unchanged from pre-4.1 behavior).
|
|
101
|
+
* 2. Embedding near-duplicates: for entries that survive pass 1 and carry
|
|
102
|
+
* an `embedding`, query the already-populated `HNSWIndex` (the same
|
|
103
|
+
* index instance this method already holds a handle to for removal
|
|
104
|
+
* bookkeeping) for cosine-similarity neighbors above
|
|
105
|
+
* `opts.similarityThreshold` (default 0.95). This catches paraphrases
|
|
106
|
+
* and reformattings that byte-exact hashing structurally cannot, at no
|
|
107
|
+
* extra backend round-trip cost — the embedding and the index are both
|
|
108
|
+
* already resident in memory at this call site.
|
|
109
|
+
*
|
|
110
|
+
* Pass 2 only runs when the index's configured metric is `'cosine'` (its
|
|
111
|
+
* distances are `1 - similarity`) and the threshold is `< 1`; otherwise
|
|
112
|
+
* behavior is identical to hash-only dedup.
|
|
113
|
+
*
|
|
114
|
+
* - `keep-newest`: keep the entry with the highest `updatedAt`, drop the rest.
|
|
115
|
+
* - `keep-oldest`: keep the entry with the lowest `createdAt`, drop the rest.
|
|
116
|
+
* - `merge-tags`: keep newest, but union the tag sets of the duplicates first.
|
|
117
|
+
*/
|
|
118
|
+
dedup(strategy?: DedupStrategy): Promise<DedupResult>;
|
|
119
|
+
/**
|
|
120
|
+
* Rebuild the HNSW index from the current set of entries with embeddings.
|
|
121
|
+
* Returns a count snapshot + duration.
|
|
122
|
+
*/
|
|
123
|
+
compactHnsw(): Promise<CompactResult>;
|
|
124
|
+
/**
|
|
125
|
+
* Sweep → dedup → compact. Used by the background timer and by the
|
|
126
|
+
* `nightlyLearner` AgentDB controller.
|
|
127
|
+
*/
|
|
128
|
+
runAll(): Promise<ConsolidationResult>;
|
|
129
|
+
}
|
|
130
|
+
export default MemoryConsolidator;
|
|
131
|
+
//# sourceMappingURL=consolidator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolidator.d.ts","sourceRoot":"","sources":["../src/consolidator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,aAAa,GAAG,YAAY,CAAC;AAEzE,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAoBD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,UAAU,WAAW;IACnB,UAAU,IAAI;QAEZ,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAClC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QACzC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC9B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QAEnC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;CACH;AAED,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADJ,OAAO,EAAE,WAAW,EACpB,IAAI,GAAE,mBAAwB;IAGjD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC;IA4C1C;;;;OAIG;IACH,OAAO,CAAC,YAAY;IASpB;;;;OAIG;YACW,UAAU;IA8CxB;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,KAAK,CAAC,QAAQ,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC;IAmF3D;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,aAAa,CAAC;IAsC3C;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,mBAAmB,CAAC;CAY7C;AAED,eAAe,kBAAkB,CAAC"}
|