@soulcraft/brainy 0.20.0 → 0.22.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/README.md +1 -1
- package/dist/augmentations/conduitAugmentations.js +1158 -0
- package/dist/augmentations/conduitAugmentations.js.map +1 -0
- package/dist/augmentations/memoryAugmentations.js +255 -0
- package/dist/augmentations/memoryAugmentations.js.map +1 -0
- package/dist/augmentations/serverSearchAugmentations.js +531 -0
- package/dist/augmentations/serverSearchAugmentations.js.map +1 -0
- package/dist/coreTypes.d.ts +12 -0
- package/dist/examples/basicUsage.js +128 -0
- package/dist/examples/basicUsage.js.map +1 -0
- package/dist/hnsw/hnswIndex.js +550 -0
- package/dist/hnsw/hnswIndex.js.map +1 -0
- package/dist/hnsw/hnswIndexOptimized.js +441 -0
- package/dist/hnsw/hnswIndexOptimized.js.map +1 -0
- package/dist/mcp/brainyMCPAdapter.js +142 -0
- package/dist/mcp/brainyMCPAdapter.js.map +1 -0
- package/dist/mcp/brainyMCPService.js +248 -0
- package/dist/mcp/brainyMCPService.js.map +1 -0
- package/dist/mcp/index.js +17 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/mcpAugmentationToolset.js +180 -0
- package/dist/mcp/mcpAugmentationToolset.js.map +1 -0
- package/dist/storage/adapters/baseStorageAdapter.d.ts +14 -0
- package/dist/storage/adapters/baseStorageAdapter.d.ts.map +1 -1
- package/dist/storage/adapters/baseStorageAdapter.js +233 -0
- package/dist/storage/adapters/baseStorageAdapter.js.map +1 -0
- package/dist/storage/adapters/fileSystemStorage.d.ts +27 -27
- package/dist/storage/adapters/fileSystemStorage.d.ts.map +1 -1
- package/dist/storage/adapters/fileSystemStorage.js +568 -0
- package/dist/storage/adapters/fileSystemStorage.js.map +1 -0
- package/dist/storage/adapters/memoryStorage.d.ts +27 -27
- package/dist/storage/adapters/memoryStorage.d.ts.map +1 -1
- package/dist/storage/adapters/memoryStorage.js +300 -0
- package/dist/storage/adapters/memoryStorage.js.map +1 -0
- package/dist/storage/adapters/opfsStorage.d.ts +64 -6
- package/dist/storage/adapters/opfsStorage.d.ts.map +1 -1
- package/dist/storage/adapters/opfsStorage.js +778 -0
- package/dist/storage/adapters/opfsStorage.js.map +1 -0
- package/dist/storage/adapters/s3CompatibleStorage.d.ts +86 -1
- package/dist/storage/adapters/s3CompatibleStorage.d.ts.map +1 -1
- package/dist/storage/adapters/s3CompatibleStorage.js +1021 -0
- package/dist/storage/adapters/s3CompatibleStorage.js.map +1 -0
- package/dist/storage/baseStorage.d.ts +24 -24
- package/dist/storage/baseStorage.d.ts.map +1 -1
- package/dist/storage/baseStorage.js +126 -0
- package/dist/storage/baseStorage.js.map +1 -0
- package/dist/storage/storageFactory.js +183 -0
- package/dist/storage/storageFactory.js.map +1 -0
- package/dist/types/augmentations.js +16 -0
- package/dist/types/augmentations.js.map +1 -0
- package/dist/types/brainyDataInterface.js +8 -0
- package/dist/types/brainyDataInterface.js.map +1 -0
- package/dist/types/fileSystemTypes.js +8 -0
- package/dist/types/fileSystemTypes.js.map +1 -0
- package/dist/types/graphTypes.d.ts +2 -1
- package/dist/types/graphTypes.d.ts.map +1 -1
- package/dist/types/graphTypes.js +36 -0
- package/dist/types/graphTypes.js.map +1 -0
- package/dist/types/mcpTypes.js +22 -0
- package/dist/types/mcpTypes.js.map +1 -0
- package/dist/types/pipelineTypes.js +7 -0
- package/dist/types/pipelineTypes.js.map +1 -0
- package/dist/types/tensorflowTypes.js +6 -0
- package/dist/types/tensorflowTypes.js.map +1 -0
- package/dist/unified.js +983 -336
- package/dist/unified.min.js +623 -623
- package/dist/utils/distance.js +239 -0
- package/dist/utils/distance.js.map +1 -0
- package/dist/utils/embedding.js +622 -0
- package/dist/utils/embedding.js.map +1 -0
- package/dist/utils/environment.js +75 -0
- package/dist/utils/environment.js.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/statistics.js +25 -0
- package/dist/utils/statistics.js.map +1 -0
- package/dist/utils/tensorflowUtils.js +25 -0
- package/dist/utils/tensorflowUtils.js.map +1 -0
- package/dist/utils/textEncoding.js +281 -0
- package/dist/utils/textEncoding.js.map +1 -0
- package/dist/utils/workerUtils.js +458 -0
- package/dist/utils/workerUtils.js.map +1 -0
- package/package.json +4 -3
package/dist/unified.js
CHANGED
|
@@ -5195,19 +5195,115 @@ class HNSWIndexOptimized extends HNSWIndex {
|
|
|
5195
5195
|
* Base class for storage adapters that implements statistics tracking
|
|
5196
5196
|
*/
|
|
5197
5197
|
class BaseStorageAdapter {
|
|
5198
|
+
constructor() {
|
|
5199
|
+
// Statistics cache
|
|
5200
|
+
this.statisticsCache = null;
|
|
5201
|
+
// Batch update timer ID
|
|
5202
|
+
this.statisticsBatchUpdateTimerId = null;
|
|
5203
|
+
// Flag to indicate if statistics have been modified since last save
|
|
5204
|
+
this.statisticsModified = false;
|
|
5205
|
+
// Time of last statistics flush to storage
|
|
5206
|
+
this.lastStatisticsFlushTime = 0;
|
|
5207
|
+
// Minimum time between statistics flushes (5 seconds)
|
|
5208
|
+
this.MIN_FLUSH_INTERVAL_MS = 5000;
|
|
5209
|
+
// Maximum time to wait before flushing statistics (30 seconds)
|
|
5210
|
+
this.MAX_FLUSH_DELAY_MS = 30000;
|
|
5211
|
+
}
|
|
5198
5212
|
/**
|
|
5199
5213
|
* Save statistics data
|
|
5200
5214
|
* @param statistics The statistics data to save
|
|
5201
5215
|
*/
|
|
5202
5216
|
async saveStatistics(statistics) {
|
|
5203
|
-
|
|
5217
|
+
// Update the cache with a deep copy to avoid reference issues
|
|
5218
|
+
this.statisticsCache = {
|
|
5219
|
+
nounCount: { ...statistics.nounCount },
|
|
5220
|
+
verbCount: { ...statistics.verbCount },
|
|
5221
|
+
metadataCount: { ...statistics.metadataCount },
|
|
5222
|
+
hnswIndexSize: statistics.hnswIndexSize,
|
|
5223
|
+
lastUpdated: statistics.lastUpdated
|
|
5224
|
+
};
|
|
5225
|
+
// Schedule a batch update instead of saving immediately
|
|
5226
|
+
this.scheduleBatchUpdate();
|
|
5204
5227
|
}
|
|
5205
5228
|
/**
|
|
5206
5229
|
* Get statistics data
|
|
5207
5230
|
* @returns Promise that resolves to the statistics data
|
|
5208
5231
|
*/
|
|
5209
5232
|
async getStatistics() {
|
|
5210
|
-
return
|
|
5233
|
+
// If we have cached statistics, return a deep copy
|
|
5234
|
+
if (this.statisticsCache) {
|
|
5235
|
+
return {
|
|
5236
|
+
nounCount: { ...this.statisticsCache.nounCount },
|
|
5237
|
+
verbCount: { ...this.statisticsCache.verbCount },
|
|
5238
|
+
metadataCount: { ...this.statisticsCache.metadataCount },
|
|
5239
|
+
hnswIndexSize: this.statisticsCache.hnswIndexSize,
|
|
5240
|
+
lastUpdated: this.statisticsCache.lastUpdated
|
|
5241
|
+
};
|
|
5242
|
+
}
|
|
5243
|
+
// Otherwise, get from storage
|
|
5244
|
+
const statistics = await this.getStatisticsData();
|
|
5245
|
+
// If we found statistics, update the cache
|
|
5246
|
+
if (statistics) {
|
|
5247
|
+
// Update the cache with a deep copy
|
|
5248
|
+
this.statisticsCache = {
|
|
5249
|
+
nounCount: { ...statistics.nounCount },
|
|
5250
|
+
verbCount: { ...statistics.verbCount },
|
|
5251
|
+
metadataCount: { ...statistics.metadataCount },
|
|
5252
|
+
hnswIndexSize: statistics.hnswIndexSize,
|
|
5253
|
+
lastUpdated: statistics.lastUpdated
|
|
5254
|
+
};
|
|
5255
|
+
}
|
|
5256
|
+
return statistics;
|
|
5257
|
+
}
|
|
5258
|
+
/**
|
|
5259
|
+
* Schedule a batch update of statistics
|
|
5260
|
+
*/
|
|
5261
|
+
scheduleBatchUpdate() {
|
|
5262
|
+
// Mark statistics as modified
|
|
5263
|
+
this.statisticsModified = true;
|
|
5264
|
+
// If a timer is already set, don't set another one
|
|
5265
|
+
if (this.statisticsBatchUpdateTimerId !== null) {
|
|
5266
|
+
return;
|
|
5267
|
+
}
|
|
5268
|
+
// Calculate time since last flush
|
|
5269
|
+
const now = Date.now();
|
|
5270
|
+
const timeSinceLastFlush = now - this.lastStatisticsFlushTime;
|
|
5271
|
+
// If we've recently flushed, wait longer before the next flush
|
|
5272
|
+
const delayMs = timeSinceLastFlush < this.MIN_FLUSH_INTERVAL_MS
|
|
5273
|
+
? this.MAX_FLUSH_DELAY_MS
|
|
5274
|
+
: this.MIN_FLUSH_INTERVAL_MS;
|
|
5275
|
+
// Schedule the batch update
|
|
5276
|
+
this.statisticsBatchUpdateTimerId = setTimeout(() => {
|
|
5277
|
+
this.flushStatistics();
|
|
5278
|
+
}, delayMs);
|
|
5279
|
+
}
|
|
5280
|
+
/**
|
|
5281
|
+
* Flush statistics to storage
|
|
5282
|
+
*/
|
|
5283
|
+
async flushStatistics() {
|
|
5284
|
+
// Clear the timer
|
|
5285
|
+
if (this.statisticsBatchUpdateTimerId !== null) {
|
|
5286
|
+
clearTimeout(this.statisticsBatchUpdateTimerId);
|
|
5287
|
+
this.statisticsBatchUpdateTimerId = null;
|
|
5288
|
+
}
|
|
5289
|
+
// If statistics haven't been modified, no need to flush
|
|
5290
|
+
if (!this.statisticsModified || !this.statisticsCache) {
|
|
5291
|
+
return;
|
|
5292
|
+
}
|
|
5293
|
+
try {
|
|
5294
|
+
// Save the statistics to storage
|
|
5295
|
+
await this.saveStatisticsData(this.statisticsCache);
|
|
5296
|
+
// Update the last flush time
|
|
5297
|
+
this.lastStatisticsFlushTime = Date.now();
|
|
5298
|
+
// Reset the modified flag
|
|
5299
|
+
this.statisticsModified = false;
|
|
5300
|
+
}
|
|
5301
|
+
catch (error) {
|
|
5302
|
+
console.error('Failed to flush statistics data:', error);
|
|
5303
|
+
// Mark as still modified so we'll try again later
|
|
5304
|
+
this.statisticsModified = true;
|
|
5305
|
+
// Don't throw the error to avoid disrupting the application
|
|
5306
|
+
}
|
|
5211
5307
|
}
|
|
5212
5308
|
/**
|
|
5213
5309
|
* Increment a statistic counter
|
|
@@ -5216,23 +5312,34 @@ class BaseStorageAdapter {
|
|
|
5216
5312
|
* @param amount The amount to increment by (default: 1)
|
|
5217
5313
|
*/
|
|
5218
5314
|
async incrementStatistic(type, service, amount = 1) {
|
|
5219
|
-
// Get current statistics
|
|
5220
|
-
let statistics =
|
|
5315
|
+
// Get current statistics from cache or storage
|
|
5316
|
+
let statistics = this.statisticsCache;
|
|
5221
5317
|
if (!statistics) {
|
|
5222
|
-
statistics = this.
|
|
5318
|
+
statistics = await this.getStatisticsData();
|
|
5319
|
+
if (!statistics) {
|
|
5320
|
+
statistics = this.createDefaultStatistics();
|
|
5321
|
+
}
|
|
5322
|
+
// Update the cache
|
|
5323
|
+
this.statisticsCache = {
|
|
5324
|
+
nounCount: { ...statistics.nounCount },
|
|
5325
|
+
verbCount: { ...statistics.verbCount },
|
|
5326
|
+
metadataCount: { ...statistics.metadataCount },
|
|
5327
|
+
hnswIndexSize: statistics.hnswIndexSize,
|
|
5328
|
+
lastUpdated: statistics.lastUpdated
|
|
5329
|
+
};
|
|
5223
5330
|
}
|
|
5224
5331
|
// Increment the appropriate counter
|
|
5225
5332
|
const counterMap = {
|
|
5226
|
-
noun:
|
|
5227
|
-
verb:
|
|
5228
|
-
metadata:
|
|
5333
|
+
noun: this.statisticsCache.nounCount,
|
|
5334
|
+
verb: this.statisticsCache.verbCount,
|
|
5335
|
+
metadata: this.statisticsCache.metadataCount
|
|
5229
5336
|
};
|
|
5230
5337
|
const counter = counterMap[type];
|
|
5231
5338
|
counter[service] = (counter[service] || 0) + amount;
|
|
5232
5339
|
// Update timestamp
|
|
5233
|
-
|
|
5234
|
-
//
|
|
5235
|
-
|
|
5340
|
+
this.statisticsCache.lastUpdated = new Date().toISOString();
|
|
5341
|
+
// Schedule a batch update instead of saving immediately
|
|
5342
|
+
this.scheduleBatchUpdate();
|
|
5236
5343
|
}
|
|
5237
5344
|
/**
|
|
5238
5345
|
* Decrement a statistic counter
|
|
@@ -5241,40 +5348,62 @@ class BaseStorageAdapter {
|
|
|
5241
5348
|
* @param amount The amount to decrement by (default: 1)
|
|
5242
5349
|
*/
|
|
5243
5350
|
async decrementStatistic(type, service, amount = 1) {
|
|
5244
|
-
// Get current statistics
|
|
5245
|
-
let statistics =
|
|
5351
|
+
// Get current statistics from cache or storage
|
|
5352
|
+
let statistics = this.statisticsCache;
|
|
5246
5353
|
if (!statistics) {
|
|
5247
|
-
statistics = this.
|
|
5354
|
+
statistics = await this.getStatisticsData();
|
|
5355
|
+
if (!statistics) {
|
|
5356
|
+
statistics = this.createDefaultStatistics();
|
|
5357
|
+
}
|
|
5358
|
+
// Update the cache
|
|
5359
|
+
this.statisticsCache = {
|
|
5360
|
+
nounCount: { ...statistics.nounCount },
|
|
5361
|
+
verbCount: { ...statistics.verbCount },
|
|
5362
|
+
metadataCount: { ...statistics.metadataCount },
|
|
5363
|
+
hnswIndexSize: statistics.hnswIndexSize,
|
|
5364
|
+
lastUpdated: statistics.lastUpdated
|
|
5365
|
+
};
|
|
5248
5366
|
}
|
|
5249
5367
|
// Decrement the appropriate counter
|
|
5250
5368
|
const counterMap = {
|
|
5251
|
-
noun:
|
|
5252
|
-
verb:
|
|
5253
|
-
metadata:
|
|
5369
|
+
noun: this.statisticsCache.nounCount,
|
|
5370
|
+
verb: this.statisticsCache.verbCount,
|
|
5371
|
+
metadata: this.statisticsCache.metadataCount
|
|
5254
5372
|
};
|
|
5255
5373
|
const counter = counterMap[type];
|
|
5256
5374
|
counter[service] = Math.max(0, (counter[service] || 0) - amount);
|
|
5257
5375
|
// Update timestamp
|
|
5258
|
-
|
|
5259
|
-
//
|
|
5260
|
-
|
|
5376
|
+
this.statisticsCache.lastUpdated = new Date().toISOString();
|
|
5377
|
+
// Schedule a batch update instead of saving immediately
|
|
5378
|
+
this.scheduleBatchUpdate();
|
|
5261
5379
|
}
|
|
5262
5380
|
/**
|
|
5263
5381
|
* Update the HNSW index size statistic
|
|
5264
5382
|
* @param size The new size of the HNSW index
|
|
5265
5383
|
*/
|
|
5266
5384
|
async updateHnswIndexSize(size) {
|
|
5267
|
-
// Get current statistics
|
|
5268
|
-
let statistics =
|
|
5385
|
+
// Get current statistics from cache or storage
|
|
5386
|
+
let statistics = this.statisticsCache;
|
|
5269
5387
|
if (!statistics) {
|
|
5270
|
-
statistics = this.
|
|
5388
|
+
statistics = await this.getStatisticsData();
|
|
5389
|
+
if (!statistics) {
|
|
5390
|
+
statistics = this.createDefaultStatistics();
|
|
5391
|
+
}
|
|
5392
|
+
// Update the cache
|
|
5393
|
+
this.statisticsCache = {
|
|
5394
|
+
nounCount: { ...statistics.nounCount },
|
|
5395
|
+
verbCount: { ...statistics.verbCount },
|
|
5396
|
+
metadataCount: { ...statistics.metadataCount },
|
|
5397
|
+
hnswIndexSize: statistics.hnswIndexSize,
|
|
5398
|
+
lastUpdated: statistics.lastUpdated
|
|
5399
|
+
};
|
|
5271
5400
|
}
|
|
5272
5401
|
// Update HNSW index size
|
|
5273
|
-
|
|
5402
|
+
this.statisticsCache.hnswIndexSize = size;
|
|
5274
5403
|
// Update timestamp
|
|
5275
|
-
|
|
5276
|
-
//
|
|
5277
|
-
|
|
5404
|
+
this.statisticsCache.lastUpdated = new Date().toISOString();
|
|
5405
|
+
// Schedule a batch update instead of saving immediately
|
|
5406
|
+
this.scheduleBatchUpdate();
|
|
5278
5407
|
}
|
|
5279
5408
|
/**
|
|
5280
5409
|
* Create default statistics data
|
|
@@ -5323,21 +5452,21 @@ class BaseStorage extends BaseStorageAdapter {
|
|
|
5323
5452
|
*/
|
|
5324
5453
|
async saveNoun(noun) {
|
|
5325
5454
|
await this.ensureInitialized();
|
|
5326
|
-
return this.
|
|
5455
|
+
return this.saveNoun_internal(noun);
|
|
5327
5456
|
}
|
|
5328
5457
|
/**
|
|
5329
5458
|
* Get a noun from storage
|
|
5330
5459
|
*/
|
|
5331
5460
|
async getNoun(id) {
|
|
5332
5461
|
await this.ensureInitialized();
|
|
5333
|
-
return this.
|
|
5462
|
+
return this.getNoun_internal(id);
|
|
5334
5463
|
}
|
|
5335
5464
|
/**
|
|
5336
5465
|
* Get all nouns from storage
|
|
5337
5466
|
*/
|
|
5338
5467
|
async getAllNouns() {
|
|
5339
5468
|
await this.ensureInitialized();
|
|
5340
|
-
return this.
|
|
5469
|
+
return this.getAllNouns_internal();
|
|
5341
5470
|
}
|
|
5342
5471
|
/**
|
|
5343
5472
|
* Get nouns by noun type
|
|
@@ -5346,63 +5475,63 @@ class BaseStorage extends BaseStorageAdapter {
|
|
|
5346
5475
|
*/
|
|
5347
5476
|
async getNounsByNounType(nounType) {
|
|
5348
5477
|
await this.ensureInitialized();
|
|
5349
|
-
return this.
|
|
5478
|
+
return this.getNounsByNounType_internal(nounType);
|
|
5350
5479
|
}
|
|
5351
5480
|
/**
|
|
5352
5481
|
* Delete a noun from storage
|
|
5353
5482
|
*/
|
|
5354
5483
|
async deleteNoun(id) {
|
|
5355
5484
|
await this.ensureInitialized();
|
|
5356
|
-
return this.
|
|
5485
|
+
return this.deleteNoun_internal(id);
|
|
5357
5486
|
}
|
|
5358
5487
|
/**
|
|
5359
5488
|
* Save a verb to storage
|
|
5360
5489
|
*/
|
|
5361
5490
|
async saveVerb(verb) {
|
|
5362
5491
|
await this.ensureInitialized();
|
|
5363
|
-
return this.
|
|
5492
|
+
return this.saveVerb_internal(verb);
|
|
5364
5493
|
}
|
|
5365
5494
|
/**
|
|
5366
5495
|
* Get a verb from storage
|
|
5367
5496
|
*/
|
|
5368
5497
|
async getVerb(id) {
|
|
5369
5498
|
await this.ensureInitialized();
|
|
5370
|
-
return this.
|
|
5499
|
+
return this.getVerb_internal(id);
|
|
5371
5500
|
}
|
|
5372
5501
|
/**
|
|
5373
5502
|
* Get all verbs from storage
|
|
5374
5503
|
*/
|
|
5375
5504
|
async getAllVerbs() {
|
|
5376
5505
|
await this.ensureInitialized();
|
|
5377
|
-
return this.
|
|
5506
|
+
return this.getAllVerbs_internal();
|
|
5378
5507
|
}
|
|
5379
5508
|
/**
|
|
5380
5509
|
* Get verbs by source
|
|
5381
5510
|
*/
|
|
5382
5511
|
async getVerbsBySource(sourceId) {
|
|
5383
5512
|
await this.ensureInitialized();
|
|
5384
|
-
return this.
|
|
5513
|
+
return this.getVerbsBySource_internal(sourceId);
|
|
5385
5514
|
}
|
|
5386
5515
|
/**
|
|
5387
5516
|
* Get verbs by target
|
|
5388
5517
|
*/
|
|
5389
5518
|
async getVerbsByTarget(targetId) {
|
|
5390
5519
|
await this.ensureInitialized();
|
|
5391
|
-
return this.
|
|
5520
|
+
return this.getVerbsByTarget_internal(targetId);
|
|
5392
5521
|
}
|
|
5393
5522
|
/**
|
|
5394
5523
|
* Get verbs by type
|
|
5395
5524
|
*/
|
|
5396
5525
|
async getVerbsByType(type) {
|
|
5397
5526
|
await this.ensureInitialized();
|
|
5398
|
-
return this.
|
|
5527
|
+
return this.getVerbsByType_internal(type);
|
|
5399
5528
|
}
|
|
5400
5529
|
/**
|
|
5401
5530
|
* Delete a verb from storage
|
|
5402
5531
|
*/
|
|
5403
5532
|
async deleteVerb(id) {
|
|
5404
5533
|
await this.ensureInitialized();
|
|
5405
|
-
return this.
|
|
5534
|
+
return this.deleteVerb_internal(id);
|
|
5406
5535
|
}
|
|
5407
5536
|
/**
|
|
5408
5537
|
* Helper method to convert a Map to a plain object for serialization
|
|
@@ -5441,201 +5570,230 @@ class MemoryStorage extends BaseStorage {
|
|
|
5441
5570
|
this.isInitialized = true;
|
|
5442
5571
|
}
|
|
5443
5572
|
/**
|
|
5444
|
-
* Save a
|
|
5573
|
+
* Save a noun to storage
|
|
5445
5574
|
*/
|
|
5446
|
-
async
|
|
5575
|
+
async saveNoun_internal(noun) {
|
|
5447
5576
|
// Create a deep copy to avoid reference issues
|
|
5448
|
-
const
|
|
5449
|
-
id:
|
|
5450
|
-
vector: [...
|
|
5577
|
+
const nounCopy = {
|
|
5578
|
+
id: noun.id,
|
|
5579
|
+
vector: [...noun.vector],
|
|
5451
5580
|
connections: new Map()
|
|
5452
5581
|
};
|
|
5453
5582
|
// Copy connections
|
|
5454
|
-
for (const [level, connections] of
|
|
5455
|
-
|
|
5583
|
+
for (const [level, connections] of noun.connections.entries()) {
|
|
5584
|
+
nounCopy.connections.set(level, new Set(connections));
|
|
5456
5585
|
}
|
|
5457
|
-
// Save the
|
|
5458
|
-
this.nouns.set(
|
|
5586
|
+
// Save the noun directly in the nouns map
|
|
5587
|
+
this.nouns.set(noun.id, nounCopy);
|
|
5459
5588
|
}
|
|
5460
5589
|
/**
|
|
5461
|
-
* Get a
|
|
5590
|
+
* Get a noun from storage
|
|
5462
5591
|
*/
|
|
5463
|
-
async
|
|
5464
|
-
// Get the
|
|
5465
|
-
const
|
|
5592
|
+
async getNoun_internal(id) {
|
|
5593
|
+
// Get the noun directly from the nouns map
|
|
5594
|
+
const noun = this.nouns.get(id);
|
|
5466
5595
|
// If not found, return null
|
|
5467
|
-
if (!
|
|
5596
|
+
if (!noun) {
|
|
5468
5597
|
return null;
|
|
5469
5598
|
}
|
|
5470
5599
|
// Return a deep copy to avoid reference issues
|
|
5471
|
-
const
|
|
5472
|
-
id:
|
|
5473
|
-
vector: [...
|
|
5600
|
+
const nounCopy = {
|
|
5601
|
+
id: noun.id,
|
|
5602
|
+
vector: [...noun.vector],
|
|
5474
5603
|
connections: new Map()
|
|
5475
5604
|
};
|
|
5476
5605
|
// Copy connections
|
|
5477
|
-
for (const [level, connections] of
|
|
5478
|
-
|
|
5606
|
+
for (const [level, connections] of noun.connections.entries()) {
|
|
5607
|
+
nounCopy.connections.set(level, new Set(connections));
|
|
5479
5608
|
}
|
|
5480
|
-
return
|
|
5609
|
+
return nounCopy;
|
|
5481
5610
|
}
|
|
5482
5611
|
/**
|
|
5483
|
-
* Get all
|
|
5612
|
+
* Get all nouns from storage
|
|
5484
5613
|
*/
|
|
5485
|
-
async
|
|
5486
|
-
const
|
|
5487
|
-
// Iterate through all
|
|
5488
|
-
for (const [
|
|
5614
|
+
async getAllNouns_internal() {
|
|
5615
|
+
const allNouns = [];
|
|
5616
|
+
// Iterate through all nouns in the nouns map
|
|
5617
|
+
for (const [nounId, noun] of this.nouns.entries()) {
|
|
5489
5618
|
// Return a deep copy to avoid reference issues
|
|
5490
|
-
const
|
|
5491
|
-
id:
|
|
5492
|
-
vector: [...
|
|
5619
|
+
const nounCopy = {
|
|
5620
|
+
id: noun.id,
|
|
5621
|
+
vector: [...noun.vector],
|
|
5493
5622
|
connections: new Map()
|
|
5494
5623
|
};
|
|
5495
5624
|
// Copy connections
|
|
5496
|
-
for (const [level, connections] of
|
|
5497
|
-
|
|
5625
|
+
for (const [level, connections] of noun.connections.entries()) {
|
|
5626
|
+
nounCopy.connections.set(level, new Set(connections));
|
|
5498
5627
|
}
|
|
5499
|
-
|
|
5628
|
+
allNouns.push(nounCopy);
|
|
5500
5629
|
}
|
|
5501
|
-
return
|
|
5630
|
+
return allNouns;
|
|
5502
5631
|
}
|
|
5503
5632
|
/**
|
|
5504
|
-
* Get
|
|
5633
|
+
* Get nouns by noun type
|
|
5505
5634
|
* @param nounType The noun type to filter by
|
|
5506
|
-
* @returns Promise that resolves to an array of
|
|
5635
|
+
* @returns Promise that resolves to an array of nouns of the specified noun type
|
|
5507
5636
|
*/
|
|
5508
|
-
async
|
|
5509
|
-
const
|
|
5510
|
-
// Iterate through all
|
|
5511
|
-
for (const [
|
|
5637
|
+
async getNounsByNounType_internal(nounType) {
|
|
5638
|
+
const nouns = [];
|
|
5639
|
+
// Iterate through all nouns and filter by noun type using metadata
|
|
5640
|
+
for (const [nounId, noun] of this.nouns.entries()) {
|
|
5512
5641
|
// Get the metadata to check the noun type
|
|
5513
|
-
const metadata = await this.getMetadata(
|
|
5514
|
-
// Include the
|
|
5642
|
+
const metadata = await this.getMetadata(nounId);
|
|
5643
|
+
// Include the noun if its noun type matches the requested type
|
|
5515
5644
|
if (metadata && metadata.noun === nounType) {
|
|
5516
5645
|
// Return a deep copy to avoid reference issues
|
|
5517
|
-
const
|
|
5518
|
-
id:
|
|
5519
|
-
vector: [...
|
|
5646
|
+
const nounCopy = {
|
|
5647
|
+
id: noun.id,
|
|
5648
|
+
vector: [...noun.vector],
|
|
5520
5649
|
connections: new Map()
|
|
5521
5650
|
};
|
|
5522
5651
|
// Copy connections
|
|
5523
|
-
for (const [level, connections] of
|
|
5524
|
-
|
|
5652
|
+
for (const [level, connections] of noun.connections.entries()) {
|
|
5653
|
+
nounCopy.connections.set(level, new Set(connections));
|
|
5525
5654
|
}
|
|
5526
|
-
|
|
5655
|
+
nouns.push(nounCopy);
|
|
5527
5656
|
}
|
|
5528
5657
|
}
|
|
5529
|
-
return
|
|
5658
|
+
return nouns;
|
|
5530
5659
|
}
|
|
5531
5660
|
/**
|
|
5532
|
-
* Delete a
|
|
5661
|
+
* Delete a noun from storage
|
|
5533
5662
|
*/
|
|
5534
|
-
async
|
|
5535
|
-
// Delete the node directly from the nouns map
|
|
5663
|
+
async deleteNoun_internal(id) {
|
|
5536
5664
|
this.nouns.delete(id);
|
|
5537
5665
|
}
|
|
5538
5666
|
/**
|
|
5539
|
-
* Save
|
|
5667
|
+
* Save a verb to storage
|
|
5540
5668
|
*/
|
|
5541
|
-
async
|
|
5669
|
+
async saveVerb_internal(verb) {
|
|
5542
5670
|
// Create a deep copy to avoid reference issues
|
|
5543
|
-
const
|
|
5544
|
-
id:
|
|
5545
|
-
vector: [...
|
|
5671
|
+
const verbCopy = {
|
|
5672
|
+
id: verb.id,
|
|
5673
|
+
vector: [...verb.vector],
|
|
5546
5674
|
connections: new Map(),
|
|
5547
|
-
sourceId:
|
|
5548
|
-
targetId:
|
|
5549
|
-
type:
|
|
5550
|
-
weight:
|
|
5551
|
-
metadata:
|
|
5675
|
+
sourceId: verb.sourceId,
|
|
5676
|
+
targetId: verb.targetId,
|
|
5677
|
+
type: verb.type,
|
|
5678
|
+
weight: verb.weight,
|
|
5679
|
+
metadata: verb.metadata
|
|
5552
5680
|
};
|
|
5553
5681
|
// Copy connections
|
|
5554
|
-
for (const [level, connections] of
|
|
5555
|
-
|
|
5682
|
+
for (const [level, connections] of verb.connections.entries()) {
|
|
5683
|
+
verbCopy.connections.set(level, new Set(connections));
|
|
5556
5684
|
}
|
|
5557
|
-
// Save the
|
|
5558
|
-
this.verbs.set(
|
|
5685
|
+
// Save the verb directly in the verbs map
|
|
5686
|
+
this.verbs.set(verb.id, verbCopy);
|
|
5559
5687
|
}
|
|
5560
5688
|
/**
|
|
5561
|
-
* Get
|
|
5689
|
+
* Get a verb from storage
|
|
5562
5690
|
*/
|
|
5563
|
-
async
|
|
5564
|
-
// Get the
|
|
5565
|
-
const
|
|
5691
|
+
async getVerb_internal(id) {
|
|
5692
|
+
// Get the verb directly from the verbs map
|
|
5693
|
+
const verb = this.verbs.get(id);
|
|
5566
5694
|
// If not found, return null
|
|
5567
|
-
if (!
|
|
5695
|
+
if (!verb) {
|
|
5568
5696
|
return null;
|
|
5569
5697
|
}
|
|
5698
|
+
// Create default timestamp if not present
|
|
5699
|
+
const defaultTimestamp = {
|
|
5700
|
+
seconds: Math.floor(Date.now() / 1000),
|
|
5701
|
+
nanoseconds: (Date.now() % 1000) * 1000000
|
|
5702
|
+
};
|
|
5703
|
+
// Create default createdBy if not present
|
|
5704
|
+
const defaultCreatedBy = {
|
|
5705
|
+
augmentation: 'unknown',
|
|
5706
|
+
version: '1.0'
|
|
5707
|
+
};
|
|
5570
5708
|
// Return a deep copy to avoid reference issues
|
|
5571
|
-
const
|
|
5572
|
-
id:
|
|
5573
|
-
vector: [...
|
|
5709
|
+
const verbCopy = {
|
|
5710
|
+
id: verb.id,
|
|
5711
|
+
vector: [...verb.vector],
|
|
5574
5712
|
connections: new Map(),
|
|
5575
|
-
sourceId:
|
|
5576
|
-
targetId:
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5713
|
+
sourceId: (verb.sourceId || verb.source || ""),
|
|
5714
|
+
targetId: (verb.targetId || verb.target || ""),
|
|
5715
|
+
source: (verb.sourceId || verb.source || ""),
|
|
5716
|
+
target: (verb.targetId || verb.target || ""),
|
|
5717
|
+
verb: verb.type || verb.verb,
|
|
5718
|
+
weight: verb.weight,
|
|
5719
|
+
metadata: verb.metadata,
|
|
5720
|
+
createdAt: verb.createdAt || defaultTimestamp,
|
|
5721
|
+
updatedAt: verb.updatedAt || defaultTimestamp,
|
|
5722
|
+
createdBy: verb.createdBy || defaultCreatedBy
|
|
5580
5723
|
};
|
|
5581
5724
|
// Copy connections
|
|
5582
|
-
for (const [level, connections] of
|
|
5583
|
-
|
|
5725
|
+
for (const [level, connections] of verb.connections.entries()) {
|
|
5726
|
+
verbCopy.connections.set(level, new Set(connections));
|
|
5584
5727
|
}
|
|
5585
|
-
return
|
|
5728
|
+
return verbCopy;
|
|
5586
5729
|
}
|
|
5587
5730
|
/**
|
|
5588
|
-
* Get all
|
|
5731
|
+
* Get all verbs from storage
|
|
5589
5732
|
*/
|
|
5590
|
-
async
|
|
5591
|
-
const
|
|
5592
|
-
// Iterate through all
|
|
5593
|
-
for (const [
|
|
5733
|
+
async getAllVerbs_internal() {
|
|
5734
|
+
const allVerbs = [];
|
|
5735
|
+
// Iterate through all verbs in the verbs map
|
|
5736
|
+
for (const [verbId, verb] of this.verbs.entries()) {
|
|
5737
|
+
// Create default timestamp if not present
|
|
5738
|
+
const defaultTimestamp = {
|
|
5739
|
+
seconds: Math.floor(Date.now() / 1000),
|
|
5740
|
+
nanoseconds: (Date.now() % 1000) * 1000000
|
|
5741
|
+
};
|
|
5742
|
+
// Create default createdBy if not present
|
|
5743
|
+
const defaultCreatedBy = {
|
|
5744
|
+
augmentation: 'unknown',
|
|
5745
|
+
version: '1.0'
|
|
5746
|
+
};
|
|
5594
5747
|
// Return a deep copy to avoid reference issues
|
|
5595
|
-
const
|
|
5596
|
-
id:
|
|
5597
|
-
vector: [...
|
|
5748
|
+
const verbCopy = {
|
|
5749
|
+
id: verb.id,
|
|
5750
|
+
vector: [...verb.vector],
|
|
5598
5751
|
connections: new Map(),
|
|
5599
|
-
sourceId:
|
|
5600
|
-
targetId:
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5752
|
+
sourceId: (verb.sourceId || verb.source || ""),
|
|
5753
|
+
targetId: (verb.targetId || verb.target || ""),
|
|
5754
|
+
source: (verb.sourceId || verb.source || ""),
|
|
5755
|
+
target: (verb.targetId || verb.target || ""),
|
|
5756
|
+
verb: verb.type || verb.verb,
|
|
5757
|
+
weight: verb.weight,
|
|
5758
|
+
metadata: verb.metadata,
|
|
5759
|
+
createdAt: verb.createdAt || defaultTimestamp,
|
|
5760
|
+
updatedAt: verb.updatedAt || defaultTimestamp,
|
|
5761
|
+
createdBy: verb.createdBy || defaultCreatedBy
|
|
5604
5762
|
};
|
|
5605
5763
|
// Copy connections
|
|
5606
|
-
for (const [level, connections] of
|
|
5607
|
-
|
|
5764
|
+
for (const [level, connections] of verb.connections.entries()) {
|
|
5765
|
+
verbCopy.connections.set(level, new Set(connections));
|
|
5608
5766
|
}
|
|
5609
|
-
|
|
5767
|
+
allVerbs.push(verbCopy);
|
|
5610
5768
|
}
|
|
5611
|
-
return
|
|
5769
|
+
return allVerbs;
|
|
5612
5770
|
}
|
|
5613
5771
|
/**
|
|
5614
|
-
* Get
|
|
5772
|
+
* Get verbs by source
|
|
5615
5773
|
*/
|
|
5616
|
-
async
|
|
5617
|
-
const
|
|
5618
|
-
return
|
|
5774
|
+
async getVerbsBySource_internal(sourceId) {
|
|
5775
|
+
const allVerbs = await this.getAllVerbs_internal();
|
|
5776
|
+
return allVerbs.filter((verb) => (verb.sourceId || verb.source) === sourceId);
|
|
5619
5777
|
}
|
|
5620
5778
|
/**
|
|
5621
|
-
* Get
|
|
5779
|
+
* Get verbs by target
|
|
5622
5780
|
*/
|
|
5623
|
-
async
|
|
5624
|
-
const
|
|
5625
|
-
return
|
|
5781
|
+
async getVerbsByTarget_internal(targetId) {
|
|
5782
|
+
const allVerbs = await this.getAllVerbs_internal();
|
|
5783
|
+
return allVerbs.filter((verb) => (verb.targetId || verb.target) === targetId);
|
|
5626
5784
|
}
|
|
5627
5785
|
/**
|
|
5628
|
-
* Get
|
|
5786
|
+
* Get verbs by type
|
|
5629
5787
|
*/
|
|
5630
|
-
async
|
|
5631
|
-
const
|
|
5632
|
-
return
|
|
5788
|
+
async getVerbsByType_internal(type) {
|
|
5789
|
+
const allVerbs = await this.getAllVerbs_internal();
|
|
5790
|
+
return allVerbs.filter((verb) => (verb.type || verb.verb) === type);
|
|
5633
5791
|
}
|
|
5634
5792
|
/**
|
|
5635
|
-
* Delete
|
|
5793
|
+
* Delete a verb from storage
|
|
5636
5794
|
*/
|
|
5637
|
-
async
|
|
5638
|
-
// Delete the
|
|
5795
|
+
async deleteVerb_internal(id) {
|
|
5796
|
+
// Delete the verb directly from the verbs map
|
|
5639
5797
|
this.verbs.delete(id);
|
|
5640
5798
|
}
|
|
5641
5799
|
/**
|
|
@@ -5682,6 +5840,7 @@ class MemoryStorage extends BaseStorage {
|
|
|
5682
5840
|
* @param statistics The statistics data to save
|
|
5683
5841
|
*/
|
|
5684
5842
|
async saveStatisticsData(statistics) {
|
|
5843
|
+
// For memory storage, we just need to store the statistics in memory
|
|
5685
5844
|
// Create a deep copy to avoid reference issues
|
|
5686
5845
|
this.statistics = {
|
|
5687
5846
|
nounCount: { ...statistics.nounCount },
|
|
@@ -5690,6 +5849,8 @@ class MemoryStorage extends BaseStorage {
|
|
|
5690
5849
|
hnswIndexSize: statistics.hnswIndexSize,
|
|
5691
5850
|
lastUpdated: statistics.lastUpdated
|
|
5692
5851
|
};
|
|
5852
|
+
// Since this is in-memory, there's no need for time-based partitioning
|
|
5853
|
+
// or legacy file handling
|
|
5693
5854
|
}
|
|
5694
5855
|
/**
|
|
5695
5856
|
* Get statistics data from storage
|
|
@@ -5707,6 +5868,8 @@ class MemoryStorage extends BaseStorage {
|
|
|
5707
5868
|
hnswIndexSize: this.statistics.hnswIndexSize,
|
|
5708
5869
|
lastUpdated: this.statistics.lastUpdated
|
|
5709
5870
|
};
|
|
5871
|
+
// Since this is in-memory, there's no need for fallback mechanisms
|
|
5872
|
+
// to check multiple storage locations
|
|
5710
5873
|
}
|
|
5711
5874
|
}
|
|
5712
5875
|
|
|
@@ -5833,46 +5996,46 @@ class OPFSStorage extends BaseStorage {
|
|
|
5833
5996
|
}
|
|
5834
5997
|
}
|
|
5835
5998
|
/**
|
|
5836
|
-
* Save a
|
|
5999
|
+
* Save a noun to storage
|
|
5837
6000
|
*/
|
|
5838
|
-
async
|
|
6001
|
+
async saveNoun_internal(noun) {
|
|
5839
6002
|
await this.ensureInitialized();
|
|
5840
6003
|
try {
|
|
5841
6004
|
// Convert connections Map to a serializable format
|
|
5842
|
-
const
|
|
5843
|
-
...
|
|
5844
|
-
connections: this.mapToObject(
|
|
6005
|
+
const serializableNoun = {
|
|
6006
|
+
...noun,
|
|
6007
|
+
connections: this.mapToObject(noun.connections, (set) => Array.from(set))
|
|
5845
6008
|
};
|
|
5846
6009
|
// Create or get the file for this noun
|
|
5847
|
-
const fileHandle = await this.nounsDir.getFileHandle(
|
|
6010
|
+
const fileHandle = await this.nounsDir.getFileHandle(noun.id, {
|
|
5848
6011
|
create: true
|
|
5849
6012
|
});
|
|
5850
6013
|
// Write the noun data to the file
|
|
5851
6014
|
const writable = await fileHandle.createWritable();
|
|
5852
|
-
await writable.write(JSON.stringify(
|
|
6015
|
+
await writable.write(JSON.stringify(serializableNoun));
|
|
5853
6016
|
await writable.close();
|
|
5854
6017
|
}
|
|
5855
6018
|
catch (error) {
|
|
5856
|
-
console.error(`Failed to save
|
|
5857
|
-
throw new Error(`Failed to save
|
|
6019
|
+
console.error(`Failed to save noun ${noun.id}:`, error);
|
|
6020
|
+
throw new Error(`Failed to save noun ${noun.id}: ${error}`);
|
|
5858
6021
|
}
|
|
5859
6022
|
}
|
|
5860
6023
|
/**
|
|
5861
|
-
* Get a
|
|
6024
|
+
* Get a noun from storage
|
|
5862
6025
|
*/
|
|
5863
|
-
async
|
|
6026
|
+
async getNoun_internal(id) {
|
|
5864
6027
|
await this.ensureInitialized();
|
|
5865
6028
|
try {
|
|
5866
|
-
// Get the file handle for this
|
|
6029
|
+
// Get the file handle for this noun
|
|
5867
6030
|
const fileHandle = await this.nounsDir.getFileHandle(id);
|
|
5868
|
-
// Read the
|
|
6031
|
+
// Read the noun data from the file
|
|
5869
6032
|
const file = await fileHandle.getFile();
|
|
5870
6033
|
const text = await file.text();
|
|
5871
6034
|
const data = JSON.parse(text);
|
|
5872
6035
|
// Convert serialized connections back to Map<number, Set<string>>
|
|
5873
6036
|
const connections = new Map();
|
|
5874
|
-
for (const [level,
|
|
5875
|
-
connections.set(Number(level), new Set(
|
|
6037
|
+
for (const [level, nounIds] of Object.entries(data.connections)) {
|
|
6038
|
+
connections.set(Number(level), new Set(nounIds));
|
|
5876
6039
|
}
|
|
5877
6040
|
return {
|
|
5878
6041
|
id: data.id,
|
|
@@ -5881,38 +6044,38 @@ class OPFSStorage extends BaseStorage {
|
|
|
5881
6044
|
};
|
|
5882
6045
|
}
|
|
5883
6046
|
catch (error) {
|
|
5884
|
-
//
|
|
6047
|
+
// Noun not found or other error
|
|
5885
6048
|
return null;
|
|
5886
6049
|
}
|
|
5887
6050
|
}
|
|
5888
6051
|
/**
|
|
5889
|
-
* Get all
|
|
6052
|
+
* Get all nouns from storage
|
|
5890
6053
|
*/
|
|
5891
|
-
async
|
|
6054
|
+
async getAllNouns_internal() {
|
|
5892
6055
|
await this.ensureInitialized();
|
|
5893
|
-
const
|
|
6056
|
+
const allNouns = [];
|
|
5894
6057
|
try {
|
|
5895
6058
|
// Iterate through all files in the nouns directory
|
|
5896
6059
|
for await (const [name, handle] of this.nounsDir.entries()) {
|
|
5897
6060
|
if (handle.kind === 'file') {
|
|
5898
6061
|
try {
|
|
5899
|
-
// Read the
|
|
6062
|
+
// Read the noun data from the file
|
|
5900
6063
|
const file = await safeGetFile(handle);
|
|
5901
6064
|
const text = await file.text();
|
|
5902
6065
|
const data = JSON.parse(text);
|
|
5903
6066
|
// Convert serialized connections back to Map<number, Set<string>>
|
|
5904
6067
|
const connections = new Map();
|
|
5905
|
-
for (const [level,
|
|
5906
|
-
connections.set(Number(level), new Set(
|
|
6068
|
+
for (const [level, nounIds] of Object.entries(data.connections)) {
|
|
6069
|
+
connections.set(Number(level), new Set(nounIds));
|
|
5907
6070
|
}
|
|
5908
|
-
|
|
6071
|
+
allNouns.push({
|
|
5909
6072
|
id: data.id,
|
|
5910
6073
|
vector: data.vector,
|
|
5911
6074
|
connections
|
|
5912
6075
|
});
|
|
5913
6076
|
}
|
|
5914
6077
|
catch (error) {
|
|
5915
|
-
console.error(`Error reading
|
|
6078
|
+
console.error(`Error reading noun file ${name}:`, error);
|
|
5916
6079
|
}
|
|
5917
6080
|
}
|
|
5918
6081
|
}
|
|
@@ -5920,7 +6083,15 @@ class OPFSStorage extends BaseStorage {
|
|
|
5920
6083
|
catch (error) {
|
|
5921
6084
|
console.error('Error reading nouns directory:', error);
|
|
5922
6085
|
}
|
|
5923
|
-
return
|
|
6086
|
+
return allNouns;
|
|
6087
|
+
}
|
|
6088
|
+
/**
|
|
6089
|
+
* Get nouns by noun type (internal implementation)
|
|
6090
|
+
* @param nounType The noun type to filter by
|
|
6091
|
+
* @returns Promise that resolves to an array of nouns of the specified noun type
|
|
6092
|
+
*/
|
|
6093
|
+
async getNounsByNounType_internal(nounType) {
|
|
6094
|
+
return this.getNodesByNounType(nounType);
|
|
5924
6095
|
}
|
|
5925
6096
|
/**
|
|
5926
6097
|
* Get nodes by noun type
|
|
@@ -5966,6 +6137,12 @@ class OPFSStorage extends BaseStorage {
|
|
|
5966
6137
|
}
|
|
5967
6138
|
return nodes;
|
|
5968
6139
|
}
|
|
6140
|
+
/**
|
|
6141
|
+
* Delete a noun from storage (internal implementation)
|
|
6142
|
+
*/
|
|
6143
|
+
async deleteNoun_internal(id) {
|
|
6144
|
+
return this.deleteNode(id);
|
|
6145
|
+
}
|
|
5969
6146
|
/**
|
|
5970
6147
|
* Delete a node from storage
|
|
5971
6148
|
*/
|
|
@@ -5982,6 +6159,12 @@ class OPFSStorage extends BaseStorage {
|
|
|
5982
6159
|
}
|
|
5983
6160
|
}
|
|
5984
6161
|
}
|
|
6162
|
+
/**
|
|
6163
|
+
* Save a verb to storage (internal implementation)
|
|
6164
|
+
*/
|
|
6165
|
+
async saveVerb_internal(verb) {
|
|
6166
|
+
return this.saveEdge(verb);
|
|
6167
|
+
}
|
|
5985
6168
|
/**
|
|
5986
6169
|
* Save an edge to storage
|
|
5987
6170
|
*/
|
|
@@ -6007,6 +6190,12 @@ class OPFSStorage extends BaseStorage {
|
|
|
6007
6190
|
throw new Error(`Failed to save edge ${edge.id}: ${error}`);
|
|
6008
6191
|
}
|
|
6009
6192
|
}
|
|
6193
|
+
/**
|
|
6194
|
+
* Get a verb from storage (internal implementation)
|
|
6195
|
+
*/
|
|
6196
|
+
async getVerb_internal(id) {
|
|
6197
|
+
return this.getEdge(id);
|
|
6198
|
+
}
|
|
6010
6199
|
/**
|
|
6011
6200
|
* Get an edge from storage
|
|
6012
6201
|
*/
|
|
@@ -6024,15 +6213,30 @@ class OPFSStorage extends BaseStorage {
|
|
|
6024
6213
|
for (const [level, nodeIds] of Object.entries(data.connections)) {
|
|
6025
6214
|
connections.set(Number(level), new Set(nodeIds));
|
|
6026
6215
|
}
|
|
6216
|
+
// Create default timestamp if not present
|
|
6217
|
+
const defaultTimestamp = {
|
|
6218
|
+
seconds: Math.floor(Date.now() / 1000),
|
|
6219
|
+
nanoseconds: (Date.now() % 1000) * 1000000
|
|
6220
|
+
};
|
|
6221
|
+
// Create default createdBy if not present
|
|
6222
|
+
const defaultCreatedBy = {
|
|
6223
|
+
augmentation: 'unknown',
|
|
6224
|
+
version: '1.0'
|
|
6225
|
+
};
|
|
6027
6226
|
return {
|
|
6028
6227
|
id: data.id,
|
|
6029
6228
|
vector: data.vector,
|
|
6030
6229
|
connections,
|
|
6031
|
-
sourceId: data.sourceId,
|
|
6032
|
-
targetId: data.targetId,
|
|
6033
|
-
|
|
6230
|
+
sourceId: data.sourceId || data.source,
|
|
6231
|
+
targetId: data.targetId || data.target,
|
|
6232
|
+
source: data.sourceId || data.source,
|
|
6233
|
+
target: data.targetId || data.target,
|
|
6234
|
+
verb: data.type || data.verb,
|
|
6034
6235
|
weight: data.weight,
|
|
6035
|
-
metadata: data.metadata
|
|
6236
|
+
metadata: data.metadata,
|
|
6237
|
+
createdAt: data.createdAt || defaultTimestamp,
|
|
6238
|
+
updatedAt: data.updatedAt || defaultTimestamp,
|
|
6239
|
+
createdBy: data.createdBy || defaultCreatedBy
|
|
6036
6240
|
};
|
|
6037
6241
|
}
|
|
6038
6242
|
catch (error) {
|
|
@@ -6040,6 +6244,12 @@ class OPFSStorage extends BaseStorage {
|
|
|
6040
6244
|
return null;
|
|
6041
6245
|
}
|
|
6042
6246
|
}
|
|
6247
|
+
/**
|
|
6248
|
+
* Get all verbs from storage (internal implementation)
|
|
6249
|
+
*/
|
|
6250
|
+
async getAllVerbs_internal() {
|
|
6251
|
+
return this.getAllEdges();
|
|
6252
|
+
}
|
|
6043
6253
|
/**
|
|
6044
6254
|
* Get all edges from storage
|
|
6045
6255
|
*/
|
|
@@ -6060,15 +6270,30 @@ class OPFSStorage extends BaseStorage {
|
|
|
6060
6270
|
for (const [level, nodeIds] of Object.entries(data.connections)) {
|
|
6061
6271
|
connections.set(Number(level), new Set(nodeIds));
|
|
6062
6272
|
}
|
|
6273
|
+
// Create default timestamp if not present
|
|
6274
|
+
const defaultTimestamp = {
|
|
6275
|
+
seconds: Math.floor(Date.now() / 1000),
|
|
6276
|
+
nanoseconds: (Date.now() % 1000) * 1000000
|
|
6277
|
+
};
|
|
6278
|
+
// Create default createdBy if not present
|
|
6279
|
+
const defaultCreatedBy = {
|
|
6280
|
+
augmentation: 'unknown',
|
|
6281
|
+
version: '1.0'
|
|
6282
|
+
};
|
|
6063
6283
|
allEdges.push({
|
|
6064
6284
|
id: data.id,
|
|
6065
6285
|
vector: data.vector,
|
|
6066
6286
|
connections,
|
|
6067
|
-
sourceId: data.sourceId,
|
|
6068
|
-
targetId: data.targetId,
|
|
6069
|
-
|
|
6287
|
+
sourceId: data.sourceId || data.source,
|
|
6288
|
+
targetId: data.targetId || data.target,
|
|
6289
|
+
source: data.sourceId || data.source,
|
|
6290
|
+
target: data.targetId || data.target,
|
|
6291
|
+
verb: data.type || data.verb,
|
|
6070
6292
|
weight: data.weight,
|
|
6071
|
-
metadata: data.metadata
|
|
6293
|
+
metadata: data.metadata,
|
|
6294
|
+
createdAt: data.createdAt || defaultTimestamp,
|
|
6295
|
+
updatedAt: data.updatedAt || defaultTimestamp,
|
|
6296
|
+
createdBy: data.createdBy || defaultCreatedBy
|
|
6072
6297
|
});
|
|
6073
6298
|
}
|
|
6074
6299
|
catch (error) {
|
|
@@ -6082,26 +6307,50 @@ class OPFSStorage extends BaseStorage {
|
|
|
6082
6307
|
}
|
|
6083
6308
|
return allEdges;
|
|
6084
6309
|
}
|
|
6310
|
+
/**
|
|
6311
|
+
* Get verbs by source (internal implementation)
|
|
6312
|
+
*/
|
|
6313
|
+
async getVerbsBySource_internal(sourceId) {
|
|
6314
|
+
return this.getEdgesBySource(sourceId);
|
|
6315
|
+
}
|
|
6085
6316
|
/**
|
|
6086
6317
|
* Get edges by source
|
|
6087
6318
|
*/
|
|
6088
6319
|
async getEdgesBySource(sourceId) {
|
|
6089
6320
|
const edges = await this.getAllEdges();
|
|
6090
|
-
return edges.filter((edge) => edge.sourceId === sourceId);
|
|
6321
|
+
return edges.filter((edge) => (edge.sourceId || edge.source) === sourceId);
|
|
6322
|
+
}
|
|
6323
|
+
/**
|
|
6324
|
+
* Get verbs by target (internal implementation)
|
|
6325
|
+
*/
|
|
6326
|
+
async getVerbsByTarget_internal(targetId) {
|
|
6327
|
+
return this.getEdgesByTarget(targetId);
|
|
6091
6328
|
}
|
|
6092
6329
|
/**
|
|
6093
6330
|
* Get edges by target
|
|
6094
6331
|
*/
|
|
6095
6332
|
async getEdgesByTarget(targetId) {
|
|
6096
6333
|
const edges = await this.getAllEdges();
|
|
6097
|
-
return edges.filter((edge) => edge.targetId === targetId);
|
|
6334
|
+
return edges.filter((edge) => (edge.targetId || edge.target) === targetId);
|
|
6335
|
+
}
|
|
6336
|
+
/**
|
|
6337
|
+
* Get verbs by type (internal implementation)
|
|
6338
|
+
*/
|
|
6339
|
+
async getVerbsByType_internal(type) {
|
|
6340
|
+
return this.getEdgesByType(type);
|
|
6098
6341
|
}
|
|
6099
6342
|
/**
|
|
6100
6343
|
* Get edges by type
|
|
6101
6344
|
*/
|
|
6102
6345
|
async getEdgesByType(type) {
|
|
6103
6346
|
const edges = await this.getAllEdges();
|
|
6104
|
-
return edges.filter((edge) => edge.type === type);
|
|
6347
|
+
return edges.filter((edge) => (edge.type || edge.verb) === type);
|
|
6348
|
+
}
|
|
6349
|
+
/**
|
|
6350
|
+
* Delete a verb from storage (internal implementation)
|
|
6351
|
+
*/
|
|
6352
|
+
async deleteVerb_internal(id) {
|
|
6353
|
+
return this.deleteEdge(id);
|
|
6105
6354
|
}
|
|
6106
6355
|
/**
|
|
6107
6356
|
* Delete an edge from storage
|
|
@@ -6315,6 +6564,31 @@ class OPFSStorage extends BaseStorage {
|
|
|
6315
6564
|
};
|
|
6316
6565
|
}
|
|
6317
6566
|
}
|
|
6567
|
+
/**
|
|
6568
|
+
* Get the statistics key for a specific date
|
|
6569
|
+
* @param date The date to get the key for
|
|
6570
|
+
* @returns The statistics key for the specified date
|
|
6571
|
+
*/
|
|
6572
|
+
getStatisticsKeyForDate(date) {
|
|
6573
|
+
const year = date.getUTCFullYear();
|
|
6574
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
|
|
6575
|
+
const day = String(date.getUTCDate()).padStart(2, '0');
|
|
6576
|
+
return `statistics_${year}${month}${day}.json`;
|
|
6577
|
+
}
|
|
6578
|
+
/**
|
|
6579
|
+
* Get the current statistics key
|
|
6580
|
+
* @returns The current statistics key
|
|
6581
|
+
*/
|
|
6582
|
+
getCurrentStatisticsKey() {
|
|
6583
|
+
return this.getStatisticsKeyForDate(new Date());
|
|
6584
|
+
}
|
|
6585
|
+
/**
|
|
6586
|
+
* Get the legacy statistics key (for backward compatibility)
|
|
6587
|
+
* @returns The legacy statistics key
|
|
6588
|
+
*/
|
|
6589
|
+
getLegacyStatisticsKey() {
|
|
6590
|
+
return 'statistics.json';
|
|
6591
|
+
}
|
|
6318
6592
|
/**
|
|
6319
6593
|
* Save statistics data to storage
|
|
6320
6594
|
* @param statistics The statistics data to save
|
|
@@ -6335,8 +6609,10 @@ class OPFSStorage extends BaseStorage {
|
|
|
6335
6609
|
if (!this.indexDir) {
|
|
6336
6610
|
throw new Error('Index directory not initialized');
|
|
6337
6611
|
}
|
|
6612
|
+
// Get the current statistics key
|
|
6613
|
+
const currentKey = this.getCurrentStatisticsKey();
|
|
6338
6614
|
// Create a file for the statistics data
|
|
6339
|
-
const fileHandle = await this.indexDir.getFileHandle(
|
|
6615
|
+
const fileHandle = await this.indexDir.getFileHandle(currentKey, {
|
|
6340
6616
|
create: true
|
|
6341
6617
|
});
|
|
6342
6618
|
// Create a writable stream
|
|
@@ -6345,6 +6621,16 @@ class OPFSStorage extends BaseStorage {
|
|
|
6345
6621
|
await writable.write(JSON.stringify(this.statistics, null, 2));
|
|
6346
6622
|
// Close the stream
|
|
6347
6623
|
await writable.close();
|
|
6624
|
+
// Also update the legacy key for backward compatibility, but less frequently
|
|
6625
|
+
if (Math.random() < 0.1) {
|
|
6626
|
+
const legacyKey = this.getLegacyStatisticsKey();
|
|
6627
|
+
const legacyFileHandle = await this.indexDir.getFileHandle(legacyKey, {
|
|
6628
|
+
create: true
|
|
6629
|
+
});
|
|
6630
|
+
const legacyWritable = await legacyFileHandle.createWritable();
|
|
6631
|
+
await legacyWritable.write(JSON.stringify(this.statistics, null, 2));
|
|
6632
|
+
await legacyWritable.close();
|
|
6633
|
+
}
|
|
6348
6634
|
}
|
|
6349
6635
|
catch (error) {
|
|
6350
6636
|
console.error('Failed to save statistics data:', error);
|
|
@@ -6372,17 +6658,15 @@ class OPFSStorage extends BaseStorage {
|
|
|
6372
6658
|
if (!this.indexDir) {
|
|
6373
6659
|
throw new Error('Index directory not initialized');
|
|
6374
6660
|
}
|
|
6661
|
+
// First try to get statistics from today's file
|
|
6662
|
+
const currentKey = this.getCurrentStatisticsKey();
|
|
6375
6663
|
try {
|
|
6376
|
-
|
|
6377
|
-
const fileHandle = await this.indexDir.getFileHandle('statistics.json', {
|
|
6664
|
+
const fileHandle = await this.indexDir.getFileHandle(currentKey, {
|
|
6378
6665
|
create: false
|
|
6379
6666
|
});
|
|
6380
|
-
// Get the file data
|
|
6381
6667
|
const file = await fileHandle.getFile();
|
|
6382
6668
|
const text = await file.text();
|
|
6383
|
-
// Parse the statistics data
|
|
6384
6669
|
this.statistics = JSON.parse(text);
|
|
6385
|
-
// Return a deep copy
|
|
6386
6670
|
if (this.statistics) {
|
|
6387
6671
|
return {
|
|
6388
6672
|
nounCount: { ...this.statistics.nounCount },
|
|
@@ -6392,13 +6676,57 @@ class OPFSStorage extends BaseStorage {
|
|
|
6392
6676
|
lastUpdated: this.statistics.lastUpdated
|
|
6393
6677
|
};
|
|
6394
6678
|
}
|
|
6395
|
-
// If statistics is null, return default statistics
|
|
6396
|
-
return this.createDefaultStatistics();
|
|
6397
6679
|
}
|
|
6398
6680
|
catch (error) {
|
|
6399
|
-
// If
|
|
6400
|
-
|
|
6681
|
+
// If today's file doesn't exist, try yesterday's file
|
|
6682
|
+
const yesterday = new Date();
|
|
6683
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
6684
|
+
const yesterdayKey = this.getStatisticsKeyForDate(yesterday);
|
|
6685
|
+
try {
|
|
6686
|
+
const fileHandle = await this.indexDir.getFileHandle(yesterdayKey, {
|
|
6687
|
+
create: false
|
|
6688
|
+
});
|
|
6689
|
+
const file = await fileHandle.getFile();
|
|
6690
|
+
const text = await file.text();
|
|
6691
|
+
this.statistics = JSON.parse(text);
|
|
6692
|
+
if (this.statistics) {
|
|
6693
|
+
return {
|
|
6694
|
+
nounCount: { ...this.statistics.nounCount },
|
|
6695
|
+
verbCount: { ...this.statistics.verbCount },
|
|
6696
|
+
metadataCount: { ...this.statistics.metadataCount },
|
|
6697
|
+
hnswIndexSize: this.statistics.hnswIndexSize,
|
|
6698
|
+
lastUpdated: this.statistics.lastUpdated
|
|
6699
|
+
};
|
|
6700
|
+
}
|
|
6701
|
+
}
|
|
6702
|
+
catch (error) {
|
|
6703
|
+
// If yesterday's file doesn't exist, try the legacy file
|
|
6704
|
+
const legacyKey = this.getLegacyStatisticsKey();
|
|
6705
|
+
try {
|
|
6706
|
+
const fileHandle = await this.indexDir.getFileHandle(legacyKey, {
|
|
6707
|
+
create: false
|
|
6708
|
+
});
|
|
6709
|
+
const file = await fileHandle.getFile();
|
|
6710
|
+
const text = await file.text();
|
|
6711
|
+
this.statistics = JSON.parse(text);
|
|
6712
|
+
if (this.statistics) {
|
|
6713
|
+
return {
|
|
6714
|
+
nounCount: { ...this.statistics.nounCount },
|
|
6715
|
+
verbCount: { ...this.statistics.verbCount },
|
|
6716
|
+
metadataCount: { ...this.statistics.metadataCount },
|
|
6717
|
+
hnswIndexSize: this.statistics.hnswIndexSize,
|
|
6718
|
+
lastUpdated: this.statistics.lastUpdated
|
|
6719
|
+
};
|
|
6720
|
+
}
|
|
6721
|
+
}
|
|
6722
|
+
catch (error) {
|
|
6723
|
+
// If the legacy file doesn't exist either, return null
|
|
6724
|
+
return null;
|
|
6725
|
+
}
|
|
6726
|
+
}
|
|
6401
6727
|
}
|
|
6728
|
+
// If we get here and statistics is null, return default statistics
|
|
6729
|
+
return this.statistics ? this.statistics : null;
|
|
6402
6730
|
}
|
|
6403
6731
|
catch (error) {
|
|
6404
6732
|
console.error('Failed to get statistics data:', error);
|
|
@@ -6490,66 +6818,66 @@ class FileSystemStorage extends BaseStorage {
|
|
|
6490
6818
|
}
|
|
6491
6819
|
}
|
|
6492
6820
|
/**
|
|
6493
|
-
* Save a
|
|
6821
|
+
* Save a noun to storage
|
|
6494
6822
|
*/
|
|
6495
|
-
async
|
|
6823
|
+
async saveNoun_internal(noun) {
|
|
6496
6824
|
await this.ensureInitialized();
|
|
6497
6825
|
// Convert connections Map to a serializable format
|
|
6498
|
-
const
|
|
6499
|
-
...
|
|
6500
|
-
connections: this.mapToObject(
|
|
6826
|
+
const serializableNoun = {
|
|
6827
|
+
...noun,
|
|
6828
|
+
connections: this.mapToObject(noun.connections, (set) => Array.from(set))
|
|
6501
6829
|
};
|
|
6502
|
-
const filePath = path.join(this.nounsDir, `${
|
|
6503
|
-
await fs.promises.writeFile(filePath, JSON.stringify(
|
|
6830
|
+
const filePath = path.join(this.nounsDir, `${noun.id}.json`);
|
|
6831
|
+
await fs.promises.writeFile(filePath, JSON.stringify(serializableNoun, null, 2));
|
|
6504
6832
|
}
|
|
6505
6833
|
/**
|
|
6506
|
-
* Get a
|
|
6834
|
+
* Get a noun from storage
|
|
6507
6835
|
*/
|
|
6508
|
-
async
|
|
6836
|
+
async getNoun_internal(id) {
|
|
6509
6837
|
await this.ensureInitialized();
|
|
6510
6838
|
const filePath = path.join(this.nounsDir, `${id}.json`);
|
|
6511
6839
|
try {
|
|
6512
6840
|
const data = await fs.promises.readFile(filePath, 'utf-8');
|
|
6513
|
-
const
|
|
6841
|
+
const parsedNoun = JSON.parse(data);
|
|
6514
6842
|
// Convert serialized connections back to Map<number, Set<string>>
|
|
6515
6843
|
const connections = new Map();
|
|
6516
|
-
for (const [level,
|
|
6517
|
-
connections.set(Number(level), new Set(
|
|
6844
|
+
for (const [level, nounIds] of Object.entries(parsedNoun.connections)) {
|
|
6845
|
+
connections.set(Number(level), new Set(nounIds));
|
|
6518
6846
|
}
|
|
6519
6847
|
return {
|
|
6520
|
-
id:
|
|
6521
|
-
vector:
|
|
6848
|
+
id: parsedNoun.id,
|
|
6849
|
+
vector: parsedNoun.vector,
|
|
6522
6850
|
connections
|
|
6523
6851
|
};
|
|
6524
6852
|
}
|
|
6525
6853
|
catch (error) {
|
|
6526
6854
|
if (error.code !== 'ENOENT') {
|
|
6527
|
-
console.error(`Error reading
|
|
6855
|
+
console.error(`Error reading noun ${id}:`, error);
|
|
6528
6856
|
}
|
|
6529
6857
|
return null;
|
|
6530
6858
|
}
|
|
6531
6859
|
}
|
|
6532
6860
|
/**
|
|
6533
|
-
* Get all
|
|
6861
|
+
* Get all nouns from storage
|
|
6534
6862
|
*/
|
|
6535
|
-
async
|
|
6863
|
+
async getAllNouns_internal() {
|
|
6536
6864
|
await this.ensureInitialized();
|
|
6537
|
-
const
|
|
6865
|
+
const allNouns = [];
|
|
6538
6866
|
try {
|
|
6539
6867
|
const files = await fs.promises.readdir(this.nounsDir);
|
|
6540
6868
|
for (const file of files) {
|
|
6541
6869
|
if (file.endsWith('.json')) {
|
|
6542
6870
|
const filePath = path.join(this.nounsDir, file);
|
|
6543
6871
|
const data = await fs.promises.readFile(filePath, 'utf-8');
|
|
6544
|
-
const
|
|
6872
|
+
const parsedNoun = JSON.parse(data);
|
|
6545
6873
|
// Convert serialized connections back to Map<number, Set<string>>
|
|
6546
6874
|
const connections = new Map();
|
|
6547
|
-
for (const [level,
|
|
6548
|
-
connections.set(Number(level), new Set(
|
|
6875
|
+
for (const [level, nounIds] of Object.entries(parsedNoun.connections)) {
|
|
6876
|
+
connections.set(Number(level), new Set(nounIds));
|
|
6549
6877
|
}
|
|
6550
|
-
|
|
6551
|
-
id:
|
|
6552
|
-
vector:
|
|
6878
|
+
allNouns.push({
|
|
6879
|
+
id: parsedNoun.id,
|
|
6880
|
+
vector: parsedNoun.vector,
|
|
6553
6881
|
connections
|
|
6554
6882
|
});
|
|
6555
6883
|
}
|
|
@@ -6560,14 +6888,14 @@ class FileSystemStorage extends BaseStorage {
|
|
|
6560
6888
|
console.error(`Error reading directory ${this.nounsDir}:`, error);
|
|
6561
6889
|
}
|
|
6562
6890
|
}
|
|
6563
|
-
return
|
|
6891
|
+
return allNouns;
|
|
6564
6892
|
}
|
|
6565
6893
|
/**
|
|
6566
|
-
* Get
|
|
6894
|
+
* Get nouns by noun type
|
|
6567
6895
|
* @param nounType The noun type to filter by
|
|
6568
|
-
* @returns Promise that resolves to an array of
|
|
6896
|
+
* @returns Promise that resolves to an array of nouns of the specified noun type
|
|
6569
6897
|
*/
|
|
6570
|
-
async
|
|
6898
|
+
async getNounsByNounType_internal(nounType) {
|
|
6571
6899
|
await this.ensureInitialized();
|
|
6572
6900
|
const nouns = [];
|
|
6573
6901
|
try {
|
|
@@ -6576,19 +6904,19 @@ class FileSystemStorage extends BaseStorage {
|
|
|
6576
6904
|
if (file.endsWith('.json')) {
|
|
6577
6905
|
const filePath = path.join(this.nounsDir, file);
|
|
6578
6906
|
const data = await fs.promises.readFile(filePath, 'utf-8');
|
|
6579
|
-
const
|
|
6907
|
+
const parsedNoun = JSON.parse(data);
|
|
6580
6908
|
// Filter by noun type using metadata
|
|
6581
|
-
const
|
|
6582
|
-
const metadata = await this.getMetadata(
|
|
6909
|
+
const nounId = parsedNoun.id;
|
|
6910
|
+
const metadata = await this.getMetadata(nounId);
|
|
6583
6911
|
if (metadata && metadata.noun === nounType) {
|
|
6584
6912
|
// Convert serialized connections back to Map<number, Set<string>>
|
|
6585
6913
|
const connections = new Map();
|
|
6586
|
-
for (const [level,
|
|
6587
|
-
connections.set(Number(level), new Set(
|
|
6914
|
+
for (const [level, nounIds] of Object.entries(parsedNoun.connections)) {
|
|
6915
|
+
connections.set(Number(level), new Set(nounIds));
|
|
6588
6916
|
}
|
|
6589
6917
|
nouns.push({
|
|
6590
|
-
id:
|
|
6591
|
-
vector:
|
|
6918
|
+
id: parsedNoun.id,
|
|
6919
|
+
vector: parsedNoun.vector,
|
|
6592
6920
|
connections
|
|
6593
6921
|
});
|
|
6594
6922
|
}
|
|
@@ -6603,9 +6931,9 @@ class FileSystemStorage extends BaseStorage {
|
|
|
6603
6931
|
return nouns;
|
|
6604
6932
|
}
|
|
6605
6933
|
/**
|
|
6606
|
-
* Delete a
|
|
6934
|
+
* Delete a noun from storage
|
|
6607
6935
|
*/
|
|
6608
|
-
async
|
|
6936
|
+
async deleteNoun_internal(id) {
|
|
6609
6937
|
await this.ensureInitialized();
|
|
6610
6938
|
const filePath = path.join(this.nounsDir, `${id}.json`);
|
|
6611
6939
|
try {
|
|
@@ -6613,83 +6941,98 @@ class FileSystemStorage extends BaseStorage {
|
|
|
6613
6941
|
}
|
|
6614
6942
|
catch (error) {
|
|
6615
6943
|
if (error.code !== 'ENOENT') {
|
|
6616
|
-
console.error(`Error deleting
|
|
6944
|
+
console.error(`Error deleting noun ${id}:`, error);
|
|
6617
6945
|
throw error;
|
|
6618
6946
|
}
|
|
6619
6947
|
}
|
|
6620
6948
|
}
|
|
6621
6949
|
/**
|
|
6622
|
-
* Save
|
|
6950
|
+
* Save a verb to storage
|
|
6623
6951
|
*/
|
|
6624
|
-
async
|
|
6952
|
+
async saveVerb_internal(verb) {
|
|
6625
6953
|
await this.ensureInitialized();
|
|
6626
6954
|
// Convert connections Map to a serializable format
|
|
6627
|
-
const
|
|
6628
|
-
...
|
|
6629
|
-
connections: this.mapToObject(
|
|
6955
|
+
const serializableVerb = {
|
|
6956
|
+
...verb,
|
|
6957
|
+
connections: this.mapToObject(verb.connections, (set) => Array.from(set))
|
|
6630
6958
|
};
|
|
6631
|
-
const filePath = path.join(this.verbsDir, `${
|
|
6632
|
-
await fs.promises.writeFile(filePath, JSON.stringify(
|
|
6959
|
+
const filePath = path.join(this.verbsDir, `${verb.id}.json`);
|
|
6960
|
+
await fs.promises.writeFile(filePath, JSON.stringify(serializableVerb, null, 2));
|
|
6633
6961
|
}
|
|
6634
6962
|
/**
|
|
6635
|
-
* Get
|
|
6963
|
+
* Get a verb from storage
|
|
6636
6964
|
*/
|
|
6637
|
-
async
|
|
6965
|
+
async getVerb_internal(id) {
|
|
6638
6966
|
await this.ensureInitialized();
|
|
6639
6967
|
const filePath = path.join(this.verbsDir, `${id}.json`);
|
|
6640
6968
|
try {
|
|
6641
6969
|
const data = await fs.promises.readFile(filePath, 'utf-8');
|
|
6642
|
-
const
|
|
6970
|
+
const parsedVerb = JSON.parse(data);
|
|
6643
6971
|
// Convert serialized connections back to Map<number, Set<string>>
|
|
6644
6972
|
const connections = new Map();
|
|
6645
|
-
for (const [level, nodeIds] of Object.entries(
|
|
6973
|
+
for (const [level, nodeIds] of Object.entries(parsedVerb.connections)) {
|
|
6646
6974
|
connections.set(Number(level), new Set(nodeIds));
|
|
6647
6975
|
}
|
|
6976
|
+
// Create default timestamp if not present
|
|
6977
|
+
const defaultTimestamp = {
|
|
6978
|
+
seconds: Math.floor(Date.now() / 1000),
|
|
6979
|
+
nanoseconds: (Date.now() % 1000) * 1000000
|
|
6980
|
+
};
|
|
6981
|
+
// Create default createdBy if not present
|
|
6982
|
+
const defaultCreatedBy = {
|
|
6983
|
+
augmentation: 'unknown',
|
|
6984
|
+
version: '1.0'
|
|
6985
|
+
};
|
|
6648
6986
|
return {
|
|
6649
|
-
id:
|
|
6650
|
-
vector:
|
|
6987
|
+
id: parsedVerb.id,
|
|
6988
|
+
vector: parsedVerb.vector,
|
|
6651
6989
|
connections,
|
|
6652
|
-
sourceId:
|
|
6653
|
-
targetId:
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6990
|
+
sourceId: parsedVerb.sourceId || parsedVerb.source,
|
|
6991
|
+
targetId: parsedVerb.targetId || parsedVerb.target,
|
|
6992
|
+
source: parsedVerb.sourceId || parsedVerb.source,
|
|
6993
|
+
target: parsedVerb.targetId || parsedVerb.target,
|
|
6994
|
+
verb: parsedVerb.type || parsedVerb.verb,
|
|
6995
|
+
weight: parsedVerb.weight,
|
|
6996
|
+
metadata: parsedVerb.metadata,
|
|
6997
|
+
createdAt: parsedVerb.createdAt || defaultTimestamp,
|
|
6998
|
+
updatedAt: parsedVerb.updatedAt || defaultTimestamp,
|
|
6999
|
+
createdBy: parsedVerb.createdBy || defaultCreatedBy
|
|
6657
7000
|
};
|
|
6658
7001
|
}
|
|
6659
7002
|
catch (error) {
|
|
6660
7003
|
if (error.code !== 'ENOENT') {
|
|
6661
|
-
console.error(`Error reading
|
|
7004
|
+
console.error(`Error reading verb ${id}:`, error);
|
|
6662
7005
|
}
|
|
6663
7006
|
return null;
|
|
6664
7007
|
}
|
|
6665
7008
|
}
|
|
6666
7009
|
/**
|
|
6667
|
-
* Get all
|
|
7010
|
+
* Get all verbs from storage
|
|
6668
7011
|
*/
|
|
6669
|
-
async
|
|
7012
|
+
async getAllVerbs_internal() {
|
|
6670
7013
|
await this.ensureInitialized();
|
|
6671
|
-
const
|
|
7014
|
+
const allVerbs = [];
|
|
6672
7015
|
try {
|
|
6673
7016
|
const files = await fs.promises.readdir(this.verbsDir);
|
|
6674
7017
|
for (const file of files) {
|
|
6675
7018
|
if (file.endsWith('.json')) {
|
|
6676
7019
|
const filePath = path.join(this.verbsDir, file);
|
|
6677
7020
|
const data = await fs.promises.readFile(filePath, 'utf-8');
|
|
6678
|
-
const
|
|
7021
|
+
const parsedVerb = JSON.parse(data);
|
|
6679
7022
|
// Convert serialized connections back to Map<number, Set<string>>
|
|
6680
7023
|
const connections = new Map();
|
|
6681
|
-
for (const [level, nodeIds] of Object.entries(
|
|
7024
|
+
for (const [level, nodeIds] of Object.entries(parsedVerb.connections)) {
|
|
6682
7025
|
connections.set(Number(level), new Set(nodeIds));
|
|
6683
7026
|
}
|
|
6684
|
-
|
|
6685
|
-
id:
|
|
6686
|
-
vector:
|
|
7027
|
+
allVerbs.push({
|
|
7028
|
+
id: parsedVerb.id,
|
|
7029
|
+
vector: parsedVerb.vector,
|
|
6687
7030
|
connections,
|
|
6688
|
-
sourceId:
|
|
6689
|
-
targetId:
|
|
6690
|
-
type:
|
|
6691
|
-
weight:
|
|
6692
|
-
metadata:
|
|
7031
|
+
sourceId: parsedVerb.sourceId,
|
|
7032
|
+
targetId: parsedVerb.targetId,
|
|
7033
|
+
type: parsedVerb.type,
|
|
7034
|
+
weight: parsedVerb.weight,
|
|
7035
|
+
metadata: parsedVerb.metadata
|
|
6693
7036
|
});
|
|
6694
7037
|
}
|
|
6695
7038
|
}
|
|
@@ -6699,33 +7042,33 @@ class FileSystemStorage extends BaseStorage {
|
|
|
6699
7042
|
console.error(`Error reading directory ${this.verbsDir}:`, error);
|
|
6700
7043
|
}
|
|
6701
7044
|
}
|
|
6702
|
-
return
|
|
7045
|
+
return allVerbs;
|
|
6703
7046
|
}
|
|
6704
7047
|
/**
|
|
6705
|
-
* Get
|
|
7048
|
+
* Get verbs by source
|
|
6706
7049
|
*/
|
|
6707
|
-
async
|
|
6708
|
-
const
|
|
6709
|
-
return
|
|
7050
|
+
async getVerbsBySource_internal(sourceId) {
|
|
7051
|
+
const verbs = await this.getAllVerbs_internal();
|
|
7052
|
+
return verbs.filter((verb) => (verb.sourceId || verb.source) === sourceId);
|
|
6710
7053
|
}
|
|
6711
7054
|
/**
|
|
6712
|
-
* Get
|
|
7055
|
+
* Get verbs by target
|
|
6713
7056
|
*/
|
|
6714
|
-
async
|
|
6715
|
-
const
|
|
6716
|
-
return
|
|
7057
|
+
async getVerbsByTarget_internal(targetId) {
|
|
7058
|
+
const verbs = await this.getAllVerbs_internal();
|
|
7059
|
+
return verbs.filter((verb) => (verb.targetId || verb.target) === targetId);
|
|
6717
7060
|
}
|
|
6718
7061
|
/**
|
|
6719
|
-
* Get
|
|
7062
|
+
* Get verbs by type
|
|
6720
7063
|
*/
|
|
6721
|
-
async
|
|
6722
|
-
const
|
|
6723
|
-
return
|
|
7064
|
+
async getVerbsByType_internal(type) {
|
|
7065
|
+
const verbs = await this.getAllVerbs_internal();
|
|
7066
|
+
return verbs.filter((verb) => (verb.type || verb.verb) === type);
|
|
6724
7067
|
}
|
|
6725
7068
|
/**
|
|
6726
|
-
* Delete
|
|
7069
|
+
* Delete a verb from storage
|
|
6727
7070
|
*/
|
|
6728
|
-
async
|
|
7071
|
+
async deleteVerb_internal(id) {
|
|
6729
7072
|
await this.ensureInitialized();
|
|
6730
7073
|
const filePath = path.join(this.verbsDir, `${id}.json`);
|
|
6731
7074
|
try {
|
|
@@ -6733,7 +7076,7 @@ class FileSystemStorage extends BaseStorage {
|
|
|
6733
7076
|
}
|
|
6734
7077
|
catch (error) {
|
|
6735
7078
|
if (error.code !== 'ENOENT') {
|
|
6736
|
-
console.error(`Error deleting
|
|
7079
|
+
console.error(`Error deleting verb file ${filePath}:`, error);
|
|
6737
7080
|
throw error;
|
|
6738
7081
|
}
|
|
6739
7082
|
}
|
|
@@ -6893,8 +7236,20 @@ class FileSystemStorage extends BaseStorage {
|
|
|
6893
7236
|
async saveStatisticsData(statistics) {
|
|
6894
7237
|
await this.ensureInitialized();
|
|
6895
7238
|
try {
|
|
6896
|
-
|
|
6897
|
-
|
|
7239
|
+
// Get the current date for time-based partitioning
|
|
7240
|
+
const now = new Date();
|
|
7241
|
+
const year = now.getUTCFullYear();
|
|
7242
|
+
const month = String(now.getUTCMonth() + 1).padStart(2, '0');
|
|
7243
|
+
const day = String(now.getUTCDate()).padStart(2, '0');
|
|
7244
|
+
const dateStr = `${year}${month}${day}`;
|
|
7245
|
+
// Save to the current day's file
|
|
7246
|
+
const currentFilePath = path.join(this.indexDir, `${STATISTICS_KEY}_${dateStr}.json`);
|
|
7247
|
+
await fs.promises.writeFile(currentFilePath, JSON.stringify(statistics, null, 2));
|
|
7248
|
+
// Also update the legacy file for backward compatibility (less frequently)
|
|
7249
|
+
if (Math.random() < 0.1) {
|
|
7250
|
+
const legacyFilePath = path.join(this.indexDir, `${STATISTICS_KEY}.json`);
|
|
7251
|
+
await fs.promises.writeFile(legacyFilePath, JSON.stringify(statistics, null, 2));
|
|
7252
|
+
}
|
|
6898
7253
|
}
|
|
6899
7254
|
catch (error) {
|
|
6900
7255
|
console.error('Failed to save statistics data:', error);
|
|
@@ -6908,15 +7263,54 @@ class FileSystemStorage extends BaseStorage {
|
|
|
6908
7263
|
async getStatisticsData() {
|
|
6909
7264
|
await this.ensureInitialized();
|
|
6910
7265
|
try {
|
|
6911
|
-
|
|
6912
|
-
const
|
|
6913
|
-
|
|
7266
|
+
// Try to get statistics from today's file first
|
|
7267
|
+
const now = new Date();
|
|
7268
|
+
const year = now.getUTCFullYear();
|
|
7269
|
+
const month = String(now.getUTCMonth() + 1).padStart(2, '0');
|
|
7270
|
+
const day = String(now.getUTCDate()).padStart(2, '0');
|
|
7271
|
+
const dateStr = `${year}${month}${day}`;
|
|
7272
|
+
const currentFilePath = path.join(this.indexDir, `${STATISTICS_KEY}_${dateStr}.json`);
|
|
7273
|
+
try {
|
|
7274
|
+
const data = await fs.promises.readFile(currentFilePath, 'utf-8');
|
|
7275
|
+
return JSON.parse(data);
|
|
7276
|
+
}
|
|
7277
|
+
catch (currentError) {
|
|
7278
|
+
// If today's file doesn't exist, try yesterday's file
|
|
7279
|
+
if (currentError.code === 'ENOENT') {
|
|
7280
|
+
const yesterday = new Date();
|
|
7281
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
7282
|
+
const yesterdayYear = yesterday.getUTCFullYear();
|
|
7283
|
+
const yesterdayMonth = String(yesterday.getUTCMonth() + 1).padStart(2, '0');
|
|
7284
|
+
const yesterdayDay = String(yesterday.getUTCDate()).padStart(2, '0');
|
|
7285
|
+
const yesterdayDateStr = `${yesterdayYear}${yesterdayMonth}${yesterdayDay}`;
|
|
7286
|
+
const yesterdayFilePath = path.join(this.indexDir, `${STATISTICS_KEY}_${yesterdayDateStr}.json`);
|
|
7287
|
+
try {
|
|
7288
|
+
const yesterdayData = await fs.promises.readFile(yesterdayFilePath, 'utf-8');
|
|
7289
|
+
return JSON.parse(yesterdayData);
|
|
7290
|
+
}
|
|
7291
|
+
catch (yesterdayError) {
|
|
7292
|
+
// If yesterday's file doesn't exist, try the legacy file
|
|
7293
|
+
if (yesterdayError.code === 'ENOENT') {
|
|
7294
|
+
const legacyFilePath = path.join(this.indexDir, `${STATISTICS_KEY}.json`);
|
|
7295
|
+
try {
|
|
7296
|
+
const legacyData = await fs.promises.readFile(legacyFilePath, 'utf-8');
|
|
7297
|
+
return JSON.parse(legacyData);
|
|
7298
|
+
}
|
|
7299
|
+
catch (legacyError) {
|
|
7300
|
+
// If the legacy file doesn't exist either, return null
|
|
7301
|
+
if (legacyError.code === 'ENOENT') {
|
|
7302
|
+
return null;
|
|
7303
|
+
}
|
|
7304
|
+
throw legacyError;
|
|
7305
|
+
}
|
|
7306
|
+
}
|
|
7307
|
+
throw yesterdayError;
|
|
7308
|
+
}
|
|
7309
|
+
}
|
|
7310
|
+
throw currentError;
|
|
7311
|
+
}
|
|
6914
7312
|
}
|
|
6915
7313
|
catch (error) {
|
|
6916
|
-
// If the file doesn't exist, return null
|
|
6917
|
-
if (error.code === 'ENOENT') {
|
|
6918
|
-
return null;
|
|
6919
|
-
}
|
|
6920
7314
|
console.error('Error getting statistics data:', error);
|
|
6921
7315
|
throw error;
|
|
6922
7316
|
}
|
|
@@ -6960,6 +7354,16 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
6960
7354
|
this.s3Client = null;
|
|
6961
7355
|
// Statistics caching for better performance
|
|
6962
7356
|
this.statisticsCache = null;
|
|
7357
|
+
// Batch update timer ID
|
|
7358
|
+
this.statisticsBatchUpdateTimerId = null;
|
|
7359
|
+
// Flag to indicate if statistics have been modified since last save
|
|
7360
|
+
this.statisticsModified = false;
|
|
7361
|
+
// Time of last statistics flush to storage
|
|
7362
|
+
this.lastStatisticsFlushTime = 0;
|
|
7363
|
+
// Minimum time between statistics flushes (5 seconds)
|
|
7364
|
+
this.MIN_FLUSH_INTERVAL_MS = 5000;
|
|
7365
|
+
// Maximum time to wait before flushing statistics (30 seconds)
|
|
7366
|
+
this.MAX_FLUSH_DELAY_MS = 30000;
|
|
6963
7367
|
this.bucketName = options.bucketName;
|
|
6964
7368
|
this.region = options.region || 'auto';
|
|
6965
7369
|
this.endpoint = options.endpoint;
|
|
@@ -7018,6 +7422,12 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7018
7422
|
throw new Error(`Failed to initialize ${this.serviceType} storage: ${error}`);
|
|
7019
7423
|
}
|
|
7020
7424
|
}
|
|
7425
|
+
/**
|
|
7426
|
+
* Save a noun to storage (internal implementation)
|
|
7427
|
+
*/
|
|
7428
|
+
async saveNoun_internal(noun) {
|
|
7429
|
+
return this.saveNode(noun);
|
|
7430
|
+
}
|
|
7021
7431
|
/**
|
|
7022
7432
|
* Save a node to storage
|
|
7023
7433
|
*/
|
|
@@ -7067,6 +7477,12 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7067
7477
|
throw new Error(`Failed to save node ${node.id}: ${error}`);
|
|
7068
7478
|
}
|
|
7069
7479
|
}
|
|
7480
|
+
/**
|
|
7481
|
+
* Get a noun from storage (internal implementation)
|
|
7482
|
+
*/
|
|
7483
|
+
async getNoun_internal(id) {
|
|
7484
|
+
return this.getNode(id);
|
|
7485
|
+
}
|
|
7070
7486
|
/**
|
|
7071
7487
|
* Get a node from storage
|
|
7072
7488
|
*/
|
|
@@ -7124,6 +7540,12 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7124
7540
|
return null;
|
|
7125
7541
|
}
|
|
7126
7542
|
}
|
|
7543
|
+
/**
|
|
7544
|
+
* Get all nouns from storage (internal implementation)
|
|
7545
|
+
*/
|
|
7546
|
+
async getAllNouns_internal() {
|
|
7547
|
+
return this.getAllNodes();
|
|
7548
|
+
}
|
|
7127
7549
|
/**
|
|
7128
7550
|
* Get all nodes from storage
|
|
7129
7551
|
*/
|
|
@@ -7222,6 +7644,14 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7222
7644
|
return [];
|
|
7223
7645
|
}
|
|
7224
7646
|
}
|
|
7647
|
+
/**
|
|
7648
|
+
* Get nouns by noun type (internal implementation)
|
|
7649
|
+
* @param nounType The noun type to filter by
|
|
7650
|
+
* @returns Promise that resolves to an array of nouns of the specified noun type
|
|
7651
|
+
*/
|
|
7652
|
+
async getNounsByNounType_internal(nounType) {
|
|
7653
|
+
return this.getNodesByNounType(nounType);
|
|
7654
|
+
}
|
|
7225
7655
|
/**
|
|
7226
7656
|
* Get nodes by noun type
|
|
7227
7657
|
* @param nounType The noun type to filter by
|
|
@@ -7247,6 +7677,12 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7247
7677
|
return [];
|
|
7248
7678
|
}
|
|
7249
7679
|
}
|
|
7680
|
+
/**
|
|
7681
|
+
* Delete a noun from storage (internal implementation)
|
|
7682
|
+
*/
|
|
7683
|
+
async deleteNoun_internal(id) {
|
|
7684
|
+
return this.deleteNode(id);
|
|
7685
|
+
}
|
|
7250
7686
|
/**
|
|
7251
7687
|
* Delete a node from storage
|
|
7252
7688
|
*/
|
|
@@ -7266,6 +7702,12 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7266
7702
|
throw new Error(`Failed to delete node ${id}: ${error}`);
|
|
7267
7703
|
}
|
|
7268
7704
|
}
|
|
7705
|
+
/**
|
|
7706
|
+
* Save a verb to storage (internal implementation)
|
|
7707
|
+
*/
|
|
7708
|
+
async saveVerb_internal(verb) {
|
|
7709
|
+
return this.saveEdge(verb);
|
|
7710
|
+
}
|
|
7269
7711
|
/**
|
|
7270
7712
|
* Save an edge to storage
|
|
7271
7713
|
*/
|
|
@@ -7292,6 +7734,12 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7292
7734
|
throw new Error(`Failed to save edge ${edge.id}: ${error}`);
|
|
7293
7735
|
}
|
|
7294
7736
|
}
|
|
7737
|
+
/**
|
|
7738
|
+
* Get a verb from storage (internal implementation)
|
|
7739
|
+
*/
|
|
7740
|
+
async getVerb_internal(id) {
|
|
7741
|
+
return this.getEdge(id);
|
|
7742
|
+
}
|
|
7295
7743
|
/**
|
|
7296
7744
|
* Get an edge from storage
|
|
7297
7745
|
*/
|
|
@@ -7322,7 +7770,9 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7322
7770
|
console.log(`Parsed edge data for ${id}:`, parsedEdge);
|
|
7323
7771
|
// Ensure the parsed edge has the expected properties
|
|
7324
7772
|
if (!parsedEdge || !parsedEdge.id || !parsedEdge.vector || !parsedEdge.connections ||
|
|
7325
|
-
!parsedEdge.sourceId ||
|
|
7773
|
+
!(parsedEdge.sourceId || parsedEdge.source) ||
|
|
7774
|
+
!(parsedEdge.targetId || parsedEdge.target) ||
|
|
7775
|
+
!(parsedEdge.type || parsedEdge.verb)) {
|
|
7326
7776
|
console.error(`Invalid edge data for ${id}:`, parsedEdge);
|
|
7327
7777
|
return null;
|
|
7328
7778
|
}
|
|
@@ -7331,15 +7781,31 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7331
7781
|
for (const [level, nodeIds] of Object.entries(parsedEdge.connections)) {
|
|
7332
7782
|
connections.set(Number(level), new Set(nodeIds));
|
|
7333
7783
|
}
|
|
7784
|
+
// Create default timestamp if not present
|
|
7785
|
+
const defaultTimestamp = {
|
|
7786
|
+
seconds: Math.floor(Date.now() / 1000),
|
|
7787
|
+
nanoseconds: (Date.now() % 1000) * 1000000
|
|
7788
|
+
};
|
|
7789
|
+
// Create default createdBy if not present
|
|
7790
|
+
const defaultCreatedBy = {
|
|
7791
|
+
augmentation: 'unknown',
|
|
7792
|
+
version: '1.0'
|
|
7793
|
+
};
|
|
7334
7794
|
const edge = {
|
|
7335
7795
|
id: parsedEdge.id,
|
|
7336
7796
|
vector: parsedEdge.vector,
|
|
7337
7797
|
connections,
|
|
7338
|
-
sourceId: parsedEdge.sourceId,
|
|
7339
|
-
targetId: parsedEdge.targetId,
|
|
7340
|
-
|
|
7798
|
+
sourceId: parsedEdge.sourceId || parsedEdge.source,
|
|
7799
|
+
targetId: parsedEdge.targetId || parsedEdge.target,
|
|
7800
|
+
source: parsedEdge.sourceId || parsedEdge.source,
|
|
7801
|
+
target: parsedEdge.targetId || parsedEdge.target,
|
|
7802
|
+
verb: parsedEdge.type || parsedEdge.verb,
|
|
7803
|
+
type: parsedEdge.type || parsedEdge.verb,
|
|
7341
7804
|
weight: parsedEdge.weight || 1.0, // Default weight if not provided
|
|
7342
|
-
metadata: parsedEdge.metadata || {}
|
|
7805
|
+
metadata: parsedEdge.metadata || {},
|
|
7806
|
+
createdAt: parsedEdge.createdAt || defaultTimestamp,
|
|
7807
|
+
updatedAt: parsedEdge.updatedAt || defaultTimestamp,
|
|
7808
|
+
createdBy: parsedEdge.createdBy || defaultCreatedBy
|
|
7343
7809
|
};
|
|
7344
7810
|
console.log(`Successfully retrieved edge ${id}:`, edge);
|
|
7345
7811
|
return edge;
|
|
@@ -7355,6 +7821,12 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7355
7821
|
return null;
|
|
7356
7822
|
}
|
|
7357
7823
|
}
|
|
7824
|
+
/**
|
|
7825
|
+
* Get all verbs from storage (internal implementation)
|
|
7826
|
+
*/
|
|
7827
|
+
async getAllVerbs_internal() {
|
|
7828
|
+
return this.getAllEdges();
|
|
7829
|
+
}
|
|
7358
7830
|
/**
|
|
7359
7831
|
* Get all edges from storage
|
|
7360
7832
|
*/
|
|
@@ -7391,15 +7863,31 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7391
7863
|
for (const [level, nodeIds] of Object.entries(parsedEdge.connections)) {
|
|
7392
7864
|
connections.set(Number(level), new Set(nodeIds));
|
|
7393
7865
|
}
|
|
7866
|
+
// Create default timestamp if not present
|
|
7867
|
+
const defaultTimestamp = {
|
|
7868
|
+
seconds: Math.floor(Date.now() / 1000),
|
|
7869
|
+
nanoseconds: (Date.now() % 1000) * 1000000
|
|
7870
|
+
};
|
|
7871
|
+
// Create default createdBy if not present
|
|
7872
|
+
const defaultCreatedBy = {
|
|
7873
|
+
augmentation: 'unknown',
|
|
7874
|
+
version: '1.0'
|
|
7875
|
+
};
|
|
7394
7876
|
return {
|
|
7395
7877
|
id: parsedEdge.id,
|
|
7396
7878
|
vector: parsedEdge.vector,
|
|
7397
7879
|
connections,
|
|
7398
|
-
sourceId: parsedEdge.sourceId,
|
|
7399
|
-
targetId: parsedEdge.targetId,
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7880
|
+
sourceId: parsedEdge.sourceId || parsedEdge.source,
|
|
7881
|
+
targetId: parsedEdge.targetId || parsedEdge.target,
|
|
7882
|
+
source: parsedEdge.sourceId || parsedEdge.source,
|
|
7883
|
+
target: parsedEdge.targetId || parsedEdge.target,
|
|
7884
|
+
verb: parsedEdge.type || parsedEdge.verb,
|
|
7885
|
+
type: parsedEdge.type || parsedEdge.verb,
|
|
7886
|
+
weight: parsedEdge.weight || 1.0,
|
|
7887
|
+
metadata: parsedEdge.metadata || {},
|
|
7888
|
+
createdAt: parsedEdge.createdAt || defaultTimestamp,
|
|
7889
|
+
updatedAt: parsedEdge.updatedAt || defaultTimestamp,
|
|
7890
|
+
createdBy: parsedEdge.createdBy || defaultCreatedBy
|
|
7403
7891
|
};
|
|
7404
7892
|
}
|
|
7405
7893
|
catch (error) {
|
|
@@ -7416,26 +7904,50 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7416
7904
|
return [];
|
|
7417
7905
|
}
|
|
7418
7906
|
}
|
|
7907
|
+
/**
|
|
7908
|
+
* Get verbs by source (internal implementation)
|
|
7909
|
+
*/
|
|
7910
|
+
async getVerbsBySource_internal(sourceId) {
|
|
7911
|
+
return this.getEdgesBySource(sourceId);
|
|
7912
|
+
}
|
|
7419
7913
|
/**
|
|
7420
7914
|
* Get edges by source
|
|
7421
7915
|
*/
|
|
7422
7916
|
async getEdgesBySource(sourceId) {
|
|
7423
7917
|
const edges = await this.getAllEdges();
|
|
7424
|
-
return edges.filter((edge) => edge.sourceId === sourceId);
|
|
7918
|
+
return edges.filter((edge) => (edge.sourceId || edge.source) === sourceId);
|
|
7919
|
+
}
|
|
7920
|
+
/**
|
|
7921
|
+
* Get verbs by target (internal implementation)
|
|
7922
|
+
*/
|
|
7923
|
+
async getVerbsByTarget_internal(targetId) {
|
|
7924
|
+
return this.getEdgesByTarget(targetId);
|
|
7425
7925
|
}
|
|
7426
7926
|
/**
|
|
7427
7927
|
* Get edges by target
|
|
7428
7928
|
*/
|
|
7429
7929
|
async getEdgesByTarget(targetId) {
|
|
7430
7930
|
const edges = await this.getAllEdges();
|
|
7431
|
-
return edges.filter((edge) => edge.targetId === targetId);
|
|
7931
|
+
return edges.filter((edge) => (edge.targetId || edge.target) === targetId);
|
|
7932
|
+
}
|
|
7933
|
+
/**
|
|
7934
|
+
* Get verbs by type (internal implementation)
|
|
7935
|
+
*/
|
|
7936
|
+
async getVerbsByType_internal(type) {
|
|
7937
|
+
return this.getEdgesByType(type);
|
|
7432
7938
|
}
|
|
7433
7939
|
/**
|
|
7434
7940
|
* Get edges by type
|
|
7435
7941
|
*/
|
|
7436
7942
|
async getEdgesByType(type) {
|
|
7437
7943
|
const edges = await this.getAllEdges();
|
|
7438
|
-
return edges.filter((edge) => edge.type === type);
|
|
7944
|
+
return edges.filter((edge) => (edge.type || edge.verb) === type);
|
|
7945
|
+
}
|
|
7946
|
+
/**
|
|
7947
|
+
* Delete a verb from storage (internal implementation)
|
|
7948
|
+
*/
|
|
7949
|
+
async deleteVerb_internal(id) {
|
|
7950
|
+
return this.deleteEdge(id);
|
|
7439
7951
|
}
|
|
7440
7952
|
/**
|
|
7441
7953
|
* Delete an edge from storage
|
|
@@ -7723,6 +8235,102 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7723
8235
|
};
|
|
7724
8236
|
}
|
|
7725
8237
|
}
|
|
8238
|
+
/**
|
|
8239
|
+
* Get the statistics key for a specific date
|
|
8240
|
+
* @param date The date to get the key for
|
|
8241
|
+
* @returns The statistics key for the specified date
|
|
8242
|
+
*/
|
|
8243
|
+
getStatisticsKeyForDate(date) {
|
|
8244
|
+
const year = date.getUTCFullYear();
|
|
8245
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
|
|
8246
|
+
const day = String(date.getUTCDate()).padStart(2, '0');
|
|
8247
|
+
return `${this.indexPrefix}${STATISTICS_KEY}_${year}${month}${day}.json`;
|
|
8248
|
+
}
|
|
8249
|
+
/**
|
|
8250
|
+
* Get the current statistics key
|
|
8251
|
+
* @returns The current statistics key
|
|
8252
|
+
*/
|
|
8253
|
+
getCurrentStatisticsKey() {
|
|
8254
|
+
return this.getStatisticsKeyForDate(new Date());
|
|
8255
|
+
}
|
|
8256
|
+
/**
|
|
8257
|
+
* Get the legacy statistics key (for backward compatibility)
|
|
8258
|
+
* @returns The legacy statistics key
|
|
8259
|
+
*/
|
|
8260
|
+
getLegacyStatisticsKey() {
|
|
8261
|
+
return `${this.indexPrefix}${STATISTICS_KEY}.json`;
|
|
8262
|
+
}
|
|
8263
|
+
/**
|
|
8264
|
+
* Schedule a batch update of statistics
|
|
8265
|
+
*/
|
|
8266
|
+
scheduleBatchUpdate() {
|
|
8267
|
+
// Mark statistics as modified
|
|
8268
|
+
this.statisticsModified = true;
|
|
8269
|
+
// If a timer is already set, don't set another one
|
|
8270
|
+
if (this.statisticsBatchUpdateTimerId !== null) {
|
|
8271
|
+
return;
|
|
8272
|
+
}
|
|
8273
|
+
// Calculate time since last flush
|
|
8274
|
+
const now = Date.now();
|
|
8275
|
+
const timeSinceLastFlush = now - this.lastStatisticsFlushTime;
|
|
8276
|
+
// If we've recently flushed, wait longer before the next flush
|
|
8277
|
+
const delayMs = timeSinceLastFlush < this.MIN_FLUSH_INTERVAL_MS
|
|
8278
|
+
? this.MAX_FLUSH_DELAY_MS
|
|
8279
|
+
: this.MIN_FLUSH_INTERVAL_MS;
|
|
8280
|
+
// Schedule the batch update
|
|
8281
|
+
this.statisticsBatchUpdateTimerId = setTimeout(() => {
|
|
8282
|
+
this.flushStatistics();
|
|
8283
|
+
}, delayMs);
|
|
8284
|
+
}
|
|
8285
|
+
/**
|
|
8286
|
+
* Flush statistics to storage
|
|
8287
|
+
*/
|
|
8288
|
+
async flushStatistics() {
|
|
8289
|
+
// Clear the timer
|
|
8290
|
+
if (this.statisticsBatchUpdateTimerId !== null) {
|
|
8291
|
+
clearTimeout(this.statisticsBatchUpdateTimerId);
|
|
8292
|
+
this.statisticsBatchUpdateTimerId = null;
|
|
8293
|
+
}
|
|
8294
|
+
// If statistics haven't been modified, no need to flush
|
|
8295
|
+
if (!this.statisticsModified || !this.statisticsCache) {
|
|
8296
|
+
return;
|
|
8297
|
+
}
|
|
8298
|
+
try {
|
|
8299
|
+
// Import the PutObjectCommand only when needed
|
|
8300
|
+
const { PutObjectCommand } = await import('@aws-sdk/client-s3');
|
|
8301
|
+
// Get the current statistics key
|
|
8302
|
+
const key = this.getCurrentStatisticsKey();
|
|
8303
|
+
const body = JSON.stringify(this.statisticsCache, null, 2);
|
|
8304
|
+
// Save the statistics to S3-compatible storage
|
|
8305
|
+
await this.s3Client.send(new PutObjectCommand({
|
|
8306
|
+
Bucket: this.bucketName,
|
|
8307
|
+
Key: key,
|
|
8308
|
+
Body: body,
|
|
8309
|
+
ContentType: 'application/json'
|
|
8310
|
+
}));
|
|
8311
|
+
// Update the last flush time
|
|
8312
|
+
this.lastStatisticsFlushTime = Date.now();
|
|
8313
|
+
// Reset the modified flag
|
|
8314
|
+
this.statisticsModified = false;
|
|
8315
|
+
// Also update the legacy key for backward compatibility, but less frequently
|
|
8316
|
+
// Only update it once every 10 flushes (approximately)
|
|
8317
|
+
if (Math.random() < 0.1) {
|
|
8318
|
+
const legacyKey = this.getLegacyStatisticsKey();
|
|
8319
|
+
await this.s3Client.send(new PutObjectCommand({
|
|
8320
|
+
Bucket: this.bucketName,
|
|
8321
|
+
Key: legacyKey,
|
|
8322
|
+
Body: body,
|
|
8323
|
+
ContentType: 'application/json'
|
|
8324
|
+
}));
|
|
8325
|
+
}
|
|
8326
|
+
}
|
|
8327
|
+
catch (error) {
|
|
8328
|
+
console.error('Failed to flush statistics data:', error);
|
|
8329
|
+
// Mark as still modified so we'll try again later
|
|
8330
|
+
this.statisticsModified = true;
|
|
8331
|
+
// Don't throw the error to avoid disrupting the application
|
|
8332
|
+
}
|
|
8333
|
+
}
|
|
7726
8334
|
/**
|
|
7727
8335
|
* Save statistics data to storage
|
|
7728
8336
|
* @param statistics The statistics data to save
|
|
@@ -7738,17 +8346,8 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7738
8346
|
hnswIndexSize: statistics.hnswIndexSize,
|
|
7739
8347
|
lastUpdated: statistics.lastUpdated
|
|
7740
8348
|
};
|
|
7741
|
-
//
|
|
7742
|
-
|
|
7743
|
-
const key = `${this.indexPrefix}${STATISTICS_KEY}.json`;
|
|
7744
|
-
const body = JSON.stringify(statistics, null, 2);
|
|
7745
|
-
// Save the statistics to S3-compatible storage
|
|
7746
|
-
await this.s3Client.send(new PutObjectCommand({
|
|
7747
|
-
Bucket: this.bucketName,
|
|
7748
|
-
Key: key,
|
|
7749
|
-
Body: body,
|
|
7750
|
-
ContentType: 'application/json'
|
|
7751
|
-
}));
|
|
8349
|
+
// Schedule a batch update instead of saving immediately
|
|
8350
|
+
this.scheduleBatchUpdate();
|
|
7752
8351
|
}
|
|
7753
8352
|
catch (error) {
|
|
7754
8353
|
console.error('Failed to save statistics data:', error);
|
|
@@ -7774,8 +8373,49 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7774
8373
|
try {
|
|
7775
8374
|
// Import the GetObjectCommand only when needed
|
|
7776
8375
|
const { GetObjectCommand } = await import('@aws-sdk/client-s3');
|
|
7777
|
-
|
|
7778
|
-
|
|
8376
|
+
// First try to get statistics from today's file
|
|
8377
|
+
const currentKey = this.getCurrentStatisticsKey();
|
|
8378
|
+
let statistics = await this.tryGetStatisticsFromKey(currentKey);
|
|
8379
|
+
// If not found, try yesterday's file (in case it's just after midnight)
|
|
8380
|
+
if (!statistics) {
|
|
8381
|
+
const yesterday = new Date();
|
|
8382
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
8383
|
+
const yesterdayKey = this.getStatisticsKeyForDate(yesterday);
|
|
8384
|
+
statistics = await this.tryGetStatisticsFromKey(yesterdayKey);
|
|
8385
|
+
}
|
|
8386
|
+
// If still not found, try the legacy location
|
|
8387
|
+
if (!statistics) {
|
|
8388
|
+
const legacyKey = this.getLegacyStatisticsKey();
|
|
8389
|
+
statistics = await this.tryGetStatisticsFromKey(legacyKey);
|
|
8390
|
+
}
|
|
8391
|
+
// If we found statistics, update the cache
|
|
8392
|
+
if (statistics) {
|
|
8393
|
+
// Update the cache with a deep copy
|
|
8394
|
+
this.statisticsCache = {
|
|
8395
|
+
nounCount: { ...statistics.nounCount },
|
|
8396
|
+
verbCount: { ...statistics.verbCount },
|
|
8397
|
+
metadataCount: { ...statistics.metadataCount },
|
|
8398
|
+
hnswIndexSize: statistics.hnswIndexSize,
|
|
8399
|
+
lastUpdated: statistics.lastUpdated
|
|
8400
|
+
};
|
|
8401
|
+
}
|
|
8402
|
+
return statistics;
|
|
8403
|
+
}
|
|
8404
|
+
catch (error) {
|
|
8405
|
+
console.error('Error getting statistics data:', error);
|
|
8406
|
+
throw error;
|
|
8407
|
+
}
|
|
8408
|
+
}
|
|
8409
|
+
/**
|
|
8410
|
+
* Try to get statistics from a specific key
|
|
8411
|
+
* @param key The key to try to get statistics from
|
|
8412
|
+
* @returns The statistics data or null if not found
|
|
8413
|
+
*/
|
|
8414
|
+
async tryGetStatisticsFromKey(key) {
|
|
8415
|
+
try {
|
|
8416
|
+
// Import the GetObjectCommand only when needed
|
|
8417
|
+
const { GetObjectCommand } = await import('@aws-sdk/client-s3');
|
|
8418
|
+
// Try to get the statistics from the specified key
|
|
7779
8419
|
const response = await this.s3Client.send(new GetObjectCommand({
|
|
7780
8420
|
Bucket: this.bucketName,
|
|
7781
8421
|
Key: key
|
|
@@ -7786,17 +8426,8 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7786
8426
|
}
|
|
7787
8427
|
// Convert the response body to a string
|
|
7788
8428
|
const bodyContents = await response.Body.transformToString();
|
|
7789
|
-
// Parse the JSON string
|
|
7790
|
-
|
|
7791
|
-
// Update the cache with a deep copy
|
|
7792
|
-
this.statisticsCache = {
|
|
7793
|
-
nounCount: { ...statistics.nounCount },
|
|
7794
|
-
verbCount: { ...statistics.verbCount },
|
|
7795
|
-
metadataCount: { ...statistics.metadataCount },
|
|
7796
|
-
hnswIndexSize: statistics.hnswIndexSize,
|
|
7797
|
-
lastUpdated: statistics.lastUpdated
|
|
7798
|
-
};
|
|
7799
|
-
return statistics;
|
|
8429
|
+
// Parse the JSON string
|
|
8430
|
+
return JSON.parse(bodyContents);
|
|
7800
8431
|
}
|
|
7801
8432
|
catch (error) {
|
|
7802
8433
|
// Check if this is a "NoSuchKey" error (object doesn't exist)
|
|
@@ -7806,7 +8437,7 @@ class S3CompatibleStorage extends BaseStorage {
|
|
|
7806
8437
|
error.message.includes('does not exist')))) {
|
|
7807
8438
|
return null;
|
|
7808
8439
|
}
|
|
7809
|
-
|
|
8440
|
+
// For other errors, propagate them
|
|
7810
8441
|
throw error;
|
|
7811
8442
|
}
|
|
7812
8443
|
}
|
|
@@ -11325,16 +11956,32 @@ class BrainyData {
|
|
|
11325
11956
|
verbType = VerbType.RelatedTo;
|
|
11326
11957
|
}
|
|
11327
11958
|
}
|
|
11959
|
+
// Get service name from options or current augmentation
|
|
11960
|
+
const service = options.service || this.getCurrentAugmentation();
|
|
11961
|
+
// Create timestamp for creation/update time
|
|
11962
|
+
const now = new Date();
|
|
11963
|
+
const timestamp = {
|
|
11964
|
+
seconds: Math.floor(now.getTime() / 1000),
|
|
11965
|
+
nanoseconds: (now.getTime() % 1000) * 1000000
|
|
11966
|
+
};
|
|
11328
11967
|
// Create verb
|
|
11329
11968
|
const verb = {
|
|
11330
11969
|
id,
|
|
11331
11970
|
vector: verbVector,
|
|
11332
11971
|
connections: new Map(),
|
|
11333
|
-
sourceId,
|
|
11334
|
-
targetId,
|
|
11335
|
-
|
|
11972
|
+
sourceId: sourceId,
|
|
11973
|
+
targetId: targetId,
|
|
11974
|
+
source: sourceId,
|
|
11975
|
+
target: targetId,
|
|
11976
|
+
verb: verbType,
|
|
11336
11977
|
weight: options.weight,
|
|
11337
|
-
metadata: options.metadata
|
|
11978
|
+
metadata: options.metadata,
|
|
11979
|
+
createdAt: timestamp,
|
|
11980
|
+
updatedAt: timestamp,
|
|
11981
|
+
createdBy: {
|
|
11982
|
+
augmentation: service,
|
|
11983
|
+
version: '1.0' // TODO: Get actual version from augmentation
|
|
11984
|
+
}
|
|
11338
11985
|
};
|
|
11339
11986
|
// Add to index
|
|
11340
11987
|
await this.index.addItem({ id, vector: verbVector });
|
|
@@ -11348,8 +11995,8 @@ class BrainyData {
|
|
|
11348
11995
|
// Save verb to storage
|
|
11349
11996
|
await this.storage.saveVerb(verb);
|
|
11350
11997
|
// Track verb statistics
|
|
11351
|
-
const
|
|
11352
|
-
await this.storage.incrementStatistic('verb',
|
|
11998
|
+
const serviceForStats = options.service || 'default';
|
|
11999
|
+
await this.storage.incrementStatistic('verb', serviceForStats);
|
|
11353
12000
|
// Update HNSW index size (excluding verbs)
|
|
11354
12001
|
await this.storage.updateHnswIndexSize(await this.getNounCount());
|
|
11355
12002
|
return id;
|