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,317 @@
1
+ "use strict";
2
+ /**
3
+ * Test: Temporal Pattern Detection
4
+ *
5
+ * Tests detection of recurring events, cyclical relationships,
6
+ * temporal correlations, and seasonal trends
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const cozo_node_1 = require("cozo-node");
10
+ const embedding_service_1 = require("./embedding-service");
11
+ const temporal_pattern_detection_1 = require("./temporal-pattern-detection");
12
+ const uuid_1 = require("uuid");
13
+ async function testTemporalPatterns() {
14
+ console.log('\n=== Testing Temporal Pattern Detection ===\n');
15
+ const db = new cozo_node_1.CozoDb('mem', '');
16
+ const embeddings = new embedding_service_1.EmbeddingService();
17
+ const patternDetection = new temporal_pattern_detection_1.TemporalPatternDetectionService(db, embeddings, {
18
+ min_occurrences: 3,
19
+ min_confidence: 0.5,
20
+ time_window_days: 365,
21
+ similarity_threshold: 0.7,
22
+ seasonal_buckets: 12
23
+ });
24
+ try {
25
+ // Setup schema
26
+ console.log('Setting up schema...');
27
+ await db.run(`
28
+ :create entity {
29
+ id: String =>
30
+ name: String,
31
+ type: String,
32
+ embedding: <F32; 1024>,
33
+ name_embedding: <F32; 1024>,
34
+ metadata: Json,
35
+ created_at: Validity
36
+ }
37
+ `);
38
+ await db.run(`
39
+ :create observation {
40
+ id: String,
41
+ created_at: Validity =>
42
+ entity_id: String,
43
+ text: String,
44
+ embedding: <F32; 1024>,
45
+ metadata: Json
46
+ }
47
+ `);
48
+ await db.run(`
49
+ :create relationship {
50
+ from_id: String,
51
+ to_id: String,
52
+ created_at: Validity =>
53
+ relation_type: String,
54
+ strength: Float,
55
+ metadata: Json
56
+ }
57
+ `);
58
+ // Clean up any existing temporal_pattern relation from previous runs
59
+ try {
60
+ await db.run(`:remove temporal_pattern`);
61
+ }
62
+ catch (error) {
63
+ // Relation might not exist, that's fine
64
+ }
65
+ // Create test entities
66
+ console.log('\n1. Creating test entities...');
67
+ const projectId = (0, uuid_1.v4)();
68
+ const budgetId = (0, uuid_1.v4)();
69
+ const teamId = (0, uuid_1.v4)();
70
+ const entities = [
71
+ { id: projectId, name: 'Quarterly Review Project', type: 'Project' },
72
+ { id: budgetId, name: 'Budget Department', type: 'Department' },
73
+ { id: teamId, name: 'Development Team', type: 'Team' }
74
+ ];
75
+ const now = Date.now() * 1000;
76
+ for (const entity of entities) {
77
+ const embedding = await embeddings.embed(entity.name);
78
+ await db.run(`
79
+ ?[id, created_at, name, type, embedding, name_embedding, metadata] :=
80
+ id = $id,
81
+ created_at = $created_at,
82
+ name = $name,
83
+ type = $type,
84
+ embedding = $embedding,
85
+ name_embedding = $name_embedding,
86
+ metadata = $metadata
87
+
88
+ :put entity {
89
+ id, created_at => name, type, embedding, name_embedding, metadata
90
+ }
91
+ `, {
92
+ id: entity.id,
93
+ created_at: [now, true],
94
+ name: entity.name,
95
+ type: entity.type,
96
+ embedding,
97
+ name_embedding: embedding,
98
+ metadata: {}
99
+ });
100
+ console.log(` ✓ Created entity: ${entity.name}`);
101
+ }
102
+ // Create relationships for cyclical pattern
103
+ console.log('\n2. Creating cyclical relationships...');
104
+ const relationships = [
105
+ { from: projectId, to: budgetId, type: 'requires_approval_from' },
106
+ { from: budgetId, to: teamId, type: 'allocates_resources_to' },
107
+ { from: teamId, to: projectId, type: 'works_on' }
108
+ ];
109
+ for (const rel of relationships) {
110
+ await db.run(`
111
+ ?[from_id, to_id, created_at, relation_type, strength, metadata] :=
112
+ from_id = $from_id,
113
+ to_id = $to_id,
114
+ created_at = $created_at,
115
+ relation_type = $relation_type,
116
+ strength = $strength,
117
+ metadata = $metadata
118
+
119
+ :put relationship {
120
+ from_id, to_id, created_at => relation_type, strength, metadata
121
+ }
122
+ `, {
123
+ from_id: rel.from,
124
+ to_id: rel.to,
125
+ created_at: [now, true],
126
+ relation_type: rel.type,
127
+ strength: 1.0,
128
+ metadata: {}
129
+ });
130
+ console.log(` ✓ Created relationship: ${rel.type}`);
131
+ }
132
+ // Create recurring event observations (quarterly reviews)
133
+ console.log('\n3. Creating recurring event observations...');
134
+ const recurringText = 'Quarterly budget review meeting scheduled';
135
+ const recurringEmbedding = await embeddings.embed(recurringText);
136
+ // Create 5 occurrences at ~90 day intervals
137
+ const quarterlyIntervalMs = 90 * 24 * 60 * 60 * 1000;
138
+ for (let i = 0; i < 5; i++) {
139
+ const obsId = (0, uuid_1.v4)();
140
+ const obsTime = now - (4 - i) * quarterlyIntervalMs * 1000; // microseconds
141
+ await db.run(`
142
+ ?[id, created_at, entity_id, text, embedding, metadata] :=
143
+ id = $id,
144
+ created_at = $created_at,
145
+ entity_id = $entity_id,
146
+ text = $text,
147
+ embedding = $embedding,
148
+ metadata = $metadata
149
+
150
+ :put observation {
151
+ id, created_at => entity_id, text, embedding, metadata
152
+ }
153
+ `, {
154
+ id: obsId,
155
+ created_at: [obsTime, true],
156
+ entity_id: projectId,
157
+ text: recurringText,
158
+ embedding: recurringEmbedding,
159
+ metadata: {}
160
+ });
161
+ const date = new Date(obsTime / 1000);
162
+ console.log(` ✓ Created occurrence ${i + 1}/5 at ${date.toISOString().split('T')[0]}`);
163
+ }
164
+ // Create temporal correlation observations
165
+ console.log('\n4. Creating temporal correlation observations...');
166
+ const correlatedEvents = [
167
+ { text: 'Sprint planning initiated', days_ago: 7 },
168
+ { text: 'Budget allocation requested', days_ago: 6 },
169
+ { text: 'Sprint planning initiated', days_ago: 21 },
170
+ { text: 'Budget allocation requested', days_ago: 20 },
171
+ { text: 'Sprint planning initiated', days_ago: 35 },
172
+ { text: 'Budget allocation requested', days_ago: 34 }
173
+ ];
174
+ for (const event of correlatedEvents) {
175
+ const obsId = (0, uuid_1.v4)();
176
+ const obsTime = now - (event.days_ago * 24 * 60 * 60 * 1000 * 1000);
177
+ const obsEmbedding = await embeddings.embed(event.text);
178
+ await db.run(`
179
+ ?[id, created_at, entity_id, text, embedding, metadata] :=
180
+ id = $id,
181
+ created_at = $created_at,
182
+ entity_id = $entity_id,
183
+ text = $text,
184
+ embedding = $embedding,
185
+ metadata = $metadata
186
+
187
+ :put observation {
188
+ id, created_at => entity_id, text, embedding, metadata
189
+ }
190
+ `, {
191
+ id: obsId,
192
+ created_at: [obsTime, true],
193
+ entity_id: projectId,
194
+ text: event.text,
195
+ embedding: obsEmbedding,
196
+ metadata: {}
197
+ });
198
+ }
199
+ console.log(` ✓ Created ${correlatedEvents.length} correlated observations`);
200
+ // Create seasonal trend observations (more activity in certain months)
201
+ console.log('\n5. Creating seasonal trend observations...');
202
+ const monthlyActivity = [
203
+ { month: 0, count: 2 }, // January
204
+ { month: 1, count: 2 }, // February
205
+ { month: 2, count: 3 }, // March
206
+ { month: 3, count: 8 }, // April (high activity)
207
+ { month: 4, count: 2 }, // May
208
+ { month: 5, count: 2 }, // June
209
+ { month: 6, count: 2 }, // July
210
+ { month: 7, count: 2 }, // August
211
+ { month: 8, count: 3 }, // September
212
+ { month: 9, count: 8 }, // October (high activity)
213
+ { month: 10, count: 2 }, // November
214
+ { month: 11, count: 2 } // December
215
+ ];
216
+ for (const activity of monthlyActivity) {
217
+ for (let i = 0; i < activity.count; i++) {
218
+ const obsId = (0, uuid_1.v4)();
219
+ const date = new Date(2024, activity.month, 1 + i);
220
+ const obsTime = date.getTime() * 1000; // microseconds
221
+ const obsText = `Activity in ${date.toLocaleString('default', { month: 'long' })}`;
222
+ const obsEmbedding = await embeddings.embed(obsText);
223
+ await db.run(`
224
+ ?[id, created_at, entity_id, text, embedding, metadata] :=
225
+ id = $id,
226
+ created_at = $created_at,
227
+ entity_id = $entity_id,
228
+ text = $text,
229
+ embedding = $embedding,
230
+ metadata = $metadata
231
+
232
+ :put observation {
233
+ id, created_at => entity_id, text, embedding, metadata
234
+ }
235
+ `, {
236
+ id: obsId,
237
+ created_at: [obsTime, true],
238
+ entity_id: projectId,
239
+ text: obsText,
240
+ embedding: obsEmbedding,
241
+ metadata: {}
242
+ });
243
+ }
244
+ }
245
+ console.log(` ✓ Created seasonal observations across 12 months`);
246
+ // Detect all patterns
247
+ console.log('\n6. Detecting patterns...');
248
+ const patterns = await patternDetection.detectPatterns(projectId);
249
+ console.log(` ✓ Found ${patterns.length} patterns\n`);
250
+ // Display detected patterns
251
+ console.log('7. Pattern details:\n');
252
+ const recurringPatterns = patterns.filter(p => p.pattern_type === temporal_pattern_detection_1.PatternType.RECURRING_EVENT);
253
+ console.log(` Recurring Events (${recurringPatterns.length}):`);
254
+ for (const pattern of recurringPatterns) {
255
+ console.log(` • ${pattern.description}`);
256
+ console.log(` Frequency: ${pattern.frequency} occurrences`);
257
+ console.log(` Confidence: ${(pattern.confidence * 100).toFixed(1)}%`);
258
+ console.log(` Interval: ~${pattern.interval_days?.toFixed(0)} days`);
259
+ }
260
+ const cyclicalPatterns = patterns.filter(p => p.pattern_type === temporal_pattern_detection_1.PatternType.CYCLICAL_RELATIONSHIP);
261
+ console.log(`\n Cyclical Relationships (${cyclicalPatterns.length}):`);
262
+ for (const pattern of cyclicalPatterns) {
263
+ console.log(` • ${pattern.description}`);
264
+ console.log(` Confidence: ${(pattern.confidence * 100).toFixed(1)}%`);
265
+ }
266
+ const correlationPatterns = patterns.filter(p => p.pattern_type === temporal_pattern_detection_1.PatternType.TEMPORAL_CORRELATION);
267
+ console.log(`\n Temporal Correlations (${correlationPatterns.length}):`);
268
+ for (const pattern of correlationPatterns) {
269
+ console.log(` • ${pattern.description}`);
270
+ console.log(` Frequency: ${pattern.frequency} occurrences`);
271
+ console.log(` Confidence: ${(pattern.confidence * 100).toFixed(1)}%`);
272
+ }
273
+ const seasonalPatterns = patterns.filter(p => p.pattern_type === temporal_pattern_detection_1.PatternType.SEASONAL_TREND);
274
+ console.log(`\n Seasonal Trends (${seasonalPatterns.length}):`);
275
+ for (const pattern of seasonalPatterns) {
276
+ console.log(` • ${pattern.description}`);
277
+ console.log(` Frequency: ${pattern.frequency} occurrences`);
278
+ console.log(` Confidence: ${(pattern.confidence * 100).toFixed(1)}%`);
279
+ }
280
+ // Store patterns
281
+ console.log('\n8. Storing patterns...');
282
+ for (const pattern of patterns.slice(0, 3)) { // Store first 3
283
+ await patternDetection.storePattern(pattern);
284
+ console.log(` ✓ Stored pattern: ${pattern.pattern_type}`);
285
+ }
286
+ // Retrieve stored patterns
287
+ console.log('\n9. Retrieving stored patterns...');
288
+ const storedPatterns = await patternDetection.getStoredPatterns(projectId);
289
+ console.log(` ✓ Retrieved ${storedPatterns.length} stored patterns`);
290
+ // Test configuration
291
+ console.log('\n10. Testing configuration...');
292
+ const config = patternDetection.getConfig();
293
+ console.log(` Min occurrences: ${config.min_occurrences}`);
294
+ console.log(` Min confidence: ${config.min_confidence}`);
295
+ console.log(` Time window: ${config.time_window_days} days`);
296
+ console.log(` Similarity threshold: ${config.similarity_threshold}`);
297
+ console.log(` Seasonal buckets: ${config.seasonal_buckets}`);
298
+ // Update configuration
299
+ patternDetection.updateConfig({
300
+ min_confidence: 0.7
301
+ });
302
+ console.log(` ✓ Updated min confidence to 0.7`);
303
+ console.log('\n✅ All temporal pattern detection tests passed!\n');
304
+ }
305
+ catch (error) {
306
+ console.error('\n❌ Test failed:', error.message);
307
+ if (error.display) {
308
+ console.error('CozoDB Error:', error.display);
309
+ }
310
+ console.error(error.stack);
311
+ }
312
+ finally {
313
+ db.close();
314
+ }
315
+ }
316
+ // Run tests
317
+ testTemporalPatterns().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozo-memory",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
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",