cozo-memory 1.1.6 → 1.1.7

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,226 @@
1
+ "use strict";
2
+ /**
3
+ * Test: Query-Aware Flow Diffusion Traversal
4
+ *
5
+ * Tests the QAFD-RAG inspired query-aware graph traversal.
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 query_aware_traversal_1 = require("./query-aware-traversal");
11
+ const DB_PATH = 'test_query_aware.cozo.db';
12
+ const EMBEDDING_DIM = 1024;
13
+ async function main() {
14
+ console.log('='.repeat(80));
15
+ console.log('Query-Aware Flow Diffusion Traversal Test');
16
+ console.log('='.repeat(80));
17
+ // Delete existing database
18
+ const fs = require('fs');
19
+ if (fs.existsSync(DB_PATH)) {
20
+ fs.unlinkSync(DB_PATH);
21
+ console.log('\nšŸ—‘ļø Deleted existing database');
22
+ }
23
+ // Initialize services
24
+ const db = new cozo_node_1.CozoDb('sqlite', DB_PATH);
25
+ const embeddingService = new embedding_service_1.EmbeddingService();
26
+ const queryAwareTraversal = new query_aware_traversal_1.QueryAwareTraversal(db, embeddingService);
27
+ try {
28
+ // Initialize database schema
29
+ console.log('\nšŸ“Š Initializing database schema...');
30
+ await initializeSchema(db);
31
+ // Create test data
32
+ console.log('\nšŸ“ Creating test data...');
33
+ await createTestData(db, embeddingService);
34
+ // Test 1: Single-seed traversal
35
+ console.log('\n' + '='.repeat(80));
36
+ console.log('Test 1: Query-Aware Traversal from Single Seed');
37
+ console.log('='.repeat(80));
38
+ const results1 = await queryAwareTraversal.traverse('typescript', 'TypeScript programming and development', {
39
+ maxHops: 2,
40
+ dampingFactor: 0.85,
41
+ minScore: 0.05,
42
+ topK: 10
43
+ });
44
+ console.log(`\nFound ${results1.length} results:\n`);
45
+ results1.forEach((result, index) => {
46
+ console.log(`${index + 1}. ${result.name} (${result.type})`);
47
+ console.log(` Score: ${result.score.toFixed(4)}`);
48
+ console.log(` Hops: ${result.hops}`);
49
+ console.log(` Path Score: ${result.path_score.toFixed(4)}`);
50
+ });
51
+ // Test 2: Multi-seed traversal
52
+ console.log('\n' + '='.repeat(80));
53
+ console.log('Test 2: Query-Aware Traversal from Multiple Seeds');
54
+ console.log('='.repeat(80));
55
+ const results2 = await queryAwareTraversal.traverseFromSeeds(['typescript', 'react'], 'modern web development frameworks', {
56
+ maxHops: 2,
57
+ dampingFactor: 0.85,
58
+ minScore: 0.05,
59
+ topK: 10
60
+ });
61
+ console.log(`\nFound ${results2.length} results:\n`);
62
+ results2.forEach((result, index) => {
63
+ console.log(`${index + 1}. ${result.name} (${result.type})`);
64
+ console.log(` Score: ${result.score.toFixed(4)}`);
65
+ console.log(` Hops: ${result.hops}`);
66
+ });
67
+ // Test 3: Hybrid search (Vector + Query-Aware Traversal)
68
+ console.log('\n' + '='.repeat(80));
69
+ console.log('Test 3: Hybrid Search (Vector Seeds + Query-Aware Traversal)');
70
+ console.log('='.repeat(80));
71
+ const results3 = await queryAwareTraversal.hybridSearch('JavaScript frameworks for building user interfaces', {
72
+ seedTopK: 3,
73
+ maxHops: 2,
74
+ dampingFactor: 0.85,
75
+ minScore: 0.05,
76
+ topK: 10
77
+ });
78
+ console.log(`\nFound ${results3.length} results:\n`);
79
+ results3.forEach((result, index) => {
80
+ console.log(`${index + 1}. ${result.name} (${result.type})`);
81
+ console.log(` Score: ${result.score.toFixed(4)}`);
82
+ console.log(` Hops: ${result.hops}`);
83
+ console.log(` Source: ${result.source}`);
84
+ });
85
+ // Test 4: Filtered by relationship type
86
+ console.log('\n' + '='.repeat(80));
87
+ console.log('Test 4: Query-Aware Traversal with Relationship Filter');
88
+ console.log('='.repeat(80));
89
+ const results4 = await queryAwareTraversal.traverse('alice', 'TypeScript expertise and projects', {
90
+ maxHops: 2,
91
+ dampingFactor: 0.85,
92
+ minScore: 0.05,
93
+ topK: 10,
94
+ relationTypes: ['expert_in', 'works_on']
95
+ });
96
+ console.log(`\nFound ${results4.length} results (filtered by expert_in, works_on):\n`);
97
+ results4.forEach((result, index) => {
98
+ console.log(`${index + 1}. ${result.name} (${result.type})`);
99
+ console.log(` Score: ${result.score.toFixed(4)}`);
100
+ console.log(` Hops: ${result.hops}`);
101
+ });
102
+ console.log('\n' + '='.repeat(80));
103
+ console.log('āœ… All tests completed successfully!');
104
+ console.log('='.repeat(80));
105
+ }
106
+ catch (error) {
107
+ console.error('\nāŒ Test failed:', error);
108
+ throw error;
109
+ }
110
+ finally {
111
+ db.close();
112
+ }
113
+ }
114
+ async function initializeSchema(db) {
115
+ // Drop existing relations if they exist
116
+ try {
117
+ await db.run('::remove entity');
118
+ }
119
+ catch (e) { }
120
+ try {
121
+ await db.run('::remove relationship');
122
+ }
123
+ catch (e) { }
124
+ // Entity relation with embeddings
125
+ await db.run(`
126
+ :create entity {
127
+ id: String,
128
+ created_at: Validity
129
+ =>
130
+ name: String,
131
+ type: String,
132
+ embedding: <F32; ${EMBEDDING_DIM}>,
133
+ metadata: Json
134
+ }
135
+ `);
136
+ // HNSW index for semantic search
137
+ await db.run(`
138
+ ::hnsw create entity:semantic {
139
+ dim: ${EMBEDDING_DIM},
140
+ m: 50,
141
+ ef_construction: 200,
142
+ fields: [embedding],
143
+ distance: Cosine,
144
+ extend_candidates: true,
145
+ keep_pruned_connections: true
146
+ }
147
+ `);
148
+ // Relationship relation
149
+ await db.run(`
150
+ :create relationship {
151
+ from_id: String,
152
+ to_id: String,
153
+ relation_type: String,
154
+ created_at: Validity
155
+ =>
156
+ strength: Float,
157
+ metadata: Json
158
+ }
159
+ `);
160
+ console.log('āœ… Schema initialized');
161
+ }
162
+ async function createTestData(db, embeddingService) {
163
+ const now = Date.now() * 1000; // microseconds
164
+ // Create entities with embeddings
165
+ const entities = [
166
+ { id: 'alice', name: 'Alice Johnson', type: 'Person', text: 'Alice is a senior TypeScript developer and expert in React' },
167
+ { id: 'bob', name: 'Bob Smith', type: 'Person', text: 'Bob is a JavaScript developer learning TypeScript and Vue.js' },
168
+ { id: 'typescript', name: 'TypeScript', type: 'Technology', text: 'TypeScript is a typed superset of JavaScript for large-scale applications' },
169
+ { id: 'javascript', name: 'JavaScript', type: 'Technology', text: 'JavaScript is a dynamic programming language for web development' },
170
+ { id: 'react', name: 'React', type: 'Framework', text: 'React is a JavaScript library for building user interfaces' },
171
+ { id: 'vue', name: 'Vue.js', type: 'Framework', text: 'Vue.js is a progressive JavaScript framework for building UIs' },
172
+ { id: 'angular', name: 'Angular', type: 'Framework', text: 'Angular is a TypeScript-based web application framework' },
173
+ { id: 'project-x', name: 'Project X', type: 'Project', text: 'Project X is a large-scale TypeScript application using React' },
174
+ { id: 'project-y', name: 'Project Y', type: 'Project', text: 'Project Y is a Vue.js application with TypeScript support' },
175
+ { id: 'nodejs', name: 'Node.js', type: 'Runtime', text: 'Node.js is a JavaScript runtime built on Chrome V8 engine' }
176
+ ];
177
+ for (const entity of entities) {
178
+ const embedding = await embeddingService.embed(entity.text);
179
+ await db.run(`
180
+ ?[id, created_at, name, type, embedding, metadata] <- [[$id, $created_at, $name, $type, $embedding, $metadata]]
181
+ :put entity {id, created_at, name, type, embedding, metadata}
182
+ `, {
183
+ id: entity.id,
184
+ created_at: [now, true],
185
+ name: entity.name,
186
+ type: entity.type,
187
+ embedding,
188
+ metadata: {}
189
+ });
190
+ }
191
+ // Create relationships
192
+ const relationships = [
193
+ { from: 'alice', to: 'typescript', type: 'expert_in', strength: 0.95 },
194
+ { from: 'alice', to: 'react', type: 'expert_in', strength: 0.90 },
195
+ { from: 'alice', to: 'project-x', type: 'works_on', strength: 0.85 },
196
+ { from: 'bob', to: 'javascript', type: 'expert_in', strength: 0.80 },
197
+ { from: 'bob', to: 'typescript', type: 'learning', strength: 0.60 },
198
+ { from: 'bob', to: 'vue', type: 'uses', strength: 0.75 },
199
+ { from: 'bob', to: 'project-y', type: 'works_on', strength: 0.70 },
200
+ { from: 'typescript', to: 'javascript', type: 'extends', strength: 1.0 },
201
+ { from: 'react', to: 'javascript', type: 'uses', strength: 0.95 },
202
+ { from: 'vue', to: 'javascript', type: 'uses', strength: 0.95 },
203
+ { from: 'angular', to: 'typescript', type: 'uses', strength: 1.0 },
204
+ { from: 'project-x', to: 'typescript', type: 'uses', strength: 1.0 },
205
+ { from: 'project-x', to: 'react', type: 'uses', strength: 0.95 },
206
+ { from: 'project-y', to: 'vue', type: 'uses', strength: 0.95 },
207
+ { from: 'project-y', to: 'typescript', type: 'uses', strength: 0.80 },
208
+ { from: 'react', to: 'nodejs', type: 'runs_on', strength: 0.85 },
209
+ { from: 'vue', to: 'nodejs', type: 'runs_on', strength: 0.85 }
210
+ ];
211
+ for (const rel of relationships) {
212
+ await db.run(`
213
+ ?[from_id, to_id, relation_type, created_at, strength, metadata] <- [[$from_id, $to_id, $relation_type, $created_at, $strength, $metadata]]
214
+ :put relationship {from_id, to_id, relation_type, created_at, strength, metadata}
215
+ `, {
216
+ from_id: rel.from,
217
+ to_id: rel.to,
218
+ relation_type: rel.type,
219
+ created_at: [now, true],
220
+ strength: rel.strength,
221
+ metadata: {}
222
+ });
223
+ }
224
+ console.log('āœ… Test data created');
225
+ }
226
+ main().catch(console.error);
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const cozo_node_1 = require("cozo-node");
4
+ const embedding_service_1 = require("./embedding-service");
5
+ const hybrid_search_1 = require("./hybrid-search");
6
+ const reranker_service_1 = require("./reranker-service");
7
+ const query_pipeline_1 = require("./query-pipeline");
8
+ async function testQueryPipeline() {
9
+ console.log('=== Query Pipeline Test ===\n');
10
+ const db = new cozo_node_1.CozoDb();
11
+ const embeddingService = new embedding_service_1.EmbeddingService();
12
+ const hybridSearch = new hybrid_search_1.HybridSearch(db, embeddingService);
13
+ const reranker = new reranker_service_1.RerankerService();
14
+ try {
15
+ console.log('--- Test 1: Pipeline Builder ---');
16
+ const customPipeline = new query_pipeline_1.PipelineBuilder('test-pipeline')
17
+ .addPreprocess(query_pipeline_1.preprocessStages.queryNormalization())
18
+ .addPreprocess(query_pipeline_1.preprocessStages.embedQuery(embeddingService))
19
+ .addPostProcess(query_pipeline_1.postProcessStages.scoreNormalization())
20
+ .addPostProcess(query_pipeline_1.postProcessStages.topK())
21
+ .build();
22
+ console.log('Pipeline name:', customPipeline.name);
23
+ console.log('Number of stages:', customPipeline.stages.length);
24
+ console.log('Stage types:', customPipeline.stages.map(s => s.type).join(', '));
25
+ console.log('āœ“ Pipeline builder works\n');
26
+ // Test 2: Preprocessing Stages
27
+ console.log('--- Test 2: Preprocessing Stages ---');
28
+ const pipeline = new query_pipeline_1.QueryPipeline(customPipeline);
29
+ const result = await pipeline.execute(' HELLO WORLD ', { topK: 5 });
30
+ console.log('Original query: " HELLO WORLD "');
31
+ console.log('Normalized query:', result.metadata.query || 'not stored');
32
+ console.log('Embedding generated:', result.metadata.embedding ? 'yes' : 'no');
33
+ console.log('Metrics:', result.metrics);
34
+ console.log('āœ“ Preprocessing stages work\n');
35
+ // Test 3: Conditional Execution
36
+ console.log('--- Test 3: Conditional Execution ---');
37
+ const conditionalConfig = new query_pipeline_1.PipelineBuilder('conditional')
38
+ .addPreprocess(query_pipeline_1.preprocessStages.queryNormalization())
39
+ .addPreprocess({
40
+ type: 'preprocess',
41
+ name: 'conditional-stage',
42
+ enabled: true,
43
+ condition: (ctx) => ctx.query.length > 5,
44
+ execute: async (ctx) => {
45
+ ctx.metadata.conditionalExecuted = true;
46
+ return ctx;
47
+ }
48
+ })
49
+ .build();
50
+ const conditionalPipeline = new query_pipeline_1.QueryPipeline(conditionalConfig);
51
+ const shortResult = await conditionalPipeline.execute('hi', {});
52
+ console.log('Short query "hi":');
53
+ console.log(' Conditional executed:', shortResult.metadata.conditionalExecuted || false);
54
+ const longResult = await conditionalPipeline.execute('hello world', {});
55
+ console.log('Long query "hello world":');
56
+ console.log(' Conditional executed:', longResult.metadata.conditionalExecuted || false);
57
+ console.log('āœ“ Conditional execution works\n');
58
+ // Test 4: Stage Metrics
59
+ console.log('--- Test 4: Stage Metrics ---');
60
+ const metricsConfig = new query_pipeline_1.PipelineBuilder('metrics-test')
61
+ .addPreprocess(query_pipeline_1.preprocessStages.queryNormalization())
62
+ .addPreprocess(query_pipeline_1.preprocessStages.embedQuery(embeddingService))
63
+ .addPostProcess(query_pipeline_1.postProcessStages.scoreNormalization())
64
+ .build();
65
+ const metricsPipeline = new query_pipeline_1.QueryPipeline(metricsConfig);
66
+ const metricsResult = await metricsPipeline.execute('test query', {});
67
+ console.log('Stage execution times:');
68
+ for (const [stage, time] of Object.entries(metricsResult.metrics)) {
69
+ console.log(` ${stage}: ${time}ms`);
70
+ }
71
+ console.log('āœ“ Metrics collection works\n');
72
+ // Test 5: Diversity Reranking (without actual results)
73
+ console.log('--- Test 5: Diversity Reranking ---');
74
+ const mockResults = [
75
+ { id: '1', score: 0.9, embedding: [1, 0, 0] },
76
+ { id: '2', score: 0.85, embedding: [0.9, 0.1, 0] },
77
+ { id: '3', score: 0.8, embedding: [0, 1, 0] },
78
+ { id: '4', score: 0.75, embedding: [0, 0.9, 0.1] }
79
+ ];
80
+ const diversityConfig = new query_pipeline_1.PipelineBuilder('diversity-test')
81
+ .addRerank(query_pipeline_1.rerankStages.diversityRerank())
82
+ .build();
83
+ const diversityPipeline = new query_pipeline_1.QueryPipeline(diversityConfig);
84
+ const diversityResult = await diversityPipeline.execute('test', {});
85
+ diversityResult.results = mockResults;
86
+ // Manually execute diversity rerank
87
+ const rerankStage = diversityConfig.stages[0];
88
+ const ctx = {
89
+ query: 'test',
90
+ results: mockResults,
91
+ metadata: { diversityWeight: 0.5 },
92
+ metrics: {}
93
+ };
94
+ await rerankStage.execute(ctx);
95
+ console.log('Original order:', mockResults.map(r => r.id).join(', '));
96
+ console.log('Reranked order:', ctx.results?.map((r) => r.id).join(', '));
97
+ console.log('āœ“ Diversity reranking works\n');
98
+ // Test 6: Deduplication
99
+ console.log('--- Test 6: Deduplication ---');
100
+ const duplicateResults = [
101
+ { id: '1', entity_id: 'e1', score: 0.9, embedding: [1, 0, 0] },
102
+ { id: '2', entity_id: 'e1', score: 0.85, embedding: [1, 0, 0] }, // duplicate entity_id
103
+ { id: '3', entity_id: 'e2', score: 0.8, embedding: [0.99, 0.01, 0] }, // similar embedding
104
+ { id: '4', entity_id: 'e3', score: 0.75, embedding: [0, 1, 0] }
105
+ ];
106
+ const dedupConfig = new query_pipeline_1.PipelineBuilder('dedup-test')
107
+ .addPostProcess(query_pipeline_1.postProcessStages.deduplication())
108
+ .build();
109
+ const dedupPipeline = new query_pipeline_1.QueryPipeline(dedupConfig);
110
+ const dedupCtx = {
111
+ query: 'test',
112
+ results: duplicateResults,
113
+ metadata: { dedupThreshold: 0.95 },
114
+ metrics: {}
115
+ };
116
+ await dedupConfig.stages[0].execute(dedupCtx);
117
+ console.log('Before dedup:', duplicateResults.length, 'results');
118
+ console.log('After dedup:', dedupCtx.results?.length, 'results');
119
+ console.log('Removed:', duplicateResults.length - (dedupCtx.results?.length || 0), 'duplicates');
120
+ console.log('āœ“ Deduplication works\n');
121
+ // Test 7: Score Normalization
122
+ console.log('--- Test 7: Score Normalization ---');
123
+ const unnormalizedResults = [
124
+ { id: '1', score: 100 },
125
+ { id: '2', score: 75 },
126
+ { id: '3', score: 50 },
127
+ { id: '4', score: 25 }
128
+ ];
129
+ const normalizeConfig = new query_pipeline_1.PipelineBuilder('normalize-test')
130
+ .addPostProcess(query_pipeline_1.postProcessStages.scoreNormalization())
131
+ .build();
132
+ const normalizeCtx = {
133
+ query: 'test',
134
+ results: [...unnormalizedResults],
135
+ metadata: {},
136
+ metrics: {}
137
+ };
138
+ await normalizeConfig.stages[0].execute(normalizeCtx);
139
+ console.log('Original scores:', unnormalizedResults.map(r => r.score).join(', '));
140
+ console.log('Normalized scores:', normalizeCtx.results?.map((r) => r.score.toFixed(2)).join(', '));
141
+ console.log('āœ“ Score normalization works\n');
142
+ console.log('āœ“ All pipeline tests completed successfully!');
143
+ }
144
+ catch (error) {
145
+ console.error('Test failed:', error);
146
+ }
147
+ finally {
148
+ db.close();
149
+ }
150
+ }
151
+ testQueryPipeline();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozo-memory",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "mcpName": "io.github.tobs-code/cozo-memory",
5
5
  "description": "Local-first persistent memory system for AI agents with hybrid search, graph reasoning, and MCP integration",
6
6
  "main": "dist/index.js",