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.
@@ -0,0 +1,482 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TemporalPatternDetectionService = exports.PatternType = void 0;
4
+ const uuid_1 = require("uuid");
5
+ var PatternType;
6
+ (function (PatternType) {
7
+ PatternType["RECURRING_EVENT"] = "recurring_event";
8
+ PatternType["CYCLICAL_RELATIONSHIP"] = "cyclical_relationship";
9
+ PatternType["TEMPORAL_CORRELATION"] = "temporal_correlation";
10
+ PatternType["SEASONAL_TREND"] = "seasonal_trend";
11
+ PatternType["ANOMALY"] = "anomaly";
12
+ })(PatternType || (exports.PatternType = PatternType = {}));
13
+ class TemporalPatternDetectionService {
14
+ db;
15
+ embeddings;
16
+ config;
17
+ constructor(db, embeddings, config = {}) {
18
+ this.db = db;
19
+ this.embeddings = embeddings;
20
+ this.config = {
21
+ min_occurrences: config.min_occurrences ?? 3,
22
+ min_confidence: config.min_confidence ?? 0.6,
23
+ time_window_days: config.time_window_days ?? 365,
24
+ similarity_threshold: config.similarity_threshold ?? 0.75,
25
+ seasonal_buckets: config.seasonal_buckets ?? 12
26
+ };
27
+ }
28
+ async detectPatterns(entityId) {
29
+ const patterns = [];
30
+ // Get entity name (without time travel since test doesn't use Validity)
31
+ let entityName = 'Unknown';
32
+ try {
33
+ const entityResult = await this.db.run(`
34
+ ?[name] := *entity{id, name, @ "NOW"}, id = $entity_id
35
+ `, { entity_id: entityId });
36
+ entityName = entityResult.rows.length > 0 ? entityResult.rows[0][0] : 'Unknown';
37
+ }
38
+ catch (error) {
39
+ // Fallback if Validity is not used
40
+ const entityResult = await this.db.run(`
41
+ ?[name] := *entity{id, name}, id = $entity_id
42
+ `, { entity_id: entityId });
43
+ entityName = entityResult.rows.length > 0 ? entityResult.rows[0][0] : 'Unknown';
44
+ }
45
+ // 1. Detect recurring events
46
+ const recurringPatterns = await this.detectRecurringEvents(entityId, entityName);
47
+ patterns.push(...recurringPatterns);
48
+ // 2. Detect cyclical relationships
49
+ const cyclicalPatterns = await this.detectCyclicalRelationships(entityId, entityName);
50
+ patterns.push(...cyclicalPatterns);
51
+ // 3. Detect temporal correlations
52
+ const correlationPatterns = await this.detectTemporalCorrelations(entityId, entityName);
53
+ patterns.push(...correlationPatterns);
54
+ // 4. Detect seasonal trends
55
+ const seasonalPatterns = await this.detectSeasonalTrends(entityId, entityName);
56
+ patterns.push(...seasonalPatterns);
57
+ return patterns;
58
+ }
59
+ async detectRecurringEvents(entityId, entityName) {
60
+ const patterns = [];
61
+ const timeWindowMicros = this.config.time_window_days * 24 * 60 * 60 * 1000 * 1000;
62
+ const nowMicros = Date.now() * 1000;
63
+ const startTime = nowMicros - timeWindowMicros;
64
+ // Get all observations for this entity within time window
65
+ const obsResult = await this.db.run(`
66
+ ?[id, text, embedding, created_at] :=
67
+ *observation{id, entity_id, text, embedding, created_at},
68
+ entity_id = $entity_id,
69
+ to_int(created_at) >= $start_time
70
+ `, { entity_id: entityId, start_time: startTime });
71
+ if (obsResult.rows.length < this.config.min_occurrences) {
72
+ return patterns;
73
+ }
74
+ // Group similar observations using embeddings
75
+ const observations = obsResult.rows.map((row) => {
76
+ const createdAt = row[3];
77
+ const timestamp = Array.isArray(createdAt) ? createdAt[0] : createdAt;
78
+ return {
79
+ id: row[0],
80
+ text: row[1],
81
+ embedding: row[2],
82
+ timestamp
83
+ };
84
+ });
85
+ // Cluster similar observations
86
+ const clusters = this.clusterObservations(observations);
87
+ // Analyze each cluster for recurring patterns
88
+ for (const cluster of clusters) {
89
+ if (cluster.length < this.config.min_occurrences)
90
+ continue;
91
+ // Sort by timestamp
92
+ cluster.sort((a, b) => a.timestamp - b.timestamp);
93
+ // Calculate intervals between occurrences
94
+ const intervals = [];
95
+ for (let i = 1; i < cluster.length; i++) {
96
+ const intervalMs = (cluster[i].timestamp - cluster[i - 1].timestamp) / 1000;
97
+ const intervalDays = intervalMs / (24 * 60 * 60 * 1000);
98
+ intervals.push(intervalDays);
99
+ }
100
+ // Check if intervals are relatively consistent (recurring pattern)
101
+ const avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length;
102
+ const variance = intervals.reduce((sum, interval) => sum + Math.pow(interval - avgInterval, 2), 0) / intervals.length;
103
+ const stdDev = Math.sqrt(variance);
104
+ const coefficientOfVariation = stdDev / avgInterval;
105
+ // If coefficient of variation is low, it's a recurring pattern
106
+ if (coefficientOfVariation < 0.3) {
107
+ const confidence = Math.max(0, 1 - coefficientOfVariation);
108
+ if (confidence >= this.config.min_confidence) {
109
+ patterns.push({
110
+ id: (0, uuid_1.v4)(),
111
+ pattern_type: PatternType.RECURRING_EVENT,
112
+ entity_id: entityId,
113
+ entity_name: entityName,
114
+ description: `Recurring event: "${cluster[0].text}" occurs approximately every ${avgInterval.toFixed(0)} days`,
115
+ frequency: cluster.length,
116
+ confidence,
117
+ first_occurrence: cluster[0].timestamp,
118
+ last_occurrence: cluster[cluster.length - 1].timestamp,
119
+ interval_days: avgInterval,
120
+ occurrences: cluster.map(obs => ({
121
+ timestamp: obs.timestamp,
122
+ observation_id: obs.id,
123
+ text: obs.text
124
+ })),
125
+ metadata: {
126
+ avg_interval_days: avgInterval,
127
+ std_dev_days: stdDev,
128
+ coefficient_of_variation: coefficientOfVariation
129
+ }
130
+ });
131
+ }
132
+ }
133
+ }
134
+ return patterns;
135
+ }
136
+ async detectCyclicalRelationships(entityId, entityName) {
137
+ const patterns = [];
138
+ // Find cycles in the relationship graph starting from this entity
139
+ // A cycle is: A -> B -> ... -> A
140
+ const cycleResult = await this.db.run(`
141
+ cycle[from_id, to_id, path] :=
142
+ *relationship{from_id, to_id},
143
+ from_id = $entity_id,
144
+ path = [from_id, to_id]
145
+
146
+ cycle[from_id, to_id, path] :=
147
+ cycle[from_id, mid, prev_path],
148
+ *relationship{from_id: mid, to_id},
149
+ to_id != from_id,
150
+ !is_in(to_id, prev_path),
151
+ length(prev_path) < 10,
152
+ path = append(prev_path, to_id)
153
+
154
+ ?[from_id, to_id, path] :=
155
+ cycle[from_id, to_id, path],
156
+ to_id = $entity_id,
157
+ length(path) >= 3
158
+ `, { entity_id: entityId });
159
+ for (const row of cycleResult.rows) {
160
+ const path = row[2];
161
+ const cycleLength = path.length;
162
+ // Get relationship types in the cycle
163
+ const relationTypes = [];
164
+ for (let i = 0; i < path.length - 1; i++) {
165
+ const relResult = await this.db.run(`
166
+ ?[relation_type] :=
167
+ *relationship{from_id, to_id, relation_type},
168
+ from_id = $from_id,
169
+ to_id = $to_id
170
+ `, { from_id: path[i], to_id: path[i + 1] });
171
+ if (relResult.rows.length > 0) {
172
+ relationTypes.push(relResult.rows[0][0]);
173
+ }
174
+ }
175
+ // Add closing relationship
176
+ const closingRelResult = await this.db.run(`
177
+ ?[relation_type] :=
178
+ *relationship{from_id, to_id, relation_type},
179
+ from_id = $from_id,
180
+ to_id = $to_id
181
+ `, { from_id: path[path.length - 1], to_id: path[0] });
182
+ if (closingRelResult.rows.length > 0) {
183
+ relationTypes.push(closingRelResult.rows[0][0]);
184
+ }
185
+ const confidence = Math.min(1.0, 0.7 + (0.1 * cycleLength));
186
+ if (confidence >= this.config.min_confidence) {
187
+ patterns.push({
188
+ id: (0, uuid_1.v4)(),
189
+ pattern_type: PatternType.CYCLICAL_RELATIONSHIP,
190
+ entity_id: entityId,
191
+ entity_name: entityName,
192
+ description: `Cyclical relationship detected: ${relationTypes.join(' → ')}`,
193
+ frequency: 1,
194
+ confidence,
195
+ first_occurrence: Date.now() * 1000,
196
+ last_occurrence: Date.now() * 1000,
197
+ occurrences: [],
198
+ metadata: {
199
+ cycle_path: path,
200
+ cycle_length: cycleLength,
201
+ relation_types: relationTypes
202
+ }
203
+ });
204
+ }
205
+ }
206
+ return patterns;
207
+ }
208
+ async detectTemporalCorrelations(entityId, entityName) {
209
+ const patterns = [];
210
+ const timeWindowMicros = this.config.time_window_days * 24 * 60 * 60 * 1000 * 1000;
211
+ const nowMicros = Date.now() * 1000;
212
+ const startTime = nowMicros - timeWindowMicros;
213
+ // Get all observations
214
+ const obsResult = await this.db.run(`
215
+ ?[id, text, embedding, created_at] :=
216
+ *observation{id, entity_id, text, embedding, created_at},
217
+ entity_id = $entity_id,
218
+ to_int(created_at) >= $start_time
219
+ `, { entity_id: entityId, start_time: startTime });
220
+ if (obsResult.rows.length < this.config.min_occurrences * 2) {
221
+ return patterns;
222
+ }
223
+ const observations = obsResult.rows.map((row) => {
224
+ const createdAt = row[3];
225
+ const timestamp = Array.isArray(createdAt) ? createdAt[0] : createdAt;
226
+ return {
227
+ id: row[0],
228
+ text: row[1],
229
+ embedding: row[2],
230
+ timestamp
231
+ };
232
+ });
233
+ // Find pairs of different observation types that occur close together
234
+ const correlationWindow = 2 * 24 * 60 * 60 * 1000 * 1000; // 2 days in microseconds
235
+ const correlations = new Map();
236
+ for (let i = 0; i < observations.length; i++) {
237
+ for (let j = i + 1; j < observations.length; j++) {
238
+ const timeDiff = Math.abs(observations[j].timestamp - observations[i].timestamp);
239
+ if (timeDiff <= correlationWindow) {
240
+ const similarity = this.cosineSimilarity(observations[i].embedding, observations[j].embedding);
241
+ // Only correlate different types of events (low similarity)
242
+ if (similarity < 0.5) {
243
+ const key = [observations[i].text, observations[j].text].sort().join('|||');
244
+ if (!correlations.has(key)) {
245
+ correlations.set(key, []);
246
+ }
247
+ correlations.get(key).push({
248
+ obs1: observations[i],
249
+ obs2: observations[j]
250
+ });
251
+ }
252
+ }
253
+ }
254
+ }
255
+ // Analyze correlations
256
+ for (const [key, pairs] of correlations.entries()) {
257
+ if (pairs.length >= this.config.min_occurrences) {
258
+ const [text1, text2] = key.split('|||');
259
+ const confidence = Math.min(1.0, pairs.length / (this.config.min_occurrences * 2));
260
+ if (confidence >= this.config.min_confidence) {
261
+ patterns.push({
262
+ id: (0, uuid_1.v4)(),
263
+ pattern_type: PatternType.TEMPORAL_CORRELATION,
264
+ entity_id: entityId,
265
+ entity_name: entityName,
266
+ description: `Temporal correlation: "${text1}" often occurs near "${text2}"`,
267
+ frequency: pairs.length,
268
+ confidence,
269
+ first_occurrence: Math.min(...pairs.map(p => Math.min(p.obs1.timestamp, p.obs2.timestamp))),
270
+ last_occurrence: Math.max(...pairs.map(p => Math.max(p.obs1.timestamp, p.obs2.timestamp))),
271
+ occurrences: pairs.flatMap(p => [
272
+ { timestamp: p.obs1.timestamp, observation_id: p.obs1.id, text: p.obs1.text },
273
+ { timestamp: p.obs2.timestamp, observation_id: p.obs2.id, text: p.obs2.text }
274
+ ]),
275
+ metadata: {
276
+ event_pair: [text1, text2],
277
+ correlation_window_days: correlationWindow / (24 * 60 * 60 * 1000 * 1000)
278
+ }
279
+ });
280
+ }
281
+ }
282
+ }
283
+ return patterns;
284
+ }
285
+ async detectSeasonalTrends(entityId, entityName) {
286
+ const patterns = [];
287
+ const timeWindowMicros = this.config.time_window_days * 24 * 60 * 60 * 1000 * 1000;
288
+ const nowMicros = Date.now() * 1000;
289
+ const startTime = nowMicros - timeWindowMicros;
290
+ // Get all observations
291
+ const obsResult = await this.db.run(`
292
+ ?[id, text, created_at] :=
293
+ *observation{id, entity_id, text, created_at},
294
+ entity_id = $entity_id,
295
+ to_int(created_at) >= $start_time
296
+ `, { entity_id: entityId, start_time: startTime });
297
+ if (obsResult.rows.length < this.config.min_occurrences) {
298
+ return patterns;
299
+ }
300
+ // Group observations by seasonal bucket (month or quarter)
301
+ const buckets = new Map();
302
+ for (const row of obsResult.rows) {
303
+ const createdAt = row[2];
304
+ // Handle both Validity tuple [timestamp, valid] and plain timestamp
305
+ const timestamp = Array.isArray(createdAt) ? createdAt[0] : createdAt;
306
+ const date = new Date(timestamp / 1000); // Convert microseconds to milliseconds
307
+ const bucket = date.getMonth(); // 0-11 for monthly buckets
308
+ if (!buckets.has(bucket)) {
309
+ buckets.set(bucket, []);
310
+ }
311
+ buckets.get(bucket).push({
312
+ id: row[0],
313
+ text: row[1],
314
+ timestamp
315
+ });
316
+ }
317
+ // Calculate average activity per bucket
318
+ const avgActivity = obsResult.rows.length / this.config.seasonal_buckets;
319
+ // Find buckets with significantly higher activity
320
+ for (const [bucket, observations] of buckets.entries()) {
321
+ const activityRatio = observations.length / avgActivity;
322
+ // If activity is 2x or more than average, it's a seasonal trend
323
+ if (activityRatio >= 2.0 && observations.length >= this.config.min_occurrences) {
324
+ const confidence = Math.min(1.0, activityRatio / 3);
325
+ if (confidence >= this.config.min_confidence) {
326
+ const monthName = new Date(2024, bucket, 1).toLocaleString('default', { month: 'long' });
327
+ patterns.push({
328
+ id: (0, uuid_1.v4)(),
329
+ pattern_type: PatternType.SEASONAL_TREND,
330
+ entity_id: entityId,
331
+ entity_name: entityName,
332
+ description: `Seasonal trend: Increased activity in ${monthName} (${activityRatio.toFixed(1)}x average)`,
333
+ frequency: observations.length,
334
+ confidence,
335
+ first_occurrence: Math.min(...observations.map(o => o.timestamp)),
336
+ last_occurrence: Math.max(...observations.map(o => o.timestamp)),
337
+ occurrences: observations.map(obs => ({
338
+ timestamp: obs.timestamp,
339
+ observation_id: obs.id,
340
+ text: obs.text
341
+ })),
342
+ metadata: {
343
+ seasonal_bucket: bucket,
344
+ bucket_name: monthName,
345
+ activity_ratio: activityRatio,
346
+ avg_activity: avgActivity
347
+ }
348
+ });
349
+ }
350
+ }
351
+ }
352
+ return patterns;
353
+ }
354
+ clusterObservations(observations) {
355
+ const clusters = [];
356
+ const used = new Set();
357
+ for (let i = 0; i < observations.length; i++) {
358
+ if (used.has(i))
359
+ continue;
360
+ const cluster = [observations[i]];
361
+ used.add(i);
362
+ for (let j = i + 1; j < observations.length; j++) {
363
+ if (used.has(j))
364
+ continue;
365
+ const similarity = this.cosineSimilarity(observations[i].embedding, observations[j].embedding);
366
+ if (similarity >= this.config.similarity_threshold) {
367
+ cluster.push(observations[j]);
368
+ used.add(j);
369
+ }
370
+ }
371
+ clusters.push(cluster);
372
+ }
373
+ return clusters;
374
+ }
375
+ cosineSimilarity(a, b) {
376
+ if (a.length !== b.length)
377
+ return 0;
378
+ let dotProduct = 0;
379
+ let normA = 0;
380
+ let normB = 0;
381
+ for (let i = 0; i < a.length; i++) {
382
+ dotProduct += a[i] * b[i];
383
+ normA += a[i] * a[i];
384
+ normB += b[i] * b[i];
385
+ }
386
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
387
+ return denominator === 0 ? 0 : dotProduct / denominator;
388
+ }
389
+ async storePattern(pattern) {
390
+ // Create temporal_pattern relation if it doesn't exist (only once)
391
+ try {
392
+ await this.db.run(`
393
+ :create temporal_pattern {
394
+ id: String =>
395
+ pattern_type: String,
396
+ entity_id: String,
397
+ entity_name: String,
398
+ description: String,
399
+ frequency: Int,
400
+ confidence: Float,
401
+ first_occurrence: Int,
402
+ last_occurrence: Int,
403
+ interval_days: Float?,
404
+ metadata: Json
405
+ }
406
+ `);
407
+ }
408
+ catch (error) {
409
+ // Relation already exists, that's fine - continue with insert
410
+ if (!error.display?.includes('already exists') && !error.display?.includes('conflicts with an existing')) {
411
+ throw error;
412
+ }
413
+ }
414
+ await this.db.run(`
415
+ ?[id, pattern_type, entity_id, entity_name, description, frequency, confidence, first_occurrence, last_occurrence, interval_days, metadata] :=
416
+ id = $id,
417
+ pattern_type = $pattern_type,
418
+ entity_id = $entity_id,
419
+ entity_name = $entity_name,
420
+ description = $description,
421
+ frequency = $frequency,
422
+ confidence = $confidence,
423
+ first_occurrence = $first_occurrence,
424
+ last_occurrence = $last_occurrence,
425
+ interval_days = $interval_days,
426
+ metadata = $metadata
427
+
428
+ :put temporal_pattern {
429
+ id => pattern_type, entity_id, entity_name, description, frequency, confidence, first_occurrence, last_occurrence, interval_days, metadata
430
+ }
431
+ `, {
432
+ id: pattern.id,
433
+ pattern_type: pattern.pattern_type,
434
+ entity_id: pattern.entity_id,
435
+ entity_name: pattern.entity_name,
436
+ description: pattern.description,
437
+ frequency: pattern.frequency,
438
+ confidence: pattern.confidence,
439
+ first_occurrence: pattern.first_occurrence,
440
+ last_occurrence: pattern.last_occurrence,
441
+ interval_days: pattern.interval_days ?? null,
442
+ metadata: pattern.metadata ?? {}
443
+ });
444
+ }
445
+ async getStoredPatterns(entityId, patternType) {
446
+ let query = `
447
+ ?[id, pattern_type, entity_id, entity_name, description, frequency, confidence, first_occurrence, last_occurrence, interval_days, metadata] :=
448
+ *temporal_pattern{id, pattern_type, entity_id, entity_name, description, frequency, confidence, first_occurrence, last_occurrence, interval_days, metadata}
449
+ `;
450
+ const params = {};
451
+ if (entityId) {
452
+ query += `, entity_id = $entity_id`;
453
+ params.entity_id = entityId;
454
+ }
455
+ if (patternType) {
456
+ query += `, pattern_type = $pattern_type`;
457
+ params.pattern_type = patternType;
458
+ }
459
+ const result = await this.db.run(query, params);
460
+ return result.rows.map((row) => ({
461
+ id: row[0],
462
+ pattern_type: row[1],
463
+ entity_id: row[2],
464
+ entity_name: row[3],
465
+ description: row[4],
466
+ frequency: row[5],
467
+ confidence: row[6],
468
+ first_occurrence: row[7],
469
+ last_occurrence: row[8],
470
+ interval_days: row[9],
471
+ occurrences: [],
472
+ metadata: row[10]
473
+ }));
474
+ }
475
+ updateConfig(config) {
476
+ this.config = { ...this.config, ...config };
477
+ }
478
+ getConfig() {
479
+ return { ...this.config };
480
+ }
481
+ }
482
+ exports.TemporalPatternDetectionService = TemporalPatternDetectionService;
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ /**
3
+ * Test: Adaptive Query Fusion with Dynamic Weights
4
+ *
5
+ * Tests query classification and adaptive weight selection
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ const cozo_node_1 = require("cozo-node");
42
+ const embedding_service_1 = require("./embedding-service");
43
+ const adaptive_query_fusion_1 = require("./adaptive-query-fusion");
44
+ const fs = __importStar(require("fs"));
45
+ const DB_PATH = 'test-adaptive-fusion.db';
46
+ async function testAdaptiveQueryFusion() {
47
+ // Clean up old database
48
+ if (fs.existsSync(DB_PATH)) {
49
+ try {
50
+ fs.unlinkSync(DB_PATH);
51
+ }
52
+ catch (e) { }
53
+ }
54
+ const db = new cozo_node_1.CozoDb('sqlite', DB_PATH);
55
+ const embeddingService = new embedding_service_1.EmbeddingService();
56
+ try {
57
+ console.log('=== Adaptive Query Fusion Tests ===\n');
58
+ const fusion = new adaptive_query_fusion_1.AdaptiveQueryFusion(db, embeddingService, {
59
+ enableLLM: false, // Disable LLM for testing (no Ollama required)
60
+ cacheClassifications: true,
61
+ maxCacheSize: 100
62
+ });
63
+ // Test cases with expected classifications
64
+ const testQueries = [
65
+ // FINDER queries (factual information seeking)
66
+ {
67
+ query: 'What is the capital of France?',
68
+ expectedIntent: adaptive_query_fusion_1.QueryIntent.FINDER,
69
+ description: 'Simple factual question'
70
+ },
71
+ {
72
+ query: 'When was the first iPhone released?',
73
+ expectedIntent: adaptive_query_fusion_1.QueryIntent.FINDER,
74
+ description: 'Factual query with temporal aspect'
75
+ },
76
+ {
77
+ query: 'Find me the latest news about AI developments',
78
+ expectedIntent: adaptive_query_fusion_1.QueryIntent.FINDER,
79
+ description: 'Information seeking with recency'
80
+ },
81
+ // EVALUATOR queries (comparison/evaluation)
82
+ {
83
+ query: 'Compare TypeScript vs JavaScript',
84
+ expectedIntent: adaptive_query_fusion_1.QueryIntent.EVALUATOR,
85
+ description: 'Direct comparison'
86
+ },
87
+ {
88
+ query: 'What are the pros and cons of microservices?',
89
+ expectedIntent: adaptive_query_fusion_1.QueryIntent.EVALUATOR,
90
+ description: 'Evaluation query'
91
+ },
92
+ {
93
+ query: 'Which database is better for my use case?',
94
+ expectedIntent: adaptive_query_fusion_1.QueryIntent.EVALUATOR,
95
+ description: 'Comparative evaluation'
96
+ },
97
+ // EXPLAINER queries (concept understanding)
98
+ {
99
+ query: 'How does machine learning work?',
100
+ expectedIntent: adaptive_query_fusion_1.QueryIntent.EXPLAINER,
101
+ description: 'Concept explanation'
102
+ },
103
+ {
104
+ query: 'Explain the concept of blockchain',
105
+ expectedIntent: adaptive_query_fusion_1.QueryIntent.EXPLAINER,
106
+ description: 'Direct explanation request'
107
+ },
108
+ {
109
+ query: 'Why is caching important in web development?',
110
+ expectedIntent: adaptive_query_fusion_1.QueryIntent.EXPLAINER,
111
+ description: 'Why question (explanation)'
112
+ },
113
+ // GENERATOR queries (content creation)
114
+ {
115
+ query: 'Write a Python function to calculate fibonacci',
116
+ expectedIntent: adaptive_query_fusion_1.QueryIntent.GENERATOR,
117
+ description: 'Code generation'
118
+ },
119
+ {
120
+ query: 'Create a marketing strategy for a startup',
121
+ expectedIntent: adaptive_query_fusion_1.QueryIntent.GENERATOR,
122
+ description: 'Content generation'
123
+ },
124
+ {
125
+ query: 'Suggest a project structure for a Node.js app',
126
+ expectedIntent: adaptive_query_fusion_1.QueryIntent.GENERATOR,
127
+ description: 'Suggestion/recommendation'
128
+ }
129
+ ];
130
+ console.log('--- Test 1: Query Classification ---\n');
131
+ for (const testCase of testQueries) {
132
+ const classification = await fusion.classifyQuery(testCase.query);
133
+ const match = classification.intent === testCase.expectedIntent ? '✓' : '✗';
134
+ console.log(`${match} "${testCase.query}"`);
135
+ console.log(` Description: ${testCase.description}`);
136
+ console.log(` Intent: ${classification.intent} (expected: ${testCase.expectedIntent})`);
137
+ console.log(` Complexity: ${classification.complexity}`);
138
+ console.log(` Confidence: ${(classification.confidence * 100).toFixed(0)}%`);
139
+ console.log(` Method: ${classification.method}`);
140
+ console.log();
141
+ }
142
+ console.log('--- Test 2: Adaptive Weights ---\n');
143
+ const weightTestQueries = [
144
+ 'What is TypeScript?', // FINDER + SIMPLE
145
+ 'Compare React vs Vue', // EVALUATOR + MODERATE
146
+ 'Explain how async/await works', // EXPLAINER + MODERATE
147
+ 'Create a REST API' // GENERATOR + MODERATE
148
+ ];
149
+ for (const query of weightTestQueries) {
150
+ const weights = await fusion.getAdaptiveWeights(query);
151
+ const classification = await fusion.getClassificationDetails(query);
152
+ console.log(`Query: "${query}"`);
153
+ console.log(`Intent: ${classification.intent}, Complexity: ${classification.complexity}`);
154
+ console.log(`Weights:`);
155
+ console.log(` Vector: ${(weights.vector * 100).toFixed(0)}%`);
156
+ console.log(` Sparse: ${(weights.sparse * 100).toFixed(0)}%`);
157
+ console.log(` FTS: ${(weights.fts * 100).toFixed(0)}%`);
158
+ console.log(` Graph: ${(weights.graph * 100).toFixed(0)}%`);
159
+ console.log();
160
+ }
161
+ console.log('--- Test 3: Caching ---\n');
162
+ const cacheTestQuery = 'What is the meaning of life?';
163
+ // First call (not cached)
164
+ console.log(`First call: "${cacheTestQuery}"`);
165
+ const start1 = Date.now();
166
+ const result1 = await fusion.classifyQuery(cacheTestQuery);
167
+ const time1 = Date.now() - start1;
168
+ console.log(`Time: ${time1}ms`);
169
+ // Second call (cached)
170
+ console.log(`\nSecond call (should be cached): "${cacheTestQuery}"`);
171
+ const start2 = Date.now();
172
+ const result2 = await fusion.classifyQuery(cacheTestQuery);
173
+ const time2 = Date.now() - start2;
174
+ console.log(`Time: ${time2}ms`);
175
+ console.log(`Speedup: ${(time1 / time2).toFixed(1)}x faster`);
176
+ const cacheStats = fusion.getCacheStats();
177
+ console.log(`\nCache stats: ${cacheStats.size}/${cacheStats.maxSize} entries`);
178
+ console.log('\n--- Test 4: Complexity Classification ---\n');
179
+ const complexityTests = [
180
+ { query: 'What is AI?', expected: adaptive_query_fusion_1.QueryComplexity.SIMPLE },
181
+ { query: 'How does machine learning relate to AI?', expected: adaptive_query_fusion_1.QueryComplexity.MODERATE },
182
+ { query: 'Explain the relationship between neural networks, deep learning, and AI, and how they impact modern software development', expected: adaptive_query_fusion_1.QueryComplexity.COMPLEX },
183
+ { query: 'Tell me everything about cloud computing', expected: adaptive_query_fusion_1.QueryComplexity.EXPLORATORY }
184
+ ];
185
+ for (const test of complexityTests) {
186
+ const classification = await fusion.classifyQuery(test.query);
187
+ const match = classification.complexity === test.expected ? '✓' : '✗';
188
+ console.log(`${match} "${test.query}"`);
189
+ console.log(` Complexity: ${classification.complexity} (expected: ${test.expected})`);
190
+ console.log();
191
+ }
192
+ console.log('=== All Tests Completed ===');
193
+ }
194
+ catch (error) {
195
+ console.error('Test failed:', error);
196
+ }
197
+ finally {
198
+ await db.close();
199
+ // Clean up
200
+ if (fs.existsSync(DB_PATH)) {
201
+ try {
202
+ fs.unlinkSync(DB_PATH);
203
+ }
204
+ catch (e) { }
205
+ }
206
+ }
207
+ }
208
+ testAdaptiveQueryFusion();