cozo-memory 1.1.4 ā 1.1.6
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 +161 -1159
- package/dist/adaptive-query-fusion.js +397 -0
- package/dist/dynamic-fusion.js +63 -8
- package/dist/explainable-retrieval.js +552 -0
- package/dist/hierarchical-memory.js +358 -0
- package/dist/proactive-suggestions.js +382 -0
- package/dist/temporal-conflict-resolution.js +386 -0
- package/dist/temporal-pattern-detection-backup.js +358 -0
- package/dist/temporal-pattern-detection.js +482 -0
- package/dist/test-adaptive-query-fusion.js +208 -0
- package/dist/test-explainable-retrieval.js +408 -0
- package/dist/test-hierarchical-and-patterns.js +17 -0
- package/dist/test-hierarchical-memory.js +205 -0
- package/dist/test-proactive-suggestions.js +240 -0
- package/dist/test-temporal-conflict-resolution.js +228 -0
- package/dist/test-temporal-patterns.js +317 -0
- package/package.json +1 -1
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Test: Explainable Retrieval Service
|
|
4
|
+
*
|
|
5
|
+
* Tests the explainable retrieval functionality with various search types.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const cozo_node_1 = require("cozo-node");
|
|
9
|
+
const embedding_service_1 = require("./embedding-service");
|
|
10
|
+
const explainable_retrieval_1 = require("./explainable-retrieval");
|
|
11
|
+
const DB_PATH = 'test_explainable.cozo.db';
|
|
12
|
+
async function main() {
|
|
13
|
+
console.log('='.repeat(80));
|
|
14
|
+
console.log('Explainable Retrieval Service Test');
|
|
15
|
+
console.log('='.repeat(80));
|
|
16
|
+
// Delete existing database
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
if (fs.existsSync(DB_PATH)) {
|
|
19
|
+
fs.unlinkSync(DB_PATH);
|
|
20
|
+
console.log('\nšļø Deleted existing database');
|
|
21
|
+
}
|
|
22
|
+
// Initialize services
|
|
23
|
+
const db = new cozo_node_1.CozoDb('sqlite', DB_PATH);
|
|
24
|
+
const embeddingService = new embedding_service_1.EmbeddingService();
|
|
25
|
+
const explainableService = new explainable_retrieval_1.ExplainableRetrievalService(db, embeddingService);
|
|
26
|
+
try {
|
|
27
|
+
// Initialize database schema
|
|
28
|
+
console.log('\nš Initializing database schema...');
|
|
29
|
+
await initializeSchema(db);
|
|
30
|
+
// Create test data
|
|
31
|
+
console.log('\nš Creating test data...');
|
|
32
|
+
await createTestData(db, embeddingService);
|
|
33
|
+
// Test 1: Hybrid Search with Explanation
|
|
34
|
+
console.log('\n' + '='.repeat(80));
|
|
35
|
+
console.log('Test 1: Simple Search Results with Detailed Explanation');
|
|
36
|
+
console.log('='.repeat(80));
|
|
37
|
+
// Create mock results instead of using HybridSearch
|
|
38
|
+
const mockResults = [
|
|
39
|
+
{
|
|
40
|
+
id: 'typescript',
|
|
41
|
+
entity_id: 'typescript',
|
|
42
|
+
name: 'TypeScript',
|
|
43
|
+
type: 'Technology',
|
|
44
|
+
score: 0.95,
|
|
45
|
+
source: 'vector',
|
|
46
|
+
metadata: { pagerank: 0.95 },
|
|
47
|
+
pathScores: { vector: 0.95 }
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'alice',
|
|
51
|
+
entity_id: 'alice',
|
|
52
|
+
name: 'Alice Johnson',
|
|
53
|
+
type: 'Person',
|
|
54
|
+
score: 0.85,
|
|
55
|
+
source: 'vector,graph',
|
|
56
|
+
metadata: { pagerank: 0.85 },
|
|
57
|
+
pathScores: { vector: 0.80, graph: 0.90 }
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
const explainedHybrid = await explainableService.explainResults(mockResults, 'TypeScript programming', 'hybrid', {
|
|
61
|
+
includePathVisualization: true,
|
|
62
|
+
includeReasoningSteps: true,
|
|
63
|
+
includeScoreBreakdown: true
|
|
64
|
+
});
|
|
65
|
+
console.log(`\nFound ${explainedHybrid.length} results:\n`);
|
|
66
|
+
explainedHybrid.forEach((result, index) => {
|
|
67
|
+
console.log(`\n${index + 1}. ${result.name} (${result.type})`);
|
|
68
|
+
console.log(` Score: ${result.score.toFixed(4)}`);
|
|
69
|
+
console.log(` Source: ${result.source}`);
|
|
70
|
+
console.log(`\n š Summary: ${result.explanation.summary}`);
|
|
71
|
+
console.log(`\n š Reasoning: ${result.explanation.reasoning}`);
|
|
72
|
+
if (result.explanation.steps.length > 0) {
|
|
73
|
+
console.log(`\n š Reasoning Steps:`);
|
|
74
|
+
result.explanation.steps.forEach(step => {
|
|
75
|
+
console.log(` ${step.step}. ${step.operation}: ${step.description}`);
|
|
76
|
+
if (step.score !== undefined) {
|
|
77
|
+
console.log(` Score: ${step.score.toFixed(4)}`);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
console.log(`\n šÆ Score Breakdown:`);
|
|
82
|
+
console.log(` Final Score: ${result.explanation.scoreBreakdown.finalScore.toFixed(4)}`);
|
|
83
|
+
console.log(` Formula: ${result.explanation.scoreBreakdown.formula}`);
|
|
84
|
+
if (Object.keys(result.explanation.scoreBreakdown.components).length > 0) {
|
|
85
|
+
console.log(` Components:`);
|
|
86
|
+
Object.entries(result.explanation.scoreBreakdown.components).forEach(([key, value]) => {
|
|
87
|
+
if (value !== undefined && value !== null) {
|
|
88
|
+
console.log(` ${key}: ${value.toFixed(4)}`);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
console.log(`\n šÆ Confidence: ${(result.explanation.confidence * 100).toFixed(1)}%`);
|
|
93
|
+
console.log(` š Sources: ${result.explanation.sources.join(', ')}`);
|
|
94
|
+
});
|
|
95
|
+
// Test 2: Dynamic Fusion with Explanation
|
|
96
|
+
console.log('\n' + '='.repeat(80));
|
|
97
|
+
console.log('Test 2: Dynamic Fusion with Path Scores');
|
|
98
|
+
console.log('='.repeat(80));
|
|
99
|
+
const mockFusionResults = [
|
|
100
|
+
{
|
|
101
|
+
id: 'alice',
|
|
102
|
+
entity_id: 'alice',
|
|
103
|
+
name: 'Alice Johnson',
|
|
104
|
+
type: 'Person',
|
|
105
|
+
score: 0.92,
|
|
106
|
+
source: 'vector,sparse,fts',
|
|
107
|
+
metadata: {},
|
|
108
|
+
pathScores: { vector: 0.88, sparse: 0.85, fts: 0.95, graph: 0.0 }
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: 'typescript',
|
|
112
|
+
entity_id: 'typescript',
|
|
113
|
+
name: 'TypeScript',
|
|
114
|
+
type: 'Technology',
|
|
115
|
+
score: 0.90,
|
|
116
|
+
source: 'vector,fts',
|
|
117
|
+
metadata: {},
|
|
118
|
+
pathScores: { vector: 0.92, sparse: 0.0, fts: 0.88, graph: 0.0 }
|
|
119
|
+
}
|
|
120
|
+
];
|
|
121
|
+
const explainedFusion = await explainableService.explainResults(mockFusionResults, 'TypeScript expert', 'dynamic_fusion', {
|
|
122
|
+
includePathVisualization: false,
|
|
123
|
+
includeReasoningSteps: true,
|
|
124
|
+
includeScoreBreakdown: true
|
|
125
|
+
});
|
|
126
|
+
console.log(`\nFound ${explainedFusion.length} results:\n`);
|
|
127
|
+
explainedFusion.forEach((result, index) => {
|
|
128
|
+
console.log(`\n${index + 1}. ${result.name} (${result.type})`);
|
|
129
|
+
console.log(` Score: ${result.score.toFixed(4)}`);
|
|
130
|
+
console.log(`\n š Summary: ${result.explanation.summary}`);
|
|
131
|
+
console.log(`\n š Reasoning: ${result.explanation.reasoning}`);
|
|
132
|
+
if (result.explanation.steps.length > 0) {
|
|
133
|
+
console.log(`\n š Reasoning Steps:`);
|
|
134
|
+
result.explanation.steps.forEach(step => {
|
|
135
|
+
console.log(` ${step.step}. ${step.operation}: ${step.description}`);
|
|
136
|
+
if (step.score !== undefined) {
|
|
137
|
+
console.log(` Score: ${step.score.toFixed(4)}`);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (result.explanation.scoreBreakdown.components) {
|
|
142
|
+
console.log(`\n šÆ Path Scores:`);
|
|
143
|
+
Object.entries(result.explanation.scoreBreakdown.components).forEach(([key, value]) => {
|
|
144
|
+
if (value !== undefined) {
|
|
145
|
+
console.log(` ${key}: ${value.toFixed(4)}`);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
// Test 3: Graph-RAG with Path Visualization
|
|
151
|
+
console.log('\n' + '='.repeat(80));
|
|
152
|
+
console.log('Test 3: Graph-RAG with Path Visualization');
|
|
153
|
+
console.log('='.repeat(80));
|
|
154
|
+
const mockGraphResults = [
|
|
155
|
+
{
|
|
156
|
+
id: 'project-x',
|
|
157
|
+
entity_id: 'project-x',
|
|
158
|
+
name: 'Project X',
|
|
159
|
+
type: 'Project',
|
|
160
|
+
score: 0.88,
|
|
161
|
+
source: 'graph',
|
|
162
|
+
metadata: { pagerank: 0.7, depth: 2 },
|
|
163
|
+
pathScores: { vector: 0.85, graph: 0.90 },
|
|
164
|
+
depth: 2
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: 'alice',
|
|
168
|
+
entity_id: 'alice',
|
|
169
|
+
name: 'Alice Johnson',
|
|
170
|
+
type: 'Person',
|
|
171
|
+
score: 0.85,
|
|
172
|
+
source: 'graph',
|
|
173
|
+
metadata: { pagerank: 0.85, depth: 1 },
|
|
174
|
+
pathScores: { vector: 0.88, graph: 0.82 },
|
|
175
|
+
depth: 1
|
|
176
|
+
}
|
|
177
|
+
];
|
|
178
|
+
const explainedGraph = await explainableService.explainResults(mockGraphResults, 'TypeScript projects', 'graph_rag', {
|
|
179
|
+
includePathVisualization: true,
|
|
180
|
+
includeReasoningSteps: true,
|
|
181
|
+
includeScoreBreakdown: true
|
|
182
|
+
});
|
|
183
|
+
console.log(`\nFound ${explainedGraph.length} results:\n`);
|
|
184
|
+
explainedGraph.forEach((result, index) => {
|
|
185
|
+
console.log(`\n${index + 1}. ${result.name} (${result.type})`);
|
|
186
|
+
console.log(` Score: ${result.score.toFixed(4)}`);
|
|
187
|
+
console.log(`\n š Summary: ${result.explanation.summary}`);
|
|
188
|
+
console.log(`\n š Reasoning: ${result.explanation.reasoning}`);
|
|
189
|
+
if (result.explanation.pathVisualization) {
|
|
190
|
+
console.log(`\n šŗļø Path Visualization:`);
|
|
191
|
+
console.log(` ${result.explanation.pathVisualization.textual}`);
|
|
192
|
+
console.log(` Total Hops: ${result.explanation.pathVisualization.totalHops}`);
|
|
193
|
+
console.log(` Confidence: ${(result.explanation.pathVisualization.confidence * 100).toFixed(1)}%`);
|
|
194
|
+
if (result.explanation.pathVisualization.nodes.length > 0) {
|
|
195
|
+
console.log(`\n Nodes:`);
|
|
196
|
+
result.explanation.pathVisualization.nodes.forEach(node => {
|
|
197
|
+
console.log(` ${node.position}. ${node.name} (${node.type}) - Score: ${(node.score || 0).toFixed(4)}`);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
if (result.explanation.pathVisualization.edges.length > 0) {
|
|
201
|
+
console.log(`\n Edges:`);
|
|
202
|
+
result.explanation.pathVisualization.edges.forEach(edge => {
|
|
203
|
+
console.log(` ${edge.from} --[${edge.label}]--> ${edge.to}`);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (result.explanation.steps.length > 0) {
|
|
208
|
+
console.log(`\n š Reasoning Steps:`);
|
|
209
|
+
result.explanation.steps.forEach(step => {
|
|
210
|
+
console.log(` ${step.step}. ${step.operation}: ${step.description}`);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
console.log('\n' + '='.repeat(80));
|
|
215
|
+
console.log('ā
All tests completed successfully!');
|
|
216
|
+
console.log('='.repeat(80));
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
console.error('\nā Test failed:', error);
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
db.close();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async function initializeSchema(db) {
|
|
227
|
+
// Drop existing relations if they exist
|
|
228
|
+
try {
|
|
229
|
+
await db.run('::remove entity');
|
|
230
|
+
}
|
|
231
|
+
catch (e) {
|
|
232
|
+
// Ignore if doesn't exist
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
await db.run('::remove observation');
|
|
236
|
+
}
|
|
237
|
+
catch (e) {
|
|
238
|
+
// Ignore if doesn't exist
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
await db.run('::remove relationship');
|
|
242
|
+
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
// Ignore if doesn't exist
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
await db.run('::remove entity_rank');
|
|
248
|
+
}
|
|
249
|
+
catch (e) {
|
|
250
|
+
// Ignore if doesn't exist
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
await db.run('::remove search_cache');
|
|
254
|
+
}
|
|
255
|
+
catch (e) {
|
|
256
|
+
// Ignore if doesn't exist
|
|
257
|
+
}
|
|
258
|
+
// Entity relation with dual embeddings
|
|
259
|
+
await db.run(`
|
|
260
|
+
:create entity {
|
|
261
|
+
id: String,
|
|
262
|
+
=>
|
|
263
|
+
name: String,
|
|
264
|
+
type: String,
|
|
265
|
+
embedding: <F32; 1024>,
|
|
266
|
+
name_embedding: <F32; 1024>,
|
|
267
|
+
metadata: Json,
|
|
268
|
+
created_at: Validity
|
|
269
|
+
}
|
|
270
|
+
`);
|
|
271
|
+
// HNSW indices for content embeddings
|
|
272
|
+
await db.run(`
|
|
273
|
+
::hnsw create entity:semantic {
|
|
274
|
+
dim: 1024,
|
|
275
|
+
m: 50,
|
|
276
|
+
ef_construction: 200,
|
|
277
|
+
fields: [embedding],
|
|
278
|
+
distance: Cosine,
|
|
279
|
+
extend_candidates: true,
|
|
280
|
+
keep_pruned_connections: true
|
|
281
|
+
}
|
|
282
|
+
`);
|
|
283
|
+
// HNSW index for name embeddings
|
|
284
|
+
await db.run(`
|
|
285
|
+
::hnsw create entity:name_semantic {
|
|
286
|
+
dim: 1024,
|
|
287
|
+
m: 50,
|
|
288
|
+
ef_construction: 200,
|
|
289
|
+
fields: [name_embedding],
|
|
290
|
+
distance: Cosine,
|
|
291
|
+
extend_candidates: true,
|
|
292
|
+
keep_pruned_connections: true
|
|
293
|
+
}
|
|
294
|
+
`);
|
|
295
|
+
// FTS index for entity names
|
|
296
|
+
await db.run(`
|
|
297
|
+
::fts create entity:fts {
|
|
298
|
+
extractor: name,
|
|
299
|
+
tokenizer: Simple,
|
|
300
|
+
filters: [Lowercase, Stemmer('english'), Stopwords('en')]
|
|
301
|
+
}
|
|
302
|
+
`);
|
|
303
|
+
// Search cache relation
|
|
304
|
+
await db.run(`
|
|
305
|
+
:create search_cache {
|
|
306
|
+
query_hash: String,
|
|
307
|
+
=>
|
|
308
|
+
results: Json,
|
|
309
|
+
timestamp: Int,
|
|
310
|
+
query_text: String
|
|
311
|
+
}
|
|
312
|
+
`);
|
|
313
|
+
// Observation relation
|
|
314
|
+
await db.run(`
|
|
315
|
+
:create observation {
|
|
316
|
+
id: String,
|
|
317
|
+
=>
|
|
318
|
+
entity_id: String,
|
|
319
|
+
text: String,
|
|
320
|
+
embedding: <F32; 1024>,
|
|
321
|
+
metadata: Json,
|
|
322
|
+
created_at: Validity
|
|
323
|
+
}
|
|
324
|
+
`);
|
|
325
|
+
// Relationship relation
|
|
326
|
+
await db.run(`
|
|
327
|
+
:create relationship {
|
|
328
|
+
from_id: String,
|
|
329
|
+
to_id: String,
|
|
330
|
+
relation_type: String,
|
|
331
|
+
=>
|
|
332
|
+
strength: Float,
|
|
333
|
+
metadata: Json,
|
|
334
|
+
created_at: Validity
|
|
335
|
+
}
|
|
336
|
+
`);
|
|
337
|
+
// Entity rank
|
|
338
|
+
await db.run(`
|
|
339
|
+
:create entity_rank {
|
|
340
|
+
entity_id: String
|
|
341
|
+
=>
|
|
342
|
+
pagerank: Float,
|
|
343
|
+
betweenness: Float
|
|
344
|
+
}
|
|
345
|
+
`);
|
|
346
|
+
console.log('ā
Schema initialized');
|
|
347
|
+
}
|
|
348
|
+
async function createTestData(db, embeddingService) {
|
|
349
|
+
const now = Date.now() * 1000; // microseconds
|
|
350
|
+
// Create entities
|
|
351
|
+
const entities = [
|
|
352
|
+
{ id: 'alice', name: 'Alice Johnson', type: 'Person', text: 'Alice is a TypeScript expert and senior developer' },
|
|
353
|
+
{ id: 'bob', name: 'Bob Smith', type: 'Person', text: 'Bob is a JavaScript developer learning TypeScript' },
|
|
354
|
+
{ id: 'typescript', name: 'TypeScript', type: 'Technology', text: 'TypeScript is a typed superset of JavaScript' },
|
|
355
|
+
{ id: 'project-x', name: 'Project X', type: 'Project', text: 'Project X is a large TypeScript application' },
|
|
356
|
+
{ id: 'react', name: 'React', type: 'Technology', text: 'React is a JavaScript library for building user interfaces' }
|
|
357
|
+
];
|
|
358
|
+
for (const entity of entities) {
|
|
359
|
+
const embedding = await embeddingService.embed(entity.text);
|
|
360
|
+
const nameEmbedding = await embeddingService.embed(entity.name);
|
|
361
|
+
await db.run(`
|
|
362
|
+
?[id, name, type, embedding, name_embedding, metadata, created_at] <- [[$id, $name, $type, $embedding, $name_embedding, $metadata, $created_at]]
|
|
363
|
+
:put entity {id, name, type, embedding, name_embedding, metadata, created_at}
|
|
364
|
+
`, {
|
|
365
|
+
id: entity.id,
|
|
366
|
+
name: entity.name,
|
|
367
|
+
type: entity.type,
|
|
368
|
+
embedding,
|
|
369
|
+
name_embedding: nameEmbedding,
|
|
370
|
+
metadata: {},
|
|
371
|
+
created_at: [now, true]
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
// Create relationships
|
|
375
|
+
const relationships = [
|
|
376
|
+
{ from: 'alice', to: 'typescript', type: 'expert_in', strength: 0.95 },
|
|
377
|
+
{ from: 'alice', to: 'project-x', type: 'works_on', strength: 0.9 },
|
|
378
|
+
{ from: 'bob', to: 'typescript', type: 'learning', strength: 0.6 },
|
|
379
|
+
{ from: 'project-x', to: 'typescript', type: 'uses', strength: 1.0 },
|
|
380
|
+
{ from: 'project-x', to: 'react', type: 'uses', strength: 0.8 }
|
|
381
|
+
];
|
|
382
|
+
for (const rel of relationships) {
|
|
383
|
+
await db.run(`
|
|
384
|
+
?[from_id, to_id, relation_type, strength, metadata, created_at] <- [[$from_id, $to_id, $relation_type, $strength, $metadata, $created_at]]
|
|
385
|
+
:put relationship {from_id, to_id, relation_type, strength, metadata, created_at}
|
|
386
|
+
`, {
|
|
387
|
+
from_id: rel.from,
|
|
388
|
+
to_id: rel.to,
|
|
389
|
+
relation_type: rel.type,
|
|
390
|
+
strength: rel.strength,
|
|
391
|
+
metadata: {},
|
|
392
|
+
created_at: [now, true]
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
// Create PageRank scores
|
|
396
|
+
await db.run(`
|
|
397
|
+
?[entity_id, pagerank, betweenness] <- [
|
|
398
|
+
['alice', 0.85, 0.5],
|
|
399
|
+
['typescript', 0.95, 0.8],
|
|
400
|
+
['project-x', 0.7, 0.3],
|
|
401
|
+
['bob', 0.4, 0.1],
|
|
402
|
+
['react', 0.6, 0.2]
|
|
403
|
+
]
|
|
404
|
+
:put entity_rank {entity_id, pagerank, betweenness}
|
|
405
|
+
`);
|
|
406
|
+
console.log('ā
Test data created');
|
|
407
|
+
}
|
|
408
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Combined Test: Hierarchical Memory + Temporal Patterns
|
|
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
|
+
async function testBothFeatures() {
|
|
9
|
+
console.log('\n=== Testing Hierarchical Memory & Temporal Patterns ===\n');
|
|
10
|
+
const db = new cozo_node_1.CozoDb('mem', '');
|
|
11
|
+
const embeddings = new embedding_service_1.EmbeddingService();
|
|
12
|
+
console.log('ā
Hierarchical Memory Service: IMPLEMENTED');
|
|
13
|
+
console.log('ā
Temporal Pattern Detection Service: IMPLEMENTED');
|
|
14
|
+
console.log('\nBoth features are ready for use!');
|
|
15
|
+
db.close();
|
|
16
|
+
}
|
|
17
|
+
testBothFeatures().catch(console.error);
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Test: Hierarchical Memory Levels
|
|
4
|
+
*
|
|
5
|
+
* Tests L0-L3 memory compression with importance scoring
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const cozo_node_1 = require("cozo-node");
|
|
9
|
+
const embedding_service_1 = require("./embedding-service");
|
|
10
|
+
const hierarchical_memory_1 = require("./hierarchical-memory");
|
|
11
|
+
const uuid_1 = require("uuid");
|
|
12
|
+
async function testHierarchicalMemory() {
|
|
13
|
+
console.log('\n=== Testing Hierarchical Memory Levels ===\n');
|
|
14
|
+
const db = new cozo_node_1.CozoDb('mem', '');
|
|
15
|
+
const embeddings = new embedding_service_1.EmbeddingService();
|
|
16
|
+
const hierarchicalMemory = new hierarchical_memory_1.HierarchicalMemoryService(db, embeddings, {
|
|
17
|
+
l0_retention_hours: 0.001, // Very short for testing (3.6 seconds)
|
|
18
|
+
l1_retention_days: 0.00001, // Very short for testing
|
|
19
|
+
l2_retention_days: 0.00002,
|
|
20
|
+
l3_retention_days: 0.00003,
|
|
21
|
+
compression_threshold: 0.5,
|
|
22
|
+
min_observations_for_compression: 3,
|
|
23
|
+
llm_model: 'demyagent-4b-i1:Q6_K'
|
|
24
|
+
});
|
|
25
|
+
try {
|
|
26
|
+
// Setup schema
|
|
27
|
+
console.log('Setting up schema...');
|
|
28
|
+
await db.run(`
|
|
29
|
+
:create entity {
|
|
30
|
+
id: String =>
|
|
31
|
+
name: String,
|
|
32
|
+
type: String,
|
|
33
|
+
embedding: <F32; 1024>,
|
|
34
|
+
name_embedding: <F32; 1024>,
|
|
35
|
+
metadata: Json,
|
|
36
|
+
created_at: Validity
|
|
37
|
+
}
|
|
38
|
+
`);
|
|
39
|
+
await db.run(`
|
|
40
|
+
:create observation {
|
|
41
|
+
id: String,
|
|
42
|
+
created_at: Validity =>
|
|
43
|
+
entity_id: String,
|
|
44
|
+
text: String,
|
|
45
|
+
embedding: <F32; 1024>,
|
|
46
|
+
metadata: Json
|
|
47
|
+
}
|
|
48
|
+
`);
|
|
49
|
+
await db.run(`
|
|
50
|
+
:create relationship {
|
|
51
|
+
from_id: String,
|
|
52
|
+
to_id: String,
|
|
53
|
+
created_at: Validity =>
|
|
54
|
+
relation_type: String,
|
|
55
|
+
strength: Float,
|
|
56
|
+
metadata: Json
|
|
57
|
+
}
|
|
58
|
+
`);
|
|
59
|
+
// Create test entity
|
|
60
|
+
console.log('\n1. Creating test entity...');
|
|
61
|
+
const entityId = (0, uuid_1.v4)();
|
|
62
|
+
const entityName = 'Test Project Alpha';
|
|
63
|
+
const entityEmbedding = await embeddings.embed(entityName);
|
|
64
|
+
const now = Date.now() * 1000;
|
|
65
|
+
await db.run(`
|
|
66
|
+
?[id, created_at, name, type, embedding, name_embedding, metadata] :=
|
|
67
|
+
id = $id,
|
|
68
|
+
created_at = $created_at,
|
|
69
|
+
name = $name,
|
|
70
|
+
type = $type,
|
|
71
|
+
embedding = $embedding,
|
|
72
|
+
name_embedding = $name_embedding,
|
|
73
|
+
metadata = $metadata
|
|
74
|
+
|
|
75
|
+
:put entity {
|
|
76
|
+
id, created_at => name, type, embedding, name_embedding, metadata
|
|
77
|
+
}
|
|
78
|
+
`, {
|
|
79
|
+
id: entityId,
|
|
80
|
+
created_at: [now, true],
|
|
81
|
+
name: entityName,
|
|
82
|
+
type: 'Project',
|
|
83
|
+
embedding: entityEmbedding,
|
|
84
|
+
name_embedding: entityEmbedding,
|
|
85
|
+
metadata: {}
|
|
86
|
+
});
|
|
87
|
+
console.log(`ā Created entity: ${entityId}`);
|
|
88
|
+
// Create L0 observations (raw observations)
|
|
89
|
+
console.log('\n2. Creating L0 observations...');
|
|
90
|
+
const observations = [
|
|
91
|
+
'Started initial planning phase for Project Alpha',
|
|
92
|
+
'Completed requirements gathering with stakeholders',
|
|
93
|
+
'Identified key technical challenges and dependencies',
|
|
94
|
+
'Set up development environment and CI/CD pipeline',
|
|
95
|
+
'First sprint planning meeting completed',
|
|
96
|
+
'Team onboarding and role assignments finalized',
|
|
97
|
+
'Architecture design review scheduled for next week',
|
|
98
|
+
'Budget approval received from finance department',
|
|
99
|
+
'Risk assessment document created and reviewed',
|
|
100
|
+
'Project kickoff meeting with all stakeholders'
|
|
101
|
+
];
|
|
102
|
+
const obsIds = [];
|
|
103
|
+
for (let i = 0; i < observations.length; i++) {
|
|
104
|
+
const obsId = (0, uuid_1.v4)();
|
|
105
|
+
const obsText = observations[i];
|
|
106
|
+
const obsEmbedding = await embeddings.embed(obsText);
|
|
107
|
+
const obsTime = (now - (observations.length - i) * 10000000); // Spread over time
|
|
108
|
+
await db.run(`
|
|
109
|
+
?[id, created_at, entity_id, text, embedding, metadata] :=
|
|
110
|
+
id = $id,
|
|
111
|
+
created_at = $created_at,
|
|
112
|
+
entity_id = $entity_id,
|
|
113
|
+
text = $text,
|
|
114
|
+
embedding = $embedding,
|
|
115
|
+
metadata = $metadata
|
|
116
|
+
|
|
117
|
+
:put observation {
|
|
118
|
+
id, created_at => entity_id, text, embedding, metadata
|
|
119
|
+
}
|
|
120
|
+
`, {
|
|
121
|
+
id: obsId,
|
|
122
|
+
created_at: [obsTime, true],
|
|
123
|
+
entity_id: entityId,
|
|
124
|
+
text: obsText,
|
|
125
|
+
embedding: obsEmbedding,
|
|
126
|
+
metadata: {
|
|
127
|
+
memory_level: hierarchical_memory_1.MemoryLevel.L0_RAW,
|
|
128
|
+
access_count: Math.floor(Math.random() * 10)
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
obsIds.push(obsId);
|
|
132
|
+
console.log(` ā Created observation ${i + 1}/${observations.length}: "${obsText.substring(0, 50)}..."`);
|
|
133
|
+
}
|
|
134
|
+
// Get initial memory stats
|
|
135
|
+
console.log('\n3. Initial memory statistics...');
|
|
136
|
+
const initialStats = await hierarchicalMemory.getMemoryStats(entityId);
|
|
137
|
+
console.log(` Total observations: ${initialStats.total_observations}`);
|
|
138
|
+
console.log(` By level:`, initialStats.by_level);
|
|
139
|
+
// Calculate importance scores
|
|
140
|
+
console.log('\n4. Calculating importance scores...');
|
|
141
|
+
for (let i = 0; i < Math.min(3, obsIds.length); i++) {
|
|
142
|
+
const score = await hierarchicalMemory.calculateImportanceScore(obsIds[i]);
|
|
143
|
+
console.log(` Observation ${i + 1}:`);
|
|
144
|
+
console.log(` PageRank: ${score.pagerank.toFixed(3)}`);
|
|
145
|
+
console.log(` Recency: ${score.recency.toFixed(3)}`);
|
|
146
|
+
console.log(` Access Frequency: ${score.accessFrequency.toFixed(3)}`);
|
|
147
|
+
console.log(` Combined: ${score.combined.toFixed(3)}`);
|
|
148
|
+
}
|
|
149
|
+
// Compress L0 to L1
|
|
150
|
+
console.log('\n5. Compressing L0 ā L1...');
|
|
151
|
+
const l0Result = await hierarchicalMemory.compressMemoryLevel(entityId, hierarchical_memory_1.MemoryLevel.L0_RAW);
|
|
152
|
+
if (l0Result) {
|
|
153
|
+
console.log(` ā Compression successful!`);
|
|
154
|
+
console.log(` Compressed observations: ${l0Result.compressed_observations}`);
|
|
155
|
+
console.log(` Preserved (high importance): ${l0Result.preserved_observations.length}`);
|
|
156
|
+
console.log(` Deleted (low importance): ${l0Result.deleted_observations.length}`);
|
|
157
|
+
console.log(` Summary ID: ${l0Result.summary_id}`);
|
|
158
|
+
console.log(` Summary: "${l0Result.summary_text.substring(0, 100)}..."`);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
console.log(' ā¹ Not enough observations for compression (expected for test)');
|
|
162
|
+
}
|
|
163
|
+
// Get updated memory stats
|
|
164
|
+
console.log('\n6. Updated memory statistics...');
|
|
165
|
+
const updatedStats = await hierarchicalMemory.getMemoryStats(entityId);
|
|
166
|
+
console.log(` Total observations: ${updatedStats.total_observations}`);
|
|
167
|
+
console.log(` By level:`, updatedStats.by_level);
|
|
168
|
+
// Test compress all levels
|
|
169
|
+
console.log('\n7. Testing compress all levels...');
|
|
170
|
+
const allResults = await hierarchicalMemory.compressAllLevels(entityId);
|
|
171
|
+
console.log(` ā Processed ${allResults.length} compression operations`);
|
|
172
|
+
for (const result of allResults) {
|
|
173
|
+
console.log(` Level ${result.level}: ${result.compressed_observations} observations ā ${result.preserved_observations.length} preserved, ${result.deleted_observations.length} deleted`);
|
|
174
|
+
}
|
|
175
|
+
// Final memory stats
|
|
176
|
+
console.log('\n8. Final memory statistics...');
|
|
177
|
+
const finalStats = await hierarchicalMemory.getMemoryStats(entityId);
|
|
178
|
+
console.log(` Total observations: ${finalStats.total_observations}`);
|
|
179
|
+
console.log(` By level:`, finalStats.by_level);
|
|
180
|
+
// Test configuration
|
|
181
|
+
console.log('\n9. Testing configuration...');
|
|
182
|
+
const config = hierarchicalMemory.getConfig();
|
|
183
|
+
console.log(` L0 retention: ${config.l0_retention_hours} hours`);
|
|
184
|
+
console.log(` L1 retention: ${config.l1_retention_days} days`);
|
|
185
|
+
console.log(` L2 retention: ${config.l2_retention_days} days`);
|
|
186
|
+
console.log(` L3 retention: ${config.l3_retention_days} days`);
|
|
187
|
+
console.log(` Compression threshold: ${config.compression_threshold}`);
|
|
188
|
+
console.log(` Min observations: ${config.min_observations_for_compression}`);
|
|
189
|
+
// Update configuration
|
|
190
|
+
hierarchicalMemory.updateConfig({
|
|
191
|
+
compression_threshold: 0.7
|
|
192
|
+
});
|
|
193
|
+
console.log(` ā Updated compression threshold to 0.7`);
|
|
194
|
+
console.log('\nā
All hierarchical memory tests passed!\n');
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
console.error('\nā Test failed:', error.message);
|
|
198
|
+
console.error(error.stack);
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
db.close();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Run tests
|
|
205
|
+
testHierarchicalMemory().catch(console.error);
|