cozo-memory 1.1.2 → 1.1.4

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,313 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TemporalEmbeddingService = void 0;
4
+ /**
5
+ * Temporal Graph Neural Network Embedding Service
6
+ *
7
+ * Implements time-aware node embeddings that capture:
8
+ * 1. Historical context aggregation (past observations)
9
+ * 2. Temporal smoothness (gradual changes over time)
10
+ * 3. Time encoding (Time2Vec-inspired approach)
11
+ * 4. Recency weighting (recent events matter more)
12
+ *
13
+ * Based on research:
14
+ * - ACM Temporal Graph Learning Primer (2025)
15
+ * - TempGNN: Temporal Graph Neural Networks (2023)
16
+ * - Time-Aware Graph Embedding with Temporal Smoothness (2021)
17
+ */
18
+ class TemporalEmbeddingService {
19
+ embeddingService;
20
+ dbQuery;
21
+ EMBEDDING_DIM = 1024;
22
+ TEMPORAL_ENCODING_DIM = 64;
23
+ MEMORY_CACHE = new Map();
24
+ constructor(embeddingService, dbQuery) {
25
+ this.embeddingService = embeddingService;
26
+ this.dbQuery = dbQuery;
27
+ }
28
+ /**
29
+ * Generate temporal embedding for an entity at a specific timepoint
30
+ *
31
+ * Combines:
32
+ * 1. Content embedding (semantic meaning)
33
+ * 2. Temporal encoding (time difference from now)
34
+ * 3. Historical context (past observations)
35
+ * 4. Neighborhood aggregation (related entities)
36
+ */
37
+ async generateTemporalEmbedding(entityId, timepoint) {
38
+ const now = timepoint || new Date();
39
+ // 1. Get entity state at timepoint via CozoDB Validity
40
+ const entityState = await this.getEntityAtTime(entityId, now);
41
+ if (!entityState) {
42
+ throw new Error(`Entity ${entityId} not found at ${now.toISOString()}`);
43
+ }
44
+ // 2. Generate base content embedding
45
+ const contentEmbedding = await this.embeddingService.embed(entityState.name + ' ' + (entityState.metadata?.description || ''));
46
+ // 3. Generate temporal encoding (Time2Vec-inspired)
47
+ const temporalEncoding = this.encodeTemporalDistance(entityState.createdAt, now);
48
+ // 4. Aggregate historical context
49
+ const historicalContext = await this.aggregateHistoricalContext(entityId, now);
50
+ // 5. Aggregate neighborhood information
51
+ const neighborhoodAggregation = await this.aggregateNeighborhood(entityId, now);
52
+ // 6. Combine all signals with learned weights
53
+ const combinedEmbedding = this.fuseEmbeddings(contentEmbedding, temporalEncoding, historicalContext, neighborhoodAggregation);
54
+ return {
55
+ entityId,
56
+ timepoint: now,
57
+ embedding: combinedEmbedding,
58
+ contentEmbedding,
59
+ temporalEncoding,
60
+ historicalContext,
61
+ neighborhoodAggregation,
62
+ confidence: this.calculateConfidence(entityState, now),
63
+ metadata: {
64
+ ageInDays: Math.floor((now.getTime() - entityState.createdAt.getTime()) / (1000 * 60 * 60 * 24)),
65
+ observationCount: entityState.observationCount || 0,
66
+ relationshipCount: entityState.relationshipCount || 0,
67
+ },
68
+ };
69
+ }
70
+ /**
71
+ * Get entity state at a specific timepoint using CozoDB Validity
72
+ * Returns the entity as it existed at that point in time
73
+ */
74
+ async getEntityAtTime(entityId, timepoint) {
75
+ try {
76
+ const result = await this.dbQuery(`?[id, name, metadata, createdAt, observationCount, relationshipCount] :=
77
+ *entity{id, name, metadata, @ $timepoint},
78
+ id = $entityId,
79
+ observationCount = count{*observation{entity_id: id}},
80
+ relationshipCount = count{*relationship{from_id: id} or *relationship{to_id: id}},
81
+ createdAt = now()`, {
82
+ entityId,
83
+ timepoint: timepoint.toISOString(),
84
+ });
85
+ if (!result.rows || result.rows.length === 0)
86
+ return null;
87
+ const [id, name, metadata, createdAt, obsCount, relCount] = result.rows[0];
88
+ return {
89
+ id,
90
+ name,
91
+ metadata: metadata ? JSON.parse(metadata) : {},
92
+ createdAt: new Date(createdAt),
93
+ observationCount: obsCount,
94
+ relationshipCount: relCount,
95
+ };
96
+ }
97
+ catch (error) {
98
+ console.error(`[TemporalEmbedding] Error fetching entity at time:`, error);
99
+ return null;
100
+ }
101
+ }
102
+ /**
103
+ * Time2Vec-inspired temporal encoding
104
+ * Captures periodicity and time differences using sinusoidal functions
105
+ *
106
+ * Formula: t_enc[i] = sin(ω_i * Ī”t) for i in [0, d/2]
107
+ * t_enc[i+d/2] = cos(ω_i * Ī”t) for i in [0, d/2]
108
+ *
109
+ * Where ω_i = 1 / 10000^(2i/d) (similar to transformer positional encoding)
110
+ */
111
+ encodeTemporalDistance(createdAt, currentTime) {
112
+ const deltaSeconds = (currentTime.getTime() - createdAt.getTime()) / 1000;
113
+ const encoding = [];
114
+ // Generate sinusoidal encodings for different frequencies
115
+ for (let i = 0; i < this.TEMPORAL_ENCODING_DIM / 2; i++) {
116
+ const omega = 1 / Math.pow(10000, (2 * i) / this.TEMPORAL_ENCODING_DIM);
117
+ // Normalize delta to reasonable range (0-1 for recent, >1 for old)
118
+ const normalizedDelta = Math.min(deltaSeconds / (365 * 24 * 3600), 10);
119
+ encoding.push(Math.sin(omega * normalizedDelta));
120
+ encoding.push(Math.cos(omega * normalizedDelta));
121
+ }
122
+ return encoding;
123
+ }
124
+ /**
125
+ * Aggregate historical context from past observations
126
+ *
127
+ * Implements temporal smoothness by:
128
+ * 1. Fetching all observations up to timepoint
129
+ * 2. Weighting by recency (exponential decay)
130
+ * 3. Embedding each observation
131
+ * 4. Averaging with recency weights
132
+ */
133
+ async aggregateHistoricalContext(entityId, timepoint) {
134
+ try {
135
+ const result = await this.dbQuery(`?[id, text, createdAt] :=
136
+ *observation{id, entity_id: $entityId, text, @ $timepoint},
137
+ createdAt = now()
138
+ | order by createdAt desc
139
+ | limit 50`, {
140
+ entityId,
141
+ timepoint: timepoint.toISOString(),
142
+ });
143
+ if (!result.rows || result.rows.length === 0) {
144
+ return new Array(this.EMBEDDING_DIM).fill(0);
145
+ }
146
+ const embeddings = [];
147
+ const weights = [];
148
+ for (const [, text, createdAt] of result.rows) {
149
+ const embedding = await this.embeddingService.embed(text);
150
+ const age = (timepoint.getTime() - new Date(createdAt).getTime()) / 1000;
151
+ const halfLife = 30 * 24 * 3600;
152
+ const weight = Math.exp(-age / halfLife);
153
+ embeddings.push(embedding);
154
+ weights.push(weight);
155
+ }
156
+ const totalWeight = weights.reduce((a, b) => a + b, 0);
157
+ const normalizedWeights = weights.map(w => w / totalWeight);
158
+ const aggregated = new Array(this.EMBEDDING_DIM).fill(0);
159
+ for (let i = 0; i < embeddings.length; i++) {
160
+ for (let j = 0; j < this.EMBEDDING_DIM; j++) {
161
+ aggregated[j] += embeddings[i][j] * normalizedWeights[i];
162
+ }
163
+ }
164
+ return aggregated;
165
+ }
166
+ catch (error) {
167
+ console.error(`[TemporalEmbedding] Error aggregating history:`, error);
168
+ return new Array(this.EMBEDDING_DIM).fill(0);
169
+ }
170
+ }
171
+ /**
172
+ * Aggregate neighborhood information
173
+ *
174
+ * Implements graph-based aggregation by:
175
+ * 1. Finding related entities (via relationships)
176
+ * 2. Embedding their content
177
+ * 3. Weighting by relationship strength and recency
178
+ * 4. Averaging to get neighborhood signal
179
+ */
180
+ async aggregateNeighborhood(entityId, timepoint) {
181
+ try {
182
+ const result = await this.dbQuery(`?[toId, relationshipType, strength, createdAt] :=
183
+ *relationship{from_id: $entityId, to_id: toId, relation_type: relationshipType, strength, @ $timepoint},
184
+ createdAt = now()
185
+ | limit 20`, {
186
+ entityId,
187
+ timepoint: timepoint.toISOString(),
188
+ });
189
+ if (!result.rows || result.rows.length === 0) {
190
+ return new Array(this.EMBEDDING_DIM).fill(0);
191
+ }
192
+ const embeddings = [];
193
+ const weights = [];
194
+ for (const [toId, , strength, createdAt] of result.rows) {
195
+ const neighbor = await this.getEntityAtTime(toId, timepoint);
196
+ if (!neighbor)
197
+ continue;
198
+ const embedding = await this.embeddingService.embed(neighbor.name);
199
+ const age = (timepoint.getTime() - new Date(createdAt).getTime()) / 1000;
200
+ const halfLife = 30 * 24 * 3600;
201
+ const recencyWeight = Math.exp(-age / halfLife);
202
+ const weight = (strength || 0.5) * recencyWeight;
203
+ embeddings.push(embedding);
204
+ weights.push(weight);
205
+ }
206
+ if (embeddings.length === 0) {
207
+ return new Array(this.EMBEDDING_DIM).fill(0);
208
+ }
209
+ const totalWeight = weights.reduce((a, b) => a + b, 0);
210
+ const normalizedWeights = weights.map(w => w / totalWeight);
211
+ const aggregated = new Array(this.EMBEDDING_DIM).fill(0);
212
+ for (let i = 0; i < embeddings.length; i++) {
213
+ for (let j = 0; j < this.EMBEDDING_DIM; j++) {
214
+ aggregated[j] += embeddings[i][j] * normalizedWeights[i];
215
+ }
216
+ }
217
+ return aggregated;
218
+ }
219
+ catch (error) {
220
+ console.error(`[TemporalEmbedding] Error aggregating neighborhood:`, error);
221
+ return new Array(this.EMBEDDING_DIM).fill(0);
222
+ }
223
+ }
224
+ /**
225
+ * Fuse multiple embedding signals into final temporal embedding
226
+ *
227
+ * Uses learned weights to combine:
228
+ * - Content embedding (semantic meaning)
229
+ * - Temporal encoding (time information)
230
+ * - Historical context (past observations)
231
+ * - Neighborhood aggregation (related entities)
232
+ */
233
+ fuseEmbeddings(contentEmbedding, temporalEncoding, historicalContext, neighborhoodAggregation) {
234
+ // Learned fusion weights (can be tuned)
235
+ const weights = {
236
+ content: 0.4,
237
+ temporal: 0.2,
238
+ history: 0.2,
239
+ neighborhood: 0.2,
240
+ };
241
+ const fused = new Array(this.EMBEDDING_DIM).fill(0);
242
+ // Combine content embedding
243
+ for (let i = 0; i < this.EMBEDDING_DIM; i++) {
244
+ fused[i] += contentEmbedding[i] * weights.content;
245
+ }
246
+ // Combine historical context
247
+ for (let i = 0; i < this.EMBEDDING_DIM; i++) {
248
+ fused[i] += historicalContext[i] * weights.history;
249
+ }
250
+ // Combine neighborhood aggregation
251
+ for (let i = 0; i < this.EMBEDDING_DIM; i++) {
252
+ fused[i] += neighborhoodAggregation[i] * weights.neighborhood;
253
+ }
254
+ // Combine temporal encoding (pad to EMBEDDING_DIM)
255
+ for (let i = 0; i < Math.min(this.TEMPORAL_ENCODING_DIM, this.EMBEDDING_DIM); i++) {
256
+ fused[i] += temporalEncoding[i] * weights.temporal;
257
+ }
258
+ // Normalize
259
+ const norm = Math.sqrt(fused.reduce((a, b) => a + b * b, 0));
260
+ if (norm > 0) {
261
+ for (let i = 0; i < fused.length; i++) {
262
+ fused[i] /= norm;
263
+ }
264
+ }
265
+ return fused;
266
+ }
267
+ /**
268
+ * Calculate confidence score for the embedding
269
+ * Based on data freshness and completeness
270
+ */
271
+ calculateConfidence(entityState, timepoint) {
272
+ let confidence = 0.5; // Base confidence
273
+ // Boost for recent entities
274
+ const ageInDays = Math.floor((timepoint.getTime() - entityState.createdAt.getTime()) / (1000 * 60 * 60 * 24));
275
+ if (ageInDays < 7)
276
+ confidence += 0.3;
277
+ else if (ageInDays < 30)
278
+ confidence += 0.2;
279
+ else if (ageInDays < 90)
280
+ confidence += 0.1;
281
+ // Boost for entities with observations
282
+ if ((entityState.observationCount || 0) > 5)
283
+ confidence += 0.15;
284
+ else if ((entityState.observationCount || 0) > 0)
285
+ confidence += 0.05;
286
+ // Boost for well-connected entities
287
+ if ((entityState.relationshipCount || 0) > 10)
288
+ confidence += 0.15;
289
+ else if ((entityState.relationshipCount || 0) > 0)
290
+ confidence += 0.05;
291
+ return Math.min(confidence, 1.0);
292
+ }
293
+ /**
294
+ * Get temporal memory for an entity
295
+ * Caches temporal state for efficient multi-hop queries
296
+ */
297
+ getTemporalMemory(entityId) {
298
+ return this.MEMORY_CACHE.get(entityId);
299
+ }
300
+ /**
301
+ * Update temporal memory for an entity
302
+ */
303
+ setTemporalMemory(entityId, memory) {
304
+ this.MEMORY_CACHE.set(entityId, memory);
305
+ }
306
+ /**
307
+ * Clear temporal memory cache
308
+ */
309
+ clearMemoryCache() {
310
+ this.MEMORY_CACHE.clear();
311
+ }
312
+ }
313
+ exports.TemporalEmbeddingService = TemporalEmbeddingService;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ /**
3
+ * Integration Test for Adaptive Retrieval in MCP Server
4
+ * Tests the full integration of GraphRAG-R1 adaptive retrieval
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const index_1 = require("./index");
8
+ async function testAdaptiveIntegration() {
9
+ console.log('=== Testing Adaptive Retrieval MCP Integration ===\n');
10
+ const server = new index_1.MemoryServer();
11
+ await server.initPromise;
12
+ console.log('āœ… MemoryServer initialized with AdaptiveRetrieval\n');
13
+ // Test 1: Simple Query
14
+ console.log('--- Test 1: Simple Query via Adaptive Retrieval ---');
15
+ const result1 = await server.adaptiveRetrieval.retrieve('Alice', 5);
16
+ console.log(`Query: "Alice"`);
17
+ console.log(`Strategy Selected: ${result1.strategy}`);
18
+ console.log(`Results: ${result1.results.length}`);
19
+ console.log(`Retrieval Count: ${result1.retrievalCount}`);
20
+ console.log(`CAF Score: ${result1.cafScore?.toFixed(3)}`);
21
+ console.log(`Latency: ${result1.latency}ms`);
22
+ if (result1.results.length > 0) {
23
+ console.log('Top Result:', result1.results[0].name);
24
+ }
25
+ console.log();
26
+ // Test 2: Complex Query
27
+ console.log('--- Test 2: Complex Multi-Hop Query ---');
28
+ const result2 = await server.adaptiveRetrieval.retrieve('What are the connections between people working on projects?', 10);
29
+ console.log(`Query: "What are the connections between people working on projects?"`);
30
+ console.log(`Strategy Selected: ${result2.strategy}`);
31
+ console.log(`Results: ${result2.results.length}`);
32
+ console.log(`Retrieval Count: ${result2.retrievalCount}`);
33
+ console.log(`CAF Score: ${result2.cafScore?.toFixed(3)}`);
34
+ console.log(`Latency: ${result2.latency}ms`);
35
+ console.log();
36
+ // Test 3: Exploratory Query
37
+ console.log('--- Test 3: Exploratory Query ---');
38
+ const result3 = await server.adaptiveRetrieval.retrieve('Show me everything about software development', 10);
39
+ console.log(`Query: "Show me everything about software development"`);
40
+ console.log(`Strategy Selected: ${result3.strategy}`);
41
+ console.log(`Results: ${result3.results.length}`);
42
+ console.log(`Retrieval Count: ${result3.retrievalCount}`);
43
+ console.log(`CAF Score: ${result3.cafScore?.toFixed(3)}`);
44
+ console.log(`Latency: ${result3.latency}ms`);
45
+ console.log();
46
+ // Test 4: Performance Statistics
47
+ console.log('--- Test 4: Performance Statistics ---');
48
+ const stats = server.adaptiveRetrieval.getPerformanceStats();
49
+ console.log(`Tracked Strategies: ${stats.size}`);
50
+ for (const [strategy, perf] of stats.entries()) {
51
+ if (perf.totalCount > 0) {
52
+ console.log(`\nStrategy: ${strategy}`);
53
+ console.log(` Success Rate: ${((perf.successCount / perf.totalCount) * 100).toFixed(1)}%`);
54
+ console.log(` Avg F1 Score: ${perf.avgF1Score.toFixed(3)}`);
55
+ console.log(` Avg Retrieval Cost: ${perf.avgRetrievalCost.toFixed(2)}`);
56
+ console.log(` Avg Latency: ${perf.avgLatency.toFixed(0)}ms`);
57
+ console.log(` Total Uses: ${perf.totalCount}`);
58
+ }
59
+ }
60
+ console.log();
61
+ // Test 5: Learning Over Time
62
+ console.log('--- Test 5: Learning Test (Repeat Query) ---');
63
+ console.log('Running same query 3 times to test adaptation...');
64
+ for (let i = 1; i <= 3; i++) {
65
+ const result = await server.adaptiveRetrieval.retrieve('TypeScript', 5);
66
+ console.log(`Run ${i}: Strategy=${result.strategy}, Results=${result.results.length}, CAF=${result.cafScore?.toFixed(3)}`);
67
+ // Simulate feedback
68
+ await server.adaptiveRetrieval.updateStrategyPerformance(result.strategy, 0.8, result.retrievalCount, result.latency, true);
69
+ }
70
+ console.log();
71
+ console.log('āœ… All integration tests completed successfully!');
72
+ console.log('\nšŸ“Š Summary:');
73
+ console.log('1. AdaptiveRetrieval successfully integrated into MemoryServer');
74
+ console.log('2. Query complexity classification working');
75
+ console.log('3. Strategy selection adapting based on query type');
76
+ console.log('4. Performance tracking persisting to CozoDB');
77
+ console.log('5. PRA and CAF scoring functioning correctly');
78
+ console.log('\nšŸŽÆ Next Step: Restart Kiro to test via MCP tool call');
79
+ }
80
+ // Run tests
81
+ testAdaptiveIntegration().catch(error => {
82
+ console.error('āŒ Integration test failed:', error);
83
+ process.exit(1);
84
+ });
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ /**
3
+ * Test for GraphRAG-R1 Inspired Adaptive Retrieval System
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const cozo_node_1 = require("cozo-node");
7
+ const embedding_service_1 = require("./embedding-service");
8
+ const adaptive_retrieval_1 = require("./adaptive-retrieval");
9
+ const DB_PATH = 'memory_db.cozo.db';
10
+ async function testAdaptiveRetrieval() {
11
+ console.log('=== Testing GraphRAG-R1 Adaptive Retrieval ===\n');
12
+ const db = new cozo_node_1.CozoDb('sqlite', DB_PATH);
13
+ const embeddingService = new embedding_service_1.EmbeddingService();
14
+ const adaptiveRetrieval = new adaptive_retrieval_1.AdaptiveGraphRetrieval(db, embeddingService, {
15
+ enablePRA: true,
16
+ enableCAF: true,
17
+ maxRetrievalCalls: 5,
18
+ explorationRate: 0.2, // 20% exploration for testing
19
+ decayFactor: 0.8,
20
+ costPenalty: 0.15
21
+ });
22
+ // Wait for initialization
23
+ await new Promise(resolve => setTimeout(resolve, 1000));
24
+ console.log('āœ… Adaptive Retrieval System initialized\n');
25
+ // Test 1: Simple Query
26
+ console.log('--- Test 1: Simple Query ---');
27
+ const simpleQuery = 'Alice';
28
+ const result1 = await adaptiveRetrieval.retrieve(simpleQuery, 5);
29
+ console.log(`Query: "${simpleQuery}"`);
30
+ console.log(`Strategy: ${result1.strategy}`);
31
+ console.log(`Results: ${result1.results.length}`);
32
+ console.log(`Retrieval Count: ${result1.retrievalCount}`);
33
+ console.log(`Latency: ${result1.latency}ms`);
34
+ console.log(`CAF Score: ${result1.cafScore?.toFixed(3)}`);
35
+ console.log('Top Results:', result1.results.slice(0, 3).map(r => r.name));
36
+ console.log();
37
+ // Simulate feedback (in production, this would come from user or evaluation)
38
+ await adaptiveRetrieval.updateStrategyPerformance(result1.strategy, 0.85, // F1 score
39
+ result1.retrievalCount, result1.latency, true // success
40
+ );
41
+ // Test 2: Moderate Complexity Query
42
+ console.log('--- Test 2: Moderate Complexity Query ---');
43
+ const moderateQuery = 'Who works on Project Alpha and knows TypeScript?';
44
+ const result2 = await adaptiveRetrieval.retrieve(moderateQuery, 5);
45
+ console.log(`Query: "${moderateQuery}"`);
46
+ console.log(`Strategy: ${result2.strategy}`);
47
+ console.log(`Results: ${result2.results.length}`);
48
+ console.log(`Retrieval Count: ${result2.retrievalCount}`);
49
+ console.log(`Latency: ${result2.latency}ms`);
50
+ console.log(`CAF Score: ${result2.cafScore?.toFixed(3)}`);
51
+ console.log('Top Results:', result2.results.slice(0, 3).map(r => r.name));
52
+ console.log();
53
+ await adaptiveRetrieval.updateStrategyPerformance(result2.strategy, 0.72, result2.retrievalCount, result2.latency, true);
54
+ // Test 3: Complex Multi-Hop Query
55
+ console.log('--- Test 3: Complex Multi-Hop Query ---');
56
+ const complexQuery = 'What are the connections between Alice and Bob through their projects?';
57
+ const result3 = await adaptiveRetrieval.retrieve(complexQuery, 5);
58
+ console.log(`Query: "${complexQuery}"`);
59
+ console.log(`Strategy: ${result3.strategy}`);
60
+ console.log(`Results: ${result3.results.length}`);
61
+ console.log(`Retrieval Count: ${result3.retrievalCount}`);
62
+ console.log(`Latency: ${result3.latency}ms`);
63
+ console.log(`CAF Score: ${result3.cafScore?.toFixed(3)}`);
64
+ console.log('Top Results:', result3.results.slice(0, 3).map(r => r.name));
65
+ console.log();
66
+ await adaptiveRetrieval.updateStrategyPerformance(result3.strategy, 0.68, result3.retrievalCount, result3.latency, true);
67
+ // Test 4: Exploratory Query
68
+ console.log('--- Test 4: Exploratory Query ---');
69
+ const exploratoryQuery = 'Show me everything related to software development';
70
+ const result4 = await adaptiveRetrieval.retrieve(exploratoryQuery, 10);
71
+ console.log(`Query: "${exploratoryQuery}"`);
72
+ console.log(`Strategy: ${result4.strategy}`);
73
+ console.log(`Results: ${result4.results.length}`);
74
+ console.log(`Retrieval Count: ${result4.retrievalCount}`);
75
+ console.log(`Latency: ${result4.latency}ms`);
76
+ console.log(`CAF Score: ${result4.cafScore?.toFixed(3)}`);
77
+ console.log('Top Results:', result4.results.slice(0, 3).map(r => r.name));
78
+ console.log();
79
+ await adaptiveRetrieval.updateStrategyPerformance(result4.strategy, 0.55, result4.retrievalCount, result4.latency, false // Lower success for exploratory
80
+ );
81
+ // Test 5: Repeat Simple Query (should use learned strategy)
82
+ console.log('--- Test 5: Repeat Simple Query (Learning Test) ---');
83
+ const result5 = await adaptiveRetrieval.retrieve(simpleQuery, 5);
84
+ console.log(`Query: "${simpleQuery}"`);
85
+ console.log(`Strategy: ${result5.strategy}`);
86
+ console.log(`Results: ${result5.results.length}`);
87
+ console.log(`Retrieval Count: ${result5.retrievalCount}`);
88
+ console.log(`Latency: ${result5.latency}ms`);
89
+ console.log(`CAF Score: ${result5.cafScore?.toFixed(3)}`);
90
+ console.log();
91
+ // Display Performance Statistics
92
+ console.log('=== Performance Statistics ===\n');
93
+ const stats = adaptiveRetrieval.getPerformanceStats();
94
+ for (const [strategy, perf] of stats.entries()) {
95
+ if (perf.totalCount > 0) {
96
+ console.log(`Strategy: ${strategy}`);
97
+ console.log(` Success Rate: ${((perf.successCount / perf.totalCount) * 100).toFixed(1)}%`);
98
+ console.log(` Avg F1 Score: ${perf.avgF1Score.toFixed(3)}`);
99
+ console.log(` Avg Retrieval Cost: ${perf.avgRetrievalCost.toFixed(2)}`);
100
+ console.log(` Avg Latency: ${perf.avgLatency.toFixed(0)}ms`);
101
+ console.log(` Total Uses: ${perf.totalCount}`);
102
+ console.log();
103
+ }
104
+ }
105
+ // Test PRA Reward Calculation
106
+ console.log('=== PRA Reward Analysis ===');
107
+ console.log('Retrieval Count | PRA Reward');
108
+ console.log('----------------|------------');
109
+ for (let i = 1; i <= 10; i++) {
110
+ const reward = Math.pow(0.8, i - 1);
111
+ console.log(`${i.toString().padStart(15)} | ${reward.toFixed(4)}`);
112
+ }
113
+ console.log();
114
+ // Test CAF Score Calculation
115
+ console.log('=== CAF Score Analysis ===');
116
+ console.log('F1=0.9, varying retrieval counts:');
117
+ console.log('Retrieval Count | CAF Score');
118
+ console.log('----------------|----------');
119
+ for (let i = 1; i <= 10; i++) {
120
+ const cafScore = 0.9 * Math.exp(-0.15 * i);
121
+ console.log(`${i.toString().padStart(15)} | ${cafScore.toFixed(4)}`);
122
+ }
123
+ console.log();
124
+ console.log('āœ… All tests completed successfully!');
125
+ console.log('\nšŸ“Š Key Insights:');
126
+ console.log('1. System adapts strategy based on query complexity');
127
+ console.log('2. PRA encourages essential retrievals, penalizes excessive ones');
128
+ console.log('3. CAF balances answer quality with computational cost');
129
+ console.log('4. Performance tracking enables continuous improvement');
130
+ }
131
+ // Run tests
132
+ testAdaptiveRetrieval().catch(error => {
133
+ console.error('āŒ Test failed:', error);
134
+ process.exit(1);
135
+ });
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_1 = require("./index");
4
+ const uuid_1 = require("uuid");
5
+ async function testCompaction() {
6
+ console.log("Starting Context Compaction Tests...");
7
+ const server = new index_1.MemoryServer();
8
+ await server.initPromise;
9
+ try {
10
+ // 1. Test Session Compaction
11
+ console.log("\n--- Testing Session Compaction ---");
12
+ const session = await server.startSession({ name: "Compaction Test Session" });
13
+ const sessionId = session.id;
14
+ console.log("Adding observations to session...");
15
+ await server.addObservation({
16
+ entity_name: "CompactionBot",
17
+ text: "Users prefers dark mode for all interfaces.",
18
+ session_id: sessionId
19
+ });
20
+ await server.addObservation({
21
+ entity_name: "CompactionBot",
22
+ text: "User is a senior software engineer specialized in TypeScript.",
23
+ session_id: sessionId
24
+ });
25
+ await server.addObservation({
26
+ entity_name: "CompactionBot",
27
+ text: "User likes concise documentation with many examples.",
28
+ session_id: sessionId
29
+ });
30
+ console.log("Stopping session (should trigger compaction)...");
31
+ const stopResult = await server.stopSession({ id: sessionId });
32
+ console.log("Stop Result:", JSON.stringify(stopResult, null, 2));
33
+ // Check if summary exists in global_user_profile
34
+ const profileObs = await server.db.run('?[text] := *observation{entity_id: "global_user_profile", text, metadata, @ "NOW"}, regex_matches(text, ".*Session Summary.*")');
35
+ console.log(`Found ${profileObs.rows.length} session summaries in profile.`);
36
+ if (profileObs.rows.length > 0) {
37
+ console.log("Latest Summary:", profileObs.rows[0][0]);
38
+ }
39
+ // 2. Test Entity Compaction (Threshold-based)
40
+ console.log("\n--- Testing Entity Compaction ---");
41
+ const entityName = `HeavyEntity_${(0, uuid_1.v4)().substring(0, 8)}`;
42
+ const createRes = await server.createEntity({ name: entityName, type: "Test" });
43
+ const entityId = createRes.id;
44
+ console.log(`Adding 25 observations to ${entityName} (Threshold is 20)...`);
45
+ for (let i = 1; i <= 25; i++) {
46
+ process.stdout.write(`.`);
47
+ await server.addObservation({
48
+ entity_id: entityId,
49
+ text: `Fact number ${i}: This is a piece of information about the heavy entity that needs to be compacted eventually.`,
50
+ deduplicate: false
51
+ });
52
+ }
53
+ console.log("\nDone adding observations.");
54
+ // The last few should have triggered compaction in background
55
+ console.log("Waiting for background compaction (30s for Ollama)...");
56
+ await new Promise(resolve => setTimeout(resolve, 30000));
57
+ // Check observation count
58
+ const countRes = await server.db.run('?[count(oid)] := *observation{entity_id: $eid, id: oid, @ "NOW"}', { eid: entityId });
59
+ const finalCount = Number(countRes.rows[0][0]);
60
+ console.log(`Final observation count for ${entityName}: ${finalCount}`);
61
+ // Check for ExecutiveSummary
62
+ // Check for ExecutiveSummary (Ollama might use bold, lowercase, or slightly different labels)
63
+ const summaryRes = await server.db.run('?[text] := *observation{entity_id: $eid, text, @ "NOW"}, regex_matches(text, "(?i).*(Executive\\\\s*Summary|Zusammenfassung|ExecutiveSummary).*")', { eid: entityId });
64
+ console.log(`Found ${summaryRes.rows.length} ExecutiveSummaries.`);
65
+ if (summaryRes.rows.length > 0) {
66
+ console.log("Executive Summary Content Preview:", summaryRes.rows[0][0].substring(0, 100) + "...");
67
+ }
68
+ else if (finalCount > 0) {
69
+ // Debug: Print what we actually have
70
+ const allObs = await server.db.run('?[text] := *observation{entity_id: $eid, text, @ "NOW"}', { eid: entityId });
71
+ console.log("Actual observations found for entity:");
72
+ allObs.rows.forEach((row, i) => {
73
+ console.log(`[${i}] ${row[0].substring(0, 200)}...`);
74
+ });
75
+ }
76
+ // 3. Test Manual Compaction via direct call (bypass MCP wrapper for test)
77
+ console.log("\n--- Testing Manual Compaction ---");
78
+ const manageResult = await server.compactEntity({
79
+ entity_id: entityId,
80
+ threshold: 2 // force compaction on remaining context
81
+ });
82
+ console.log("Manual Compact Result:", JSON.stringify(manageResult, null, 2));
83
+ }
84
+ catch (error) {
85
+ console.error("Test failed:", error);
86
+ }
87
+ finally {
88
+ process.exit(0);
89
+ }
90
+ }
91
+ testCompaction();