cozo-memory 1.2.0 → 1.2.2
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/dist/emotional-salience.js +295 -0
- package/dist/index.js +726 -9
- package/dist/memory-activation.js +64 -30
- package/dist/memory-service.js +68 -0
- package/dist/pre-storage-reasoning.js +351 -0
- package/dist/temporal-conflict-resolution.js +10 -6
- package/dist/test-activation-mcp.js +118 -0
- package/dist/test-advanced-search-mcp.js +204 -0
- package/dist/test-conflicts-mcp.js +173 -0
- package/dist/test-emotional-salience.js +177 -0
- package/dist/test-hierarchical-mcp.js +135 -0
- package/dist/test-logical-edges-mcp.js +215 -0
- package/dist/test-metadata-check.js +69 -0
- package/dist/test-metadata-update.js +92 -0
- package/dist/test-pre-storage-reasoning.js +149 -0
- package/dist/test-salience-mcp.js +94 -0
- package/dist/test-spreading-mcp.js +155 -0
- package/dist/test-suggest-connections-mcp.js +172 -0
- package/dist/test-zettelkasten-evolution.js +255 -0
- package/dist/test-zettelkasten-fixed.js +74 -0
- package/dist/test-zettelkasten-live.js +117 -0
- package/dist/test-zettelkasten-mcp.js +96 -0
- package/dist/zettelkasten-evolution.js +342 -0
- package/package.json +1 -1
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const cozo_node_1 = require("cozo-node");
|
|
4
|
+
const emotional_salience_1 = require("./emotional-salience");
|
|
5
|
+
async function testEmotionalSalience() {
|
|
6
|
+
console.log('=== Emotional Salience Weighting Test ===\n');
|
|
7
|
+
const db = new cozo_node_1.CozoDb();
|
|
8
|
+
const salienceService = new emotional_salience_1.EmotionalSalienceService(db, {
|
|
9
|
+
enableSalience: true,
|
|
10
|
+
salienceBoostFactor: 2.0,
|
|
11
|
+
decaySlowdownFactor: 0.5,
|
|
12
|
+
minSalienceThreshold: 0.3
|
|
13
|
+
});
|
|
14
|
+
try {
|
|
15
|
+
// Initialize database schema
|
|
16
|
+
await db.run(`
|
|
17
|
+
:create entity {
|
|
18
|
+
id: String,
|
|
19
|
+
=>
|
|
20
|
+
name: String,
|
|
21
|
+
type: String,
|
|
22
|
+
}
|
|
23
|
+
`);
|
|
24
|
+
await db.run(`
|
|
25
|
+
:create observation {
|
|
26
|
+
id: String,
|
|
27
|
+
=>
|
|
28
|
+
entity_id: String,
|
|
29
|
+
text: String,
|
|
30
|
+
metadata: Json?,
|
|
31
|
+
}
|
|
32
|
+
`);
|
|
33
|
+
console.log('✓ Database initialized\n');
|
|
34
|
+
// Test 1: Create test observations with varying emotional salience
|
|
35
|
+
console.log('Test 1: Creating observations with different salience levels...');
|
|
36
|
+
const testObservations = [
|
|
37
|
+
{
|
|
38
|
+
text: 'CRITICAL: Never forget to backup the database before deployment. This is extremely important!',
|
|
39
|
+
expected: 'high'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
text: 'Important reminder: The deadline for the project is next Friday.',
|
|
43
|
+
expected: 'medium'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
text: 'Interesting discovery: The new algorithm is 20% faster than the old one.',
|
|
47
|
+
expected: 'medium'
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
text: 'Note: The meeting is scheduled for 3 PM tomorrow.',
|
|
51
|
+
expected: 'low'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
text: 'The weather is nice today.',
|
|
55
|
+
expected: 'neutral'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
text: 'URGENT: Security vulnerability detected in authentication module. Immediate action required!',
|
|
59
|
+
expected: 'high'
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
text: 'Surprising breakthrough in machine learning research announced today.',
|
|
63
|
+
expected: 'medium'
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
// Create entity
|
|
67
|
+
const entityResult = await db.run(`
|
|
68
|
+
?[id, name, type] <- [['test-entity', 'Test Entity', 'Test']]
|
|
69
|
+
:put entity {id, name, type}
|
|
70
|
+
`);
|
|
71
|
+
for (const obs of testObservations) {
|
|
72
|
+
const obsId = `obs-${Math.random().toString(36).substring(7)}`;
|
|
73
|
+
await db.run(`
|
|
74
|
+
?[id, entity_id, text, metadata] <- [[
|
|
75
|
+
$id,
|
|
76
|
+
'test-entity',
|
|
77
|
+
$text,
|
|
78
|
+
{}
|
|
79
|
+
]]
|
|
80
|
+
:put observation {id, entity_id, text, metadata}
|
|
81
|
+
`, { id: obsId, text: obs.text });
|
|
82
|
+
}
|
|
83
|
+
console.log(`✓ Created ${testObservations.length} test observations\n`);
|
|
84
|
+
// Test 2: Calculate salience scores
|
|
85
|
+
console.log('Test 2: Calculating salience scores...');
|
|
86
|
+
const scores = await salienceService.scoreAllObservations();
|
|
87
|
+
console.log(`\nSalience Scores (sorted by score):`);
|
|
88
|
+
console.log('─'.repeat(80));
|
|
89
|
+
for (const score of scores.slice(0, 10)) {
|
|
90
|
+
console.log(`Score: ${score.salienceScore.toFixed(3)} | Category: ${score.category.toUpperCase()}`);
|
|
91
|
+
console.log(`Text: ${score.text.substring(0, 70)}${score.text.length > 70 ? '...' : ''}`);
|
|
92
|
+
console.log(`Keywords: ${score.detectedKeywords.join(', ')}`);
|
|
93
|
+
console.log(`Boost: Strength ×${score.boost.strengthMultiplier.toFixed(2)}, Decay -${(score.boost.decayReduction * 100).toFixed(0)}%`);
|
|
94
|
+
console.log(`Reason: ${score.reason}`);
|
|
95
|
+
console.log('─'.repeat(80));
|
|
96
|
+
}
|
|
97
|
+
// Test 3: Verify keyword detection
|
|
98
|
+
console.log('\nTest 3: Verifying keyword detection accuracy...');
|
|
99
|
+
let correctDetections = 0;
|
|
100
|
+
for (let i = 0; i < testObservations.length; i++) {
|
|
101
|
+
const expected = testObservations[i].expected;
|
|
102
|
+
const actual = scores.find(s => s.text === testObservations[i].text)?.category || 'neutral';
|
|
103
|
+
const match = expected === actual;
|
|
104
|
+
correctDetections += match ? 1 : 0;
|
|
105
|
+
console.log(`${match ? '✓' : '✗'} Expected: ${expected.padEnd(8)} | Actual: ${actual.padEnd(8)} | "${testObservations[i].text.substring(0, 50)}..."`);
|
|
106
|
+
}
|
|
107
|
+
console.log(`\nAccuracy: ${correctDetections}/${testObservations.length} (${(correctDetections / testObservations.length * 100).toFixed(1)}%)\n`);
|
|
108
|
+
// Test 4: Apply salience metadata (dry run)
|
|
109
|
+
console.log('Test 4: Applying salience metadata (dry run)...');
|
|
110
|
+
const dryRunResult = await salienceService.applySalienceMetadata(true);
|
|
111
|
+
console.log(`Would update ${dryRunResult.updated} observations (dry run)`);
|
|
112
|
+
console.log(`Observations with salience >= threshold: ${dryRunResult.scores.length}\n`);
|
|
113
|
+
// Test 5: Apply salience metadata (actual)
|
|
114
|
+
console.log('Test 5: Applying salience metadata (actual)...');
|
|
115
|
+
const applyResult = await salienceService.applySalienceMetadata(false);
|
|
116
|
+
console.log(`✓ Updated ${applyResult.updated} observations with salience metadata\n`);
|
|
117
|
+
// Test 6: Get statistics
|
|
118
|
+
console.log('Test 6: Getting salience statistics...');
|
|
119
|
+
const stats = await salienceService.getSalienceStats();
|
|
120
|
+
console.log(`Total Observations: ${stats.totalObservations}`);
|
|
121
|
+
console.log(`With Salience (>= threshold): ${stats.withSalience}`);
|
|
122
|
+
console.log(`Average Salience: ${stats.averageSalience.toFixed(3)}`);
|
|
123
|
+
console.log(`\nDistribution:`);
|
|
124
|
+
console.log(` High (>= 0.7): ${stats.distribution.high}`);
|
|
125
|
+
console.log(` Medium (0.4-0.7): ${stats.distribution.medium}`);
|
|
126
|
+
console.log(` Low (0.3-0.4): ${stats.distribution.low}`);
|
|
127
|
+
console.log(` Neutral (< 0.3): ${stats.distribution.neutral}`);
|
|
128
|
+
console.log(`\nTop Keywords:`);
|
|
129
|
+
for (const { keyword, count } of stats.topKeywords.slice(0, 5)) {
|
|
130
|
+
console.log(` ${keyword.padEnd(20)} ${count}`);
|
|
131
|
+
}
|
|
132
|
+
console.log();
|
|
133
|
+
// Test 7: Get specific observation salience
|
|
134
|
+
console.log('Test 7: Getting salience for specific observation...');
|
|
135
|
+
const firstScore = scores[0];
|
|
136
|
+
const specificSalience = await salienceService.getObservationSalience(firstScore.observationId);
|
|
137
|
+
if (specificSalience) {
|
|
138
|
+
console.log(`✓ Retrieved salience for observation: ${specificSalience.observationId}`);
|
|
139
|
+
console.log(` Score: ${specificSalience.salienceScore.toFixed(3)}`);
|
|
140
|
+
console.log(` Category: ${specificSalience.category}`);
|
|
141
|
+
console.log(` Keywords: ${specificSalience.detectedKeywords.join(', ')}`);
|
|
142
|
+
}
|
|
143
|
+
console.log();
|
|
144
|
+
// Test 8: Test edge cases
|
|
145
|
+
console.log('Test 8: Testing edge cases...');
|
|
146
|
+
const edgeCases = [
|
|
147
|
+
{ text: '', expected: 0 },
|
|
148
|
+
{ text: 'CRITICAL URGENT IMPORTANT NEVER FORGET', expected: 1.0 },
|
|
149
|
+
{ text: 'critical critical critical', expected: 1.0 }, // Duplicate keywords
|
|
150
|
+
{ text: 'This is critically important and urgent!', expected: 0.8 }
|
|
151
|
+
];
|
|
152
|
+
for (const testCase of edgeCases) {
|
|
153
|
+
const result = salienceService.calculateSalienceScore(testCase.text);
|
|
154
|
+
const pass = Math.abs(result.score - testCase.expected) < 0.2; // Allow 0.2 tolerance
|
|
155
|
+
console.log(`${pass ? '✓' : '✗'} "${testCase.text}" => ${result.score.toFixed(3)} (expected ~${testCase.expected})`);
|
|
156
|
+
}
|
|
157
|
+
console.log();
|
|
158
|
+
// Test 9: Integration with Memory Activation
|
|
159
|
+
console.log('Test 9: Demonstrating integration with Memory Activation...');
|
|
160
|
+
console.log('Salience boosts can be applied to ACT-R Memory Activation:');
|
|
161
|
+
console.log(' - High salience (0.8): Strength ×1.8, Decay -40%');
|
|
162
|
+
console.log(' - Medium salience (0.5): Strength ×1.5, Decay -25%');
|
|
163
|
+
console.log(' - Low salience (0.3): Strength ×1.3, Decay -15%');
|
|
164
|
+
console.log(' - Neutral (0.0): No boost applied');
|
|
165
|
+
console.log('\nThis slows down Ebbinghaus forgetting curve for emotionally salient memories.\n');
|
|
166
|
+
console.log('=== All Tests Completed Successfully ===');
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.error('Test failed:', error);
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
db.close();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Run tests
|
|
177
|
+
testEmotionalSalience().catch(console.error);
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const index_1 = require("./index");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const TEST_DB_PATH = 'test_hierarchical_mcp';
|
|
39
|
+
async function runTest() {
|
|
40
|
+
console.error('=== Testing compress_memory_levels and analyze_memory_distribution MCP Tools ===\n');
|
|
41
|
+
// Clean up old test database
|
|
42
|
+
const dbFile = `${TEST_DB_PATH}.db`;
|
|
43
|
+
if (fs.existsSync(dbFile)) {
|
|
44
|
+
try {
|
|
45
|
+
fs.unlinkSync(dbFile);
|
|
46
|
+
console.error('[Cleanup] Removed old test database');
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
console.error('[Cleanup] Warning: Could not remove old database:', e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const server = new index_1.MemoryServer(TEST_DB_PATH);
|
|
53
|
+
await server.initPromise;
|
|
54
|
+
try {
|
|
55
|
+
console.error('1. Creating test entity...');
|
|
56
|
+
const entity = await server.createEntity({
|
|
57
|
+
name: 'Test Project',
|
|
58
|
+
type: 'Project',
|
|
59
|
+
metadata: {}
|
|
60
|
+
});
|
|
61
|
+
const entityId = entity.id;
|
|
62
|
+
console.error(`✓ Created entity: ${entityId.substring(0, 8)}...\n`);
|
|
63
|
+
console.error('2. Adding observations at L0 (raw) level...');
|
|
64
|
+
// Add multiple observations with L0 level and varied content to avoid deduplication
|
|
65
|
+
for (let i = 0; i < 15; i++) {
|
|
66
|
+
await server.addObservation({
|
|
67
|
+
entity_id: entityId,
|
|
68
|
+
text: `Observation ${i + 1}: This is a test observation about topic ${i * 7} with unique content ${Math.random().toString(36).substring(7)}`,
|
|
69
|
+
metadata: { memory_level: 0 },
|
|
70
|
+
deduplicate: false // Disable deduplication for this test
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
console.error(`✓ Added 15 observations at L0 level\n`);
|
|
74
|
+
console.error('3. Testing analyze_memory_distribution...');
|
|
75
|
+
const stats = await server.getHierarchicalMemoryService().getMemoryStats(entityId);
|
|
76
|
+
console.error(`✓ Memory distribution analysis completed:`);
|
|
77
|
+
console.error(` - Total observations: ${stats.total_observations}`);
|
|
78
|
+
console.error(` - L0 (Raw): ${stats.by_level[0] || 0}`);
|
|
79
|
+
console.error(` - L1 (Session): ${stats.by_level[1] || 0}`);
|
|
80
|
+
console.error(` - L2 (Weekly): ${stats.by_level[2] || 0}`);
|
|
81
|
+
console.error(` - L3 (Monthly): ${stats.by_level[3] || 0}\n`);
|
|
82
|
+
if (stats.total_observations !== 15) {
|
|
83
|
+
console.error(`⚠ Warning: Expected 15 observations, got ${stats.total_observations}`);
|
|
84
|
+
}
|
|
85
|
+
console.error('4. Testing compress_memory_levels (will likely not compress due to recency)...');
|
|
86
|
+
// Note: Compression requires observations older than retention period
|
|
87
|
+
// For L0, default is 24 hours, so fresh observations won't be compressed
|
|
88
|
+
const compressionResult = await server.getHierarchicalMemoryService().compressMemoryLevel(entityId, 0);
|
|
89
|
+
if (compressionResult) {
|
|
90
|
+
console.error(`✓ Compression completed:`);
|
|
91
|
+
console.error(` - Level: ${compressionResult.level}`);
|
|
92
|
+
console.error(` - Compressed observations: ${compressionResult.compressed_observations}`);
|
|
93
|
+
console.error(` - Summary ID: ${compressionResult.summary_id.substring(0, 8)}...`);
|
|
94
|
+
console.error(` - Preserved: ${compressionResult.preserved_observations.length}`);
|
|
95
|
+
console.error(` - Deleted: ${compressionResult.deleted_observations.length}`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.error(`✓ No compression performed (observations too recent or insufficient count)`);
|
|
99
|
+
console.error(` This is expected behavior - observations must be older than retention period`);
|
|
100
|
+
}
|
|
101
|
+
console.error('\n5. Re-checking memory distribution after compression attempt...');
|
|
102
|
+
const statsAfter = await server.getHierarchicalMemoryService().getMemoryStats(entityId);
|
|
103
|
+
console.error(`✓ Updated memory distribution:`);
|
|
104
|
+
console.error(` - Total observations: ${statsAfter.total_observations}`);
|
|
105
|
+
console.error(` - L0 (Raw): ${statsAfter.by_level[0] || 0}`);
|
|
106
|
+
console.error(` - L1 (Session): ${statsAfter.by_level[1] || 0}`);
|
|
107
|
+
console.error(` - L2 (Weekly): ${statsAfter.by_level[2] || 0}`);
|
|
108
|
+
console.error(` - L3 (Monthly): ${statsAfter.by_level[3] || 0}`);
|
|
109
|
+
console.error('\n6. Testing with manually aged observations...');
|
|
110
|
+
// Create observations with old timestamps by manipulating metadata
|
|
111
|
+
const oldTimestamp = Date.now() - (30 * 24 * 60 * 60 * 1000); // 30 days ago
|
|
112
|
+
console.error(` Note: CozoDB Validity uses system time, so we cannot easily simulate old observations`);
|
|
113
|
+
console.error(` In production, compression would work on observations older than retention periods`);
|
|
114
|
+
console.error('\n=== ✓ Hierarchical Memory MCP Tools Test Passed ===\n');
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.error('\n=== ✗ Test Failed ===');
|
|
118
|
+
console.error('Error:', error);
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
// Cleanup
|
|
123
|
+
server.db.close();
|
|
124
|
+
if (fs.existsSync(dbFile)) {
|
|
125
|
+
try {
|
|
126
|
+
fs.unlinkSync(dbFile);
|
|
127
|
+
console.error('[Cleanup] Test database removed');
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
console.error('[Cleanup] Warning: Could not remove test database');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
runTest().catch(console.error);
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const index_1 = require("./index");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const TEST_DB_PATH = 'test_logical_edges_mcp';
|
|
39
|
+
async function runTest() {
|
|
40
|
+
console.error('=== Testing discover_logical_edges and materialize_logical_edges MCP Tools ===\n');
|
|
41
|
+
// Clean up old test database
|
|
42
|
+
const dbFile = `${TEST_DB_PATH}.db`;
|
|
43
|
+
if (fs.existsSync(dbFile)) {
|
|
44
|
+
try {
|
|
45
|
+
fs.unlinkSync(dbFile);
|
|
46
|
+
console.error('[Cleanup] Removed old test database');
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
console.error('[Cleanup] Warning: Could not remove old database:', e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const server = new index_1.MemoryServer(TEST_DB_PATH);
|
|
53
|
+
await server.initPromise;
|
|
54
|
+
try {
|
|
55
|
+
console.error('1. Creating test entities...');
|
|
56
|
+
// Create entities of same type
|
|
57
|
+
const project1 = await server.createEntity({
|
|
58
|
+
name: 'Project Alpha',
|
|
59
|
+
type: 'Project',
|
|
60
|
+
metadata: { category: 'software', status: 'active' }
|
|
61
|
+
});
|
|
62
|
+
console.error(`✓ Created Project Alpha: ${project1.id?.substring(0, 8)}...`);
|
|
63
|
+
const project2 = await server.createEntity({
|
|
64
|
+
name: 'Project Beta',
|
|
65
|
+
type: 'Project',
|
|
66
|
+
metadata: { category: 'software', status: 'planning' }
|
|
67
|
+
});
|
|
68
|
+
console.error(`✓ Created Project Beta: ${project2.id?.substring(0, 8)}...`);
|
|
69
|
+
const project3 = await server.createEntity({
|
|
70
|
+
name: 'Project Gamma',
|
|
71
|
+
type: 'Project',
|
|
72
|
+
metadata: { category: 'hardware', status: 'active' }
|
|
73
|
+
});
|
|
74
|
+
console.error(`✓ Created Project Gamma: ${project3.id?.substring(0, 8)}...\n`);
|
|
75
|
+
// Create some observations with similar content
|
|
76
|
+
await server.addObservation({
|
|
77
|
+
entity_id: project1.id,
|
|
78
|
+
text: 'This project focuses on machine learning algorithms',
|
|
79
|
+
metadata: {}
|
|
80
|
+
});
|
|
81
|
+
await server.addObservation({
|
|
82
|
+
entity_id: project2.id,
|
|
83
|
+
text: 'This project also uses machine learning for data analysis',
|
|
84
|
+
metadata: {}
|
|
85
|
+
});
|
|
86
|
+
console.error('2. Testing discover_logical_edges...');
|
|
87
|
+
const logicalEdges = await server.getLogicalEdgesService().discoverLogicalEdges(project1.id);
|
|
88
|
+
console.error(`✓ Logical edge discovery completed: ${logicalEdges.length} edges found`);
|
|
89
|
+
if (logicalEdges.length > 0) {
|
|
90
|
+
console.error('\n3. Analyzing discovered logical edges...');
|
|
91
|
+
logicalEdges.slice(0, 5).forEach((edge, i) => {
|
|
92
|
+
console.error(`\n Edge ${i + 1}:`);
|
|
93
|
+
console.error(` Type: ${edge.edge_type}`);
|
|
94
|
+
console.error(` From: ${edge.from_id.substring(0, 8)}...`);
|
|
95
|
+
console.error(` To: ${edge.to_id.substring(0, 8)}...`);
|
|
96
|
+
console.error(` Confidence: ${edge.confidence.toFixed(2)}`);
|
|
97
|
+
console.error(` Reason: ${edge.reason}`);
|
|
98
|
+
});
|
|
99
|
+
// Verify edge types
|
|
100
|
+
console.error('\n4. Verifying edge types...');
|
|
101
|
+
const sameTypeEdges = logicalEdges.filter((e) => e.edge_type === 'same_type');
|
|
102
|
+
const sameCategoryEdges = logicalEdges.filter((e) => e.edge_type === 'same_category');
|
|
103
|
+
const contextualEdges = logicalEdges.filter((e) => e.edge_type === 'contextual');
|
|
104
|
+
console.error(` - Same Type: ${sameTypeEdges.length}`);
|
|
105
|
+
console.error(` - Same Category: ${sameCategoryEdges.length}`);
|
|
106
|
+
console.error(` - Contextual: ${contextualEdges.length}`);
|
|
107
|
+
console.error('\n5. Testing materialize_logical_edges...');
|
|
108
|
+
const materializedCount = await server.getLogicalEdgesService().materializeLogicalEdges(project1.id);
|
|
109
|
+
console.error(`✓ Materialization completed: ${materializedCount} edges materialized`);
|
|
110
|
+
if (materializedCount > 0) {
|
|
111
|
+
console.error('\n6. Verifying materialized relationships...');
|
|
112
|
+
// Query relationships from project1
|
|
113
|
+
const relRes = await server.db.run(`
|
|
114
|
+
?[to_id, relation_type, strength] :=
|
|
115
|
+
*relationship{from_id, to_id, relation_type, strength, @ "NOW"},
|
|
116
|
+
from_id = $from_id
|
|
117
|
+
`, { from_id: project1.id });
|
|
118
|
+
console.error(` ✓ Found ${relRes.rows.length} relationships from Project Alpha`);
|
|
119
|
+
if (relRes.rows.length > 0) {
|
|
120
|
+
relRes.rows.slice(0, 3).forEach((r, i) => {
|
|
121
|
+
console.error(` ${i + 1}. Type: ${r[1]}, Strength: ${r[2].toFixed(2)}`);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
console.error('⚠ No logical edges discovered (entities may be too dissimilar)');
|
|
128
|
+
}
|
|
129
|
+
console.error('\n7. Testing detect_temporal_patterns...');
|
|
130
|
+
// Add more observations with timestamps
|
|
131
|
+
await server.addObservation({
|
|
132
|
+
entity_id: project1.id,
|
|
133
|
+
text: 'Sprint 1 completed',
|
|
134
|
+
metadata: {}
|
|
135
|
+
});
|
|
136
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
137
|
+
await server.addObservation({
|
|
138
|
+
entity_id: project1.id,
|
|
139
|
+
text: 'Sprint 2 in progress',
|
|
140
|
+
metadata: {}
|
|
141
|
+
});
|
|
142
|
+
// Use the MCP tool interface through the server
|
|
143
|
+
const patterns = await (async () => {
|
|
144
|
+
const entityRes = await server.db.run('?[name] := *entity{id: $id, name, @ "NOW"}', { id: project1.id });
|
|
145
|
+
if (entityRes.rows.length === 0) {
|
|
146
|
+
return { error: 'Entity not found' };
|
|
147
|
+
}
|
|
148
|
+
const obsRes = await server.db.run(`
|
|
149
|
+
?[id, text, created_at] :=
|
|
150
|
+
*observation{id, entity_id, text, created_at, @ "NOW"},
|
|
151
|
+
entity_id = $entity_id
|
|
152
|
+
`, { entity_id: project1.id });
|
|
153
|
+
const observations = obsRes.rows.map((r) => ({
|
|
154
|
+
id: r[0],
|
|
155
|
+
text: r[1],
|
|
156
|
+
timestamp: r[2][0] / 1000
|
|
157
|
+
}));
|
|
158
|
+
observations.sort((a, b) => a.timestamp - b.timestamp);
|
|
159
|
+
const patterns = [];
|
|
160
|
+
if (observations.length >= 3) {
|
|
161
|
+
const timeSpan = observations[observations.length - 1].timestamp - observations[0].timestamp;
|
|
162
|
+
const avgInterval = timeSpan / (observations.length - 1);
|
|
163
|
+
if (avgInterval < 7 * 24 * 60 * 60 * 1000) {
|
|
164
|
+
patterns.push({
|
|
165
|
+
type: "trending",
|
|
166
|
+
confidence: 0.8,
|
|
167
|
+
description: "High frequency of observations detected",
|
|
168
|
+
observation_count: observations.length,
|
|
169
|
+
time_span_days: (timeSpan / (24 * 60 * 60 * 1000)).toFixed(1)
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
entity_id: project1.id,
|
|
175
|
+
observation_count: observations.length,
|
|
176
|
+
patterns,
|
|
177
|
+
time_range: {
|
|
178
|
+
start: new Date(observations[0].timestamp).toISOString(),
|
|
179
|
+
end: new Date(observations[observations.length - 1].timestamp).toISOString()
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
})();
|
|
183
|
+
console.error(`✓ Temporal pattern detection completed`);
|
|
184
|
+
console.error(` - Observations analyzed: ${patterns.observation_count}`);
|
|
185
|
+
console.error(` - Patterns found: ${patterns.patterns?.length || 0}`);
|
|
186
|
+
if (patterns.patterns && patterns.patterns.length > 0) {
|
|
187
|
+
patterns.patterns.forEach((p, i) => {
|
|
188
|
+
console.error(`\n Pattern ${i + 1}:`);
|
|
189
|
+
console.error(` Type: ${p.type}`);
|
|
190
|
+
console.error(` Confidence: ${p.confidence.toFixed(2)}`);
|
|
191
|
+
console.error(` Description: ${p.description}`);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
console.error('\n=== ✓ Logical Edges and Temporal Patterns MCP Tools Test Passed ===\n');
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
console.error('\n=== ✗ Test Failed ===');
|
|
198
|
+
console.error('Error:', error);
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
// Cleanup
|
|
203
|
+
server.db.close();
|
|
204
|
+
if (fs.existsSync(dbFile)) {
|
|
205
|
+
try {
|
|
206
|
+
fs.unlinkSync(dbFile);
|
|
207
|
+
console.error('[Cleanup] Test database removed');
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
console.error('[Cleanup] Warning: Could not remove test database');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
runTest().catch(console.error);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const cozo_node_1 = require("cozo-node");
|
|
4
|
+
async function checkMetadata() {
|
|
5
|
+
console.log('=== Checking Zettelkasten Metadata ===\n');
|
|
6
|
+
const db = new cozo_node_1.CozoDb('sqlite', 'memory_db.cozo.db');
|
|
7
|
+
try {
|
|
8
|
+
// Check all observations with any metadata
|
|
9
|
+
console.log('1. All observations with metadata:');
|
|
10
|
+
const allMeta = await db.run(`
|
|
11
|
+
?[id, metadata] := *observation{id, metadata, @ "NOW"}, metadata != null
|
|
12
|
+
:limit 10
|
|
13
|
+
`);
|
|
14
|
+
console.log(` Found ${allMeta.rows.length} observations with metadata`);
|
|
15
|
+
allMeta.rows.forEach((row, i) => {
|
|
16
|
+
console.log(` ${i + 1}. ${row[0]}`);
|
|
17
|
+
console.log(` Metadata:`, JSON.stringify(row[1], null, 2));
|
|
18
|
+
});
|
|
19
|
+
console.log();
|
|
20
|
+
// Check specifically for zettelkasten_enriched
|
|
21
|
+
console.log('2. Observations with zettelkasten_enriched flag:');
|
|
22
|
+
const enriched = await db.run(`
|
|
23
|
+
?[id, metadata] :=
|
|
24
|
+
*observation{id, metadata, @ "NOW"},
|
|
25
|
+
metadata != null,
|
|
26
|
+
metadata->'zettelkasten_enriched' == true
|
|
27
|
+
:limit 10
|
|
28
|
+
`);
|
|
29
|
+
console.log(` Found ${enriched.rows.length} enriched observations`);
|
|
30
|
+
enriched.rows.forEach((row, i) => {
|
|
31
|
+
console.log(` ${i + 1}. ${row[0]}`);
|
|
32
|
+
console.log(` Metadata:`, JSON.stringify(row[1], null, 2));
|
|
33
|
+
});
|
|
34
|
+
console.log();
|
|
35
|
+
// Check the specific observation we enriched in the test
|
|
36
|
+
console.log('3. Check specific observation from test:');
|
|
37
|
+
const specific = await db.run(`
|
|
38
|
+
?[id, text, metadata] :=
|
|
39
|
+
*observation{id, text, metadata, @ "NOW"},
|
|
40
|
+
text = "Likes clean code and best practices"
|
|
41
|
+
`);
|
|
42
|
+
if (specific.rows.length > 0) {
|
|
43
|
+
console.log(` ID: ${specific.rows[0][0]}`);
|
|
44
|
+
console.log(` Text: ${specific.rows[0][1]}`);
|
|
45
|
+
console.log(` Metadata:`, JSON.stringify(specific.rows[0][2], null, 2));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.log(' Not found');
|
|
49
|
+
}
|
|
50
|
+
console.log();
|
|
51
|
+
// Check zettelkasten links
|
|
52
|
+
console.log('4. Zettelkasten links:');
|
|
53
|
+
const links = await db.run(`
|
|
54
|
+
?[from_id, to_id, strength, metadata] :=
|
|
55
|
+
*relationship{from_id, to_id, relation_type, strength, metadata, @ "NOW"},
|
|
56
|
+
relation_type == 'zettelkasten_link'
|
|
57
|
+
:limit 10
|
|
58
|
+
`);
|
|
59
|
+
console.log(` Found ${links.rows.length} zettelkasten links`);
|
|
60
|
+
links.rows.slice(0, 3).forEach((row, i) => {
|
|
61
|
+
console.log(` ${i + 1}. ${row[0]} -> ${row[1]} (strength: ${row[2]})`);
|
|
62
|
+
console.log(` Metadata:`, JSON.stringify(row[3], null, 2));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('Error:', error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
checkMetadata().catch(console.error);
|