@traqr/memory 0.2.10 → 0.2.11

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.
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Retrieval — RRF Fusion + Strategy Detection + Temporal Parsing tests.
3
+ *
4
+ * Covers the pure, deterministic core of searchMemoriesV2 (TD-158/159/160):
5
+ * reciprocalRankFusion, detectStrategies, parseTemporalRange. The Cohere rerank
6
+ * + DB-backed strategies are exercised separately (they require a live provider
7
+ * + COHERE_API_KEY); these tests pin the fusion math + routing that run on every
8
+ * memory search fleet-wide.
9
+ *
10
+ * Run: npx tsx packages/memory/src/lib/retrieval.test.ts
11
+ */
12
+ export {};
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Retrieval — RRF Fusion + Strategy Detection + Temporal Parsing tests.
3
+ *
4
+ * Covers the pure, deterministic core of searchMemoriesV2 (TD-158/159/160):
5
+ * reciprocalRankFusion, detectStrategies, parseTemporalRange. The Cohere rerank
6
+ * + DB-backed strategies are exercised separately (they require a live provider
7
+ * + COHERE_API_KEY); these tests pin the fusion math + routing that run on every
8
+ * memory search fleet-wide.
9
+ *
10
+ * Run: npx tsx packages/memory/src/lib/retrieval.test.ts
11
+ */
12
+ import { reciprocalRankFusion, detectStrategies, parseTemporalRange } from './retrieval.js';
13
+ let passed = 0;
14
+ let failed = 0;
15
+ function assert(label, condition) {
16
+ if (condition) {
17
+ console.log(` PASS ${label}`);
18
+ passed++;
19
+ }
20
+ else {
21
+ console.log(` FAIL ${label}`);
22
+ failed++;
23
+ }
24
+ }
25
+ // ============================================================
26
+ // reciprocalRankFusion
27
+ // ============================================================
28
+ console.log('\n--- reciprocalRankFusion ---');
29
+ // Empty input → empty output
30
+ assert('empty strategies → []', reciprocalRankFusion([]).length === 0);
31
+ // Single strategy, single item: score = 1/(k+rank) = 1/(60+1)
32
+ {
33
+ const fused = reciprocalRankFusion([{ strategy: 'semantic', items: [{ id: 'a', rank: 1 }] }]);
34
+ assert('single item present', fused.length === 1 && fused[0].id === 'a');
35
+ assert('RRF formula 1/(k+rank)', Math.abs(fused[0].rrfScore - 1 / 61) < 1e-9);
36
+ assert('top item normalizedScore === 1', fused[0].normalizedScore === 1);
37
+ assert('strategies tracked', fused[0].strategies.join() === 'semantic');
38
+ }
39
+ // Multi-strategy: an item in BOTH strategies outranks an item in only one
40
+ {
41
+ const fused = reciprocalRankFusion([
42
+ { strategy: 'semantic', items: [{ id: 'x', rank: 1 }, { id: 'y', rank: 2 }] },
43
+ { strategy: 'bm25', items: [{ id: 'x', rank: 1 }, { id: 'z', rank: 2 }] },
44
+ ]);
45
+ const x = fused.find((f) => f.id === 'x');
46
+ const y = fused.find((f) => f.id === 'y');
47
+ assert('item in 2 strategies ranks first', fused[0].id === 'x');
48
+ assert('cross-strategy item accumulates both', x.strategies.length === 2);
49
+ assert('cross-strategy score > single-strategy score', x.rrfScore > y.rrfScore);
50
+ assert('normalization: top === 1, others < 1', x.normalizedScore === 1 && y.normalizedScore < 1);
51
+ }
52
+ // topN slicing
53
+ {
54
+ const items = Array.from({ length: 50 }, (_, i) => ({ id: `id${i}`, rank: i + 1 }));
55
+ const fused = reciprocalRankFusion([{ strategy: 'semantic', items }], 60, 5);
56
+ assert('topN caps result length', fused.length === 5);
57
+ assert('topN keeps best-ranked', fused[0].id === 'id0');
58
+ }
59
+ // Lower rank (better position) yields higher score
60
+ {
61
+ const fused = reciprocalRankFusion([{ strategy: 'semantic', items: [{ id: 'first', rank: 1 }, { id: 'tenth', rank: 10 }] }]);
62
+ assert('rank 1 scores above rank 10', fused[0].id === 'first' && fused[0].rrfScore > fused[1].rrfScore);
63
+ }
64
+ // ============================================================
65
+ // detectStrategies
66
+ // ============================================================
67
+ console.log('\n--- detectStrategies ---');
68
+ assert('semantic + bm25 always on', (() => {
69
+ const d = detectStrategies('what do we know about caching');
70
+ return d.strategies.includes('semantic') && d.strategies.includes('bm25');
71
+ })());
72
+ assert('no temporal/graph for a plain query', (() => {
73
+ const d = detectStrategies('how does the daemon work');
74
+ return !d.strategies.includes('temporal') && !d.strategies.includes('graph');
75
+ })());
76
+ assert('date phrase activates temporal', (() => {
77
+ const d = detectStrategies('what happened last week with the deploy');
78
+ return d.strategies.includes('temporal') && d.temporalRange !== undefined;
79
+ })());
80
+ assert('ISO date activates temporal', detectStrategies('the 2026-03-15 incident').strategies.includes('temporal'));
81
+ assert('entityIds activate graph', (() => {
82
+ const d = detectStrategies('AVGO thesis', ['ent-1']);
83
+ return d.strategies.includes('graph') && d.graphSeedIds?.length === 1;
84
+ })());
85
+ // ============================================================
86
+ // parseTemporalRange
87
+ // ============================================================
88
+ console.log('\n--- parseTemporalRange ---');
89
+ assert('"yesterday" → ~1 day window', (() => {
90
+ const { start, end } = parseTemporalRange('yesterday');
91
+ const days = (end.getTime() - start.getTime()) / 86400000;
92
+ return days >= 0.5 && days <= 2;
93
+ })());
94
+ assert('"last month" → ~30 day window', (() => {
95
+ const { start, end } = parseTemporalRange('last month');
96
+ return start < end && (end.getTime() - start.getTime()) > 20 * 86400000;
97
+ })());
98
+ assert('"3 days ago" → 3-day lookback', (() => {
99
+ const { start, end } = parseTemporalRange('something 3 days ago');
100
+ const days = Math.round((end.getTime() - start.getTime()) / 86400000);
101
+ return days === 3;
102
+ })());
103
+ assert('"March 2026" → month start', (() => {
104
+ const { start } = parseTemporalRange('the March 2026 print');
105
+ return start.getFullYear() === 2026 && start.getMonth() === 2 && start.getDate() === 1;
106
+ })());
107
+ assert('ISO "2026-03-15" → that day', (() => {
108
+ const { start } = parseTemporalRange('on 2026-03-15');
109
+ return start.getFullYear() === 2026 && start.getMonth() === 2 && start.getDate() === 15;
110
+ })());
111
+ assert('no date pattern → 30-day default', (() => {
112
+ const { start, end } = parseTemporalRange('no temporal words here');
113
+ const days = Math.round((end.getTime() - start.getTime()) / 86400000);
114
+ return days === 30;
115
+ })());
116
+ // ============================================================
117
+ // Summary
118
+ // ============================================================
119
+ console.log(`\n${'='.repeat(50)}`);
120
+ console.log(`Results: ${passed} passed, ${failed} failed`);
121
+ if (failed > 0) {
122
+ console.log('RETRIEVAL TESTS FAILED');
123
+ process.exit(1);
124
+ }
125
+ else {
126
+ console.log('All retrieval tests passed!');
127
+ }
128
+ //# sourceMappingURL=retrieval.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retrieval.test.js","sourceRoot":"","sources":["../../src/lib/retrieval.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@traqr/memory",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "Persistent memory for AI agents. Multi-strategy retrieval (semantic + BM25 + RRF), 3-zone cosine triage, type-aware lifecycle, entity canonicalization. Postgres + pgvector.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",