@danielsimonjr/memory-mcp 0.47.1 → 9.8.0
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/LICENSE +22 -0
- package/README.md +2000 -194
- package/dist/__tests__/file-path.test.js +5 -5
- package/dist/__tests__/knowledge-graph.test.js +3 -8
- package/dist/core/EntityManager.d.ts +266 -0
- package/dist/core/EntityManager.d.ts.map +1 -0
- package/dist/core/EntityManager.js +85 -133
- package/dist/core/GraphEventEmitter.d.ts +202 -0
- package/dist/core/GraphEventEmitter.d.ts.map +1 -0
- package/dist/core/GraphEventEmitter.js +346 -0
- package/dist/core/GraphStorage.d.ts +395 -0
- package/dist/core/GraphStorage.d.ts.map +1 -0
- package/dist/core/GraphStorage.js +643 -31
- package/dist/core/GraphTraversal.d.ts +141 -0
- package/dist/core/GraphTraversal.d.ts.map +1 -0
- package/dist/core/GraphTraversal.js +573 -0
- package/dist/core/HierarchyManager.d.ts +111 -0
- package/dist/core/HierarchyManager.d.ts.map +1 -0
- package/dist/{features → core}/HierarchyManager.js +14 -9
- package/dist/core/ManagerContext.d.ts +72 -0
- package/dist/core/ManagerContext.d.ts.map +1 -0
- package/dist/core/ManagerContext.js +118 -0
- package/dist/core/ObservationManager.d.ts +85 -0
- package/dist/core/ObservationManager.d.ts.map +1 -0
- package/dist/core/ObservationManager.js +51 -57
- package/dist/core/RelationManager.d.ts +131 -0
- package/dist/core/RelationManager.d.ts.map +1 -0
- package/dist/core/RelationManager.js +31 -7
- package/dist/core/SQLiteStorage.d.ts +354 -0
- package/dist/core/SQLiteStorage.d.ts.map +1 -0
- package/dist/core/SQLiteStorage.js +917 -0
- package/dist/core/StorageFactory.d.ts +45 -0
- package/dist/core/StorageFactory.d.ts.map +1 -0
- package/dist/core/StorageFactory.js +64 -0
- package/dist/core/TransactionManager.d.ts +464 -0
- package/dist/core/TransactionManager.d.ts.map +1 -0
- package/dist/core/TransactionManager.js +490 -13
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +12 -2
- package/dist/features/AnalyticsManager.d.ts +44 -0
- package/dist/features/AnalyticsManager.d.ts.map +1 -0
- package/dist/features/AnalyticsManager.js +14 -13
- package/dist/features/ArchiveManager.d.ts +133 -0
- package/dist/features/ArchiveManager.d.ts.map +1 -0
- package/dist/features/ArchiveManager.js +221 -14
- package/dist/features/CompressionManager.d.ts +117 -0
- package/dist/features/CompressionManager.d.ts.map +1 -0
- package/dist/features/CompressionManager.js +189 -20
- package/dist/features/IOManager.d.ts +225 -0
- package/dist/features/IOManager.d.ts.map +1 -0
- package/dist/features/IOManager.js +1041 -0
- package/dist/features/StreamingExporter.d.ts +123 -0
- package/dist/features/StreamingExporter.d.ts.map +1 -0
- package/dist/features/StreamingExporter.js +203 -0
- package/dist/features/TagManager.d.ts +147 -0
- package/dist/features/TagManager.d.ts.map +1 -0
- package/dist/features/index.d.ts +12 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +5 -6
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -45
- package/dist/memory.jsonl +1 -18
- package/dist/search/BasicSearch.d.ts +51 -0
- package/dist/search/BasicSearch.d.ts.map +1 -0
- package/dist/search/BasicSearch.js +9 -3
- package/dist/search/BooleanSearch.d.ts +98 -0
- package/dist/search/BooleanSearch.d.ts.map +1 -0
- package/dist/search/BooleanSearch.js +156 -9
- package/dist/search/EmbeddingService.d.ts +178 -0
- package/dist/search/EmbeddingService.d.ts.map +1 -0
- package/dist/search/EmbeddingService.js +358 -0
- package/dist/search/FuzzySearch.d.ts +118 -0
- package/dist/search/FuzzySearch.d.ts.map +1 -0
- package/dist/search/FuzzySearch.js +241 -25
- package/dist/search/QueryCostEstimator.d.ts +111 -0
- package/dist/search/QueryCostEstimator.d.ts.map +1 -0
- package/dist/search/QueryCostEstimator.js +355 -0
- package/dist/search/RankedSearch.d.ts +71 -0
- package/dist/search/RankedSearch.d.ts.map +1 -0
- package/dist/search/RankedSearch.js +54 -6
- package/dist/search/SavedSearchManager.d.ts +79 -0
- package/dist/search/SavedSearchManager.d.ts.map +1 -0
- package/dist/search/SearchFilterChain.d.ts +120 -0
- package/dist/search/SearchFilterChain.d.ts.map +1 -0
- package/dist/search/SearchFilterChain.js +2 -4
- package/dist/search/SearchManager.d.ts +326 -0
- package/dist/search/SearchManager.d.ts.map +1 -0
- package/dist/search/SearchManager.js +148 -0
- package/dist/search/SearchSuggestions.d.ts +27 -0
- package/dist/search/SearchSuggestions.d.ts.map +1 -0
- package/dist/search/SearchSuggestions.js +1 -1
- package/dist/search/SemanticSearch.d.ts +149 -0
- package/dist/search/SemanticSearch.d.ts.map +1 -0
- package/dist/search/SemanticSearch.js +323 -0
- package/dist/search/TFIDFEventSync.d.ts +85 -0
- package/dist/search/TFIDFEventSync.d.ts.map +1 -0
- package/dist/search/TFIDFEventSync.js +133 -0
- package/dist/search/TFIDFIndexManager.d.ts +151 -0
- package/dist/search/TFIDFIndexManager.d.ts.map +1 -0
- package/dist/search/TFIDFIndexManager.js +232 -17
- package/dist/search/VectorStore.d.ts +235 -0
- package/dist/search/VectorStore.d.ts.map +1 -0
- package/dist/search/VectorStore.js +311 -0
- package/dist/search/index.d.ts +21 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +12 -0
- package/dist/server/MCPServer.d.ts +21 -0
- package/dist/server/MCPServer.d.ts.map +1 -0
- package/dist/server/MCPServer.js +4 -4
- package/dist/server/responseCompressor.d.ts +94 -0
- package/dist/server/responseCompressor.d.ts.map +1 -0
- package/dist/server/responseCompressor.js +127 -0
- package/dist/server/toolDefinitions.d.ts +27 -0
- package/dist/server/toolDefinitions.d.ts.map +1 -0
- package/dist/server/toolDefinitions.js +189 -18
- package/dist/server/toolHandlers.d.ts +41 -0
- package/dist/server/toolHandlers.d.ts.map +1 -0
- package/dist/server/toolHandlers.js +467 -75
- package/dist/types/index.d.ts +13 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -1
- package/dist/types/types.d.ts +1654 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/types.js +9 -0
- package/dist/utils/compressedCache.d.ts +192 -0
- package/dist/utils/compressedCache.d.ts.map +1 -0
- package/dist/utils/compressedCache.js +309 -0
- package/dist/utils/compressionUtil.d.ts +214 -0
- package/dist/utils/compressionUtil.d.ts.map +1 -0
- package/dist/utils/compressionUtil.js +247 -0
- package/dist/utils/constants.d.ts +245 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +124 -0
- package/dist/utils/entityUtils.d.ts +321 -0
- package/dist/utils/entityUtils.d.ts.map +1 -0
- package/dist/utils/entityUtils.js +434 -4
- package/dist/utils/errors.d.ts +95 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +24 -0
- package/dist/utils/formatters.d.ts +145 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/{paginationUtils.js → formatters.js} +54 -3
- package/dist/utils/index.d.ts +23 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +69 -31
- package/dist/utils/indexes.d.ts +270 -0
- package/dist/utils/indexes.d.ts.map +1 -0
- package/dist/utils/indexes.js +526 -0
- package/dist/utils/logger.d.ts +24 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/operationUtils.d.ts +124 -0
- package/dist/utils/operationUtils.d.ts.map +1 -0
- package/dist/utils/operationUtils.js +175 -0
- package/dist/utils/parallelUtils.d.ts +72 -0
- package/dist/utils/parallelUtils.d.ts.map +1 -0
- package/dist/utils/parallelUtils.js +169 -0
- package/dist/utils/schemas.d.ts +374 -0
- package/dist/utils/schemas.d.ts.map +1 -0
- package/dist/utils/schemas.js +302 -2
- package/dist/utils/searchAlgorithms.d.ts +99 -0
- package/dist/utils/searchAlgorithms.d.ts.map +1 -0
- package/dist/utils/searchAlgorithms.js +167 -0
- package/dist/utils/searchCache.d.ts +108 -0
- package/dist/utils/searchCache.d.ts.map +1 -0
- package/dist/utils/taskScheduler.d.ts +290 -0
- package/dist/utils/taskScheduler.d.ts.map +1 -0
- package/dist/utils/taskScheduler.js +466 -0
- package/dist/workers/index.d.ts +12 -0
- package/dist/workers/index.d.ts.map +1 -0
- package/dist/workers/index.js +9 -0
- package/dist/workers/levenshteinWorker.d.ts +60 -0
- package/dist/workers/levenshteinWorker.d.ts.map +1 -0
- package/dist/workers/levenshteinWorker.js +98 -0
- package/package.json +17 -4
- package/dist/__tests__/edge-cases/edge-cases.test.js +0 -406
- package/dist/__tests__/integration/workflows.test.js +0 -449
- package/dist/__tests__/performance/benchmarks.test.js +0 -413
- package/dist/__tests__/unit/core/EntityManager.test.js +0 -334
- package/dist/__tests__/unit/core/GraphStorage.test.js +0 -205
- package/dist/__tests__/unit/core/RelationManager.test.js +0 -274
- package/dist/__tests__/unit/features/CompressionManager.test.js +0 -350
- package/dist/__tests__/unit/search/BasicSearch.test.js +0 -311
- package/dist/__tests__/unit/search/BooleanSearch.test.js +0 -432
- package/dist/__tests__/unit/search/FuzzySearch.test.js +0 -448
- package/dist/__tests__/unit/search/RankedSearch.test.js +0 -379
- package/dist/__tests__/unit/utils/levenshtein.test.js +0 -77
- package/dist/core/KnowledgeGraphManager.js +0 -423
- package/dist/features/BackupManager.js +0 -311
- package/dist/features/ExportManager.js +0 -305
- package/dist/features/ImportExportManager.js +0 -50
- package/dist/features/ImportManager.js +0 -328
- package/dist/types/analytics.types.js +0 -6
- package/dist/types/entity.types.js +0 -7
- package/dist/types/import-export.types.js +0 -7
- package/dist/types/search.types.js +0 -7
- package/dist/types/tag.types.js +0 -6
- package/dist/utils/dateUtils.js +0 -89
- package/dist/utils/filterUtils.js +0 -155
- package/dist/utils/levenshtein.js +0 -62
- package/dist/utils/pathUtils.js +0 -115
- package/dist/utils/responseFormatter.js +0 -55
- package/dist/utils/tagUtils.js +0 -107
- package/dist/utils/tfidf.js +0 -90
- package/dist/utils/validationHelper.js +0 -99
- package/dist/utils/validationUtils.js +0 -109
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Performance Benchmarks
|
|
3
|
-
*
|
|
4
|
-
* Tests for performance budgets and benchmarks across all operations.
|
|
5
|
-
* Uses relative performance testing to avoid flaky failures on slow machines.
|
|
6
|
-
*
|
|
7
|
-
* Strategy: Run a baseline operation first, then verify that scaled operations
|
|
8
|
-
* complete within reasonable multiples of the baseline time.
|
|
9
|
-
*/
|
|
10
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
11
|
-
import { GraphStorage } from '../../core/GraphStorage.js';
|
|
12
|
-
import { EntityManager } from '../../core/EntityManager.js';
|
|
13
|
-
import { RelationManager } from '../../core/RelationManager.js';
|
|
14
|
-
import { CompressionManager } from '../../features/CompressionManager.js';
|
|
15
|
-
import { BasicSearch } from '../../search/BasicSearch.js';
|
|
16
|
-
import { RankedSearch } from '../../search/RankedSearch.js';
|
|
17
|
-
import { BooleanSearch } from '../../search/BooleanSearch.js';
|
|
18
|
-
import { FuzzySearch } from '../../search/FuzzySearch.js';
|
|
19
|
-
import { promises as fs } from 'fs';
|
|
20
|
-
import { join } from 'path';
|
|
21
|
-
import { tmpdir } from 'os';
|
|
22
|
-
/**
|
|
23
|
-
* Performance test configuration.
|
|
24
|
-
* Uses generous multipliers to avoid flaky tests while still catching regressions.
|
|
25
|
-
*/
|
|
26
|
-
const PERF_CONFIG = {
|
|
27
|
-
// Maximum allowed time for any single operation (prevents infinite hangs)
|
|
28
|
-
MAX_ABSOLUTE_TIME_MS: 30000,
|
|
29
|
-
// Multiplier for scaled operations (e.g., 100 entities should take < 20x the time of 10)
|
|
30
|
-
SCALE_MULTIPLIER: 25,
|
|
31
|
-
// Multiplier for complex operations vs simple ones
|
|
32
|
-
COMPLEXITY_MULTIPLIER: 15,
|
|
33
|
-
};
|
|
34
|
-
describe('Performance Benchmarks', () => {
|
|
35
|
-
let storage;
|
|
36
|
-
let entityManager;
|
|
37
|
-
let relationManager;
|
|
38
|
-
let compressionManager;
|
|
39
|
-
let basicSearch;
|
|
40
|
-
let rankedSearch;
|
|
41
|
-
let booleanSearch;
|
|
42
|
-
let fuzzySearch;
|
|
43
|
-
let testDir;
|
|
44
|
-
let testFilePath;
|
|
45
|
-
beforeEach(async () => {
|
|
46
|
-
testDir = join(tmpdir(), `perf-test-${Date.now()}-${Math.random()}`);
|
|
47
|
-
await fs.mkdir(testDir, { recursive: true });
|
|
48
|
-
testFilePath = join(testDir, 'test-graph.jsonl');
|
|
49
|
-
storage = new GraphStorage(testFilePath);
|
|
50
|
-
entityManager = new EntityManager(storage);
|
|
51
|
-
relationManager = new RelationManager(storage);
|
|
52
|
-
compressionManager = new CompressionManager(storage);
|
|
53
|
-
basicSearch = new BasicSearch(storage);
|
|
54
|
-
rankedSearch = new RankedSearch(storage);
|
|
55
|
-
booleanSearch = new BooleanSearch(storage);
|
|
56
|
-
fuzzySearch = new FuzzySearch(storage);
|
|
57
|
-
});
|
|
58
|
-
afterEach(async () => {
|
|
59
|
-
try {
|
|
60
|
-
await fs.rm(testDir, { recursive: true, force: true });
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
// Ignore cleanup errors
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
describe('Entity Creation Performance', () => {
|
|
67
|
-
it('should scale linearly when creating entities', async () => {
|
|
68
|
-
// Baseline: create 10 entities
|
|
69
|
-
const smallBatch = Array.from({ length: 10 }, (_, i) => ({
|
|
70
|
-
name: `SmallEntity${i}`,
|
|
71
|
-
entityType: 'test',
|
|
72
|
-
observations: [`Observation ${i}`],
|
|
73
|
-
}));
|
|
74
|
-
const startSmall = Date.now();
|
|
75
|
-
await entityManager.createEntities(smallBatch);
|
|
76
|
-
const smallDuration = Date.now() - startSmall;
|
|
77
|
-
// Scaled: create 100 entities (10x more)
|
|
78
|
-
const largeBatch = Array.from({ length: 100 }, (_, i) => ({
|
|
79
|
-
name: `LargeEntity${i}`,
|
|
80
|
-
entityType: 'test',
|
|
81
|
-
observations: [`Observation ${i}`],
|
|
82
|
-
importance: (i % 10) + 1,
|
|
83
|
-
}));
|
|
84
|
-
const startLarge = Date.now();
|
|
85
|
-
await entityManager.createEntities(largeBatch);
|
|
86
|
-
const largeDuration = Date.now() - startLarge;
|
|
87
|
-
// Large batch should complete within reasonable multiple of small batch
|
|
88
|
-
// Allow generous multiplier since we're comparing 10x the work
|
|
89
|
-
expect(largeDuration).toBeLessThan(Math.max(smallDuration * PERF_CONFIG.SCALE_MULTIPLIER, PERF_CONFIG.MAX_ABSOLUTE_TIME_MS));
|
|
90
|
-
});
|
|
91
|
-
it('should handle 1000 entities within absolute time limit', async () => {
|
|
92
|
-
const entities = Array.from({ length: 1000 }, (_, i) => ({
|
|
93
|
-
name: `Entity${i}`,
|
|
94
|
-
entityType: 'test',
|
|
95
|
-
observations: [`Observation ${i}`],
|
|
96
|
-
}));
|
|
97
|
-
const startTime = Date.now();
|
|
98
|
-
await entityManager.createEntities(entities);
|
|
99
|
-
const duration = Date.now() - startTime;
|
|
100
|
-
// Just ensure it completes in reasonable time (no specific threshold)
|
|
101
|
-
expect(duration).toBeLessThan(PERF_CONFIG.MAX_ABSOLUTE_TIME_MS);
|
|
102
|
-
});
|
|
103
|
-
it('should batch update entities efficiently', async () => {
|
|
104
|
-
// Create entities first
|
|
105
|
-
const entities = Array.from({ length: 100 }, (_, i) => ({
|
|
106
|
-
name: `Entity${i}`,
|
|
107
|
-
entityType: 'test',
|
|
108
|
-
observations: [`Observation ${i}`],
|
|
109
|
-
}));
|
|
110
|
-
await entityManager.createEntities(entities);
|
|
111
|
-
// Single update baseline
|
|
112
|
-
const startSingle = Date.now();
|
|
113
|
-
await entityManager.updateEntity('Entity0', { importance: 5 });
|
|
114
|
-
const singleDuration = Date.now() - startSingle;
|
|
115
|
-
// Batch update
|
|
116
|
-
const updates = Array.from({ length: 100 }, (_, i) => ({
|
|
117
|
-
name: `Entity${i}`,
|
|
118
|
-
updates: { importance: 5 },
|
|
119
|
-
}));
|
|
120
|
-
const startBatch = Date.now();
|
|
121
|
-
await entityManager.batchUpdate(updates);
|
|
122
|
-
const batchDuration = Date.now() - startBatch;
|
|
123
|
-
// Batch of 100 should be faster than 100x single updates
|
|
124
|
-
// (demonstrates batching efficiency)
|
|
125
|
-
expect(batchDuration).toBeLessThan(Math.max(singleDuration * 100, PERF_CONFIG.MAX_ABSOLUTE_TIME_MS));
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
describe('Relation Creation Performance', () => {
|
|
129
|
-
it('should scale reasonably when creating relations', async () => {
|
|
130
|
-
// Create entities first
|
|
131
|
-
const entities = Array.from({ length: 100 }, (_, i) => ({
|
|
132
|
-
name: `Entity${i}`,
|
|
133
|
-
entityType: 'test',
|
|
134
|
-
observations: ['Test'],
|
|
135
|
-
}));
|
|
136
|
-
await entityManager.createEntities(entities);
|
|
137
|
-
// Small batch: 10 relations
|
|
138
|
-
const smallRelations = Array.from({ length: 10 }, (_, i) => ({
|
|
139
|
-
from: `Entity${i}`,
|
|
140
|
-
to: `Entity${i + 1}`,
|
|
141
|
-
relationType: 'connects',
|
|
142
|
-
}));
|
|
143
|
-
const startSmall = Date.now();
|
|
144
|
-
await relationManager.createRelations(smallRelations);
|
|
145
|
-
const smallDuration = Date.now() - startSmall;
|
|
146
|
-
// Large batch: 100 relations
|
|
147
|
-
const largeRelations = Array.from({ length: 100 }, (_, i) => ({
|
|
148
|
-
from: `Entity${i % 100}`,
|
|
149
|
-
to: `Entity${(i + 10) % 100}`,
|
|
150
|
-
relationType: 'links',
|
|
151
|
-
}));
|
|
152
|
-
const startLarge = Date.now();
|
|
153
|
-
await relationManager.createRelations(largeRelations);
|
|
154
|
-
const largeDuration = Date.now() - startLarge;
|
|
155
|
-
expect(largeDuration).toBeLessThan(Math.max(smallDuration * PERF_CONFIG.SCALE_MULTIPLIER, PERF_CONFIG.MAX_ABSOLUTE_TIME_MS));
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
describe('Search Performance', () => {
|
|
159
|
-
beforeEach(async () => {
|
|
160
|
-
// Create a moderate-sized graph
|
|
161
|
-
const entities = Array.from({ length: 500 }, (_, i) => ({
|
|
162
|
-
name: `Entity${i}`,
|
|
163
|
-
entityType: i % 5 === 0 ? 'person' : 'project',
|
|
164
|
-
observations: [`This is observation ${i} with some searchable text`],
|
|
165
|
-
tags: i % 3 === 0 ? ['tagged', 'test'] : undefined,
|
|
166
|
-
importance: (i % 10) + 1,
|
|
167
|
-
}));
|
|
168
|
-
await entityManager.createEntities(entities);
|
|
169
|
-
});
|
|
170
|
-
it('should perform basic search within time limit', async () => {
|
|
171
|
-
const startTime = Date.now();
|
|
172
|
-
const results = await basicSearch.searchNodes('Entity');
|
|
173
|
-
const duration = Date.now() - startTime;
|
|
174
|
-
expect(results.entities.length).toBeGreaterThan(0);
|
|
175
|
-
expect(duration).toBeLessThan(PERF_CONFIG.MAX_ABSOLUTE_TIME_MS);
|
|
176
|
-
});
|
|
177
|
-
it('should perform ranked search within time limit', async () => {
|
|
178
|
-
const startTime = Date.now();
|
|
179
|
-
// Use "person" which only appears in 20% of entities (entityType)
|
|
180
|
-
// This ensures TF-IDF returns non-zero scores (IDF > 0 for rare terms)
|
|
181
|
-
const results = await rankedSearch.searchNodesRanked('person');
|
|
182
|
-
const duration = Date.now() - startTime;
|
|
183
|
-
// searchNodesRanked returns SearchResult[] directly (not KnowledgeGraph)
|
|
184
|
-
expect(results.length).toBeGreaterThan(0);
|
|
185
|
-
expect(duration).toBeLessThan(PERF_CONFIG.MAX_ABSOLUTE_TIME_MS);
|
|
186
|
-
});
|
|
187
|
-
it('should perform boolean search within time limit', async () => {
|
|
188
|
-
const startTime = Date.now();
|
|
189
|
-
const results = await booleanSearch.booleanSearch('person AND observation');
|
|
190
|
-
const duration = Date.now() - startTime;
|
|
191
|
-
expect(results.entities.length).toBeGreaterThan(0);
|
|
192
|
-
expect(duration).toBeLessThan(PERF_CONFIG.MAX_ABSOLUTE_TIME_MS);
|
|
193
|
-
});
|
|
194
|
-
it('should perform fuzzy search within time limit', async () => {
|
|
195
|
-
const startTime = Date.now();
|
|
196
|
-
const results = await fuzzySearch.fuzzySearch('Entty', 0.7);
|
|
197
|
-
const duration = Date.now() - startTime;
|
|
198
|
-
expect(results.entities.length).toBeGreaterThan(0);
|
|
199
|
-
expect(duration).toBeLessThan(PERF_CONFIG.MAX_ABSOLUTE_TIME_MS);
|
|
200
|
-
});
|
|
201
|
-
it('should have ranked search complete within reasonable multiple of basic search', async () => {
|
|
202
|
-
// Basic search baseline
|
|
203
|
-
const startBasic = Date.now();
|
|
204
|
-
await basicSearch.searchNodes('observation');
|
|
205
|
-
const basicDuration = Date.now() - startBasic;
|
|
206
|
-
// Ranked search (more complex)
|
|
207
|
-
const startRanked = Date.now();
|
|
208
|
-
await rankedSearch.searchNodesRanked('observation');
|
|
209
|
-
const rankedDuration = Date.now() - startRanked;
|
|
210
|
-
// Ranked search may be slower but should be within reasonable bounds
|
|
211
|
-
expect(rankedDuration).toBeLessThan(Math.max(basicDuration * PERF_CONFIG.COMPLEXITY_MULTIPLIER, PERF_CONFIG.MAX_ABSOLUTE_TIME_MS));
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
describe('Compression Performance', () => {
|
|
215
|
-
it('should scale reasonably for duplicate detection', async () => {
|
|
216
|
-
// Small set: 50 entities
|
|
217
|
-
const smallEntities = Array.from({ length: 50 }, (_, i) => ({
|
|
218
|
-
name: `SmallEntity${i}`,
|
|
219
|
-
entityType: 'person',
|
|
220
|
-
observations: [i % 10 === 0 ? 'Duplicate observation' : `Unique observation ${i}`],
|
|
221
|
-
}));
|
|
222
|
-
await entityManager.createEntities(smallEntities);
|
|
223
|
-
const startSmall = Date.now();
|
|
224
|
-
await compressionManager.findDuplicates(0.8);
|
|
225
|
-
const smallDuration = Date.now() - startSmall;
|
|
226
|
-
// Larger set: 200 entities (4x more, but O(n²) comparison so expect ~16x time)
|
|
227
|
-
const largeEntities = Array.from({ length: 200 }, (_, i) => ({
|
|
228
|
-
name: `LargeEntity${i}`,
|
|
229
|
-
entityType: 'person',
|
|
230
|
-
observations: [i % 20 === 0 ? 'Duplicate observation' : `Unique observation ${i}`],
|
|
231
|
-
}));
|
|
232
|
-
await entityManager.createEntities(largeEntities);
|
|
233
|
-
const startLarge = Date.now();
|
|
234
|
-
await compressionManager.findDuplicates(0.8);
|
|
235
|
-
const largeDuration = Date.now() - startLarge;
|
|
236
|
-
// Allow generous multiplier for O(n²) algorithm
|
|
237
|
-
expect(largeDuration).toBeLessThan(Math.max(smallDuration * PERF_CONFIG.SCALE_MULTIPLIER * 2, PERF_CONFIG.MAX_ABSOLUTE_TIME_MS));
|
|
238
|
-
});
|
|
239
|
-
it('should compress graph within time limit', async () => {
|
|
240
|
-
const entities = Array.from({ length: 100 }, (_, i) => ({
|
|
241
|
-
name: `Entity${i}`,
|
|
242
|
-
entityType: 'person',
|
|
243
|
-
observations: [i % 10 === 0 ? 'Similar observation' : `Observation ${i}`],
|
|
244
|
-
}));
|
|
245
|
-
await entityManager.createEntities(entities);
|
|
246
|
-
const startTime = Date.now();
|
|
247
|
-
await compressionManager.compressGraph(0.8, false);
|
|
248
|
-
const duration = Date.now() - startTime;
|
|
249
|
-
expect(duration).toBeLessThan(PERF_CONFIG.MAX_ABSOLUTE_TIME_MS);
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
describe('Graph Loading/Saving Performance', () => {
|
|
253
|
-
it('should load and save graphs efficiently', async () => {
|
|
254
|
-
// Create entities
|
|
255
|
-
const entities = Array.from({ length: 500 }, (_, i) => ({
|
|
256
|
-
name: `Entity${i}`,
|
|
257
|
-
entityType: 'test',
|
|
258
|
-
observations: [`Observation ${i}`],
|
|
259
|
-
}));
|
|
260
|
-
await entityManager.createEntities(entities);
|
|
261
|
-
// Measure load time
|
|
262
|
-
const startLoad = Date.now();
|
|
263
|
-
const graph = await storage.loadGraph();
|
|
264
|
-
const loadDuration = Date.now() - startLoad;
|
|
265
|
-
expect(graph.entities.length).toBe(500);
|
|
266
|
-
expect(loadDuration).toBeLessThan(PERF_CONFIG.MAX_ABSOLUTE_TIME_MS);
|
|
267
|
-
// Measure save time
|
|
268
|
-
const startSave = Date.now();
|
|
269
|
-
await storage.saveGraph(graph);
|
|
270
|
-
const saveDuration = Date.now() - startSave;
|
|
271
|
-
expect(saveDuration).toBeLessThan(PERF_CONFIG.MAX_ABSOLUTE_TIME_MS);
|
|
272
|
-
});
|
|
273
|
-
it('should scale load/save times reasonably with graph size', async () => {
|
|
274
|
-
// Small graph: 100 entities
|
|
275
|
-
const smallEntities = Array.from({ length: 100 }, (_, i) => ({
|
|
276
|
-
name: `SmallEntity${i}`,
|
|
277
|
-
entityType: 'test',
|
|
278
|
-
observations: [`Observation ${i}`],
|
|
279
|
-
}));
|
|
280
|
-
await entityManager.createEntities(smallEntities);
|
|
281
|
-
const startSmallLoad = Date.now();
|
|
282
|
-
await storage.loadGraph();
|
|
283
|
-
const smallLoadDuration = Date.now() - startSmallLoad;
|
|
284
|
-
// Large graph: 1000 entities (10x more)
|
|
285
|
-
const largeEntities = Array.from({ length: 1000 }, (_, i) => ({
|
|
286
|
-
name: `LargeEntity${i}`,
|
|
287
|
-
entityType: 'test',
|
|
288
|
-
observations: [`Observation ${i}`],
|
|
289
|
-
}));
|
|
290
|
-
await entityManager.createEntities(largeEntities);
|
|
291
|
-
const startLargeLoad = Date.now();
|
|
292
|
-
await storage.loadGraph();
|
|
293
|
-
const largeLoadDuration = Date.now() - startLargeLoad;
|
|
294
|
-
// 10x data should not take more than 25x time (allows for overhead)
|
|
295
|
-
expect(largeLoadDuration).toBeLessThan(Math.max(smallLoadDuration * PERF_CONFIG.SCALE_MULTIPLIER, PERF_CONFIG.MAX_ABSOLUTE_TIME_MS));
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
describe('Complex Workflow Performance', () => {
|
|
299
|
-
it('should complete full CRUD workflow within time limit', async () => {
|
|
300
|
-
const startTime = Date.now();
|
|
301
|
-
// Create
|
|
302
|
-
await entityManager.createEntities([
|
|
303
|
-
{ name: 'Entity1', entityType: 'test', observations: ['Test 1'] },
|
|
304
|
-
{ name: 'Entity2', entityType: 'test', observations: ['Test 2'] },
|
|
305
|
-
]);
|
|
306
|
-
// Read
|
|
307
|
-
await entityManager.getEntity('Entity1');
|
|
308
|
-
// Update
|
|
309
|
-
await entityManager.updateEntity('Entity1', { importance: 5 });
|
|
310
|
-
// Search
|
|
311
|
-
await basicSearch.searchNodes('Entity');
|
|
312
|
-
// Delete
|
|
313
|
-
await entityManager.deleteEntities(['Entity2']);
|
|
314
|
-
const duration = Date.now() - startTime;
|
|
315
|
-
expect(duration).toBeLessThan(PERF_CONFIG.MAX_ABSOLUTE_TIME_MS);
|
|
316
|
-
});
|
|
317
|
-
it('should handle bulk workflow efficiently', async () => {
|
|
318
|
-
const startTime = Date.now();
|
|
319
|
-
// Bulk create
|
|
320
|
-
const entities = Array.from({ length: 50 }, (_, i) => ({
|
|
321
|
-
name: `Entity${i}`,
|
|
322
|
-
entityType: 'test',
|
|
323
|
-
observations: [`Observation ${i}`],
|
|
324
|
-
}));
|
|
325
|
-
await entityManager.createEntities(entities);
|
|
326
|
-
// Bulk relate
|
|
327
|
-
const relations = Array.from({ length: 50 }, (_, i) => ({
|
|
328
|
-
from: `Entity${i}`,
|
|
329
|
-
to: `Entity${(i + 1) % 50}`,
|
|
330
|
-
relationType: 'connects',
|
|
331
|
-
}));
|
|
332
|
-
await relationManager.createRelations(relations);
|
|
333
|
-
// Search
|
|
334
|
-
const results = await basicSearch.searchNodes('Entity');
|
|
335
|
-
const duration = Date.now() - startTime;
|
|
336
|
-
expect(results.entities.length).toBe(50);
|
|
337
|
-
expect(duration).toBeLessThan(PERF_CONFIG.MAX_ABSOLUTE_TIME_MS);
|
|
338
|
-
});
|
|
339
|
-
it('should handle complex query workflow efficiently', async () => {
|
|
340
|
-
// Setup
|
|
341
|
-
const entities = Array.from({ length: 100 }, (_, i) => ({
|
|
342
|
-
name: `Entity${i}`,
|
|
343
|
-
entityType: i % 2 === 0 ? 'person' : 'project',
|
|
344
|
-
observations: [`Observation ${i}`],
|
|
345
|
-
tags: i % 3 === 0 ? ['important'] : undefined,
|
|
346
|
-
importance: (i % 10) + 1,
|
|
347
|
-
}));
|
|
348
|
-
await entityManager.createEntities(entities);
|
|
349
|
-
const startTime = Date.now();
|
|
350
|
-
// Multiple complex queries
|
|
351
|
-
await rankedSearch.searchNodesRanked('Observation', ['important'], 5);
|
|
352
|
-
await booleanSearch.booleanSearch('person AND (important OR project)');
|
|
353
|
-
await fuzzySearch.fuzzySearch('Observatn', 0.7);
|
|
354
|
-
const duration = Date.now() - startTime;
|
|
355
|
-
expect(duration).toBeLessThan(PERF_CONFIG.MAX_ABSOLUTE_TIME_MS);
|
|
356
|
-
});
|
|
357
|
-
});
|
|
358
|
-
describe('Memory Efficiency', () => {
|
|
359
|
-
it('should handle 2000 entities without issues', async () => {
|
|
360
|
-
// Create in batches due to 1000 entity limit
|
|
361
|
-
const batch1 = Array.from({ length: 1000 }, (_, i) => ({
|
|
362
|
-
name: `Entity${i}`,
|
|
363
|
-
entityType: 'test',
|
|
364
|
-
observations: [`Observation ${i}`],
|
|
365
|
-
}));
|
|
366
|
-
const batch2 = Array.from({ length: 1000 }, (_, i) => ({
|
|
367
|
-
name: `Entity${i + 1000}`,
|
|
368
|
-
entityType: 'test',
|
|
369
|
-
observations: [`Observation ${i + 1000}`],
|
|
370
|
-
}));
|
|
371
|
-
await entityManager.createEntities(batch1);
|
|
372
|
-
await entityManager.createEntities(batch2);
|
|
373
|
-
const graph = await storage.loadGraph();
|
|
374
|
-
expect(graph.entities).toHaveLength(2000);
|
|
375
|
-
});
|
|
376
|
-
it('should handle graph with 5000 total elements (entities + relations)', async () => {
|
|
377
|
-
// Create 1000 entities
|
|
378
|
-
const entities = Array.from({ length: 1000 }, (_, i) => ({
|
|
379
|
-
name: `Entity${i}`,
|
|
380
|
-
entityType: 'test',
|
|
381
|
-
observations: [`Observation ${i}`],
|
|
382
|
-
}));
|
|
383
|
-
await entityManager.createEntities(entities);
|
|
384
|
-
// Create 4000 relations in batches due to 1000 relation limit
|
|
385
|
-
const batch1 = Array.from({ length: 1000 }, (_, i) => ({
|
|
386
|
-
from: `Entity${i % 1000}`,
|
|
387
|
-
to: `Entity${(i + 1) % 1000}`,
|
|
388
|
-
relationType: i % 2 === 0 ? 'connects' : 'relates',
|
|
389
|
-
}));
|
|
390
|
-
const batch2 = Array.from({ length: 1000 }, (_, i) => ({
|
|
391
|
-
from: `Entity${(i + 1) % 1000}`,
|
|
392
|
-
to: `Entity${(i + 2) % 1000}`,
|
|
393
|
-
relationType: i % 2 === 0 ? 'links' : 'relates_to',
|
|
394
|
-
}));
|
|
395
|
-
const batch3 = Array.from({ length: 1000 }, (_, i) => ({
|
|
396
|
-
from: `Entity${(i + 2) % 1000}`,
|
|
397
|
-
to: `Entity${(i + 3) % 1000}`,
|
|
398
|
-
relationType: i % 2 === 0 ? 'connects_to' : 'associates',
|
|
399
|
-
}));
|
|
400
|
-
const batch4 = Array.from({ length: 1000 }, (_, i) => ({
|
|
401
|
-
from: `Entity${(i + 3) % 1000}`,
|
|
402
|
-
to: `Entity${(i + 4) % 1000}`,
|
|
403
|
-
relationType: i % 2 === 0 ? 'joins' : 'interacts',
|
|
404
|
-
}));
|
|
405
|
-
await relationManager.createRelations(batch1);
|
|
406
|
-
await relationManager.createRelations(batch2);
|
|
407
|
-
await relationManager.createRelations(batch3);
|
|
408
|
-
await relationManager.createRelations(batch4);
|
|
409
|
-
const graph = await storage.loadGraph();
|
|
410
|
-
expect(graph.entities.length + graph.relations.length).toBe(5000);
|
|
411
|
-
});
|
|
412
|
-
});
|
|
413
|
-
});
|