@soulcraft/brainy 0.39.0 → 0.41.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/dist/brainyData.d.ts +5 -0
- package/dist/coreTypes.d.ts +48 -0
- package/dist/hnsw/hnswIndex.d.ts +9 -0
- package/dist/hnsw/hnswIndex.d.ts.map +1 -1
- package/dist/hnsw/hnswIndexOptimized.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/storage/adapters/fileSystemStorage.d.ts.map +1 -1
- package/dist/storage/adapters/memoryStorage.d.ts.map +1 -1
- package/dist/storage/adapters/opfsStorage.d.ts.map +1 -1
- package/dist/storage/adapters/optimizedS3Search.d.ts +79 -0
- package/dist/storage/adapters/optimizedS3Search.d.ts.map +1 -0
- package/dist/storage/adapters/s3CompatibleStorage.d.ts +21 -0
- package/dist/storage/adapters/s3CompatibleStorage.d.ts.map +1 -1
- package/dist/storage/baseStorage.d.ts +1 -0
- package/dist/storage/baseStorage.d.ts.map +1 -1
- package/dist/storage/cacheManager.d.ts +2 -2
- package/dist/storage/cacheManager.d.ts.map +1 -1
- package/dist/unified.js +721 -131
- package/dist/unified.min.js +991 -991
- package/dist/utils/logger.d.ts +45 -92
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/statisticsCollector.d.ts +55 -0
- package/dist/utils/statisticsCollector.d.ts.map +1 -0
- package/package.json +4 -1
package/dist/unified.js
CHANGED
|
@@ -4939,7 +4939,8 @@ class HNSWIndex {
|
|
|
4939
4939
|
const noun = {
|
|
4940
4940
|
id,
|
|
4941
4941
|
vector,
|
|
4942
|
-
connections: new Map()
|
|
4942
|
+
connections: new Map(),
|
|
4943
|
+
level: nounLevel
|
|
4943
4944
|
};
|
|
4944
4945
|
// Initialize empty connection sets for each level
|
|
4945
4946
|
for (let level = 0; level <= nounLevel; level++) {
|
|
@@ -5264,6 +5265,29 @@ class HNSWIndex {
|
|
|
5264
5265
|
getConfig() {
|
|
5265
5266
|
return { ...this.config };
|
|
5266
5267
|
}
|
|
5268
|
+
/**
|
|
5269
|
+
* Get index health metrics
|
|
5270
|
+
*/
|
|
5271
|
+
getIndexHealth() {
|
|
5272
|
+
let totalConnections = 0;
|
|
5273
|
+
const layerCounts = new Array(this.maxLevel + 1).fill(0);
|
|
5274
|
+
// Count connections and layer distribution
|
|
5275
|
+
this.nouns.forEach(noun => {
|
|
5276
|
+
// Count connections at each layer
|
|
5277
|
+
for (let level = 0; level <= noun.level; level++) {
|
|
5278
|
+
totalConnections += noun.connections.get(level)?.size || 0;
|
|
5279
|
+
layerCounts[level]++;
|
|
5280
|
+
}
|
|
5281
|
+
});
|
|
5282
|
+
const totalNodes = this.nouns.size;
|
|
5283
|
+
const averageConnections = totalNodes > 0 ? totalConnections / totalNodes : 0;
|
|
5284
|
+
return {
|
|
5285
|
+
averageConnections,
|
|
5286
|
+
layerDistribution: layerCounts,
|
|
5287
|
+
maxLayer: this.maxLevel,
|
|
5288
|
+
totalNodes
|
|
5289
|
+
};
|
|
5290
|
+
}
|
|
5267
5291
|
/**
|
|
5268
5292
|
* Search within a specific layer
|
|
5269
5293
|
* Returns a map of noun IDs to distances, sorted by distance
|
|
@@ -5721,7 +5745,8 @@ class HNSWIndexOptimized extends HNSWIndex {
|
|
|
5721
5745
|
const noun = {
|
|
5722
5746
|
id,
|
|
5723
5747
|
vector,
|
|
5724
|
-
connections: new Map()
|
|
5748
|
+
connections: new Map(),
|
|
5749
|
+
level: 0
|
|
5725
5750
|
};
|
|
5726
5751
|
// Store the noun
|
|
5727
5752
|
this.storage.saveNoun(noun).catch((error) => {
|
|
@@ -6253,6 +6278,7 @@ class BaseStorage extends BaseStorageAdapter {
|
|
|
6253
6278
|
constructor() {
|
|
6254
6279
|
super(...arguments);
|
|
6255
6280
|
this.isInitialized = false;
|
|
6281
|
+
this.readOnly = false;
|
|
6256
6282
|
}
|
|
6257
6283
|
/**
|
|
6258
6284
|
* Ensure the storage adapter is initialized
|
|
@@ -6869,7 +6895,8 @@ class MemoryStorage extends BaseStorage {
|
|
|
6869
6895
|
const nounCopy = {
|
|
6870
6896
|
id: noun.id,
|
|
6871
6897
|
vector: [...noun.vector],
|
|
6872
|
-
connections: new Map()
|
|
6898
|
+
connections: new Map(),
|
|
6899
|
+
level: noun.level || 0
|
|
6873
6900
|
};
|
|
6874
6901
|
// Copy connections
|
|
6875
6902
|
for (const [level, connections] of noun.connections.entries()) {
|
|
@@ -6892,7 +6919,8 @@ class MemoryStorage extends BaseStorage {
|
|
|
6892
6919
|
const nounCopy = {
|
|
6893
6920
|
id: noun.id,
|
|
6894
6921
|
vector: [...noun.vector],
|
|
6895
|
-
connections: new Map()
|
|
6922
|
+
connections: new Map(),
|
|
6923
|
+
level: noun.level || 0
|
|
6896
6924
|
};
|
|
6897
6925
|
// Copy connections
|
|
6898
6926
|
for (const [level, connections] of noun.connections.entries()) {
|
|
@@ -6911,7 +6939,8 @@ class MemoryStorage extends BaseStorage {
|
|
|
6911
6939
|
const nounCopy = {
|
|
6912
6940
|
id: noun.id,
|
|
6913
6941
|
vector: [...noun.vector],
|
|
6914
|
-
connections: new Map()
|
|
6942
|
+
connections: new Map(),
|
|
6943
|
+
level: noun.level || 0
|
|
6915
6944
|
};
|
|
6916
6945
|
// Copy connections
|
|
6917
6946
|
for (const [level, connections] of noun.connections.entries()) {
|
|
@@ -6986,7 +7015,8 @@ class MemoryStorage extends BaseStorage {
|
|
|
6986
7015
|
const nounCopy = {
|
|
6987
7016
|
id: noun.id,
|
|
6988
7017
|
vector: [...noun.vector],
|
|
6989
|
-
connections: new Map()
|
|
7018
|
+
connections: new Map(),
|
|
7019
|
+
level: noun.level || 0
|
|
6990
7020
|
};
|
|
6991
7021
|
// Copy connections
|
|
6992
7022
|
for (const [level, connections] of noun.connections.entries()) {
|
|
@@ -7294,6 +7324,9 @@ class MemoryStorage extends BaseStorage {
|
|
|
7294
7324
|
this.nounMetadata.clear();
|
|
7295
7325
|
this.verbMetadata.clear();
|
|
7296
7326
|
this.statistics = null;
|
|
7327
|
+
// Clear the statistics cache
|
|
7328
|
+
this.statisticsCache = null;
|
|
7329
|
+
this.statisticsModified = false;
|
|
7297
7330
|
}
|
|
7298
7331
|
/**
|
|
7299
7332
|
* Get information about storage usage and capacity
|
|
@@ -7527,7 +7560,8 @@ class OPFSStorage extends BaseStorage {
|
|
|
7527
7560
|
return {
|
|
7528
7561
|
id: data.id,
|
|
7529
7562
|
vector: data.vector,
|
|
7530
|
-
connections
|
|
7563
|
+
connections,
|
|
7564
|
+
level: data.level || 0
|
|
7531
7565
|
};
|
|
7532
7566
|
}
|
|
7533
7567
|
catch (error) {
|
|
@@ -7558,7 +7592,8 @@ class OPFSStorage extends BaseStorage {
|
|
|
7558
7592
|
allNouns.push({
|
|
7559
7593
|
id: data.id,
|
|
7560
7594
|
vector: data.vector,
|
|
7561
|
-
connections
|
|
7595
|
+
connections,
|
|
7596
|
+
level: data.level || 0
|
|
7562
7597
|
});
|
|
7563
7598
|
}
|
|
7564
7599
|
catch (error) {
|
|
@@ -7609,7 +7644,8 @@ class OPFSStorage extends BaseStorage {
|
|
|
7609
7644
|
nodes.push({
|
|
7610
7645
|
id: data.id,
|
|
7611
7646
|
vector: data.vector,
|
|
7612
|
-
connections
|
|
7647
|
+
connections,
|
|
7648
|
+
level: data.level || 0
|
|
7613
7649
|
});
|
|
7614
7650
|
}
|
|
7615
7651
|
}
|
|
@@ -7979,6 +8015,9 @@ class OPFSStorage extends BaseStorage {
|
|
|
7979
8015
|
await removeDirectoryContents(this.verbMetadataDir);
|
|
7980
8016
|
// Remove all files in the index directory
|
|
7981
8017
|
await removeDirectoryContents(this.indexDir);
|
|
8018
|
+
// Clear the statistics cache
|
|
8019
|
+
this.statisticsCache = null;
|
|
8020
|
+
this.statisticsModified = false;
|
|
7982
8021
|
}
|
|
7983
8022
|
catch (error) {
|
|
7984
8023
|
console.error('Error clearing storage:', error);
|
|
@@ -10001,6 +10040,135 @@ class CacheManager {
|
|
|
10001
10040
|
}
|
|
10002
10041
|
}
|
|
10003
10042
|
|
|
10043
|
+
/**
|
|
10044
|
+
* Centralized logging utility for Brainy
|
|
10045
|
+
* Provides configurable log levels and consistent logging across the codebase
|
|
10046
|
+
*/
|
|
10047
|
+
var LogLevel;
|
|
10048
|
+
(function (LogLevel) {
|
|
10049
|
+
LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
|
|
10050
|
+
LogLevel[LogLevel["WARN"] = 1] = "WARN";
|
|
10051
|
+
LogLevel[LogLevel["INFO"] = 2] = "INFO";
|
|
10052
|
+
LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG";
|
|
10053
|
+
LogLevel[LogLevel["TRACE"] = 4] = "TRACE";
|
|
10054
|
+
})(LogLevel || (LogLevel = {}));
|
|
10055
|
+
let Logger$1 = class Logger {
|
|
10056
|
+
constructor() {
|
|
10057
|
+
this.config = {
|
|
10058
|
+
level: LogLevel.WARN, // Default to WARN - only critical messages
|
|
10059
|
+
timestamps: true,
|
|
10060
|
+
includeModule: true
|
|
10061
|
+
};
|
|
10062
|
+
// Set log level from environment variable if available
|
|
10063
|
+
const envLogLevel = process.env.BRAINY_LOG_LEVEL;
|
|
10064
|
+
if (envLogLevel) {
|
|
10065
|
+
const level = LogLevel[envLogLevel.toUpperCase()];
|
|
10066
|
+
if (level !== undefined) {
|
|
10067
|
+
this.config.level = level;
|
|
10068
|
+
}
|
|
10069
|
+
}
|
|
10070
|
+
// Parse module-specific log levels
|
|
10071
|
+
const moduleLogLevels = process.env.BRAINY_MODULE_LOG_LEVELS;
|
|
10072
|
+
if (moduleLogLevels) {
|
|
10073
|
+
try {
|
|
10074
|
+
this.config.modules = JSON.parse(moduleLogLevels);
|
|
10075
|
+
}
|
|
10076
|
+
catch (e) {
|
|
10077
|
+
// Ignore parsing errors
|
|
10078
|
+
}
|
|
10079
|
+
}
|
|
10080
|
+
}
|
|
10081
|
+
static getInstance() {
|
|
10082
|
+
if (!Logger.instance) {
|
|
10083
|
+
Logger.instance = new Logger();
|
|
10084
|
+
}
|
|
10085
|
+
return Logger.instance;
|
|
10086
|
+
}
|
|
10087
|
+
configure(config) {
|
|
10088
|
+
this.config = { ...this.config, ...config };
|
|
10089
|
+
}
|
|
10090
|
+
shouldLog(level, module) {
|
|
10091
|
+
// Check module-specific level first
|
|
10092
|
+
if (this.config.modules && this.config.modules[module] !== undefined) {
|
|
10093
|
+
return level <= this.config.modules[module];
|
|
10094
|
+
}
|
|
10095
|
+
// Otherwise use global level
|
|
10096
|
+
return level <= this.config.level;
|
|
10097
|
+
}
|
|
10098
|
+
formatMessage(level, module, message) {
|
|
10099
|
+
const parts = [];
|
|
10100
|
+
if (this.config.timestamps) {
|
|
10101
|
+
parts.push(`[${new Date().toISOString()}]`);
|
|
10102
|
+
}
|
|
10103
|
+
parts.push(`[${LogLevel[level]}]`);
|
|
10104
|
+
if (this.config.includeModule) {
|
|
10105
|
+
parts.push(`[${module}]`);
|
|
10106
|
+
}
|
|
10107
|
+
parts.push(message);
|
|
10108
|
+
return parts.join(' ');
|
|
10109
|
+
}
|
|
10110
|
+
log(level, module, message, ...args) {
|
|
10111
|
+
if (!this.shouldLog(level, module)) {
|
|
10112
|
+
return;
|
|
10113
|
+
}
|
|
10114
|
+
if (this.config.handler) {
|
|
10115
|
+
this.config.handler(level, module, message, ...args);
|
|
10116
|
+
return;
|
|
10117
|
+
}
|
|
10118
|
+
const formattedMessage = this.formatMessage(level, module, message);
|
|
10119
|
+
switch (level) {
|
|
10120
|
+
case LogLevel.ERROR:
|
|
10121
|
+
console.error(formattedMessage, ...args);
|
|
10122
|
+
break;
|
|
10123
|
+
case LogLevel.WARN:
|
|
10124
|
+
console.warn(formattedMessage, ...args);
|
|
10125
|
+
break;
|
|
10126
|
+
case LogLevel.INFO:
|
|
10127
|
+
console.info(formattedMessage, ...args);
|
|
10128
|
+
break;
|
|
10129
|
+
case LogLevel.DEBUG:
|
|
10130
|
+
case LogLevel.TRACE:
|
|
10131
|
+
console.log(formattedMessage, ...args);
|
|
10132
|
+
break;
|
|
10133
|
+
}
|
|
10134
|
+
}
|
|
10135
|
+
error(module, message, ...args) {
|
|
10136
|
+
this.log(LogLevel.ERROR, module, message, ...args);
|
|
10137
|
+
}
|
|
10138
|
+
warn(module, message, ...args) {
|
|
10139
|
+
this.log(LogLevel.WARN, module, message, ...args);
|
|
10140
|
+
}
|
|
10141
|
+
info(module, message, ...args) {
|
|
10142
|
+
this.log(LogLevel.INFO, module, message, ...args);
|
|
10143
|
+
}
|
|
10144
|
+
debug(module, message, ...args) {
|
|
10145
|
+
this.log(LogLevel.DEBUG, module, message, ...args);
|
|
10146
|
+
}
|
|
10147
|
+
trace(module, message, ...args) {
|
|
10148
|
+
this.log(LogLevel.TRACE, module, message, ...args);
|
|
10149
|
+
}
|
|
10150
|
+
// Create a module-specific logger
|
|
10151
|
+
createModuleLogger(module) {
|
|
10152
|
+
return {
|
|
10153
|
+
error: (message, ...args) => this.error(module, message, ...args),
|
|
10154
|
+
warn: (message, ...args) => this.warn(module, message, ...args),
|
|
10155
|
+
info: (message, ...args) => this.info(module, message, ...args),
|
|
10156
|
+
debug: (message, ...args) => this.debug(module, message, ...args),
|
|
10157
|
+
trace: (message, ...args) => this.trace(module, message, ...args)
|
|
10158
|
+
};
|
|
10159
|
+
}
|
|
10160
|
+
};
|
|
10161
|
+
// Export singleton instance
|
|
10162
|
+
const logger = Logger$1.getInstance();
|
|
10163
|
+
// Export convenience function for creating module loggers
|
|
10164
|
+
function createModuleLogger(module) {
|
|
10165
|
+
return logger.createModuleLogger(module);
|
|
10166
|
+
}
|
|
10167
|
+
// Export function to configure logger
|
|
10168
|
+
function configureLogger(config) {
|
|
10169
|
+
logger.configure(config);
|
|
10170
|
+
}
|
|
10171
|
+
|
|
10004
10172
|
/**
|
|
10005
10173
|
* S3-Compatible Storage Adapter
|
|
10006
10174
|
* Uses the AWS S3 client to interact with S3-compatible storage services
|
|
@@ -10043,6 +10211,8 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10043
10211
|
this.activeLocks = new Set();
|
|
10044
10212
|
// Change log for efficient synchronization
|
|
10045
10213
|
this.changeLogPrefix = 'change-log/';
|
|
10214
|
+
// Module logger
|
|
10215
|
+
this.logger = createModuleLogger('S3Storage');
|
|
10046
10216
|
// Node cache to avoid redundant API calls
|
|
10047
10217
|
this.nodeCache = new Map();
|
|
10048
10218
|
// Batch update timer ID
|
|
@@ -10063,6 +10233,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10063
10233
|
this.secretAccessKey = options.secretAccessKey;
|
|
10064
10234
|
this.sessionToken = options.sessionToken;
|
|
10065
10235
|
this.serviceType = options.serviceType || 's3';
|
|
10236
|
+
this.readOnly = options.readOnly || false;
|
|
10066
10237
|
// Initialize operation executors with timeout and retry configuration
|
|
10067
10238
|
this.operationExecutors = new StorageOperationExecutors(options.operationConfig);
|
|
10068
10239
|
// Set up prefixes for different types of data
|
|
@@ -10184,9 +10355,10 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10184
10355
|
this.nounCacheManager.setStorageAdapters(nounStorageAdapter, nounStorageAdapter);
|
|
10185
10356
|
this.verbCacheManager.setStorageAdapters(verbStorageAdapter, verbStorageAdapter);
|
|
10186
10357
|
this.isInitialized = true;
|
|
10358
|
+
this.logger.info(`Initialized ${this.serviceType} storage with bucket ${this.bucketName}`);
|
|
10187
10359
|
}
|
|
10188
10360
|
catch (error) {
|
|
10189
|
-
|
|
10361
|
+
this.logger.error(`Failed to initialize ${this.serviceType} storage:`, error);
|
|
10190
10362
|
throw new Error(`Failed to initialize ${this.serviceType} storage: ${error}`);
|
|
10191
10363
|
}
|
|
10192
10364
|
}
|
|
@@ -10202,7 +10374,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10202
10374
|
async saveNode(node) {
|
|
10203
10375
|
await this.ensureInitialized();
|
|
10204
10376
|
try {
|
|
10205
|
-
|
|
10377
|
+
this.logger.trace(`Saving node ${node.id}`);
|
|
10206
10378
|
// Convert connections Map to a serializable format
|
|
10207
10379
|
const serializableNode = {
|
|
10208
10380
|
...node,
|
|
@@ -10212,8 +10384,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10212
10384
|
const { PutObjectCommand } = await import('@aws-sdk/client-s3');
|
|
10213
10385
|
const key = `${this.nounPrefix}${node.id}.json`;
|
|
10214
10386
|
const body = JSON.stringify(serializableNode, null, 2);
|
|
10215
|
-
|
|
10216
|
-
console.log(`Node data: ${body.substring(0, 100)}${body.length > 100 ? '...' : ''}`);
|
|
10387
|
+
this.logger.trace(`Saving to key: ${key}`);
|
|
10217
10388
|
// Save the node to S3-compatible storage
|
|
10218
10389
|
const result = await this.s3Client.send(new PutObjectCommand({
|
|
10219
10390
|
Bucket: this.bucketName,
|
|
@@ -10221,7 +10392,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10221
10392
|
Body: body,
|
|
10222
10393
|
ContentType: 'application/json'
|
|
10223
10394
|
}));
|
|
10224
|
-
|
|
10395
|
+
this.logger.debug(`Node ${node.id} saved successfully`);
|
|
10225
10396
|
// Log the change for efficient synchronization
|
|
10226
10397
|
await this.appendToChangeLog({
|
|
10227
10398
|
timestamp: Date.now(),
|
|
@@ -10241,18 +10412,18 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10241
10412
|
Key: key
|
|
10242
10413
|
}));
|
|
10243
10414
|
if (verifyResponse && verifyResponse.Body) {
|
|
10244
|
-
|
|
10415
|
+
this.logger.trace(`Verified node ${node.id} was saved correctly`);
|
|
10245
10416
|
}
|
|
10246
10417
|
else {
|
|
10247
|
-
|
|
10418
|
+
this.logger.warn(`Failed to verify node ${node.id} was saved correctly: no response or body`);
|
|
10248
10419
|
}
|
|
10249
10420
|
}
|
|
10250
10421
|
catch (verifyError) {
|
|
10251
|
-
|
|
10422
|
+
this.logger.warn(`Failed to verify node ${node.id} was saved correctly:`, verifyError);
|
|
10252
10423
|
}
|
|
10253
10424
|
}
|
|
10254
10425
|
catch (error) {
|
|
10255
|
-
|
|
10426
|
+
this.logger.error(`Failed to save node ${node.id}:`, error);
|
|
10256
10427
|
throw new Error(`Failed to save node ${node.id}: ${error}`);
|
|
10257
10428
|
}
|
|
10258
10429
|
}
|
|
@@ -10270,9 +10441,8 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10270
10441
|
try {
|
|
10271
10442
|
// Import the GetObjectCommand only when needed
|
|
10272
10443
|
const { GetObjectCommand } = await import('@aws-sdk/client-s3');
|
|
10273
|
-
console.log(`Getting node ${id} from bucket ${this.bucketName}`);
|
|
10274
10444
|
const key = `${this.nounPrefix}${id}.json`;
|
|
10275
|
-
|
|
10445
|
+
this.logger.trace(`Getting node ${id} from key: ${key}`);
|
|
10276
10446
|
// Try to get the node from the nouns directory
|
|
10277
10447
|
const response = await this.s3Client.send(new GetObjectCommand({
|
|
10278
10448
|
Bucket: this.bucketName,
|
|
@@ -10280,22 +10450,22 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10280
10450
|
}));
|
|
10281
10451
|
// Check if response is null or undefined
|
|
10282
10452
|
if (!response || !response.Body) {
|
|
10283
|
-
|
|
10453
|
+
this.logger.trace(`No node found for ${id}`);
|
|
10284
10454
|
return null;
|
|
10285
10455
|
}
|
|
10286
10456
|
// Convert the response body to a string
|
|
10287
10457
|
const bodyContents = await response.Body.transformToString();
|
|
10288
|
-
|
|
10458
|
+
this.logger.trace(`Retrieved node body for ${id}`);
|
|
10289
10459
|
// Parse the JSON string
|
|
10290
10460
|
try {
|
|
10291
10461
|
const parsedNode = JSON.parse(bodyContents);
|
|
10292
|
-
|
|
10462
|
+
this.logger.trace(`Parsed node data for ${id}`);
|
|
10293
10463
|
// Ensure the parsed node has the expected properties
|
|
10294
10464
|
if (!parsedNode ||
|
|
10295
10465
|
!parsedNode.id ||
|
|
10296
10466
|
!parsedNode.vector ||
|
|
10297
10467
|
!parsedNode.connections) {
|
|
10298
|
-
|
|
10468
|
+
this.logger.warn(`Invalid node data for ${id}`);
|
|
10299
10469
|
return null;
|
|
10300
10470
|
}
|
|
10301
10471
|
// Convert serialized connections back to Map<number, Set<string>>
|
|
@@ -10306,19 +10476,20 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10306
10476
|
const node = {
|
|
10307
10477
|
id: parsedNode.id,
|
|
10308
10478
|
vector: parsedNode.vector,
|
|
10309
|
-
connections
|
|
10479
|
+
connections,
|
|
10480
|
+
level: parsedNode.level || 0
|
|
10310
10481
|
};
|
|
10311
|
-
|
|
10482
|
+
this.logger.trace(`Successfully retrieved node ${id}`);
|
|
10312
10483
|
return node;
|
|
10313
10484
|
}
|
|
10314
10485
|
catch (parseError) {
|
|
10315
|
-
|
|
10486
|
+
this.logger.error(`Failed to parse node data for ${id}:`, parseError);
|
|
10316
10487
|
return null;
|
|
10317
10488
|
}
|
|
10318
10489
|
}
|
|
10319
10490
|
catch (error) {
|
|
10320
10491
|
// Node not found or other error
|
|
10321
|
-
|
|
10492
|
+
this.logger.trace(`Node not found for ${id}`);
|
|
10322
10493
|
return null;
|
|
10323
10494
|
}
|
|
10324
10495
|
}
|
|
@@ -10326,7 +10497,12 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10326
10497
|
* Get all nouns from storage (internal implementation)
|
|
10327
10498
|
*/
|
|
10328
10499
|
async getAllNouns_internal() {
|
|
10329
|
-
|
|
10500
|
+
// Use paginated method to avoid deprecation warning
|
|
10501
|
+
const result = await this.getNodesWithPagination({
|
|
10502
|
+
limit: 1000,
|
|
10503
|
+
useCache: true
|
|
10504
|
+
});
|
|
10505
|
+
return result.nodes;
|
|
10330
10506
|
}
|
|
10331
10507
|
/**
|
|
10332
10508
|
* Get all nodes from storage
|
|
@@ -10335,7 +10511,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10335
10511
|
*/
|
|
10336
10512
|
async getAllNodes() {
|
|
10337
10513
|
await this.ensureInitialized();
|
|
10338
|
-
|
|
10514
|
+
this.logger.warn('getAllNodes() is deprecated and will be removed in a future version. Use getNodesWithPagination() instead.');
|
|
10339
10515
|
try {
|
|
10340
10516
|
// Use the paginated method with a large limit to maintain backward compatibility
|
|
10341
10517
|
// but warn about potential issues
|
|
@@ -10344,12 +10520,12 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10344
10520
|
useCache: true
|
|
10345
10521
|
});
|
|
10346
10522
|
if (result.hasMore) {
|
|
10347
|
-
|
|
10523
|
+
this.logger.warn(`Only returning the first 1000 nodes. There are more nodes available. Use getNodesWithPagination() for proper pagination.`);
|
|
10348
10524
|
}
|
|
10349
10525
|
return result.nodes;
|
|
10350
10526
|
}
|
|
10351
10527
|
catch (error) {
|
|
10352
|
-
|
|
10528
|
+
this.logger.error('Failed to get all nodes:', error);
|
|
10353
10529
|
return [];
|
|
10354
10530
|
}
|
|
10355
10531
|
}
|
|
@@ -10437,7 +10613,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10437
10613
|
};
|
|
10438
10614
|
}
|
|
10439
10615
|
catch (error) {
|
|
10440
|
-
|
|
10616
|
+
this.logger.error('Failed to get nodes with pagination:', error);
|
|
10441
10617
|
return {
|
|
10442
10618
|
nodes: [],
|
|
10443
10619
|
hasMore: false
|
|
@@ -10483,14 +10659,14 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10483
10659
|
cursor = result.nextCursor;
|
|
10484
10660
|
// Safety check to prevent infinite loops
|
|
10485
10661
|
if (!cursor && hasMore) {
|
|
10486
|
-
|
|
10662
|
+
this.logger.warn('No cursor returned but hasMore is true, breaking loop');
|
|
10487
10663
|
break;
|
|
10488
10664
|
}
|
|
10489
10665
|
}
|
|
10490
10666
|
return filteredNodes;
|
|
10491
10667
|
}
|
|
10492
10668
|
catch (error) {
|
|
10493
|
-
|
|
10669
|
+
this.logger.error(`Failed to get nodes by noun type ${nounType}:`, error);
|
|
10494
10670
|
return [];
|
|
10495
10671
|
}
|
|
10496
10672
|
}
|
|
@@ -10522,7 +10698,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10522
10698
|
});
|
|
10523
10699
|
}
|
|
10524
10700
|
catch (error) {
|
|
10525
|
-
|
|
10701
|
+
this.logger.error(`Failed to delete node ${id}:`, error);
|
|
10526
10702
|
throw new Error(`Failed to delete node ${id}: ${error}`);
|
|
10527
10703
|
}
|
|
10528
10704
|
}
|
|
@@ -10564,7 +10740,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10564
10740
|
});
|
|
10565
10741
|
}
|
|
10566
10742
|
catch (error) {
|
|
10567
|
-
|
|
10743
|
+
this.logger.error(`Failed to save edge ${edge.id}:`, error);
|
|
10568
10744
|
throw new Error(`Failed to save edge ${edge.id}: ${error}`);
|
|
10569
10745
|
}
|
|
10570
10746
|
}
|
|
@@ -10582,9 +10758,8 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10582
10758
|
try {
|
|
10583
10759
|
// Import the GetObjectCommand only when needed
|
|
10584
10760
|
const { GetObjectCommand } = await import('@aws-sdk/client-s3');
|
|
10585
|
-
console.log(`Getting edge ${id} from bucket ${this.bucketName}`);
|
|
10586
10761
|
const key = `${this.verbPrefix}${id}.json`;
|
|
10587
|
-
|
|
10762
|
+
this.logger.trace(`Getting edge ${id} from key: ${key}`);
|
|
10588
10763
|
// Try to get the edge from the verbs directory
|
|
10589
10764
|
const response = await this.s3Client.send(new GetObjectCommand({
|
|
10590
10765
|
Bucket: this.bucketName,
|
|
@@ -10592,22 +10767,22 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10592
10767
|
}));
|
|
10593
10768
|
// Check if response is null or undefined
|
|
10594
10769
|
if (!response || !response.Body) {
|
|
10595
|
-
|
|
10770
|
+
this.logger.trace(`No edge found for ${id}`);
|
|
10596
10771
|
return null;
|
|
10597
10772
|
}
|
|
10598
10773
|
// Convert the response body to a string
|
|
10599
10774
|
const bodyContents = await response.Body.transformToString();
|
|
10600
|
-
|
|
10775
|
+
this.logger.trace(`Retrieved edge body for ${id}`);
|
|
10601
10776
|
// Parse the JSON string
|
|
10602
10777
|
try {
|
|
10603
10778
|
const parsedEdge = JSON.parse(bodyContents);
|
|
10604
|
-
|
|
10779
|
+
this.logger.trace(`Parsed edge data for ${id}`);
|
|
10605
10780
|
// Ensure the parsed edge has the expected properties
|
|
10606
10781
|
if (!parsedEdge ||
|
|
10607
10782
|
!parsedEdge.id ||
|
|
10608
10783
|
!parsedEdge.vector ||
|
|
10609
10784
|
!parsedEdge.connections) {
|
|
10610
|
-
|
|
10785
|
+
this.logger.warn(`Invalid edge data for ${id}`);
|
|
10611
10786
|
return null;
|
|
10612
10787
|
}
|
|
10613
10788
|
// Convert serialized connections back to Map<number, Set<string>>
|
|
@@ -10620,17 +10795,17 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10620
10795
|
vector: parsedEdge.vector,
|
|
10621
10796
|
connections
|
|
10622
10797
|
};
|
|
10623
|
-
|
|
10798
|
+
this.logger.trace(`Successfully retrieved edge ${id}`);
|
|
10624
10799
|
return edge;
|
|
10625
10800
|
}
|
|
10626
10801
|
catch (parseError) {
|
|
10627
|
-
|
|
10802
|
+
this.logger.error(`Failed to parse edge data for ${id}:`, parseError);
|
|
10628
10803
|
return null;
|
|
10629
10804
|
}
|
|
10630
10805
|
}
|
|
10631
10806
|
catch (error) {
|
|
10632
10807
|
// Edge not found or other error
|
|
10633
|
-
|
|
10808
|
+
this.logger.trace(`Edge not found for ${id}`);
|
|
10634
10809
|
return null;
|
|
10635
10810
|
}
|
|
10636
10811
|
}
|
|
@@ -10640,7 +10815,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10640
10815
|
* It can cause memory issues with large datasets. Use getVerbsWithPagination() instead.
|
|
10641
10816
|
*/
|
|
10642
10817
|
async getAllVerbs_internal() {
|
|
10643
|
-
|
|
10818
|
+
this.logger.warn('getAllVerbs_internal() is deprecated and will be removed in a future version. Use getVerbsWithPagination() instead.');
|
|
10644
10819
|
return this.getAllEdges();
|
|
10645
10820
|
}
|
|
10646
10821
|
/**
|
|
@@ -10650,7 +10825,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10650
10825
|
*/
|
|
10651
10826
|
async getAllEdges() {
|
|
10652
10827
|
await this.ensureInitialized();
|
|
10653
|
-
|
|
10828
|
+
this.logger.warn('getAllEdges() is deprecated and will be removed in a future version. Use getEdgesWithPagination() instead.');
|
|
10654
10829
|
try {
|
|
10655
10830
|
// Use the paginated method with a large limit to maintain backward compatibility
|
|
10656
10831
|
// but warn about potential issues
|
|
@@ -10659,12 +10834,12 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10659
10834
|
useCache: true
|
|
10660
10835
|
});
|
|
10661
10836
|
if (result.hasMore) {
|
|
10662
|
-
|
|
10837
|
+
this.logger.warn(`Only returning the first 1000 edges. There are more edges available. Use getEdgesWithPagination() for proper pagination.`);
|
|
10663
10838
|
}
|
|
10664
10839
|
return result.edges;
|
|
10665
10840
|
}
|
|
10666
10841
|
catch (error) {
|
|
10667
|
-
|
|
10842
|
+
this.logger.error('Failed to get all edges:', error);
|
|
10668
10843
|
return [];
|
|
10669
10844
|
}
|
|
10670
10845
|
}
|
|
@@ -10761,7 +10936,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10761
10936
|
};
|
|
10762
10937
|
}
|
|
10763
10938
|
catch (error) {
|
|
10764
|
-
|
|
10939
|
+
this.logger.error('Failed to get edges with pagination:', error);
|
|
10765
10940
|
return {
|
|
10766
10941
|
edges: [],
|
|
10767
10942
|
hasMore: false
|
|
@@ -10777,7 +10952,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10777
10952
|
filterEdge(edge, filter) {
|
|
10778
10953
|
// HNSWVerb filtering is not supported since metadata is stored separately
|
|
10779
10954
|
// This method is deprecated and should not be used with the new storage pattern
|
|
10780
|
-
|
|
10955
|
+
this.logger.trace('Edge filtering is deprecated and not supported with the new storage pattern');
|
|
10781
10956
|
return true; // Return all edges since filtering requires metadata
|
|
10782
10957
|
}
|
|
10783
10958
|
/**
|
|
@@ -10842,7 +11017,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10842
11017
|
async getEdgesBySource(sourceId) {
|
|
10843
11018
|
// This method is deprecated and would require loading metadata for each edge
|
|
10844
11019
|
// For now, return empty array since this is not efficiently implementable with new storage pattern
|
|
10845
|
-
|
|
11020
|
+
this.logger.trace('getEdgesBySource is deprecated and not efficiently supported in new storage pattern');
|
|
10846
11021
|
return [];
|
|
10847
11022
|
}
|
|
10848
11023
|
/**
|
|
@@ -10857,7 +11032,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10857
11032
|
async getEdgesByTarget(targetId) {
|
|
10858
11033
|
// This method is deprecated and would require loading metadata for each edge
|
|
10859
11034
|
// For now, return empty array since this is not efficiently implementable with new storage pattern
|
|
10860
|
-
|
|
11035
|
+
this.logger.trace('getEdgesByTarget is deprecated and not efficiently supported in new storage pattern');
|
|
10861
11036
|
return [];
|
|
10862
11037
|
}
|
|
10863
11038
|
/**
|
|
@@ -10872,7 +11047,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10872
11047
|
async getEdgesByType(type) {
|
|
10873
11048
|
// This method is deprecated and would require loading metadata for each edge
|
|
10874
11049
|
// For now, return empty array since this is not efficiently implementable with new storage pattern
|
|
10875
|
-
|
|
11050
|
+
this.logger.trace('getEdgesByType is deprecated and not efficiently supported in new storage pattern');
|
|
10876
11051
|
return [];
|
|
10877
11052
|
}
|
|
10878
11053
|
/**
|
|
@@ -10903,7 +11078,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10903
11078
|
});
|
|
10904
11079
|
}
|
|
10905
11080
|
catch (error) {
|
|
10906
|
-
|
|
11081
|
+
this.logger.error(`Failed to delete edge ${id}:`, error);
|
|
10907
11082
|
throw new Error(`Failed to delete edge ${id}: ${error}`);
|
|
10908
11083
|
}
|
|
10909
11084
|
}
|
|
@@ -10913,13 +11088,11 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10913
11088
|
async saveMetadata(id, metadata) {
|
|
10914
11089
|
await this.ensureInitialized();
|
|
10915
11090
|
try {
|
|
10916
|
-
console.log(`Saving metadata for ${id} to bucket ${this.bucketName}`);
|
|
10917
11091
|
// Import the PutObjectCommand only when needed
|
|
10918
11092
|
const { PutObjectCommand } = await import('@aws-sdk/client-s3');
|
|
10919
11093
|
const key = `${this.metadataPrefix}${id}.json`;
|
|
10920
11094
|
const body = JSON.stringify(metadata, null, 2);
|
|
10921
|
-
|
|
10922
|
-
console.log(`Metadata: ${body}`);
|
|
11095
|
+
this.logger.trace(`Saving metadata for ${id} to key: ${key}`);
|
|
10923
11096
|
// Save the metadata to S3-compatible storage
|
|
10924
11097
|
const result = await this.s3Client.send(new PutObjectCommand({
|
|
10925
11098
|
Bucket: this.bucketName,
|
|
@@ -10927,7 +11100,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10927
11100
|
Body: body,
|
|
10928
11101
|
ContentType: 'application/json'
|
|
10929
11102
|
}));
|
|
10930
|
-
|
|
11103
|
+
this.logger.debug(`Metadata for ${id} saved successfully`);
|
|
10931
11104
|
// Log the change for efficient synchronization
|
|
10932
11105
|
await this.appendToChangeLog({
|
|
10933
11106
|
timestamp: Date.now(),
|
|
@@ -10944,19 +11117,18 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10944
11117
|
Key: key
|
|
10945
11118
|
}));
|
|
10946
11119
|
if (verifyResponse && verifyResponse.Body) {
|
|
10947
|
-
|
|
10948
|
-
console.log(`Verified metadata for ${id} was saved correctly: ${bodyContents}`);
|
|
11120
|
+
this.logger.trace(`Verified metadata for ${id} was saved correctly`);
|
|
10949
11121
|
}
|
|
10950
11122
|
else {
|
|
10951
|
-
|
|
11123
|
+
this.logger.warn(`Failed to verify metadata for ${id} was saved correctly: no response or body`);
|
|
10952
11124
|
}
|
|
10953
11125
|
}
|
|
10954
11126
|
catch (verifyError) {
|
|
10955
|
-
|
|
11127
|
+
this.logger.warn(`Failed to verify metadata for ${id} was saved correctly:`, verifyError);
|
|
10956
11128
|
}
|
|
10957
11129
|
}
|
|
10958
11130
|
catch (error) {
|
|
10959
|
-
|
|
11131
|
+
this.logger.error(`Failed to save metadata for ${id}:`, error);
|
|
10960
11132
|
throw new Error(`Failed to save metadata for ${id}: ${error}`);
|
|
10961
11133
|
}
|
|
10962
11134
|
}
|
|
@@ -10966,13 +11138,11 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10966
11138
|
async saveVerbMetadata(id, metadata) {
|
|
10967
11139
|
await this.ensureInitialized();
|
|
10968
11140
|
try {
|
|
10969
|
-
console.log(`Saving verb metadata for ${id} to bucket ${this.bucketName}`);
|
|
10970
11141
|
// Import the PutObjectCommand only when needed
|
|
10971
11142
|
const { PutObjectCommand } = await import('@aws-sdk/client-s3');
|
|
10972
11143
|
const key = `verb-metadata/${id}.json`;
|
|
10973
11144
|
const body = JSON.stringify(metadata, null, 2);
|
|
10974
|
-
|
|
10975
|
-
console.log(`Verb Metadata: ${body}`);
|
|
11145
|
+
this.logger.trace(`Saving verb metadata for ${id} to key: ${key}`);
|
|
10976
11146
|
// Save the verb metadata to S3-compatible storage
|
|
10977
11147
|
const result = await this.s3Client.send(new PutObjectCommand({
|
|
10978
11148
|
Bucket: this.bucketName,
|
|
@@ -10980,10 +11150,10 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10980
11150
|
Body: body,
|
|
10981
11151
|
ContentType: 'application/json'
|
|
10982
11152
|
}));
|
|
10983
|
-
|
|
11153
|
+
this.logger.debug(`Verb metadata for ${id} saved successfully`);
|
|
10984
11154
|
}
|
|
10985
11155
|
catch (error) {
|
|
10986
|
-
|
|
11156
|
+
this.logger.error(`Failed to save verb metadata for ${id}:`, error);
|
|
10987
11157
|
throw new Error(`Failed to save verb metadata for ${id}: ${error}`);
|
|
10988
11158
|
}
|
|
10989
11159
|
}
|
|
@@ -10995,9 +11165,8 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
10995
11165
|
try {
|
|
10996
11166
|
// Import the GetObjectCommand only when needed
|
|
10997
11167
|
const { GetObjectCommand } = await import('@aws-sdk/client-s3');
|
|
10998
|
-
console.log(`Getting verb metadata for ${id} from bucket ${this.bucketName}`);
|
|
10999
11168
|
const key = `verb-metadata/${id}.json`;
|
|
11000
|
-
|
|
11169
|
+
this.logger.trace(`Getting verb metadata for ${id} from key: ${key}`);
|
|
11001
11170
|
// Try to get the verb metadata
|
|
11002
11171
|
const response = await this.s3Client.send(new GetObjectCommand({
|
|
11003
11172
|
Bucket: this.bucketName,
|
|
@@ -11005,20 +11174,20 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11005
11174
|
}));
|
|
11006
11175
|
// Check if response is null or undefined
|
|
11007
11176
|
if (!response || !response.Body) {
|
|
11008
|
-
|
|
11177
|
+
this.logger.trace(`No verb metadata found for ${id}`);
|
|
11009
11178
|
return null;
|
|
11010
11179
|
}
|
|
11011
11180
|
// Convert the response body to a string
|
|
11012
11181
|
const bodyContents = await response.Body.transformToString();
|
|
11013
|
-
|
|
11182
|
+
this.logger.trace(`Retrieved verb metadata body for ${id}`);
|
|
11014
11183
|
// Parse the JSON string
|
|
11015
11184
|
try {
|
|
11016
11185
|
const parsedMetadata = JSON.parse(bodyContents);
|
|
11017
|
-
|
|
11186
|
+
this.logger.trace(`Successfully retrieved verb metadata for ${id}`);
|
|
11018
11187
|
return parsedMetadata;
|
|
11019
11188
|
}
|
|
11020
11189
|
catch (parseError) {
|
|
11021
|
-
|
|
11190
|
+
this.logger.error(`Failed to parse verb metadata for ${id}:`, parseError);
|
|
11022
11191
|
return null;
|
|
11023
11192
|
}
|
|
11024
11193
|
}
|
|
@@ -11029,7 +11198,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11029
11198
|
(error.message.includes('NoSuchKey') ||
|
|
11030
11199
|
error.message.includes('not found') ||
|
|
11031
11200
|
error.message.includes('does not exist')))) {
|
|
11032
|
-
|
|
11201
|
+
this.logger.trace(`Verb metadata not found for ${id}`);
|
|
11033
11202
|
return null;
|
|
11034
11203
|
}
|
|
11035
11204
|
// For other types of errors, convert to BrainyError for better classification
|
|
@@ -11042,13 +11211,11 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11042
11211
|
async saveNounMetadata(id, metadata) {
|
|
11043
11212
|
await this.ensureInitialized();
|
|
11044
11213
|
try {
|
|
11045
|
-
console.log(`Saving noun metadata for ${id} to bucket ${this.bucketName}`);
|
|
11046
11214
|
// Import the PutObjectCommand only when needed
|
|
11047
11215
|
const { PutObjectCommand } = await import('@aws-sdk/client-s3');
|
|
11048
11216
|
const key = `noun-metadata/${id}.json`;
|
|
11049
11217
|
const body = JSON.stringify(metadata, null, 2);
|
|
11050
|
-
|
|
11051
|
-
console.log(`Noun Metadata: ${body}`);
|
|
11218
|
+
this.logger.trace(`Saving noun metadata for ${id} to key: ${key}`);
|
|
11052
11219
|
// Save the noun metadata to S3-compatible storage
|
|
11053
11220
|
const result = await this.s3Client.send(new PutObjectCommand({
|
|
11054
11221
|
Bucket: this.bucketName,
|
|
@@ -11056,10 +11223,10 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11056
11223
|
Body: body,
|
|
11057
11224
|
ContentType: 'application/json'
|
|
11058
11225
|
}));
|
|
11059
|
-
|
|
11226
|
+
this.logger.debug(`Noun metadata for ${id} saved successfully`);
|
|
11060
11227
|
}
|
|
11061
11228
|
catch (error) {
|
|
11062
|
-
|
|
11229
|
+
this.logger.error(`Failed to save noun metadata for ${id}:`, error);
|
|
11063
11230
|
throw new Error(`Failed to save noun metadata for ${id}: ${error}`);
|
|
11064
11231
|
}
|
|
11065
11232
|
}
|
|
@@ -11071,9 +11238,8 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11071
11238
|
try {
|
|
11072
11239
|
// Import the GetObjectCommand only when needed
|
|
11073
11240
|
const { GetObjectCommand } = await import('@aws-sdk/client-s3');
|
|
11074
|
-
console.log(`Getting noun metadata for ${id} from bucket ${this.bucketName}`);
|
|
11075
11241
|
const key = `noun-metadata/${id}.json`;
|
|
11076
|
-
|
|
11242
|
+
this.logger.trace(`Getting noun metadata for ${id} from key: ${key}`);
|
|
11077
11243
|
// Try to get the noun metadata
|
|
11078
11244
|
const response = await this.s3Client.send(new GetObjectCommand({
|
|
11079
11245
|
Bucket: this.bucketName,
|
|
@@ -11081,20 +11247,20 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11081
11247
|
}));
|
|
11082
11248
|
// Check if response is null or undefined
|
|
11083
11249
|
if (!response || !response.Body) {
|
|
11084
|
-
|
|
11250
|
+
this.logger.trace(`No noun metadata found for ${id}`);
|
|
11085
11251
|
return null;
|
|
11086
11252
|
}
|
|
11087
11253
|
// Convert the response body to a string
|
|
11088
11254
|
const bodyContents = await response.Body.transformToString();
|
|
11089
|
-
|
|
11255
|
+
this.logger.trace(`Retrieved noun metadata body for ${id}`);
|
|
11090
11256
|
// Parse the JSON string
|
|
11091
11257
|
try {
|
|
11092
11258
|
const parsedMetadata = JSON.parse(bodyContents);
|
|
11093
|
-
|
|
11259
|
+
this.logger.trace(`Successfully retrieved noun metadata for ${id}`);
|
|
11094
11260
|
return parsedMetadata;
|
|
11095
11261
|
}
|
|
11096
11262
|
catch (parseError) {
|
|
11097
|
-
|
|
11263
|
+
this.logger.error(`Failed to parse noun metadata for ${id}:`, parseError);
|
|
11098
11264
|
return null;
|
|
11099
11265
|
}
|
|
11100
11266
|
}
|
|
@@ -11105,7 +11271,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11105
11271
|
(error.message.includes('NoSuchKey') ||
|
|
11106
11272
|
error.message.includes('not found') ||
|
|
11107
11273
|
error.message.includes('does not exist')))) {
|
|
11108
|
-
|
|
11274
|
+
this.logger.trace(`Noun metadata not found for ${id}`);
|
|
11109
11275
|
return null;
|
|
11110
11276
|
}
|
|
11111
11277
|
// For other types of errors, convert to BrainyError for better classification
|
|
@@ -11204,6 +11370,9 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11204
11370
|
await deleteObjectsWithPrefix(this.metadataPrefix);
|
|
11205
11371
|
// Delete all objects in the index directory
|
|
11206
11372
|
await deleteObjectsWithPrefix(this.indexPrefix);
|
|
11373
|
+
// Clear the statistics cache
|
|
11374
|
+
this.statisticsCache = null;
|
|
11375
|
+
this.statisticsModified = false;
|
|
11207
11376
|
}
|
|
11208
11377
|
catch (error) {
|
|
11209
11378
|
console.error('Failed to clear storage:', error);
|
|
@@ -11319,7 +11488,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11319
11488
|
}
|
|
11320
11489
|
}
|
|
11321
11490
|
catch (error) {
|
|
11322
|
-
|
|
11491
|
+
this.logger.warn(`Error getting metadata from ${object.Key}:`, error);
|
|
11323
11492
|
}
|
|
11324
11493
|
}
|
|
11325
11494
|
}
|
|
@@ -11340,7 +11509,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11340
11509
|
};
|
|
11341
11510
|
}
|
|
11342
11511
|
catch (error) {
|
|
11343
|
-
|
|
11512
|
+
this.logger.error('Failed to get storage status:', error);
|
|
11344
11513
|
return {
|
|
11345
11514
|
type: this.serviceType,
|
|
11346
11515
|
used: 0,
|
|
@@ -11380,6 +11549,11 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11380
11549
|
scheduleBatchUpdate() {
|
|
11381
11550
|
// Mark statistics as modified
|
|
11382
11551
|
this.statisticsModified = true;
|
|
11552
|
+
// If we're in read-only mode, don't update statistics
|
|
11553
|
+
if (this.readOnly) {
|
|
11554
|
+
this.logger.trace('Skipping statistics update in read-only mode');
|
|
11555
|
+
return;
|
|
11556
|
+
}
|
|
11383
11557
|
// If a timer is already set, don't set another one
|
|
11384
11558
|
if (this.statisticsBatchUpdateTimerId !== null) {
|
|
11385
11559
|
return;
|
|
@@ -11416,7 +11590,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11416
11590
|
if (!lockAcquired) {
|
|
11417
11591
|
// Another instance is updating statistics, skip this flush
|
|
11418
11592
|
// but keep the modified flag so we'll try again later
|
|
11419
|
-
|
|
11593
|
+
this.logger.debug('Statistics flush skipped - another instance is updating');
|
|
11420
11594
|
return;
|
|
11421
11595
|
}
|
|
11422
11596
|
try {
|
|
@@ -11435,7 +11609,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11435
11609
|
}
|
|
11436
11610
|
catch (error) {
|
|
11437
11611
|
// If we can't read current stats, proceed with local cache
|
|
11438
|
-
|
|
11612
|
+
this.logger.warn('Could not read current statistics from storage, using local cache:', error);
|
|
11439
11613
|
}
|
|
11440
11614
|
// Merge local statistics with storage statistics
|
|
11441
11615
|
let mergedStats = this.statisticsCache;
|
|
@@ -11473,7 +11647,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11473
11647
|
}
|
|
11474
11648
|
}
|
|
11475
11649
|
catch (error) {
|
|
11476
|
-
|
|
11650
|
+
this.logger.error('Failed to flush statistics data:', error);
|
|
11477
11651
|
// Mark as still modified so we'll try again later
|
|
11478
11652
|
this.statisticsModified = true;
|
|
11479
11653
|
// Don't throw the error to avoid disrupting the application
|
|
@@ -11538,7 +11712,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11538
11712
|
this.scheduleBatchUpdate();
|
|
11539
11713
|
}
|
|
11540
11714
|
catch (error) {
|
|
11541
|
-
|
|
11715
|
+
this.logger.error('Failed to save statistics data:', error);
|
|
11542
11716
|
throw new Error(`Failed to save statistics data: ${error}`);
|
|
11543
11717
|
}
|
|
11544
11718
|
}
|
|
@@ -11548,8 +11722,11 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11548
11722
|
*/
|
|
11549
11723
|
async getStatisticsData() {
|
|
11550
11724
|
await this.ensureInitialized();
|
|
11551
|
-
//
|
|
11552
|
-
if
|
|
11725
|
+
// Always fetch fresh statistics from storage to avoid inconsistencies
|
|
11726
|
+
// Only use cache if explicitly in read-only mode
|
|
11727
|
+
const shouldUseCache = this.readOnly && this.statisticsCache &&
|
|
11728
|
+
(Date.now() - this.lastStatisticsFlushTime < this.MIN_FLUSH_INTERVAL_MS);
|
|
11729
|
+
if (shouldUseCache && this.statisticsCache) {
|
|
11553
11730
|
return {
|
|
11554
11731
|
nounCount: { ...this.statisticsCache.nounCount },
|
|
11555
11732
|
verbCount: { ...this.statisticsCache.verbCount },
|
|
@@ -11590,7 +11767,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11590
11767
|
return statistics;
|
|
11591
11768
|
}
|
|
11592
11769
|
catch (error) {
|
|
11593
|
-
|
|
11770
|
+
this.logger.error('Error getting statistics data:', error);
|
|
11594
11771
|
throw error;
|
|
11595
11772
|
}
|
|
11596
11773
|
}
|
|
@@ -11660,7 +11837,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11660
11837
|
}));
|
|
11661
11838
|
}
|
|
11662
11839
|
catch (error) {
|
|
11663
|
-
|
|
11840
|
+
this.logger.warn('Failed to append to change log:', error);
|
|
11664
11841
|
// Don't throw error to avoid disrupting main operations
|
|
11665
11842
|
}
|
|
11666
11843
|
}
|
|
@@ -11705,7 +11882,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11705
11882
|
}
|
|
11706
11883
|
}
|
|
11707
11884
|
catch (error) {
|
|
11708
|
-
|
|
11885
|
+
this.logger.warn(`Failed to read change log entry ${object.Key}:`, error);
|
|
11709
11886
|
// Continue processing other entries
|
|
11710
11887
|
}
|
|
11711
11888
|
}
|
|
@@ -11714,7 +11891,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11714
11891
|
return changes.slice(0, maxEntries);
|
|
11715
11892
|
}
|
|
11716
11893
|
catch (error) {
|
|
11717
|
-
|
|
11894
|
+
this.logger.error('Failed to get changes from change log:', error);
|
|
11718
11895
|
return [];
|
|
11719
11896
|
}
|
|
11720
11897
|
}
|
|
@@ -11761,15 +11938,15 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11761
11938
|
}));
|
|
11762
11939
|
}
|
|
11763
11940
|
catch (error) {
|
|
11764
|
-
|
|
11941
|
+
this.logger.warn(`Failed to delete old change log entry ${key}:`, error);
|
|
11765
11942
|
}
|
|
11766
11943
|
}
|
|
11767
11944
|
if (entriesToDelete.length > 0) {
|
|
11768
|
-
|
|
11945
|
+
this.logger.debug(`Cleaned up ${entriesToDelete.length} old change log entries`);
|
|
11769
11946
|
}
|
|
11770
11947
|
}
|
|
11771
11948
|
catch (error) {
|
|
11772
|
-
|
|
11949
|
+
this.logger.warn('Failed to cleanup old change logs:', error);
|
|
11773
11950
|
}
|
|
11774
11951
|
}
|
|
11775
11952
|
/**
|
|
@@ -11824,13 +12001,13 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11824
12001
|
// Schedule automatic cleanup when lock expires
|
|
11825
12002
|
setTimeout(() => {
|
|
11826
12003
|
this.releaseLock(lockKey, lockValue).catch((error) => {
|
|
11827
|
-
|
|
12004
|
+
this.logger.warn(`Failed to auto-release expired lock ${lockKey}:`, error);
|
|
11828
12005
|
});
|
|
11829
12006
|
}, ttl);
|
|
11830
12007
|
return true;
|
|
11831
12008
|
}
|
|
11832
12009
|
catch (error) {
|
|
11833
|
-
|
|
12010
|
+
this.logger.warn(`Failed to acquire lock ${lockKey}:`, error);
|
|
11834
12011
|
return false;
|
|
11835
12012
|
}
|
|
11836
12013
|
}
|
|
@@ -11879,7 +12056,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11879
12056
|
this.activeLocks.delete(lockKey);
|
|
11880
12057
|
}
|
|
11881
12058
|
catch (error) {
|
|
11882
|
-
|
|
12059
|
+
this.logger.warn(`Failed to release lock ${lockKey}:`, error);
|
|
11883
12060
|
}
|
|
11884
12061
|
}
|
|
11885
12062
|
/**
|
|
@@ -11930,16 +12107,84 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
11930
12107
|
}));
|
|
11931
12108
|
}
|
|
11932
12109
|
catch (error) {
|
|
11933
|
-
|
|
12110
|
+
this.logger.warn(`Failed to delete expired lock ${lockKey}:`, error);
|
|
11934
12111
|
}
|
|
11935
12112
|
}
|
|
11936
12113
|
if (expiredLocks.length > 0) {
|
|
11937
|
-
|
|
12114
|
+
this.logger.debug(`Cleaned up ${expiredLocks.length} expired locks`);
|
|
11938
12115
|
}
|
|
11939
12116
|
}
|
|
11940
12117
|
catch (error) {
|
|
11941
|
-
|
|
12118
|
+
this.logger.warn('Failed to cleanup expired locks:', error);
|
|
12119
|
+
}
|
|
12120
|
+
}
|
|
12121
|
+
/**
|
|
12122
|
+
* Get nouns with pagination support
|
|
12123
|
+
* @param options Pagination options
|
|
12124
|
+
* @returns Promise that resolves to a paginated result of nouns
|
|
12125
|
+
*/
|
|
12126
|
+
async getNounsWithPagination(options = {}) {
|
|
12127
|
+
await this.ensureInitialized();
|
|
12128
|
+
const limit = options.limit || 100;
|
|
12129
|
+
const cursor = options.cursor;
|
|
12130
|
+
// Get paginated nodes
|
|
12131
|
+
const result = await this.getNodesWithPagination({
|
|
12132
|
+
limit,
|
|
12133
|
+
cursor,
|
|
12134
|
+
useCache: true
|
|
12135
|
+
});
|
|
12136
|
+
// Apply filters if provided
|
|
12137
|
+
let filteredNodes = result.nodes;
|
|
12138
|
+
if (options.filter) {
|
|
12139
|
+
// Filter by noun type
|
|
12140
|
+
if (options.filter.nounType) {
|
|
12141
|
+
const nounTypes = Array.isArray(options.filter.nounType)
|
|
12142
|
+
? options.filter.nounType
|
|
12143
|
+
: [options.filter.nounType];
|
|
12144
|
+
const filteredByType = [];
|
|
12145
|
+
for (const node of filteredNodes) {
|
|
12146
|
+
const metadata = await this.getNounMetadata(node.id);
|
|
12147
|
+
if (metadata && nounTypes.includes(metadata.type || metadata.noun)) {
|
|
12148
|
+
filteredByType.push(node);
|
|
12149
|
+
}
|
|
12150
|
+
}
|
|
12151
|
+
filteredNodes = filteredByType;
|
|
12152
|
+
}
|
|
12153
|
+
// Filter by service
|
|
12154
|
+
if (options.filter.service) {
|
|
12155
|
+
const services = Array.isArray(options.filter.service)
|
|
12156
|
+
? options.filter.service
|
|
12157
|
+
: [options.filter.service];
|
|
12158
|
+
const filteredByService = [];
|
|
12159
|
+
for (const node of filteredNodes) {
|
|
12160
|
+
const metadata = await this.getNounMetadata(node.id);
|
|
12161
|
+
if (metadata && services.includes(metadata.service)) {
|
|
12162
|
+
filteredByService.push(node);
|
|
12163
|
+
}
|
|
12164
|
+
}
|
|
12165
|
+
filteredNodes = filteredByService;
|
|
12166
|
+
}
|
|
12167
|
+
// Filter by metadata
|
|
12168
|
+
if (options.filter.metadata) {
|
|
12169
|
+
const metadataFilter = options.filter.metadata;
|
|
12170
|
+
const filteredByMetadata = [];
|
|
12171
|
+
for (const node of filteredNodes) {
|
|
12172
|
+
const metadata = await this.getNounMetadata(node.id);
|
|
12173
|
+
if (metadata) {
|
|
12174
|
+
const matches = Object.entries(metadataFilter).every(([key, value]) => metadata[key] === value);
|
|
12175
|
+
if (matches) {
|
|
12176
|
+
filteredByMetadata.push(node);
|
|
12177
|
+
}
|
|
12178
|
+
}
|
|
12179
|
+
}
|
|
12180
|
+
filteredNodes = filteredByMetadata;
|
|
12181
|
+
}
|
|
11942
12182
|
}
|
|
12183
|
+
return {
|
|
12184
|
+
items: filteredNodes,
|
|
12185
|
+
hasMore: result.hasMore,
|
|
12186
|
+
nextCursor: result.nextCursor
|
|
12187
|
+
};
|
|
11943
12188
|
}
|
|
11944
12189
|
}
|
|
11945
12190
|
|
|
@@ -12080,7 +12325,8 @@ class FileSystemStorage extends BaseStorage {
|
|
|
12080
12325
|
return {
|
|
12081
12326
|
id: parsedNode.id,
|
|
12082
12327
|
vector: parsedNode.vector,
|
|
12083
|
-
connections
|
|
12328
|
+
connections,
|
|
12329
|
+
level: parsedNode.level || 0
|
|
12084
12330
|
};
|
|
12085
12331
|
}
|
|
12086
12332
|
catch (error) {
|
|
@@ -12111,7 +12357,8 @@ class FileSystemStorage extends BaseStorage {
|
|
|
12111
12357
|
allNodes.push({
|
|
12112
12358
|
id: parsedNode.id,
|
|
12113
12359
|
vector: parsedNode.vector,
|
|
12114
|
-
connections
|
|
12360
|
+
connections,
|
|
12361
|
+
level: parsedNode.level || 0
|
|
12115
12362
|
});
|
|
12116
12363
|
}
|
|
12117
12364
|
}
|
|
@@ -12150,7 +12397,8 @@ class FileSystemStorage extends BaseStorage {
|
|
|
12150
12397
|
nouns.push({
|
|
12151
12398
|
id: parsedNode.id,
|
|
12152
12399
|
vector: parsedNode.vector,
|
|
12153
|
-
connections
|
|
12400
|
+
connections,
|
|
12401
|
+
level: parsedNode.level || 0
|
|
12154
12402
|
});
|
|
12155
12403
|
}
|
|
12156
12404
|
}
|
|
@@ -12415,6 +12663,9 @@ class FileSystemStorage extends BaseStorage {
|
|
|
12415
12663
|
await removeDirectoryContents(this.verbMetadataDir);
|
|
12416
12664
|
// Remove all files in the index directory
|
|
12417
12665
|
await removeDirectoryContents(this.indexDir);
|
|
12666
|
+
// Clear the statistics cache
|
|
12667
|
+
this.statisticsCache = null;
|
|
12668
|
+
this.statisticsModified = false;
|
|
12418
12669
|
}
|
|
12419
12670
|
/**
|
|
12420
12671
|
* Get information about storage usage and capacity
|
|
@@ -17110,6 +17361,230 @@ class CacheAutoConfigurator {
|
|
|
17110
17361
|
}
|
|
17111
17362
|
}
|
|
17112
17363
|
|
|
17364
|
+
/**
|
|
17365
|
+
* Lightweight statistics collector for Brainy
|
|
17366
|
+
* Designed to have minimal performance impact even with millions of entries
|
|
17367
|
+
*/
|
|
17368
|
+
class StatisticsCollector {
|
|
17369
|
+
constructor() {
|
|
17370
|
+
// Content type tracking (lightweight counters)
|
|
17371
|
+
this.contentTypes = new Map();
|
|
17372
|
+
// Data freshness tracking (only track timestamps, not full data)
|
|
17373
|
+
this.oldestTimestamp = Date.now();
|
|
17374
|
+
this.newestTimestamp = Date.now();
|
|
17375
|
+
this.updateTimestamps = [];
|
|
17376
|
+
// Search performance tracking (rolling window)
|
|
17377
|
+
this.searchMetrics = {
|
|
17378
|
+
totalSearches: 0,
|
|
17379
|
+
totalSearchTimeMs: 0,
|
|
17380
|
+
searchTimestamps: [],
|
|
17381
|
+
topSearchTerms: new Map()
|
|
17382
|
+
};
|
|
17383
|
+
// Verb type tracking
|
|
17384
|
+
this.verbTypes = new Map();
|
|
17385
|
+
// Storage size estimates (updated periodically, not on every operation)
|
|
17386
|
+
this.storageSizeCache = {
|
|
17387
|
+
lastUpdated: 0,
|
|
17388
|
+
sizes: {
|
|
17389
|
+
nouns: 0,
|
|
17390
|
+
verbs: 0,
|
|
17391
|
+
metadata: 0,
|
|
17392
|
+
index: 0
|
|
17393
|
+
}
|
|
17394
|
+
};
|
|
17395
|
+
this.MAX_TIMESTAMPS = 1000; // Keep last 1000 timestamps
|
|
17396
|
+
this.MAX_SEARCH_TERMS = 100; // Track top 100 search terms
|
|
17397
|
+
this.SIZE_UPDATE_INTERVAL = 60000; // Update sizes every minute
|
|
17398
|
+
}
|
|
17399
|
+
/**
|
|
17400
|
+
* Track content type (very lightweight)
|
|
17401
|
+
*/
|
|
17402
|
+
trackContentType(type) {
|
|
17403
|
+
this.contentTypes.set(type, (this.contentTypes.get(type) || 0) + 1);
|
|
17404
|
+
}
|
|
17405
|
+
/**
|
|
17406
|
+
* Track data update timestamp (lightweight)
|
|
17407
|
+
*/
|
|
17408
|
+
trackUpdate(timestamp) {
|
|
17409
|
+
const ts = timestamp || Date.now();
|
|
17410
|
+
// Update oldest/newest
|
|
17411
|
+
if (ts < this.oldestTimestamp)
|
|
17412
|
+
this.oldestTimestamp = ts;
|
|
17413
|
+
if (ts > this.newestTimestamp)
|
|
17414
|
+
this.newestTimestamp = ts;
|
|
17415
|
+
// Add to rolling window
|
|
17416
|
+
this.updateTimestamps.push({ timestamp: ts, count: 1 });
|
|
17417
|
+
// Keep window size limited
|
|
17418
|
+
if (this.updateTimestamps.length > this.MAX_TIMESTAMPS) {
|
|
17419
|
+
this.updateTimestamps.shift();
|
|
17420
|
+
}
|
|
17421
|
+
}
|
|
17422
|
+
/**
|
|
17423
|
+
* Track search performance (lightweight)
|
|
17424
|
+
*/
|
|
17425
|
+
trackSearch(searchTerm, durationMs) {
|
|
17426
|
+
this.searchMetrics.totalSearches++;
|
|
17427
|
+
this.searchMetrics.totalSearchTimeMs += durationMs;
|
|
17428
|
+
// Add to rolling window
|
|
17429
|
+
this.searchMetrics.searchTimestamps.push({
|
|
17430
|
+
timestamp: Date.now(),
|
|
17431
|
+
count: 1
|
|
17432
|
+
});
|
|
17433
|
+
// Keep window size limited
|
|
17434
|
+
if (this.searchMetrics.searchTimestamps.length > this.MAX_TIMESTAMPS) {
|
|
17435
|
+
this.searchMetrics.searchTimestamps.shift();
|
|
17436
|
+
}
|
|
17437
|
+
// Track search term (limit to top N)
|
|
17438
|
+
const termCount = (this.searchMetrics.topSearchTerms.get(searchTerm) || 0) + 1;
|
|
17439
|
+
this.searchMetrics.topSearchTerms.set(searchTerm, termCount);
|
|
17440
|
+
// Prune if too many terms
|
|
17441
|
+
if (this.searchMetrics.topSearchTerms.size > this.MAX_SEARCH_TERMS * 2) {
|
|
17442
|
+
this.pruneSearchTerms();
|
|
17443
|
+
}
|
|
17444
|
+
}
|
|
17445
|
+
/**
|
|
17446
|
+
* Track verb type (lightweight)
|
|
17447
|
+
*/
|
|
17448
|
+
trackVerbType(type) {
|
|
17449
|
+
this.verbTypes.set(type, (this.verbTypes.get(type) || 0) + 1);
|
|
17450
|
+
}
|
|
17451
|
+
/**
|
|
17452
|
+
* Update storage size estimates (called periodically, not on every operation)
|
|
17453
|
+
*/
|
|
17454
|
+
updateStorageSizes(sizes) {
|
|
17455
|
+
this.storageSizeCache = {
|
|
17456
|
+
lastUpdated: Date.now(),
|
|
17457
|
+
sizes
|
|
17458
|
+
};
|
|
17459
|
+
}
|
|
17460
|
+
/**
|
|
17461
|
+
* Get comprehensive statistics
|
|
17462
|
+
*/
|
|
17463
|
+
getStatistics() {
|
|
17464
|
+
const now = Date.now();
|
|
17465
|
+
const hourAgo = now - 3600000;
|
|
17466
|
+
const dayAgo = now - 86400000;
|
|
17467
|
+
const weekAgo = now - 604800000;
|
|
17468
|
+
const monthAgo = now - 2592000000;
|
|
17469
|
+
// Calculate data freshness
|
|
17470
|
+
const updatesLastHour = this.updateTimestamps.filter(t => t.timestamp > hourAgo).length;
|
|
17471
|
+
const updatesLastDay = this.updateTimestamps.filter(t => t.timestamp > dayAgo).length;
|
|
17472
|
+
// Calculate age distribution
|
|
17473
|
+
const ageDistribution = {
|
|
17474
|
+
last24h: 0,
|
|
17475
|
+
last7d: 0,
|
|
17476
|
+
last30d: 0,
|
|
17477
|
+
older: 0
|
|
17478
|
+
};
|
|
17479
|
+
// Estimate based on update patterns (not scanning all data)
|
|
17480
|
+
const totalUpdates = this.updateTimestamps.length;
|
|
17481
|
+
if (totalUpdates > 0) {
|
|
17482
|
+
const recentUpdates = this.updateTimestamps.filter(t => t.timestamp > dayAgo).length;
|
|
17483
|
+
const weekUpdates = this.updateTimestamps.filter(t => t.timestamp > weekAgo).length;
|
|
17484
|
+
const monthUpdates = this.updateTimestamps.filter(t => t.timestamp > monthAgo).length;
|
|
17485
|
+
ageDistribution.last24h = Math.round((recentUpdates / totalUpdates) * 100);
|
|
17486
|
+
ageDistribution.last7d = Math.round(((weekUpdates - recentUpdates) / totalUpdates) * 100);
|
|
17487
|
+
ageDistribution.last30d = Math.round(((monthUpdates - weekUpdates) / totalUpdates) * 100);
|
|
17488
|
+
ageDistribution.older = 100 - ageDistribution.last24h - ageDistribution.last7d - ageDistribution.last30d;
|
|
17489
|
+
}
|
|
17490
|
+
// Calculate search metrics
|
|
17491
|
+
const searchesLastHour = this.searchMetrics.searchTimestamps.filter(t => t.timestamp > hourAgo).length;
|
|
17492
|
+
const searchesLastDay = this.searchMetrics.searchTimestamps.filter(t => t.timestamp > dayAgo).length;
|
|
17493
|
+
const avgSearchTime = this.searchMetrics.totalSearches > 0
|
|
17494
|
+
? this.searchMetrics.totalSearchTimeMs / this.searchMetrics.totalSearches
|
|
17495
|
+
: 0;
|
|
17496
|
+
// Get top search terms
|
|
17497
|
+
const topSearchTerms = Array.from(this.searchMetrics.topSearchTerms.entries())
|
|
17498
|
+
.sort((a, b) => b[1] - a[1])
|
|
17499
|
+
.slice(0, 10)
|
|
17500
|
+
.map(([term]) => term);
|
|
17501
|
+
// Calculate storage metrics
|
|
17502
|
+
const totalSize = Object.values(this.storageSizeCache.sizes).reduce((a, b) => a + b, 0);
|
|
17503
|
+
return {
|
|
17504
|
+
contentTypes: Object.fromEntries(this.contentTypes),
|
|
17505
|
+
dataFreshness: {
|
|
17506
|
+
oldestEntry: new Date(this.oldestTimestamp).toISOString(),
|
|
17507
|
+
newestEntry: new Date(this.newestTimestamp).toISOString(),
|
|
17508
|
+
updatesLastHour,
|
|
17509
|
+
updatesLastDay,
|
|
17510
|
+
ageDistribution
|
|
17511
|
+
},
|
|
17512
|
+
storageMetrics: {
|
|
17513
|
+
totalSizeBytes: totalSize,
|
|
17514
|
+
nounsSizeBytes: this.storageSizeCache.sizes.nouns,
|
|
17515
|
+
verbsSizeBytes: this.storageSizeCache.sizes.verbs,
|
|
17516
|
+
metadataSizeBytes: this.storageSizeCache.sizes.metadata,
|
|
17517
|
+
indexSizeBytes: this.storageSizeCache.sizes.index
|
|
17518
|
+
},
|
|
17519
|
+
searchMetrics: {
|
|
17520
|
+
totalSearches: this.searchMetrics.totalSearches,
|
|
17521
|
+
averageSearchTimeMs: avgSearchTime,
|
|
17522
|
+
searchesLastHour,
|
|
17523
|
+
searchesLastDay,
|
|
17524
|
+
topSearchTerms
|
|
17525
|
+
},
|
|
17526
|
+
verbStatistics: {
|
|
17527
|
+
totalVerbs: Array.from(this.verbTypes.values()).reduce((a, b) => a + b, 0),
|
|
17528
|
+
verbTypes: Object.fromEntries(this.verbTypes),
|
|
17529
|
+
averageConnectionsPerVerb: 2 // Verbs connect 2 nouns
|
|
17530
|
+
}
|
|
17531
|
+
};
|
|
17532
|
+
}
|
|
17533
|
+
/**
|
|
17534
|
+
* Merge statistics from storage (for distributed systems)
|
|
17535
|
+
*/
|
|
17536
|
+
mergeFromStorage(stored) {
|
|
17537
|
+
// Merge content types
|
|
17538
|
+
if (stored.contentTypes) {
|
|
17539
|
+
for (const [type, count] of Object.entries(stored.contentTypes)) {
|
|
17540
|
+
this.contentTypes.set(type, count);
|
|
17541
|
+
}
|
|
17542
|
+
}
|
|
17543
|
+
// Merge verb types
|
|
17544
|
+
if (stored.verbStatistics?.verbTypes) {
|
|
17545
|
+
for (const [type, count] of Object.entries(stored.verbStatistics.verbTypes)) {
|
|
17546
|
+
this.verbTypes.set(type, count);
|
|
17547
|
+
}
|
|
17548
|
+
}
|
|
17549
|
+
// Merge search metrics
|
|
17550
|
+
if (stored.searchMetrics) {
|
|
17551
|
+
this.searchMetrics.totalSearches = stored.searchMetrics.totalSearches || 0;
|
|
17552
|
+
this.searchMetrics.totalSearchTimeMs = (stored.searchMetrics.averageSearchTimeMs || 0) * this.searchMetrics.totalSearches;
|
|
17553
|
+
}
|
|
17554
|
+
// Merge data freshness
|
|
17555
|
+
if (stored.dataFreshness) {
|
|
17556
|
+
this.oldestTimestamp = new Date(stored.dataFreshness.oldestEntry).getTime();
|
|
17557
|
+
this.newestTimestamp = new Date(stored.dataFreshness.newestEntry).getTime();
|
|
17558
|
+
}
|
|
17559
|
+
}
|
|
17560
|
+
/**
|
|
17561
|
+
* Reset statistics (for testing)
|
|
17562
|
+
*/
|
|
17563
|
+
reset() {
|
|
17564
|
+
this.contentTypes.clear();
|
|
17565
|
+
this.verbTypes.clear();
|
|
17566
|
+
this.updateTimestamps = [];
|
|
17567
|
+
this.searchMetrics = {
|
|
17568
|
+
totalSearches: 0,
|
|
17569
|
+
totalSearchTimeMs: 0,
|
|
17570
|
+
searchTimestamps: [],
|
|
17571
|
+
topSearchTerms: new Map()
|
|
17572
|
+
};
|
|
17573
|
+
this.oldestTimestamp = Date.now();
|
|
17574
|
+
this.newestTimestamp = Date.now();
|
|
17575
|
+
}
|
|
17576
|
+
pruneSearchTerms() {
|
|
17577
|
+
// Keep only top N search terms
|
|
17578
|
+
const sorted = Array.from(this.searchMetrics.topSearchTerms.entries())
|
|
17579
|
+
.sort((a, b) => b[1] - a[1])
|
|
17580
|
+
.slice(0, this.MAX_SEARCH_TERMS);
|
|
17581
|
+
this.searchMetrics.topSearchTerms.clear();
|
|
17582
|
+
for (const [term, count] of sorted) {
|
|
17583
|
+
this.searchMetrics.topSearchTerms.set(term, count);
|
|
17584
|
+
}
|
|
17585
|
+
}
|
|
17586
|
+
}
|
|
17587
|
+
|
|
17113
17588
|
/**
|
|
17114
17589
|
* BrainyData
|
|
17115
17590
|
* Main class that provides the vector database functionality
|
|
@@ -17170,6 +17645,8 @@ class BrainyData {
|
|
|
17170
17645
|
this.operationalMode = null;
|
|
17171
17646
|
this.domainDetector = null;
|
|
17172
17647
|
this.healthMonitor = null;
|
|
17648
|
+
// Statistics collector
|
|
17649
|
+
this.statisticsCollector = new StatisticsCollector();
|
|
17173
17650
|
// Set dimensions to fixed value of 512 (Universal Sentence Encoder dimension)
|
|
17174
17651
|
this._dimensions = 512;
|
|
17175
17652
|
// Set distance function
|
|
@@ -17433,7 +17910,8 @@ class BrainyData {
|
|
|
17433
17910
|
}
|
|
17434
17911
|
// Adapt cache configuration based on performance (every few updates)
|
|
17435
17912
|
// Only adapt every 5th update to avoid over-optimization
|
|
17436
|
-
const updateCount = Math.floor((Date.now() - (this.lastUpdateTime || 0)) /
|
|
17913
|
+
const updateCount = Math.floor((Date.now() - (this.lastUpdateTime || 0)) /
|
|
17914
|
+
this.realtimeUpdateConfig.interval);
|
|
17437
17915
|
if (updateCount % 5 === 0) {
|
|
17438
17916
|
this.adaptCacheConfiguration();
|
|
17439
17917
|
}
|
|
@@ -17751,6 +18229,16 @@ class BrainyData {
|
|
|
17751
18229
|
// Continue initialization even if remote connection fails
|
|
17752
18230
|
}
|
|
17753
18231
|
}
|
|
18232
|
+
// Initialize statistics collector with existing data
|
|
18233
|
+
try {
|
|
18234
|
+
const existingStats = await this.storage.getStatistics();
|
|
18235
|
+
if (existingStats) {
|
|
18236
|
+
this.statisticsCollector.mergeFromStorage(existingStats);
|
|
18237
|
+
}
|
|
18238
|
+
}
|
|
18239
|
+
catch (e) {
|
|
18240
|
+
// Ignore errors loading existing statistics
|
|
18241
|
+
}
|
|
17754
18242
|
this.isInitialized = true;
|
|
17755
18243
|
this.isInitializing = false;
|
|
17756
18244
|
// Start real-time updates if enabled
|
|
@@ -17962,13 +18450,15 @@ class BrainyData {
|
|
|
17962
18450
|
try {
|
|
17963
18451
|
if (this.writeOnly) {
|
|
17964
18452
|
// In write-only mode, check storage directly
|
|
17965
|
-
existingNoun =
|
|
18453
|
+
existingNoun =
|
|
18454
|
+
(await this.storage.getNoun(options.id)) ?? undefined;
|
|
17966
18455
|
}
|
|
17967
18456
|
else {
|
|
17968
18457
|
// In normal mode, check index first, then storage
|
|
17969
18458
|
existingNoun = this.index.getNouns().get(options.id);
|
|
17970
18459
|
if (!existingNoun) {
|
|
17971
|
-
existingNoun =
|
|
18460
|
+
existingNoun =
|
|
18461
|
+
(await this.storage.getNoun(options.id)) ?? undefined;
|
|
17972
18462
|
}
|
|
17973
18463
|
}
|
|
17974
18464
|
if (existingNoun) {
|
|
@@ -18003,6 +18493,7 @@ class BrainyData {
|
|
|
18003
18493
|
id,
|
|
18004
18494
|
vector,
|
|
18005
18495
|
connections: new Map(),
|
|
18496
|
+
level: 0, // Default level for new nodes
|
|
18006
18497
|
metadata: undefined // Will be set separately
|
|
18007
18498
|
};
|
|
18008
18499
|
}
|
|
@@ -18076,17 +18567,24 @@ class BrainyData {
|
|
|
18076
18567
|
// Domain already specified, keep it
|
|
18077
18568
|
const domainInfo = this.domainDetector.detectDomain(metadataToSave);
|
|
18078
18569
|
if (domainInfo.domainMetadata) {
|
|
18079
|
-
|
|
18570
|
+
;
|
|
18571
|
+
metadataToSave.domainMetadata =
|
|
18572
|
+
domainInfo.domainMetadata;
|
|
18080
18573
|
}
|
|
18081
18574
|
}
|
|
18082
18575
|
else {
|
|
18083
18576
|
// Try to detect domain from the data
|
|
18084
|
-
const dataToAnalyze = Array.isArray(vectorOrData)
|
|
18577
|
+
const dataToAnalyze = Array.isArray(vectorOrData)
|
|
18578
|
+
? metadata
|
|
18579
|
+
: vectorOrData;
|
|
18085
18580
|
const domainInfo = this.domainDetector.detectDomain(dataToAnalyze);
|
|
18086
18581
|
if (domainInfo.domain) {
|
|
18582
|
+
;
|
|
18087
18583
|
metadataToSave.domain = domainInfo.domain;
|
|
18088
18584
|
if (domainInfo.domainMetadata) {
|
|
18089
|
-
|
|
18585
|
+
;
|
|
18586
|
+
metadataToSave.domainMetadata =
|
|
18587
|
+
domainInfo.domainMetadata;
|
|
18090
18588
|
}
|
|
18091
18589
|
}
|
|
18092
18590
|
}
|
|
@@ -18101,10 +18599,19 @@ class BrainyData {
|
|
|
18101
18599
|
// Track metadata statistics
|
|
18102
18600
|
const metadataService = this.getServiceName(options);
|
|
18103
18601
|
await this.storage.incrementStatistic('metadata', metadataService);
|
|
18602
|
+
// Track content type if it's a GraphNoun
|
|
18603
|
+
if (metadataToSave &&
|
|
18604
|
+
typeof metadataToSave === 'object' &&
|
|
18605
|
+
'noun' in metadataToSave) {
|
|
18606
|
+
this.statisticsCollector.trackContentType(metadataToSave.noun);
|
|
18607
|
+
}
|
|
18608
|
+
// Track update timestamp
|
|
18609
|
+
this.statisticsCollector.trackUpdate();
|
|
18104
18610
|
}
|
|
18105
18611
|
}
|
|
18106
|
-
// Update HNSW index size
|
|
18107
|
-
|
|
18612
|
+
// Update HNSW index size with actual index size
|
|
18613
|
+
const indexSize = this.index.size();
|
|
18614
|
+
await this.storage.updateHnswIndexSize(indexSize);
|
|
18108
18615
|
// Update health metrics if in distributed mode
|
|
18109
18616
|
if (this.healthMonitor) {
|
|
18110
18617
|
const vectorCount = await this.getNounCount();
|
|
@@ -18578,7 +19085,7 @@ class BrainyData {
|
|
|
18578
19085
|
let startIndex = 0;
|
|
18579
19086
|
// If cursor provided, find starting position
|
|
18580
19087
|
if (options.cursor) {
|
|
18581
|
-
startIndex = allResults.findIndex(r => r.id === options.cursor.lastId &&
|
|
19088
|
+
startIndex = allResults.findIndex((r) => r.id === options.cursor.lastId &&
|
|
18582
19089
|
Math.abs(r.score - options.cursor.lastScore) < 0.0001);
|
|
18583
19090
|
if (startIndex >= 0) {
|
|
18584
19091
|
startIndex += 1; // Start after the cursor position
|
|
@@ -18595,7 +19102,8 @@ class BrainyData {
|
|
|
18595
19102
|
}
|
|
18596
19103
|
// Create cursor for next page
|
|
18597
19104
|
let nextCursor;
|
|
18598
|
-
const hasMoreResults =
|
|
19105
|
+
const hasMoreResults = startIndex + results.length < allResults.length ||
|
|
19106
|
+
allResults.length >= searchK;
|
|
18599
19107
|
if (results.length > 0 && hasMoreResults) {
|
|
18600
19108
|
const lastResult = results[results.length - 1];
|
|
18601
19109
|
nextCursor = {
|
|
@@ -18679,7 +19187,7 @@ class BrainyData {
|
|
|
18679
19187
|
});
|
|
18680
19188
|
}
|
|
18681
19189
|
// Filter out placeholder nouns from search results
|
|
18682
|
-
searchResults = searchResults.filter(result => {
|
|
19190
|
+
searchResults = searchResults.filter((result) => {
|
|
18683
19191
|
if (result.metadata && typeof result.metadata === 'object') {
|
|
18684
19192
|
const metadata = result.metadata;
|
|
18685
19193
|
// Exclude placeholder nouns from search results
|
|
@@ -18787,7 +19295,7 @@ class BrainyData {
|
|
|
18787
19295
|
// In write-only mode, query storage directly since index is not loaded
|
|
18788
19296
|
if (this.writeOnly) {
|
|
18789
19297
|
try {
|
|
18790
|
-
noun = await this.storage.getNoun(id) ?? undefined;
|
|
19298
|
+
noun = (await this.storage.getNoun(id)) ?? undefined;
|
|
18791
19299
|
}
|
|
18792
19300
|
catch (storageError) {
|
|
18793
19301
|
// If storage lookup fails, return null (noun doesn't exist)
|
|
@@ -18800,7 +19308,7 @@ class BrainyData {
|
|
|
18800
19308
|
// If not found in index, fallback to storage (for race conditions)
|
|
18801
19309
|
if (!noun && this.storage) {
|
|
18802
19310
|
try {
|
|
18803
|
-
noun = await this.storage.getNoun(id) ?? undefined;
|
|
19311
|
+
noun = (await this.storage.getNoun(id)) ?? undefined;
|
|
18804
19312
|
}
|
|
18805
19313
|
catch (storageError) {
|
|
18806
19314
|
// Storage lookup failed, noun doesn't exist
|
|
@@ -19192,6 +19700,7 @@ class BrainyData {
|
|
|
19192
19700
|
id: sourceId,
|
|
19193
19701
|
vector: sourcePlaceholderVector,
|
|
19194
19702
|
connections: new Map(),
|
|
19703
|
+
level: 0,
|
|
19195
19704
|
metadata: sourceMetadata
|
|
19196
19705
|
};
|
|
19197
19706
|
// Create placeholder target noun
|
|
@@ -19212,6 +19721,7 @@ class BrainyData {
|
|
|
19212
19721
|
id: targetId,
|
|
19213
19722
|
vector: targetPlaceholderVector,
|
|
19214
19723
|
connections: new Map(),
|
|
19724
|
+
level: 0,
|
|
19215
19725
|
metadata: targetMetadata
|
|
19216
19726
|
};
|
|
19217
19727
|
// Save placeholder nouns to storage (but skip indexing for speed)
|
|
@@ -19448,8 +19958,11 @@ class BrainyData {
|
|
|
19448
19958
|
// Track verb statistics
|
|
19449
19959
|
const serviceForStats = this.getServiceName(options);
|
|
19450
19960
|
await this.storage.incrementStatistic('verb', serviceForStats);
|
|
19451
|
-
//
|
|
19452
|
-
|
|
19961
|
+
// Track verb type
|
|
19962
|
+
this.statisticsCollector.trackVerbType(verbMetadata.verb);
|
|
19963
|
+
// Update HNSW index size with actual index size
|
|
19964
|
+
const indexSize = this.index.size();
|
|
19965
|
+
await this.storage.updateHnswIndexSize(indexSize);
|
|
19453
19966
|
// Invalidate search cache since verb data has changed
|
|
19454
19967
|
this.searchCache.invalidateOnDataChange('add');
|
|
19455
19968
|
return id;
|
|
@@ -19674,6 +20187,8 @@ class BrainyData {
|
|
|
19674
20187
|
await this.index.clear();
|
|
19675
20188
|
// Clear storage
|
|
19676
20189
|
await this.storage.clear();
|
|
20190
|
+
// Reset statistics collector
|
|
20191
|
+
this.statisticsCollector = new StatisticsCollector();
|
|
19677
20192
|
// Clear search cache since all data has been removed
|
|
19678
20193
|
this.searchCache.invalidateOnDataChange('delete');
|
|
19679
20194
|
}
|
|
@@ -19727,7 +20242,8 @@ class BrainyData {
|
|
|
19727
20242
|
// Apply new cache configuration
|
|
19728
20243
|
this.searchCache.updateConfig(newConfig.cacheConfig);
|
|
19729
20244
|
// Apply new real-time update configuration if needed
|
|
19730
|
-
if (newConfig.realtimeConfig.enabled !==
|
|
20245
|
+
if (newConfig.realtimeConfig.enabled !==
|
|
20246
|
+
this.realtimeUpdateConfig.enabled ||
|
|
19731
20247
|
newConfig.realtimeConfig.interval !== this.realtimeUpdateConfig.interval) {
|
|
19732
20248
|
const wasEnabled = this.realtimeUpdateConfig.enabled;
|
|
19733
20249
|
this.realtimeUpdateConfig = {
|
|
@@ -19817,6 +20333,41 @@ class BrainyData {
|
|
|
19817
20333
|
// Call the flushStatisticsToStorage method on the storage adapter
|
|
19818
20334
|
await this.storage.flushStatisticsToStorage();
|
|
19819
20335
|
}
|
|
20336
|
+
/**
|
|
20337
|
+
* Update storage sizes if needed (called periodically for performance)
|
|
20338
|
+
*/
|
|
20339
|
+
async updateStorageSizesIfNeeded() {
|
|
20340
|
+
// Only update every minute to avoid performance impact
|
|
20341
|
+
const now = Date.now();
|
|
20342
|
+
const lastUpdate = this.lastStorageSizeUpdate || 0;
|
|
20343
|
+
if (now - lastUpdate < 60000) {
|
|
20344
|
+
return; // Skip if updated recently
|
|
20345
|
+
}
|
|
20346
|
+
this.lastStorageSizeUpdate = now;
|
|
20347
|
+
try {
|
|
20348
|
+
// Estimate sizes based on counts and average sizes
|
|
20349
|
+
const stats = await this.storage.getStatistics();
|
|
20350
|
+
if (stats) {
|
|
20351
|
+
const avgNounSize = 2048; // ~2KB per noun (vector + metadata)
|
|
20352
|
+
const avgVerbSize = 512; // ~0.5KB per verb
|
|
20353
|
+
const avgMetadataSize = 256; // ~0.25KB per metadata entry
|
|
20354
|
+
const avgIndexEntrySize = 128; // ~128 bytes per index entry
|
|
20355
|
+
// Calculate total counts
|
|
20356
|
+
const totalNouns = Object.values(stats.nounCount).reduce((a, b) => a + b, 0);
|
|
20357
|
+
const totalVerbs = Object.values(stats.verbCount).reduce((a, b) => a + b, 0);
|
|
20358
|
+
const totalMetadata = Object.values(stats.metadataCount).reduce((a, b) => a + b, 0);
|
|
20359
|
+
this.statisticsCollector.updateStorageSizes({
|
|
20360
|
+
nouns: totalNouns * avgNounSize,
|
|
20361
|
+
verbs: totalVerbs * avgVerbSize,
|
|
20362
|
+
metadata: totalMetadata * avgMetadataSize,
|
|
20363
|
+
index: stats.hnswIndexSize * avgIndexEntrySize
|
|
20364
|
+
});
|
|
20365
|
+
}
|
|
20366
|
+
}
|
|
20367
|
+
catch (error) {
|
|
20368
|
+
// Ignore errors in size calculation
|
|
20369
|
+
}
|
|
20370
|
+
}
|
|
19820
20371
|
/**
|
|
19821
20372
|
* Get statistics about the current state of the database
|
|
19822
20373
|
* @param options Additional options for retrieving statistics
|
|
@@ -19891,6 +20442,40 @@ class BrainyData {
|
|
|
19891
20442
|
relate: result.verbCount,
|
|
19892
20443
|
total: result.nounCount + result.verbCount + result.metadataCount
|
|
19893
20444
|
};
|
|
20445
|
+
// Add extended statistics if requested
|
|
20446
|
+
if (true) {
|
|
20447
|
+
// Always include for now
|
|
20448
|
+
// Add index health metrics
|
|
20449
|
+
try {
|
|
20450
|
+
const indexHealth = this.index.getIndexHealth();
|
|
20451
|
+
result.indexHealth = indexHealth;
|
|
20452
|
+
}
|
|
20453
|
+
catch (e) {
|
|
20454
|
+
// Index health not available
|
|
20455
|
+
}
|
|
20456
|
+
// Add cache metrics
|
|
20457
|
+
try {
|
|
20458
|
+
const cacheStats = this.searchCache.getStats();
|
|
20459
|
+
result.cacheMetrics = cacheStats;
|
|
20460
|
+
}
|
|
20461
|
+
catch (e) {
|
|
20462
|
+
// Cache stats not available
|
|
20463
|
+
}
|
|
20464
|
+
// Add memory usage
|
|
20465
|
+
if (typeof process !== 'undefined' && process.memoryUsage) {
|
|
20466
|
+
;
|
|
20467
|
+
result.memoryUsage = process.memoryUsage().heapUsed;
|
|
20468
|
+
}
|
|
20469
|
+
// Add last updated timestamp
|
|
20470
|
+
;
|
|
20471
|
+
result.lastUpdated =
|
|
20472
|
+
stats.lastUpdated || new Date().toISOString();
|
|
20473
|
+
// Add enhanced statistics from collector
|
|
20474
|
+
const collectorStats = this.statisticsCollector.getStatistics();
|
|
20475
|
+
Object.assign(result, collectorStats);
|
|
20476
|
+
// Update storage sizes if needed (only periodically for performance)
|
|
20477
|
+
await this.updateStorageSizesIfNeeded();
|
|
20478
|
+
}
|
|
19894
20479
|
return result;
|
|
19895
20480
|
}
|
|
19896
20481
|
// If statistics are not available, return zeros instead of calculating on-demand
|
|
@@ -20250,15 +20835,20 @@ class BrainyData {
|
|
|
20250
20835
|
await this.ensureInitialized();
|
|
20251
20836
|
// Check if database is in write-only mode
|
|
20252
20837
|
this.checkWriteOnly();
|
|
20838
|
+
const searchStartTime = Date.now();
|
|
20253
20839
|
try {
|
|
20254
20840
|
// Embed the query text
|
|
20255
20841
|
const queryVector = await this.embed(query);
|
|
20256
20842
|
// Search using the embedded vector
|
|
20257
|
-
|
|
20843
|
+
const results = await this.search(queryVector, k, {
|
|
20258
20844
|
nounTypes: options.nounTypes,
|
|
20259
20845
|
includeVerbs: options.includeVerbs,
|
|
20260
20846
|
searchMode: options.searchMode
|
|
20261
20847
|
});
|
|
20848
|
+
// Track search performance
|
|
20849
|
+
const duration = Date.now() - searchStartTime;
|
|
20850
|
+
this.statisticsCollector.trackSearch(query, duration);
|
|
20851
|
+
return results;
|
|
20262
20852
|
}
|
|
20263
20853
|
catch (error) {
|
|
20264
20854
|
console.error('Failed to search with text query:', error);
|
|
@@ -127662,5 +128252,5 @@ var _osShim$1 = /*#__PURE__*/Object.freeze({
|
|
|
127662
128252
|
totalmem: totalmem
|
|
127663
128253
|
});
|
|
127664
128254
|
|
|
127665
|
-
export { AugmentationType, BrainyData, BrainyMCPAdapter, BrainyMCPService, ExecutionMode, FileSystemStorage, FileSystemStorageAugmentation, HNSWIndex, HNSWIndexOptimized, MCPAugmentationToolset, MCPRequestType, MCP_VERSION, MemoryStorage, MemoryStorageAugmentation, NounType, OPFSStorage, OPFSStorageAugmentation, Pipeline, S3CompatibleStorage as R2Storage, S3CompatibleStorage, SequentialPipeline, ServerSearchActivationAugmentation, ServerSearchConduitAugmentation, StreamlinedExecutionMode, UniversalSentenceEncoder$1 as UniversalSentenceEncoder, VerbType, WebRTCConduitAugmentation, WebSocketConduitAugmentation, addWebSocketSupport, applyTensorFlowPatch, areWebWorkersAvailable, areWorkerThreadsAvailable, areWorkerThreadsAvailableSync, augmentationPipeline, availableAugmentations, cleanupWorkerPools, cosineDistance$1 as cosineDistance, createAugmentationRegistryPlugin, createAugmentationRegistryRollupPlugin, createConduitAugmentation, createEmbeddingFunction, createMemoryAugmentation, createPipeline, createSenseAugmentation, createServerSearchAugmentations, createStorage, createStreamingPipeline, createTensorFlowEmbeddingFunction, createThreadedEmbeddingFunction, defaultEmbeddingFunction, dotProductDistance, environment, euclideanDistance, executeAugmentation, executeByType, executeInThread, executeSingle, executeStreamlined, getAugmentationsByType, getNounTypeMap, getNounTypes, getStatistics, getVerbTypeMap, getVerbTypes, initializeAugmentationPipeline, isBrowser$1 as isBrowser, isNode, isThreadingAvailable, isThreadingAvailableAsync, isWebWorker, loadAugmentationModule, loadAugmentationsFromModules, manhattanDistance, pipeline, processStaticData, processStreamingData, registerAugmentation, sequentialPipeline, setAugmentationEnabled };
|
|
128255
|
+
export { AugmentationType, BrainyData, BrainyMCPAdapter, BrainyMCPService, ExecutionMode, FileSystemStorage, FileSystemStorageAugmentation, HNSWIndex, HNSWIndexOptimized, LogLevel, MCPAugmentationToolset, MCPRequestType, MCP_VERSION, MemoryStorage, MemoryStorageAugmentation, NounType, OPFSStorage, OPFSStorageAugmentation, Pipeline, S3CompatibleStorage as R2Storage, S3CompatibleStorage, SequentialPipeline, ServerSearchActivationAugmentation, ServerSearchConduitAugmentation, StreamlinedExecutionMode, UniversalSentenceEncoder$1 as UniversalSentenceEncoder, VerbType, WebRTCConduitAugmentation, WebSocketConduitAugmentation, addWebSocketSupport, applyTensorFlowPatch, areWebWorkersAvailable, areWorkerThreadsAvailable, areWorkerThreadsAvailableSync, augmentationPipeline, availableAugmentations, cleanupWorkerPools, configureLogger, cosineDistance$1 as cosineDistance, createAugmentationRegistryPlugin, createAugmentationRegistryRollupPlugin, createConduitAugmentation, createEmbeddingFunction, createMemoryAugmentation, createModuleLogger, createPipeline, createSenseAugmentation, createServerSearchAugmentations, createStorage, createStreamingPipeline, createTensorFlowEmbeddingFunction, createThreadedEmbeddingFunction, defaultEmbeddingFunction, dotProductDistance, environment, euclideanDistance, executeAugmentation, executeByType, executeInThread, executeSingle, executeStreamlined, getAugmentationsByType, getNounTypeMap, getNounTypes, getStatistics, getVerbTypeMap, getVerbTypes, initializeAugmentationPipeline, isBrowser$1 as isBrowser, isNode, isThreadingAvailable, isThreadingAvailableAsync, isWebWorker, loadAugmentationModule, loadAugmentationsFromModules, logger, manhattanDistance, pipeline, processStaticData, processStreamingData, registerAugmentation, sequentialPipeline, setAugmentationEnabled };
|
|
127666
128256
|
//# sourceMappingURL=unified.js.map
|