cozo-memory 1.1.6 → 1.1.8

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,222 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const cozo_node_1 = require("cozo-node");
4
+ const memory_activation_1 = require("./memory-activation");
5
+ async function testMemoryActivation() {
6
+ console.log('=== Memory Activation Service Test ===\n');
7
+ const db = new cozo_node_1.CozoDb();
8
+ const activationService = new memory_activation_1.MemoryActivationService(db, {
9
+ retentionThreshold: 0.1,
10
+ initialStrength: 1.0,
11
+ strengthIncrement: 1.0,
12
+ timeUnit: 'days'
13
+ });
14
+ try {
15
+ // Setup: Create test entity and observations
16
+ console.log('--- Setup: Creating test data ---');
17
+ const entityId = 'test-entity-1';
18
+ const now = Date.now();
19
+ const oneDayAgo = now - (24 * 60 * 60 * 1000);
20
+ const threeDaysAgo = now - (3 * 24 * 60 * 60 * 1000);
21
+ const sevenDaysAgo = now - (7 * 24 * 60 * 60 * 1000);
22
+ const thirtyDaysAgo = now - (30 * 24 * 60 * 60 * 1000);
23
+ // Create entity relation
24
+ await db.run(`
25
+ :create entity {
26
+ id: String,
27
+ name: String,
28
+ type: String,
29
+ =>
30
+ metadata: String
31
+ }
32
+ `);
33
+ await db.run(`
34
+ ?[id, name, type, metadata] <- [
35
+ [$id, 'Test Entity', 'test', 'test entity']
36
+ ]
37
+ :put entity {id, name, type => metadata}
38
+ `, { id: entityId });
39
+ // Create observation relation
40
+ await db.run(`
41
+ :create observation {
42
+ id: String,
43
+ entity_id: String,
44
+ text: String,
45
+ =>
46
+ metadata: Any,
47
+ created_at: Int
48
+ }
49
+ `);
50
+ // Create observations with different access patterns
51
+ const observations = [
52
+ {
53
+ id: 'obs-1',
54
+ text: 'Recently accessed, high strength',
55
+ metadata: { access_count: 5, last_access_time: oneDayAgo },
56
+ created_at: thirtyDaysAgo
57
+ },
58
+ {
59
+ id: 'obs-2',
60
+ text: 'Moderately accessed',
61
+ metadata: { access_count: 2, last_access_time: threeDaysAgo },
62
+ created_at: thirtyDaysAgo
63
+ },
64
+ {
65
+ id: 'obs-3',
66
+ text: 'Rarely accessed, weak memory',
67
+ metadata: { access_count: 1, last_access_time: sevenDaysAgo },
68
+ created_at: thirtyDaysAgo
69
+ },
70
+ {
71
+ id: 'obs-4',
72
+ text: 'Never accessed, very weak',
73
+ metadata: { access_count: 0, last_access_time: thirtyDaysAgo },
74
+ created_at: thirtyDaysAgo
75
+ },
76
+ {
77
+ id: 'obs-5',
78
+ text: 'Frequently accessed, very strong',
79
+ metadata: { access_count: 10, last_access_time: now },
80
+ created_at: thirtyDaysAgo
81
+ }
82
+ ];
83
+ for (const obs of observations) {
84
+ await db.run(`
85
+ ?[id, entity_id, text, metadata, created_at] <- [
86
+ [$id, $entity_id, $text, $metadata, $created_at]
87
+ ]
88
+ :put observation {id, entity_id, text => metadata, created_at}
89
+ `, {
90
+ id: obs.id,
91
+ entity_id: entityId,
92
+ text: obs.text,
93
+ metadata: obs.metadata,
94
+ created_at: obs.created_at
95
+ });
96
+ }
97
+ console.log(`✓ Created ${observations.length} test observations\n`);
98
+ // Test 1: Calculate activation scores
99
+ console.log('--- Test 1: Calculate Activation Scores ---');
100
+ const scores = await activationService.calculateActivationScores(entityId);
101
+ console.log('Activation Scores (sorted by activation):');
102
+ for (const score of scores) {
103
+ console.log(` ${score.observationId}:`);
104
+ console.log(` Activation: ${score.activation.toFixed(4)}`);
105
+ console.log(` Strength: ${score.strength.toFixed(2)}`);
106
+ console.log(` Time since access: ${score.timeSinceAccess.toFixed(2)} days`);
107
+ console.log(` Access count: ${score.accessCount}`);
108
+ console.log(` Should retain: ${score.shouldRetain}`);
109
+ console.log(` Reason: ${score.reason}`);
110
+ }
111
+ console.log();
112
+ // Test 2: Get activation statistics
113
+ console.log('--- Test 2: Activation Statistics ---');
114
+ const stats = await activationService.getActivationStats(entityId);
115
+ console.log(`Total observations: ${stats.totalObservations}`);
116
+ console.log(`Average activation: ${stats.averageActivation.toFixed(4)}`);
117
+ console.log(`Average strength: ${stats.averageStrength.toFixed(2)}`);
118
+ console.log(`Below threshold: ${stats.belowThreshold}`);
119
+ console.log(`Above threshold: ${stats.aboveThreshold}`);
120
+ console.log('Distribution:');
121
+ console.log(` Very weak (<0.1): ${stats.distribution.veryWeak}`);
122
+ console.log(` Weak (0.1-0.3): ${stats.distribution.weak}`);
123
+ console.log(` Moderate (0.3-0.6): ${stats.distribution.moderate}`);
124
+ console.log(` Strong (0.6-0.9): ${stats.distribution.strong}`);
125
+ console.log(` Very strong (>0.9): ${stats.distribution.veryStrong}`);
126
+ console.log();
127
+ // Test 3: Record access (simulates recall)
128
+ console.log('--- Test 3: Record Access (Simulate Recall) ---');
129
+ const weakObsId = 'obs-3';
130
+ console.log(`Recording access for ${weakObsId}...`);
131
+ const beforeAccess = scores.find(s => s.observationId === weakObsId);
132
+ console.log(`Before: activation=${beforeAccess?.activation.toFixed(4)}, strength=${beforeAccess?.strength.toFixed(2)}`);
133
+ await activationService.recordAccess(weakObsId);
134
+ const afterScores = await activationService.calculateActivationScores(entityId);
135
+ const afterAccess = afterScores.find(s => s.observationId === weakObsId);
136
+ console.log(`After: activation=${afterAccess?.activation.toFixed(4)}, strength=${afterAccess?.strength.toFixed(2)}`);
137
+ console.log(`✓ Strength increased from ${beforeAccess?.strength.toFixed(2)} to ${afterAccess?.strength.toFixed(2)}`);
138
+ console.log();
139
+ // Test 4: Prune weak memories (dry run)
140
+ console.log('--- Test 4: Prune Weak Memories (Dry Run) ---');
141
+ const pruneResult = await activationService.pruneWeakMemories(true, entityId);
142
+ console.log(`Candidates for deletion: ${pruneResult.candidates.length}`);
143
+ for (const candidate of pruneResult.candidates) {
144
+ console.log(` ${candidate.observationId}: ${candidate.reason}`);
145
+ }
146
+ console.log(`Would preserve: ${pruneResult.preserved} observations`);
147
+ console.log();
148
+ // Test 5: Activation decay over time
149
+ console.log('--- Test 5: Activation Decay Simulation ---');
150
+ console.log('Simulating activation decay for obs-2 over time:');
151
+ const testObs = scores.find(s => s.observationId === 'obs-2');
152
+ if (testObs) {
153
+ const strength = testObs.strength;
154
+ const timePoints = [0, 1, 3, 7, 14, 30, 60, 90];
155
+ console.log(`Strength: ${strength.toFixed(2)}`);
156
+ console.log('Time (days) | Activation | Retained?');
157
+ console.log('------------|------------|----------');
158
+ for (const days of timePoints) {
159
+ const activation = Math.exp(-days / strength);
160
+ const retained = activation >= 0.1;
161
+ console.log(`${days.toString().padStart(11)} | ${activation.toFixed(4).padStart(10)} | ${retained ? 'Yes' : 'No'}`);
162
+ }
163
+ }
164
+ console.log();
165
+ // Test 6: Strength progression with repeated recalls
166
+ console.log('--- Test 6: Strength Progression with Repeated Recalls ---');
167
+ console.log('Simulating repeated recalls for a new observation:');
168
+ console.log('Recalls | Strength | Activation (7 days) | Retained?');
169
+ console.log('--------|----------|---------------------|----------');
170
+ for (let recalls = 0; recalls <= 10; recalls++) {
171
+ const strength = 1.0 + recalls * 1.0; // initialStrength + recalls * strengthIncrement
172
+ const activation = Math.exp(-7 / strength); // 7 days later
173
+ const retained = activation >= 0.1;
174
+ console.log(`${recalls.toString().padStart(7)} | ${strength.toFixed(2).padStart(8)} | ${activation.toFixed(4).padStart(19)} | ${retained ? 'Yes' : 'No'}`);
175
+ }
176
+ console.log();
177
+ // Test 7: Compare with different threshold values
178
+ console.log('--- Test 7: Impact of Different Retention Thresholds ---');
179
+ const thresholds = [0.05, 0.1, 0.2, 0.3];
180
+ console.log('Threshold | Retained | Deleted');
181
+ console.log('----------|----------|--------');
182
+ for (const threshold of thresholds) {
183
+ const testService = new memory_activation_1.MemoryActivationService(db, {
184
+ retentionThreshold: threshold,
185
+ initialStrength: 1.0,
186
+ strengthIncrement: 1.0,
187
+ timeUnit: 'days'
188
+ });
189
+ const testScores = await testService.calculateActivationScores(entityId);
190
+ const retained = testScores.filter(s => s.shouldRetain).length;
191
+ const deleted = testScores.filter(s => !s.shouldRetain).length;
192
+ console.log(`${threshold.toFixed(2).padStart(9)} | ${retained.toString().padStart(8)} | ${deleted.toString().padStart(7)}`);
193
+ }
194
+ console.log();
195
+ // Test 8: Verify Ebbinghaus curve shape
196
+ console.log('--- Test 8: Ebbinghaus Forgetting Curve Verification ---');
197
+ console.log('Classic Ebbinghaus curve: steep initial decline, then gradual leveling');
198
+ console.log('Time (days) | Retention (S=1) | Retention (S=5) | Retention (S=10)');
199
+ console.log('------------|-----------------|-----------------|------------------');
200
+ const curveTimePoints = [0, 0.5, 1, 2, 5, 10, 20, 30];
201
+ for (const days of curveTimePoints) {
202
+ const r1 = Math.exp(-days / 1);
203
+ const r5 = Math.exp(-days / 5);
204
+ const r10 = Math.exp(-days / 10);
205
+ console.log(`${days.toString().padStart(11)} | ${r1.toFixed(4).padStart(15)} | ${r5.toFixed(4).padStart(15)} | ${r10.toFixed(4).padStart(16)}`);
206
+ }
207
+ console.log();
208
+ console.log('✓ All memory activation tests completed successfully!');
209
+ console.log('\nKey Insights:');
210
+ console.log('- Higher strength (S) = slower forgetting');
211
+ console.log('- Each recall increases strength, making memories more durable');
212
+ console.log('- Activation threshold determines retention policy');
213
+ console.log('- System naturally implements spaced repetition via activation decay');
214
+ }
215
+ catch (error) {
216
+ console.error('Test failed:', error);
217
+ }
218
+ finally {
219
+ db.close();
220
+ }
221
+ }
222
+ testMemoryActivation();
@@ -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();