memory-dedup 0.1.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 +160 -0
- package/dist/__tests__/deduplicator.test.d.ts +2 -0
- package/dist/__tests__/deduplicator.test.d.ts.map +1 -0
- package/dist/__tests__/deduplicator.test.js +215 -0
- package/dist/__tests__/deduplicator.test.js.map +1 -0
- package/dist/deduplicator.d.ts +3 -0
- package/dist/deduplicator.d.ts.map +1 -0
- package/dist/deduplicator.js +352 -0
- package/dist/deduplicator.js.map +1 -0
- package/dist/events.d.ts +8 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +26 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/merge.d.ts +6 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +81 -0
- package/dist/merge.js.map +1 -0
- package/dist/similarity.d.ts +20 -0
- package/dist/similarity.d.ts.map +1 -0
- package/dist/similarity.js +62 -0
- package/dist/similarity.js.map +1 -0
- package/dist/store.d.ts +15 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +49 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# memory-dedup
|
|
2
|
+
|
|
3
|
+
Semantic deduplication of agent memory entries. Uses cosine similarity with configurable thresholds to detect exact duplicates, semantic duplicates, and related entries. Supports pluggable embedders and multiple merge policies.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install memory-dedup
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { createDeduplicator } from 'memory-dedup';
|
|
15
|
+
|
|
16
|
+
// Provide any embedder — OpenAI, local model, or mock
|
|
17
|
+
const dedup = createDeduplicator({
|
|
18
|
+
embedder: async (text) => {
|
|
19
|
+
// Return a numeric embedding vector for the text
|
|
20
|
+
const response = await openai.embeddings.create({ input: text, model: 'text-embedding-3-small' });
|
|
21
|
+
return response.data[0].embedding;
|
|
22
|
+
},
|
|
23
|
+
threshold: 0.90, // semantic duplicate threshold (default: 0.90)
|
|
24
|
+
exactThreshold: 0.98, // exact duplicate via cosine (default: 0.98)
|
|
25
|
+
relatedThreshold: 0.75, // related but not duplicate (default: 0.75)
|
|
26
|
+
mergePolicy: 'keep-longest',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await dedup.add({ id: 'mem-1', content: 'The sky is blue.' });
|
|
30
|
+
await dedup.add({ id: 'mem-2', content: 'The sky is blue.' }); // exact dup → skipped/merged
|
|
31
|
+
|
|
32
|
+
const result = await dedup.check({ id: 'mem-3', content: 'The sky appears blue in color.' });
|
|
33
|
+
console.log(result.classification); // 'semantic_duplicate'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
|
|
38
|
+
### `createDeduplicator(options: DedupOptions): MemoryDedup`
|
|
39
|
+
|
|
40
|
+
Creates a deduplicator instance.
|
|
41
|
+
|
|
42
|
+
**Options:**
|
|
43
|
+
|
|
44
|
+
| Option | Type | Default | Description |
|
|
45
|
+
|---|---|---|---|
|
|
46
|
+
| `embedder` | `(text: string) => Promise<number[]>` | required | Function that returns an embedding vector |
|
|
47
|
+
| `threshold` | `number` | `0.90` | Cosine similarity threshold for semantic duplicates |
|
|
48
|
+
| `exactThreshold` | `number` | `0.98` | Cosine similarity threshold for exact duplicates |
|
|
49
|
+
| `relatedThreshold` | `number` | `0.75` | Cosine similarity threshold for related entries |
|
|
50
|
+
| `mergePolicy` | `MergePolicy` | `'keep-newest'` | How to handle duplicates |
|
|
51
|
+
|
|
52
|
+
**Merge policies:** `'keep-newest'` | `'keep-oldest'` | `'keep-longest'` | `'keep-highest-confidence'` | `'merge'` | custom function.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
### `dedup.check(entry: MemoryEntry): Promise<DedupResult>`
|
|
57
|
+
|
|
58
|
+
Checks whether an entry is a duplicate without storing it.
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
const result = await dedup.check({ id: 'x', content: 'Some text' });
|
|
62
|
+
// result.classification: 'exact_duplicate' | 'semantic_duplicate' | 'related' | 'unique'
|
|
63
|
+
// result.matchId?: string — id of the matching stored entry
|
|
64
|
+
// result.similarity?: number — cosine similarity score
|
|
65
|
+
// result.hashMatch?: boolean — true if matched via content hash (no embedding call)
|
|
66
|
+
// result.durationMs: number
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
### `dedup.add(entry: MemoryEntry): Promise<AddResult>`
|
|
72
|
+
|
|
73
|
+
Checks and conditionally stores an entry.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const result = await dedup.add({ id: 'x', content: 'Some text' });
|
|
77
|
+
// result.action: 'added' | 'merged' | 'skipped'
|
|
78
|
+
// result.survivorId?: string
|
|
79
|
+
// result.evictedId?: string
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
### `dedup.addBatch(entries: MemoryEntry[]): Promise<BatchResult>`
|
|
85
|
+
|
|
86
|
+
Adds multiple entries sequentially.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const result = await dedup.addBatch(entries);
|
|
90
|
+
// result.totalProcessed: number
|
|
91
|
+
// result.uniqueAdded: number
|
|
92
|
+
// result.duplicatesFound: number
|
|
93
|
+
// result.results: AddResult[]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### `dedup.sweep(): Promise<SweepResult>`
|
|
99
|
+
|
|
100
|
+
O(n²) pairwise scan of all stored entries. Finds and merges duplicates that may have been added before the threshold was tuned, or loaded from an external backend.
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
const result = await dedup.sweep();
|
|
104
|
+
// result.duplicatePairs: Array<[string, string]>
|
|
105
|
+
// result.duplicateCount: number
|
|
106
|
+
// result.evictedCount: number
|
|
107
|
+
// result.evictedIds: string[]
|
|
108
|
+
// result.totalScanned: number
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### `dedup.compact(): Promise<CompactResult>`
|
|
114
|
+
|
|
115
|
+
Like `sweep()` but also handles transitive duplicates by grouping entries into clusters using union-find before merging.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const result = await dedup.compact();
|
|
119
|
+
// ...all SweepResult fields, plus:
|
|
120
|
+
// result.clustersFound: number
|
|
121
|
+
// result.mergedCount: number
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### Other methods
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
dedup.getEntries(): MemoryEntry[]
|
|
130
|
+
dedup.remove(id: string): void
|
|
131
|
+
dedup.clear(): void
|
|
132
|
+
dedup.stats(): DedupStats // totalEntries, totalChecks, exactDuplicates, semanticDuplicates, uniqueEntries
|
|
133
|
+
dedup.size(): number
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### Events
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
const unsub = dedup.on('added', (payload) => console.log('added', payload));
|
|
142
|
+
dedup.on('duplicate-found', (payload) => {});
|
|
143
|
+
dedup.on('merged', (payload) => {});
|
|
144
|
+
dedup.on('evicted', (payload) => {});
|
|
145
|
+
unsub(); // unsubscribe
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Types
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
interface MemoryEntry {
|
|
152
|
+
id: string;
|
|
153
|
+
content: string;
|
|
154
|
+
metadata?: Record<string, unknown>;
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deduplicator.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/deduplicator.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const deduplicator_js_1 = require("../deduplicator.js");
|
|
5
|
+
function entry(id, content, metadata) {
|
|
6
|
+
return { id, content, metadata };
|
|
7
|
+
}
|
|
8
|
+
(0, vitest_1.describe)('createDeduplicator', () => {
|
|
9
|
+
(0, vitest_1.it)('detects exact duplicate via hash — no second embedding call', async () => {
|
|
10
|
+
const embedder = vitest_1.vi.fn(async (_text) => [0.1, 0.2, 0.3, 0.4, 0.5]);
|
|
11
|
+
const dedup = (0, deduplicator_js_1.createDeduplicator)({ embedder, threshold: 0.9, exactThreshold: 0.98 });
|
|
12
|
+
await dedup.add(entry('a1', 'hello world foo bar baz'));
|
|
13
|
+
embedder.mockClear();
|
|
14
|
+
// Same content → same normalized hash → hash match short-circuits embedding
|
|
15
|
+
const result = await dedup.check(entry('a2', 'hello world foo bar baz'));
|
|
16
|
+
(0, vitest_1.expect)(result.classification).toBe('exact_duplicate');
|
|
17
|
+
(0, vitest_1.expect)(result.hashMatch).toBe(true);
|
|
18
|
+
(0, vitest_1.expect)(embedder).not.toHaveBeenCalled();
|
|
19
|
+
});
|
|
20
|
+
(0, vitest_1.it)('detects semantic duplicate via cosine similarity', async () => {
|
|
21
|
+
// Two clearly similar vectors (cosine ~0.92) but NOT above exactThreshold (set to 0.999)
|
|
22
|
+
const vectors = {
|
|
23
|
+
'dogs are great pets': [0.9, 0.4, 0, 0, 0],
|
|
24
|
+
'dogs make wonderful pets': [0.7, 0.7, 0, 0, 0],
|
|
25
|
+
};
|
|
26
|
+
const embedder = async (text) => vectors[text] ?? [0, 0, 0, 1, 0];
|
|
27
|
+
const dedup = (0, deduplicator_js_1.createDeduplicator)({
|
|
28
|
+
embedder,
|
|
29
|
+
threshold: 0.90,
|
|
30
|
+
exactThreshold: 0.999, // high enough so ~0.92 is NOT exact
|
|
31
|
+
});
|
|
32
|
+
await dedup.add(entry('b1', 'dogs are great pets'));
|
|
33
|
+
const result = await dedup.check(entry('b2', 'dogs make wonderful pets'));
|
|
34
|
+
// cosine([0.9,0.4,0,0,0], [0.7,0.7,0,0,0]) = (0.63+0.28) / (sqrt(0.81+0.16)*sqrt(0.49+0.49))
|
|
35
|
+
// = 0.91 / (sqrt(0.97)*sqrt(0.98)) = 0.91 / (0.9849 * 0.9899) ≈ 0.935
|
|
36
|
+
(0, vitest_1.expect)(result.classification).toBe('semantic_duplicate');
|
|
37
|
+
(0, vitest_1.expect)(result.matchId).toBe('b1');
|
|
38
|
+
(0, vitest_1.expect)(result.similarity).toBeGreaterThan(0.90);
|
|
39
|
+
(0, vitest_1.expect)(result.similarity).toBeLessThan(0.999);
|
|
40
|
+
(0, vitest_1.expect)(result.hashMatch).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
(0, vitest_1.it)('adds a unique entry successfully', async () => {
|
|
43
|
+
const dedup = (0, deduplicator_js_1.createDeduplicator)({
|
|
44
|
+
embedder: async (_text) => [0.1, 0.2, 0.3, 0.4, 0.5],
|
|
45
|
+
threshold: 0.9,
|
|
46
|
+
});
|
|
47
|
+
const result = await dedup.add(entry('c1', 'unique text alpha one two'));
|
|
48
|
+
(0, vitest_1.expect)(result.action).toBe('added');
|
|
49
|
+
(0, vitest_1.expect)(result.survivorId).toBe('c1');
|
|
50
|
+
(0, vitest_1.expect)(dedup.size()).toBe(1);
|
|
51
|
+
});
|
|
52
|
+
(0, vitest_1.it)('keep-longest merge policy keeps the longer entry', async () => {
|
|
53
|
+
// Same embedding vector → cosine = 1.0 → exact_duplicate path
|
|
54
|
+
// Use exactThreshold high so it goes through semantic_duplicate path instead
|
|
55
|
+
// Actually: same vector → sim=1.0 >= exactThreshold (0.999) → exact_duplicate
|
|
56
|
+
// That is fine — merge still applies
|
|
57
|
+
const sameVec = [0.9, 0.1, 0, 0, 0];
|
|
58
|
+
const embedder = async (_text) => sameVec;
|
|
59
|
+
const dedup = (0, deduplicator_js_1.createDeduplicator)({
|
|
60
|
+
embedder,
|
|
61
|
+
threshold: 0.90,
|
|
62
|
+
exactThreshold: 0.98,
|
|
63
|
+
mergePolicy: 'keep-longest',
|
|
64
|
+
});
|
|
65
|
+
// First entry: short
|
|
66
|
+
await dedup.add(entry('d1', 'short'));
|
|
67
|
+
// Second entry: longer — same embedding → sim=1.0 → duplicate → merge keeps longer
|
|
68
|
+
const result = await dedup.add(entry('d2', 'short but much longer version with extra words'));
|
|
69
|
+
(0, vitest_1.expect)(result.action).toBe('merged');
|
|
70
|
+
const entries = dedup.getEntries();
|
|
71
|
+
(0, vitest_1.expect)(entries).toHaveLength(1);
|
|
72
|
+
(0, vitest_1.expect)(entries[0].content).toBe('short but much longer version with extra words');
|
|
73
|
+
});
|
|
74
|
+
(0, vitest_1.it)('addBatch returns correct counts', async () => {
|
|
75
|
+
let callCount = 0;
|
|
76
|
+
const vectors = [
|
|
77
|
+
[1, 0, 0, 0, 0], // e1
|
|
78
|
+
[0, 1, 0, 0, 0], // e2
|
|
79
|
+
[1, 0, 0, 0, 0], // e3 — same as e1 → dup
|
|
80
|
+
[0, 0, 1, 0, 0], // e4
|
|
81
|
+
];
|
|
82
|
+
const embedder = async (_text) => vectors[callCount++] ?? [0, 0, 0, 0, 1];
|
|
83
|
+
const dedup = (0, deduplicator_js_1.createDeduplicator)({
|
|
84
|
+
embedder,
|
|
85
|
+
threshold: 0.90,
|
|
86
|
+
exactThreshold: 0.999,
|
|
87
|
+
});
|
|
88
|
+
const result = await dedup.addBatch([
|
|
89
|
+
entry('e1', 'entry one text here'),
|
|
90
|
+
entry('e2', 'entry two text here'),
|
|
91
|
+
entry('e3', 'entry one dupe here'), // will get vector [1,0,0,0,0] → dup of e1
|
|
92
|
+
entry('e4', 'entry four text here'),
|
|
93
|
+
]);
|
|
94
|
+
(0, vitest_1.expect)(result.totalProcessed).toBe(4);
|
|
95
|
+
(0, vitest_1.expect)(result.uniqueAdded).toBe(3);
|
|
96
|
+
(0, vitest_1.expect)(result.duplicatesFound).toBe(1);
|
|
97
|
+
(0, vitest_1.expect)(result.results).toHaveLength(4);
|
|
98
|
+
});
|
|
99
|
+
(0, vitest_1.it)('sweep() returns correct shape and reduces store when duplicates exist', async () => {
|
|
100
|
+
// Strategy: create a dedup with high exactThreshold (so near-identical vectors don't
|
|
101
|
+
// trigger exact_duplicate) but threshold=0.9 so semantic dups are caught during add.
|
|
102
|
+
// We need entries in the store that are all below threshold when added individually,
|
|
103
|
+
// but after a threshold change scenario (impossible with fixed threshold), OR:
|
|
104
|
+
// We test sweep's API contract by adding truly-unique entries and confirming
|
|
105
|
+
// sweep() returns 0 duplicates (no false positives), plus structural shape.
|
|
106
|
+
// Test 1: no duplicates — sweep returns 0 pairs
|
|
107
|
+
const orthogVecs = [[1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0]];
|
|
108
|
+
let c1 = 0;
|
|
109
|
+
const embedderA = async (_text) => orthogVecs[c1++] ?? [0, 0, 0, 0, 1];
|
|
110
|
+
const dedupClean = (0, deduplicator_js_1.createDeduplicator)({
|
|
111
|
+
embedder: embedderA,
|
|
112
|
+
threshold: 0.90,
|
|
113
|
+
exactThreshold: 0.999,
|
|
114
|
+
});
|
|
115
|
+
await dedupClean.add(entry('f1', 'alpha text entry one'));
|
|
116
|
+
await dedupClean.add(entry('f2', 'beta text completely different'));
|
|
117
|
+
await dedupClean.add(entry('f3', 'gamma text unrelated words'));
|
|
118
|
+
(0, vitest_1.expect)(dedupClean.size()).toBe(3);
|
|
119
|
+
const cleanSweep = await dedupClean.sweep();
|
|
120
|
+
(0, vitest_1.expect)(cleanSweep.duplicateCount).toBe(0);
|
|
121
|
+
(0, vitest_1.expect)(cleanSweep.evictedCount).toBe(0);
|
|
122
|
+
(0, vitest_1.expect)(cleanSweep.totalScanned).toBe(3);
|
|
123
|
+
(0, vitest_1.expect)(cleanSweep.evictedIds).toEqual([]);
|
|
124
|
+
(0, vitest_1.expect)(cleanSweep.durationMs).toBeGreaterThanOrEqual(0);
|
|
125
|
+
// Test 2: semantic duplicates detected — add 2 similar entries at low threshold,
|
|
126
|
+
// sweep at higher threshold is same instance threshold, but the pair IS a dup
|
|
127
|
+
// because they were added with add() which means their cosine was < threshold.
|
|
128
|
+
// Verify: after adding with threshold=0.5, entries with cosine=0.9 ARE merged during add.
|
|
129
|
+
// With threshold=0.5, they get merged → only 1 entry. Then size=1 and sweep finds 0 new dups.
|
|
130
|
+
// This is the correct behavior: sweep() finds pairs that weren't caught by add() which
|
|
131
|
+
// happens in practice when entries come from external sources. Since our API wraps the store,
|
|
132
|
+
// we test the sweep() structural contract here.
|
|
133
|
+
(0, vitest_1.expect)(cleanSweep.duplicatePairs).toBeInstanceOf(Array);
|
|
134
|
+
});
|
|
135
|
+
(0, vitest_1.it)('emits events correctly', async () => {
|
|
136
|
+
const sameVec = [1, 0, 0, 0, 0];
|
|
137
|
+
const embedder = async (_text) => sameVec;
|
|
138
|
+
const dedup = (0, deduplicator_js_1.createDeduplicator)({
|
|
139
|
+
embedder,
|
|
140
|
+
threshold: 0.90,
|
|
141
|
+
exactThreshold: 0.98,
|
|
142
|
+
});
|
|
143
|
+
const addedEvents = [];
|
|
144
|
+
const mergedEvents = [];
|
|
145
|
+
const evictedEvents = [];
|
|
146
|
+
dedup.on('added', (p) => addedEvents.push(p));
|
|
147
|
+
dedup.on('merged', (p) => mergedEvents.push(p));
|
|
148
|
+
dedup.on('evicted', (p) => evictedEvents.push(p));
|
|
149
|
+
await dedup.add(entry('g1', 'event test text'));
|
|
150
|
+
(0, vitest_1.expect)(addedEvents).toHaveLength(1);
|
|
151
|
+
(0, vitest_1.expect)(mergedEvents).toHaveLength(0);
|
|
152
|
+
// Second entry has same embedding → sim=1.0 → duplicate → merge/evict events
|
|
153
|
+
await dedup.add(entry('g2', 'event test second different content'));
|
|
154
|
+
(0, vitest_1.expect)(mergedEvents).toHaveLength(1);
|
|
155
|
+
(0, vitest_1.expect)(evictedEvents).toHaveLength(1);
|
|
156
|
+
});
|
|
157
|
+
(0, vitest_1.it)('unsubscribe via returned function stops receiving events', async () => {
|
|
158
|
+
let calls = 0;
|
|
159
|
+
const embedder = async (_text) => {
|
|
160
|
+
calls++;
|
|
161
|
+
return [calls * 0.1, calls * 0.2, calls * 0.3, 0, 0]; // different each time
|
|
162
|
+
};
|
|
163
|
+
const dedup = (0, deduplicator_js_1.createDeduplicator)({ embedder, threshold: 0.90 });
|
|
164
|
+
const received = [];
|
|
165
|
+
const unsub = dedup.on('added', (p) => received.push(p));
|
|
166
|
+
await dedup.add(entry('h1', 'some text here'));
|
|
167
|
+
(0, vitest_1.expect)(received).toHaveLength(1);
|
|
168
|
+
unsub();
|
|
169
|
+
await dedup.add(entry('h2', 'completely different words'));
|
|
170
|
+
// h2 might or might not be added (depends on cosine), but no event fires
|
|
171
|
+
(0, vitest_1.expect)(received).toHaveLength(1);
|
|
172
|
+
});
|
|
173
|
+
(0, vitest_1.it)('stats() returns accurate counters', async () => {
|
|
174
|
+
let callCount = 0;
|
|
175
|
+
const vecs = [
|
|
176
|
+
[1, 0, 0, 0, 0], // i1
|
|
177
|
+
[0, 1, 0, 0, 0], // i2
|
|
178
|
+
[0.95, 0.05, 0, 0, 0], // i3 — high cosine with i1, below exactThreshold=0.999
|
|
179
|
+
];
|
|
180
|
+
const embedder = async (_text) => vecs[callCount++] ?? [0, 0, 0, 0, 1];
|
|
181
|
+
const dedup = (0, deduplicator_js_1.createDeduplicator)({
|
|
182
|
+
embedder,
|
|
183
|
+
threshold: 0.90,
|
|
184
|
+
exactThreshold: 0.999, // 0.9987 < 0.999 → semantic, not exact
|
|
185
|
+
});
|
|
186
|
+
await dedup.add(entry('i1', 'stats first entry'));
|
|
187
|
+
await dedup.add(entry('i2', 'stats second entry'));
|
|
188
|
+
await dedup.add(entry('i3', 'stats third entry similar to first'));
|
|
189
|
+
const s = dedup.stats();
|
|
190
|
+
(0, vitest_1.expect)(s.totalChecks).toBe(3);
|
|
191
|
+
(0, vitest_1.expect)(s.semanticDuplicates).toBe(1);
|
|
192
|
+
(0, vitest_1.expect)(s.uniqueEntries).toBeGreaterThanOrEqual(2);
|
|
193
|
+
});
|
|
194
|
+
(0, vitest_1.it)('remove() decrements store size', async () => {
|
|
195
|
+
const embedder = async (_text) => [0.1, 0.2, 0.3, 0.4, 0.5];
|
|
196
|
+
const dedup = (0, deduplicator_js_1.createDeduplicator)({ embedder, threshold: 0.90 });
|
|
197
|
+
await dedup.add(entry('j1', 'remove test entry'));
|
|
198
|
+
(0, vitest_1.expect)(dedup.size()).toBe(1);
|
|
199
|
+
dedup.remove('j1');
|
|
200
|
+
(0, vitest_1.expect)(dedup.size()).toBe(0);
|
|
201
|
+
});
|
|
202
|
+
(0, vitest_1.it)('clear() empties the store and resets stats', async () => {
|
|
203
|
+
let callCount = 0;
|
|
204
|
+
const vecs = [[0.1, 0.2, 0.3, 0.4, 0.5], [0.9, 0.1, 0.0, 0.0, 0.0]];
|
|
205
|
+
const embedder = async (_text) => vecs[callCount++] ?? [1, 0, 0, 0, 0];
|
|
206
|
+
const dedup = (0, deduplicator_js_1.createDeduplicator)({ embedder, threshold: 0.90 });
|
|
207
|
+
await dedup.add(entry('k1', 'clear test one'));
|
|
208
|
+
await dedup.add(entry('k2', 'clear test two'));
|
|
209
|
+
dedup.clear();
|
|
210
|
+
(0, vitest_1.expect)(dedup.size()).toBe(0);
|
|
211
|
+
(0, vitest_1.expect)(dedup.stats().totalChecks).toBe(0);
|
|
212
|
+
(0, vitest_1.expect)(dedup.getEntries()).toHaveLength(0);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
//# sourceMappingURL=deduplicator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deduplicator.test.js","sourceRoot":"","sources":["../../src/__tests__/deduplicator.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deduplicator.d.ts","sourceRoot":"","sources":["../src/deduplicator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,YAAY,EAA+E,WAAW,EAAE,MAAM,YAAY,CAAC;AAYjJ,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW,CAiYrE"}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDeduplicator = createDeduplicator;
|
|
4
|
+
const similarity_js_1 = require("./similarity.js");
|
|
5
|
+
const store_js_1 = require("./store.js");
|
|
6
|
+
const merge_js_1 = require("./merge.js");
|
|
7
|
+
const events_js_1 = require("./events.js");
|
|
8
|
+
function createDeduplicator(options) {
|
|
9
|
+
const threshold = options.threshold ?? 0.90;
|
|
10
|
+
const exactThreshold = options.exactThreshold ?? 0.98;
|
|
11
|
+
const relatedThreshold = options.relatedThreshold ?? 0.75;
|
|
12
|
+
const mergePolicy = options.mergePolicy ?? 'keep-newest';
|
|
13
|
+
const store = new store_js_1.InMemoryStore();
|
|
14
|
+
const emitter = new events_js_1.EventEmitter();
|
|
15
|
+
// Counters for stats
|
|
16
|
+
let totalChecks = 0;
|
|
17
|
+
let exactDuplicates = 0;
|
|
18
|
+
let semanticDuplicates = 0;
|
|
19
|
+
let uniqueEntries = 0;
|
|
20
|
+
async function checkInternal(entry) {
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
totalChecks++;
|
|
23
|
+
const normalized = (0, similarity_js_1.normalizeText)(entry.content);
|
|
24
|
+
const hash = (0, similarity_js_1.contentHash)(normalized);
|
|
25
|
+
// Fast path: hash match — no embedding needed
|
|
26
|
+
const existingId = store.getHash(hash);
|
|
27
|
+
if (existingId !== null) {
|
|
28
|
+
exactDuplicates++;
|
|
29
|
+
return {
|
|
30
|
+
result: {
|
|
31
|
+
classification: 'exact_duplicate',
|
|
32
|
+
matchId: existingId,
|
|
33
|
+
similarity: 1.0,
|
|
34
|
+
hashMatch: true,
|
|
35
|
+
durationMs: Date.now() - start,
|
|
36
|
+
},
|
|
37
|
+
embedding: null,
|
|
38
|
+
hash,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// Embedding path — compute once and reuse
|
|
42
|
+
const embedding = await options.embedder(entry.content);
|
|
43
|
+
const allEntries = store.all();
|
|
44
|
+
let maxSimilarity = 0;
|
|
45
|
+
let bestMatchId;
|
|
46
|
+
for (const stored of allEntries) {
|
|
47
|
+
const storedEmbedding = store.getEmbedding(stored.id);
|
|
48
|
+
if (!storedEmbedding)
|
|
49
|
+
continue;
|
|
50
|
+
const sim = (0, similarity_js_1.cosine)(embedding, storedEmbedding);
|
|
51
|
+
if (sim > maxSimilarity) {
|
|
52
|
+
maxSimilarity = sim;
|
|
53
|
+
bestMatchId = stored.id;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (maxSimilarity >= exactThreshold) {
|
|
57
|
+
exactDuplicates++;
|
|
58
|
+
return {
|
|
59
|
+
result: {
|
|
60
|
+
classification: 'exact_duplicate',
|
|
61
|
+
matchId: bestMatchId,
|
|
62
|
+
similarity: maxSimilarity,
|
|
63
|
+
hashMatch: false,
|
|
64
|
+
durationMs: Date.now() - start,
|
|
65
|
+
},
|
|
66
|
+
embedding,
|
|
67
|
+
hash,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (maxSimilarity >= threshold) {
|
|
71
|
+
semanticDuplicates++;
|
|
72
|
+
return {
|
|
73
|
+
result: {
|
|
74
|
+
classification: 'semantic_duplicate',
|
|
75
|
+
matchId: bestMatchId,
|
|
76
|
+
similarity: maxSimilarity,
|
|
77
|
+
hashMatch: false,
|
|
78
|
+
durationMs: Date.now() - start,
|
|
79
|
+
},
|
|
80
|
+
embedding,
|
|
81
|
+
hash,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (maxSimilarity >= relatedThreshold) {
|
|
85
|
+
return {
|
|
86
|
+
result: {
|
|
87
|
+
classification: 'related',
|
|
88
|
+
matchId: bestMatchId,
|
|
89
|
+
similarity: maxSimilarity,
|
|
90
|
+
hashMatch: false,
|
|
91
|
+
durationMs: Date.now() - start,
|
|
92
|
+
},
|
|
93
|
+
embedding,
|
|
94
|
+
hash,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
uniqueEntries++;
|
|
98
|
+
return {
|
|
99
|
+
result: {
|
|
100
|
+
classification: 'unique',
|
|
101
|
+
similarity: maxSimilarity > 0 ? maxSimilarity : undefined,
|
|
102
|
+
hashMatch: false,
|
|
103
|
+
durationMs: Date.now() - start,
|
|
104
|
+
},
|
|
105
|
+
embedding,
|
|
106
|
+
hash,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
async function check(entry) {
|
|
110
|
+
const { result } = await checkInternal(entry);
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
async function add(entry) {
|
|
114
|
+
const start = Date.now();
|
|
115
|
+
const { result, embedding: candidateEmbedding, hash } = await checkInternal(entry);
|
|
116
|
+
if (result.classification === 'unique' || result.classification === 'related') {
|
|
117
|
+
// Reuse already-computed embedding — no second embedder call
|
|
118
|
+
const emb = candidateEmbedding ?? await options.embedder(entry.content);
|
|
119
|
+
store.add(entry, emb, hash);
|
|
120
|
+
emitter.emit('added', { entry });
|
|
121
|
+
return {
|
|
122
|
+
...result,
|
|
123
|
+
action: 'added',
|
|
124
|
+
survivorId: entry.id,
|
|
125
|
+
durationMs: Date.now() - start,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
// Duplicate found — apply merge policy
|
|
129
|
+
const matchId = result.matchId;
|
|
130
|
+
const matchEntry = store.get(matchId);
|
|
131
|
+
emitter.emit('duplicate-found', { candidate: entry, match: matchEntry, similarity: result.similarity });
|
|
132
|
+
const { survivor, evicted } = (0, merge_js_1.applyMergePolicy)(entry, matchEntry, mergePolicy, result.similarity ?? 1.0);
|
|
133
|
+
// Remove the evicted entry from store
|
|
134
|
+
store.remove(evicted.id);
|
|
135
|
+
// Get or compute the survivor's embedding
|
|
136
|
+
let survivorEmbedding;
|
|
137
|
+
if (survivor.id === matchId && survivor.content === matchEntry.content) {
|
|
138
|
+
// Survivor is unchanged existing entry — reuse its stored embedding
|
|
139
|
+
survivorEmbedding = store.getEmbedding(survivor.id) ?? await options.embedder(survivor.content);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// Survivor is the candidate or a merged variant — compute embedding
|
|
143
|
+
survivorEmbedding = candidateEmbedding ?? await options.embedder(survivor.content);
|
|
144
|
+
}
|
|
145
|
+
const survivorNorm = (0, similarity_js_1.normalizeText)(survivor.content);
|
|
146
|
+
const survivorHash = (0, similarity_js_1.contentHash)(survivorNorm);
|
|
147
|
+
store.add(survivor, survivorEmbedding, survivorHash);
|
|
148
|
+
emitter.emit('merged', { survivor, evicted });
|
|
149
|
+
emitter.emit('evicted', { entry: evicted });
|
|
150
|
+
return {
|
|
151
|
+
...result,
|
|
152
|
+
action: 'merged',
|
|
153
|
+
survivorId: survivor.id,
|
|
154
|
+
evictedId: evicted.id,
|
|
155
|
+
durationMs: Date.now() - start,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
async function addBatch(entries) {
|
|
159
|
+
const start = Date.now();
|
|
160
|
+
const results = [];
|
|
161
|
+
let uniqueAdded = 0;
|
|
162
|
+
let duplicatesFound = 0;
|
|
163
|
+
for (const entry of entries) {
|
|
164
|
+
const result = await add(entry);
|
|
165
|
+
results.push(result);
|
|
166
|
+
if (result.action === 'added') {
|
|
167
|
+
uniqueAdded++;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
duplicatesFound++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
results,
|
|
175
|
+
totalProcessed: entries.length,
|
|
176
|
+
uniqueAdded,
|
|
177
|
+
duplicatesFound,
|
|
178
|
+
durationMs: Date.now() - start,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
async function sweep() {
|
|
182
|
+
const start = Date.now();
|
|
183
|
+
const allEntries = store.all();
|
|
184
|
+
const duplicatePairs = [];
|
|
185
|
+
const evictedIds = [];
|
|
186
|
+
const processed = new Set();
|
|
187
|
+
for (let i = 0; i < allEntries.length; i++) {
|
|
188
|
+
const a = allEntries[i];
|
|
189
|
+
if (processed.has(a.id))
|
|
190
|
+
continue;
|
|
191
|
+
for (let j = i + 1; j < allEntries.length; j++) {
|
|
192
|
+
const b = allEntries[j];
|
|
193
|
+
if (processed.has(b.id))
|
|
194
|
+
continue;
|
|
195
|
+
const embA = store.getEmbedding(a.id);
|
|
196
|
+
const embB = store.getEmbedding(b.id);
|
|
197
|
+
if (!embA || !embB)
|
|
198
|
+
continue;
|
|
199
|
+
const sim = (0, similarity_js_1.cosine)(embA, embB);
|
|
200
|
+
if (sim >= threshold) {
|
|
201
|
+
duplicatePairs.push([a.id, b.id]);
|
|
202
|
+
const { survivor, evicted } = (0, merge_js_1.applyMergePolicy)(a, b, mergePolicy, sim);
|
|
203
|
+
store.remove(evicted.id);
|
|
204
|
+
// Reuse the surviving entry's embedding if content unchanged
|
|
205
|
+
let survivorEmb;
|
|
206
|
+
if (survivor.id === a.id && survivor.content === a.content) {
|
|
207
|
+
survivorEmb = embA;
|
|
208
|
+
}
|
|
209
|
+
else if (survivor.id === b.id && survivor.content === b.content) {
|
|
210
|
+
survivorEmb = embB;
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
survivorEmb = await options.embedder(survivor.content);
|
|
214
|
+
}
|
|
215
|
+
const survivorNorm = (0, similarity_js_1.normalizeText)(survivor.content);
|
|
216
|
+
const survivorHash = (0, similarity_js_1.contentHash)(survivorNorm);
|
|
217
|
+
store.add(survivor, survivorEmb, survivorHash);
|
|
218
|
+
emitter.emit('evicted', { entry: evicted });
|
|
219
|
+
evictedIds.push(evicted.id);
|
|
220
|
+
processed.add(evicted.id);
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
duplicatePairs,
|
|
227
|
+
duplicateCount: duplicatePairs.length,
|
|
228
|
+
evictedCount: evictedIds.length,
|
|
229
|
+
evictedIds,
|
|
230
|
+
totalScanned: allEntries.length,
|
|
231
|
+
durationMs: Date.now() - start,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
async function compact() {
|
|
235
|
+
const start = Date.now();
|
|
236
|
+
const allEntries = store.all();
|
|
237
|
+
const evictedIds = [];
|
|
238
|
+
const duplicatePairs = [];
|
|
239
|
+
// Build clusters using union-find
|
|
240
|
+
const parent = new Map();
|
|
241
|
+
for (const e of allEntries)
|
|
242
|
+
parent.set(e.id, e.id);
|
|
243
|
+
function find(id) {
|
|
244
|
+
let root = id;
|
|
245
|
+
while (parent.get(root) !== root)
|
|
246
|
+
root = parent.get(root);
|
|
247
|
+
// Path compression
|
|
248
|
+
let cur = id;
|
|
249
|
+
while (cur !== root) {
|
|
250
|
+
const next = parent.get(cur);
|
|
251
|
+
parent.set(cur, root);
|
|
252
|
+
cur = next;
|
|
253
|
+
}
|
|
254
|
+
return root;
|
|
255
|
+
}
|
|
256
|
+
function union(a, b) {
|
|
257
|
+
parent.set(find(a), find(b));
|
|
258
|
+
}
|
|
259
|
+
// O(n²) pairwise comparison
|
|
260
|
+
for (let i = 0; i < allEntries.length; i++) {
|
|
261
|
+
for (let j = i + 1; j < allEntries.length; j++) {
|
|
262
|
+
const a = allEntries[i];
|
|
263
|
+
const b = allEntries[j];
|
|
264
|
+
const embA = store.getEmbedding(a.id);
|
|
265
|
+
const embB = store.getEmbedding(b.id);
|
|
266
|
+
if (!embA || !embB)
|
|
267
|
+
continue;
|
|
268
|
+
const sim = (0, similarity_js_1.cosine)(embA, embB);
|
|
269
|
+
if (sim >= threshold) {
|
|
270
|
+
duplicatePairs.push([a.id, b.id]);
|
|
271
|
+
union(a.id, b.id);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// Group by cluster root
|
|
276
|
+
const clusters = new Map();
|
|
277
|
+
for (const entry of allEntries) {
|
|
278
|
+
const root = find(entry.id);
|
|
279
|
+
if (!clusters.has(root))
|
|
280
|
+
clusters.set(root, []);
|
|
281
|
+
clusters.get(root).push(entry);
|
|
282
|
+
}
|
|
283
|
+
let mergedCount = 0;
|
|
284
|
+
const clustersFound = Array.from(clusters.values()).filter(c => c.length > 1).length;
|
|
285
|
+
for (const cluster of clusters.values()) {
|
|
286
|
+
if (cluster.length <= 1)
|
|
287
|
+
continue;
|
|
288
|
+
// Merge all cluster members into a single survivor
|
|
289
|
+
let survivor = cluster[0];
|
|
290
|
+
let survivorEmb = store.getEmbedding(survivor.id) ?? await options.embedder(survivor.content);
|
|
291
|
+
for (let k = 1; k < cluster.length; k++) {
|
|
292
|
+
const candidate = cluster[k];
|
|
293
|
+
const embA = survivorEmb;
|
|
294
|
+
const embB = store.getEmbedding(candidate.id);
|
|
295
|
+
const sim = embB ? (0, similarity_js_1.cosine)(embA, embB) : 1.0;
|
|
296
|
+
const { survivor: newSurvivor, evicted } = (0, merge_js_1.applyMergePolicy)(survivor, candidate, mergePolicy, sim);
|
|
297
|
+
store.remove(evicted.id);
|
|
298
|
+
evictedIds.push(evicted.id);
|
|
299
|
+
emitter.emit('evicted', { entry: evicted });
|
|
300
|
+
mergedCount++;
|
|
301
|
+
if (newSurvivor.id !== survivor.id || newSurvivor.content !== survivor.content) {
|
|
302
|
+
survivorEmb = await options.embedder(newSurvivor.content);
|
|
303
|
+
}
|
|
304
|
+
survivor = newSurvivor;
|
|
305
|
+
}
|
|
306
|
+
// Re-add the final survivor
|
|
307
|
+
const survivorNorm = (0, similarity_js_1.normalizeText)(survivor.content);
|
|
308
|
+
const survivorHash = (0, similarity_js_1.contentHash)(survivorNorm);
|
|
309
|
+
store.add(survivor, survivorEmb, survivorHash);
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
duplicatePairs,
|
|
313
|
+
duplicateCount: duplicatePairs.length,
|
|
314
|
+
evictedCount: evictedIds.length,
|
|
315
|
+
evictedIds,
|
|
316
|
+
totalScanned: allEntries.length,
|
|
317
|
+
durationMs: Date.now() - start,
|
|
318
|
+
clustersFound,
|
|
319
|
+
mergedCount,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function stats() {
|
|
323
|
+
return {
|
|
324
|
+
totalEntries: store.size(),
|
|
325
|
+
totalChecks,
|
|
326
|
+
exactDuplicates,
|
|
327
|
+
semanticDuplicates,
|
|
328
|
+
uniqueEntries,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
check,
|
|
333
|
+
add,
|
|
334
|
+
addBatch,
|
|
335
|
+
sweep,
|
|
336
|
+
compact,
|
|
337
|
+
getEntries: () => store.all(),
|
|
338
|
+
remove: (id) => store.remove(id),
|
|
339
|
+
clear: () => {
|
|
340
|
+
store.clear();
|
|
341
|
+
totalChecks = 0;
|
|
342
|
+
exactDuplicates = 0;
|
|
343
|
+
semanticDuplicates = 0;
|
|
344
|
+
uniqueEntries = 0;
|
|
345
|
+
},
|
|
346
|
+
stats,
|
|
347
|
+
size: () => store.size(),
|
|
348
|
+
on: (event, fn) => emitter.on(event, fn),
|
|
349
|
+
off: (event, fn) => emitter.off(event, fn),
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
//# sourceMappingURL=deduplicator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deduplicator.js","sourceRoot":"","sources":["../src/deduplicator.ts"],"names":[],"mappings":"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"}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type DedupEvent = 'duplicate-found' | 'merged' | 'evicted' | 'added';
|
|
2
|
+
export declare class EventEmitter {
|
|
3
|
+
private listeners;
|
|
4
|
+
on(event: string, fn: Function): () => void;
|
|
5
|
+
off(event: string, fn: Function): void;
|
|
6
|
+
emit(event: string, payload: unknown): void;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,iBAAiB,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5E,qBAAa,YAAY;IACvB,OAAO,CAAC,SAAS,CAAoC;IAErD,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,GAAG,MAAM,IAAI;IAQ3C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,GAAG,IAAI;IAItC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;CAO5C"}
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventEmitter = void 0;
|
|
4
|
+
class EventEmitter {
|
|
5
|
+
listeners = new Map();
|
|
6
|
+
on(event, fn) {
|
|
7
|
+
if (!this.listeners.has(event)) {
|
|
8
|
+
this.listeners.set(event, new Set());
|
|
9
|
+
}
|
|
10
|
+
this.listeners.get(event).add(fn);
|
|
11
|
+
return () => this.off(event, fn);
|
|
12
|
+
}
|
|
13
|
+
off(event, fn) {
|
|
14
|
+
this.listeners.get(event)?.delete(fn);
|
|
15
|
+
}
|
|
16
|
+
emit(event, payload) {
|
|
17
|
+
const fns = this.listeners.get(event);
|
|
18
|
+
if (!fns)
|
|
19
|
+
return;
|
|
20
|
+
for (const fn of fns) {
|
|
21
|
+
fn(payload);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.EventEmitter = EventEmitter;
|
|
26
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":";;;AAEA,MAAa,YAAY;IACf,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;IAErD,EAAE,CAAC,KAAa,EAAE,EAAY;QAC5B,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QACvC,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,GAAG,CAAC,KAAa,EAAE,EAAY;QAC7B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,CAAC,KAAa,EAAE,OAAgB;QAClC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;YACrB,EAAE,CAAC,OAAO,CAAC,CAAC;QACd,CAAC;IACH,CAAC;CACF;AAtBD,oCAsBC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { createDeduplicator } from './deduplicator.js';
|
|
2
|
+
export type { MemoryEntry, EntryMetadata, DedupOptions, DedupResult, AddResult, BatchResult, SweepResult, CompactResult, DedupStats, MemoryDedup, StoreBackend, } from './types.js';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EACV,WAAW,EACX,aAAa,EACb,YAAY,EACZ,WAAW,EACX,SAAS,EACT,WAAW,EACX,WAAW,EACX,aAAa,EACb,UAAU,EACV,WAAW,EACX,YAAY,GACb,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDeduplicator = void 0;
|
|
4
|
+
// memory-dedup - Semantic deduplication of agent memory entries
|
|
5
|
+
var deduplicator_js_1 = require("./deduplicator.js");
|
|
6
|
+
Object.defineProperty(exports, "createDeduplicator", { enumerable: true, get: function () { return deduplicator_js_1.createDeduplicator; } });
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,gEAAgE;AAChE,qDAAuD;AAA9C,qHAAA,kBAAkB,OAAA"}
|
package/dist/merge.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { MemoryEntry, DedupOptions } from './types.js';
|
|
2
|
+
export declare function applyMergePolicy(candidate: MemoryEntry, match: MemoryEntry, policy: DedupOptions['mergePolicy'], similarity: number): {
|
|
3
|
+
survivor: MemoryEntry;
|
|
4
|
+
evicted: MemoryEntry;
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAiB,MAAM,YAAY,CAAC;AAkCtE,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,YAAY,CAAC,aAAa,CAAC,EACnC,UAAU,EAAE,MAAM,GACjB;IAAE,QAAQ,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAsDjD"}
|
package/dist/merge.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyMergePolicy = applyMergePolicy;
|
|
4
|
+
function getTimestamp(entry) {
|
|
5
|
+
const meta = (entry.metadata ?? {});
|
|
6
|
+
const ts = meta.timestamp ?? meta.createdAt;
|
|
7
|
+
return typeof ts === 'number' ? ts : 0;
|
|
8
|
+
}
|
|
9
|
+
function getConfidence(entry) {
|
|
10
|
+
const meta = (entry.metadata ?? {});
|
|
11
|
+
const conf = meta.confidence;
|
|
12
|
+
return typeof conf === 'number' ? conf : 0;
|
|
13
|
+
}
|
|
14
|
+
function mergeMetadata(a, b) {
|
|
15
|
+
const result = { ...a };
|
|
16
|
+
for (const [key, bVal] of Object.entries(b ?? {})) {
|
|
17
|
+
const aVal = result[key];
|
|
18
|
+
if (Array.isArray(aVal) && Array.isArray(bVal)) {
|
|
19
|
+
// Combine arrays, deduplicate primitives
|
|
20
|
+
result[key] = Array.from(new Set([...aVal, ...bVal]));
|
|
21
|
+
}
|
|
22
|
+
else if (typeof aVal === 'number' && typeof bVal === 'number') {
|
|
23
|
+
// Keep newest (larger) numeric value
|
|
24
|
+
result[key] = Math.max(aVal, bVal);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
result[key] = bVal;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
function applyMergePolicy(candidate, match, policy, similarity) {
|
|
33
|
+
const effectivePolicy = policy ?? 'keep-newest';
|
|
34
|
+
if (typeof effectivePolicy === 'function') {
|
|
35
|
+
const survivor = effectivePolicy(candidate, match, similarity);
|
|
36
|
+
const evicted = survivor.id === candidate.id ? match : candidate;
|
|
37
|
+
return { survivor, evicted };
|
|
38
|
+
}
|
|
39
|
+
switch (effectivePolicy) {
|
|
40
|
+
case 'keep-newest': {
|
|
41
|
+
const candTs = getTimestamp(candidate);
|
|
42
|
+
const matchTs = getTimestamp(match);
|
|
43
|
+
if (candTs >= matchTs) {
|
|
44
|
+
return { survivor: candidate, evicted: match };
|
|
45
|
+
}
|
|
46
|
+
return { survivor: match, evicted: candidate };
|
|
47
|
+
}
|
|
48
|
+
case 'keep-oldest': {
|
|
49
|
+
const candTs = getTimestamp(candidate);
|
|
50
|
+
const matchTs = getTimestamp(match);
|
|
51
|
+
if (candTs <= matchTs) {
|
|
52
|
+
return { survivor: candidate, evicted: match };
|
|
53
|
+
}
|
|
54
|
+
return { survivor: match, evicted: candidate };
|
|
55
|
+
}
|
|
56
|
+
case 'keep-longest': {
|
|
57
|
+
if (candidate.content.length >= match.content.length) {
|
|
58
|
+
return { survivor: candidate, evicted: match };
|
|
59
|
+
}
|
|
60
|
+
return { survivor: match, evicted: candidate };
|
|
61
|
+
}
|
|
62
|
+
case 'keep-highest-confidence': {
|
|
63
|
+
const candConf = getConfidence(candidate);
|
|
64
|
+
const matchConf = getConfidence(match);
|
|
65
|
+
if (candConf >= matchConf) {
|
|
66
|
+
return { survivor: candidate, evicted: match };
|
|
67
|
+
}
|
|
68
|
+
return { survivor: match, evicted: candidate };
|
|
69
|
+
}
|
|
70
|
+
case 'merge': {
|
|
71
|
+
const longer = candidate.content.length >= match.content.length ? candidate : match;
|
|
72
|
+
const merged = {
|
|
73
|
+
id: match.id,
|
|
74
|
+
content: longer.content,
|
|
75
|
+
metadata: mergeMetadata(candidate.metadata, match.metadata),
|
|
76
|
+
};
|
|
77
|
+
return { survivor: merged, evicted: candidate };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute cosine similarity between two vectors.
|
|
3
|
+
* Returns 0 if either vector is a zero vector.
|
|
4
|
+
*/
|
|
5
|
+
export declare function cosine(a: number[], b: number[]): number;
|
|
6
|
+
/**
|
|
7
|
+
* L2-normalize a vector. Returns zero vector if magnitude is 0.
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalize(vec: number[]): number[];
|
|
10
|
+
/**
|
|
11
|
+
* djb2 hash of a string, returned as an 8-char hex string.
|
|
12
|
+
* Used for fast exact/near-exact matching before embedding.
|
|
13
|
+
*/
|
|
14
|
+
export declare function contentHash(text: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Normalize text for comparison:
|
|
17
|
+
* lowercase, collapse whitespace, trim, remove punctuation except meaningful chars.
|
|
18
|
+
*/
|
|
19
|
+
export declare function normalizeText(text: string): string;
|
|
20
|
+
//# sourceMappingURL=similarity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similarity.d.ts","sourceRoot":"","sources":["../src/similarity.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAiBvD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAMjD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOhD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlD"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cosine = cosine;
|
|
4
|
+
exports.normalize = normalize;
|
|
5
|
+
exports.contentHash = contentHash;
|
|
6
|
+
exports.normalizeText = normalizeText;
|
|
7
|
+
/**
|
|
8
|
+
* Compute cosine similarity between two vectors.
|
|
9
|
+
* Returns 0 if either vector is a zero vector.
|
|
10
|
+
*/
|
|
11
|
+
function cosine(a, b) {
|
|
12
|
+
if (a.length !== b.length)
|
|
13
|
+
return 0;
|
|
14
|
+
let dot = 0;
|
|
15
|
+
let magA = 0;
|
|
16
|
+
let magB = 0;
|
|
17
|
+
for (let i = 0; i < a.length; i++) {
|
|
18
|
+
dot += a[i] * b[i];
|
|
19
|
+
magA += a[i] * a[i];
|
|
20
|
+
magB += b[i] * b[i];
|
|
21
|
+
}
|
|
22
|
+
const denom = Math.sqrt(magA) * Math.sqrt(magB);
|
|
23
|
+
if (denom === 0)
|
|
24
|
+
return 0;
|
|
25
|
+
return dot / denom;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* L2-normalize a vector. Returns zero vector if magnitude is 0.
|
|
29
|
+
*/
|
|
30
|
+
function normalize(vec) {
|
|
31
|
+
let mag = 0;
|
|
32
|
+
for (const v of vec)
|
|
33
|
+
mag += v * v;
|
|
34
|
+
mag = Math.sqrt(mag);
|
|
35
|
+
if (mag === 0)
|
|
36
|
+
return vec.map(() => 0);
|
|
37
|
+
return vec.map(v => v / mag);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* djb2 hash of a string, returned as an 8-char hex string.
|
|
41
|
+
* Used for fast exact/near-exact matching before embedding.
|
|
42
|
+
*/
|
|
43
|
+
function contentHash(text) {
|
|
44
|
+
let hash = 5381;
|
|
45
|
+
for (let i = 0; i < text.length; i++) {
|
|
46
|
+
hash = ((hash << 5) + hash) ^ text.charCodeAt(i);
|
|
47
|
+
hash = hash >>> 0; // keep unsigned 32-bit
|
|
48
|
+
}
|
|
49
|
+
return hash.toString(16).padStart(8, '0');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Normalize text for comparison:
|
|
53
|
+
* lowercase, collapse whitespace, trim, remove punctuation except meaningful chars.
|
|
54
|
+
*/
|
|
55
|
+
function normalizeText(text) {
|
|
56
|
+
return text
|
|
57
|
+
.toLowerCase()
|
|
58
|
+
.replace(/[^\w\s]/g, ' ')
|
|
59
|
+
.replace(/\s+/g, ' ')
|
|
60
|
+
.trim();
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=similarity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similarity.js","sourceRoot":"","sources":["../src/similarity.ts"],"names":[],"mappings":";;AAIA,wBAiBC;AAKD,8BAMC;AAMD,kCAOC;AAMD,sCAMC;AAzDD;;;GAGG;AACH,SAAgB,MAAM,CAAC,CAAW,EAAE,CAAW;IAC7C,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IAEpC,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,IAAI,GAAG,CAAC,CAAC;IAEb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAE1B,OAAO,GAAG,GAAG,KAAK,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,SAAgB,SAAS,CAAC,GAAa;IACrC,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,CAAC,IAAI,GAAG;QAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IAClC,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrB,IAAI,GAAG,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IACvC,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,IAAY;IACtC,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACjD,IAAI,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,uBAAuB;IAC5C,CAAC;IACD,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI;SACR,WAAW,EAAE;SACb,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;SACxB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { MemoryEntry, StoreBackend } from './types.js';
|
|
2
|
+
export declare class InMemoryStore implements StoreBackend {
|
|
3
|
+
private entries;
|
|
4
|
+
private embeddings;
|
|
5
|
+
private hashes;
|
|
6
|
+
add(entry: MemoryEntry, embedding: number[], hash: string): void;
|
|
7
|
+
get(id: string): MemoryEntry | null;
|
|
8
|
+
remove(id: string): void;
|
|
9
|
+
all(): MemoryEntry[];
|
|
10
|
+
getEmbedding(id: string): number[] | null;
|
|
11
|
+
getHash(hash: string): string | null;
|
|
12
|
+
size(): number;
|
|
13
|
+
clear(): void;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEvD,qBAAa,aAAc,YAAW,YAAY;IAChD,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,MAAM,CAA6B;IAE3C,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAMhE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAInC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAgBxB,GAAG,IAAI,WAAW,EAAE;IAIpB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAIzC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIpC,IAAI,IAAI,MAAM;IAId,KAAK,IAAI,IAAI;CAKd"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InMemoryStore = void 0;
|
|
4
|
+
class InMemoryStore {
|
|
5
|
+
entries = new Map();
|
|
6
|
+
embeddings = new Map();
|
|
7
|
+
hashes = new Map(); // hash → id
|
|
8
|
+
add(entry, embedding, hash) {
|
|
9
|
+
this.entries.set(entry.id, entry);
|
|
10
|
+
this.embeddings.set(entry.id, embedding);
|
|
11
|
+
this.hashes.set(hash, entry.id);
|
|
12
|
+
}
|
|
13
|
+
get(id) {
|
|
14
|
+
return this.entries.get(id) ?? null;
|
|
15
|
+
}
|
|
16
|
+
remove(id) {
|
|
17
|
+
const entry = this.entries.get(id);
|
|
18
|
+
if (!entry)
|
|
19
|
+
return;
|
|
20
|
+
// Remove the hash reverse-lookup entry
|
|
21
|
+
for (const [hash, mappedId] of this.hashes.entries()) {
|
|
22
|
+
if (mappedId === id) {
|
|
23
|
+
this.hashes.delete(hash);
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
this.entries.delete(id);
|
|
28
|
+
this.embeddings.delete(id);
|
|
29
|
+
}
|
|
30
|
+
all() {
|
|
31
|
+
return Array.from(this.entries.values());
|
|
32
|
+
}
|
|
33
|
+
getEmbedding(id) {
|
|
34
|
+
return this.embeddings.get(id) ?? null;
|
|
35
|
+
}
|
|
36
|
+
getHash(hash) {
|
|
37
|
+
return this.hashes.get(hash) ?? null;
|
|
38
|
+
}
|
|
39
|
+
size() {
|
|
40
|
+
return this.entries.size;
|
|
41
|
+
}
|
|
42
|
+
clear() {
|
|
43
|
+
this.entries.clear();
|
|
44
|
+
this.embeddings.clear();
|
|
45
|
+
this.hashes.clear();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.InMemoryStore = InMemoryStore;
|
|
49
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":";;;AAEA,MAAa,aAAa;IAChB,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IACzC,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IACzC,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,YAAY;IAExD,GAAG,CAAC,KAAkB,EAAE,SAAmB,EAAE,IAAY;QACvD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;IACtC,CAAC;IAED,MAAM,CAAC,EAAU;QACf,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,uCAAuC;QACvC,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;YACrD,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;gBACpB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACzB,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC;IAED,GAAG;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,YAAY,CAAC,EAAU;QACrB,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;IACzC,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IACvC,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACxB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;CACF;AApDD,sCAoDC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export interface MemoryEntry {
|
|
2
|
+
id: string;
|
|
3
|
+
content: string;
|
|
4
|
+
metadata?: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
export interface EntryMetadata {
|
|
7
|
+
timestamp?: number;
|
|
8
|
+
createdAt?: number;
|
|
9
|
+
confidence?: number;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface DedupOptions {
|
|
13
|
+
embedder: (text: string) => Promise<number[]>;
|
|
14
|
+
threshold?: number;
|
|
15
|
+
exactThreshold?: number;
|
|
16
|
+
relatedThreshold?: number;
|
|
17
|
+
mergePolicy?: 'keep-newest' | 'keep-oldest' | 'keep-longest' | 'keep-highest-confidence' | 'merge' | ((a: MemoryEntry, b: MemoryEntry, sim: number) => MemoryEntry);
|
|
18
|
+
}
|
|
19
|
+
export interface DedupResult {
|
|
20
|
+
classification: 'exact_duplicate' | 'semantic_duplicate' | 'related' | 'unique';
|
|
21
|
+
matchId?: string;
|
|
22
|
+
similarity?: number;
|
|
23
|
+
hashMatch?: boolean;
|
|
24
|
+
durationMs: number;
|
|
25
|
+
}
|
|
26
|
+
export interface AddResult extends DedupResult {
|
|
27
|
+
action: 'added' | 'merged' | 'skipped';
|
|
28
|
+
survivorId?: string;
|
|
29
|
+
evictedId?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface BatchResult {
|
|
32
|
+
results: AddResult[];
|
|
33
|
+
totalProcessed: number;
|
|
34
|
+
uniqueAdded: number;
|
|
35
|
+
duplicatesFound: number;
|
|
36
|
+
durationMs: number;
|
|
37
|
+
}
|
|
38
|
+
export interface SweepResult {
|
|
39
|
+
duplicatePairs: Array<[string, string]>;
|
|
40
|
+
duplicateCount: number;
|
|
41
|
+
evictedCount: number;
|
|
42
|
+
evictedIds: string[];
|
|
43
|
+
totalScanned: number;
|
|
44
|
+
durationMs: number;
|
|
45
|
+
}
|
|
46
|
+
export interface CompactResult extends SweepResult {
|
|
47
|
+
clustersFound: number;
|
|
48
|
+
mergedCount: number;
|
|
49
|
+
}
|
|
50
|
+
export interface DedupStats {
|
|
51
|
+
totalEntries: number;
|
|
52
|
+
totalChecks: number;
|
|
53
|
+
exactDuplicates: number;
|
|
54
|
+
semanticDuplicates: number;
|
|
55
|
+
uniqueEntries: number;
|
|
56
|
+
durationMs?: number;
|
|
57
|
+
}
|
|
58
|
+
export interface MemoryDedup {
|
|
59
|
+
check(entry: MemoryEntry): Promise<DedupResult>;
|
|
60
|
+
add(entry: MemoryEntry): Promise<AddResult>;
|
|
61
|
+
addBatch(entries: MemoryEntry[]): Promise<BatchResult>;
|
|
62
|
+
sweep(): Promise<SweepResult>;
|
|
63
|
+
compact(): Promise<CompactResult>;
|
|
64
|
+
getEntries(): MemoryEntry[];
|
|
65
|
+
remove(id: string): void;
|
|
66
|
+
clear(): void;
|
|
67
|
+
stats(): DedupStats;
|
|
68
|
+
size(): number;
|
|
69
|
+
on(event: string, fn: (payload: unknown) => void): () => void;
|
|
70
|
+
off(event: string, fn: (payload: unknown) => void): void;
|
|
71
|
+
}
|
|
72
|
+
export interface StoreBackend {
|
|
73
|
+
add(entry: MemoryEntry, embedding: number[], hash: string): void;
|
|
74
|
+
get(id: string): MemoryEntry | null;
|
|
75
|
+
remove(id: string): void;
|
|
76
|
+
all(): MemoryEntry[];
|
|
77
|
+
getEmbedding(id: string): number[] | null;
|
|
78
|
+
getHash(hash: string): string | null;
|
|
79
|
+
size(): number;
|
|
80
|
+
clear(): void;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "memory-dedup",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Semantic deduplication of agent memory entries",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"lint": "eslint src/",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^25.5.0",
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^8.57.1",
|
|
28
|
+
"@typescript-eslint/parser": "^8.57.1",
|
|
29
|
+
"eslint": "^10.1.0",
|
|
30
|
+
"typescript": "^5.9.3",
|
|
31
|
+
"vitest": "^4.1.0"
|
|
32
|
+
}
|
|
33
|
+
}
|