@soulcraft/brainy 3.50.1 → 4.0.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/CHANGELOG.md +242 -0
- package/README.md +358 -658
- package/dist/api/ConfigAPI.js +56 -19
- package/dist/api/DataAPI.js +24 -18
- package/dist/augmentations/storageAugmentations.d.ts +24 -0
- package/dist/augmentations/storageAugmentations.js +22 -0
- package/dist/brainy.js +32 -9
- package/dist/cli/commands/core.d.ts +20 -10
- package/dist/cli/commands/core.js +384 -82
- package/dist/cli/commands/import.d.ts +41 -0
- package/dist/cli/commands/import.js +456 -0
- package/dist/cli/commands/insights.d.ts +34 -0
- package/dist/cli/commands/insights.js +300 -0
- package/dist/cli/commands/neural.d.ts +6 -12
- package/dist/cli/commands/neural.js +113 -10
- package/dist/cli/commands/nlp.d.ts +28 -0
- package/dist/cli/commands/nlp.js +246 -0
- package/dist/cli/commands/storage.d.ts +64 -0
- package/dist/cli/commands/storage.js +730 -0
- package/dist/cli/index.js +210 -24
- package/dist/coreTypes.d.ts +206 -34
- package/dist/distributed/configManager.js +8 -6
- package/dist/distributed/shardMigration.js +2 -0
- package/dist/distributed/storageDiscovery.js +6 -4
- package/dist/embeddings/EmbeddingManager.d.ts +2 -2
- package/dist/embeddings/EmbeddingManager.js +5 -1
- package/dist/graph/lsm/LSMTree.js +32 -20
- package/dist/hnsw/typeAwareHNSWIndex.js +6 -2
- package/dist/storage/adapters/azureBlobStorage.d.ts +545 -0
- package/dist/storage/adapters/azureBlobStorage.js +1809 -0
- package/dist/storage/adapters/baseStorageAdapter.d.ts +16 -13
- package/dist/storage/adapters/fileSystemStorage.d.ts +21 -9
- package/dist/storage/adapters/fileSystemStorage.js +204 -127
- package/dist/storage/adapters/gcsStorage.d.ts +119 -9
- package/dist/storage/adapters/gcsStorage.js +317 -62
- package/dist/storage/adapters/memoryStorage.d.ts +30 -18
- package/dist/storage/adapters/memoryStorage.js +99 -94
- package/dist/storage/adapters/opfsStorage.d.ts +48 -10
- package/dist/storage/adapters/opfsStorage.js +201 -80
- package/dist/storage/adapters/r2Storage.d.ts +12 -5
- package/dist/storage/adapters/r2Storage.js +63 -15
- package/dist/storage/adapters/s3CompatibleStorage.d.ts +164 -17
- package/dist/storage/adapters/s3CompatibleStorage.js +472 -80
- package/dist/storage/adapters/typeAwareStorageAdapter.d.ts +38 -6
- package/dist/storage/adapters/typeAwareStorageAdapter.js +218 -39
- package/dist/storage/baseStorage.d.ts +41 -38
- package/dist/storage/baseStorage.js +110 -134
- package/dist/storage/storageFactory.d.ts +29 -2
- package/dist/storage/storageFactory.js +30 -1
- package/dist/utils/entityIdMapper.js +5 -2
- package/dist/utils/fieldTypeInference.js +8 -1
- package/dist/utils/metadataFilter.d.ts +3 -2
- package/dist/utils/metadataFilter.js +1 -0
- package/dist/utils/metadataIndex.d.ts +2 -1
- package/dist/utils/metadataIndex.js +9 -1
- package/dist/utils/metadataIndexChunking.js +9 -4
- package/dist/utils/periodicCleanup.js +1 -0
- package/package.json +3 -1
|
@@ -36,6 +36,11 @@ export class OPFSStorage extends BaseStorage {
|
|
|
36
36
|
this.statistics = null;
|
|
37
37
|
this.activeLocks = new Set();
|
|
38
38
|
this.lockPrefix = 'opfs-lock-';
|
|
39
|
+
// Quota monitoring configuration (v4.0.0)
|
|
40
|
+
this.quotaWarningThreshold = 0.8; // Warn at 80% usage
|
|
41
|
+
this.quotaCriticalThreshold = 0.95; // Critical at 95% usage
|
|
42
|
+
this.lastQuotaCheck = 0;
|
|
43
|
+
this.quotaCheckInterval = 60000; // Check every 60 seconds
|
|
39
44
|
// Check if OPFS is available
|
|
40
45
|
this.isAvailable =
|
|
41
46
|
typeof navigator !== 'undefined' &&
|
|
@@ -194,19 +199,15 @@ export class OPFSStorage extends BaseStorage {
|
|
|
194
199
|
for (const [level, nounIds] of Object.entries(data.connections)) {
|
|
195
200
|
connections.set(Number(level), new Set(nounIds));
|
|
196
201
|
}
|
|
202
|
+
// v4.0.0: Return ONLY vector data (no metadata field)
|
|
197
203
|
const node = {
|
|
198
204
|
id: data.id,
|
|
199
205
|
vector: data.vector,
|
|
200
206
|
connections,
|
|
201
207
|
level: data.level || 0
|
|
202
208
|
};
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
// Combine into complete noun object
|
|
206
|
-
return {
|
|
207
|
-
...node,
|
|
208
|
-
metadata: metadata || {}
|
|
209
|
-
};
|
|
209
|
+
// Return pure vector structure
|
|
210
|
+
return node;
|
|
210
211
|
}
|
|
211
212
|
catch (error) {
|
|
212
213
|
// Noun not found or other error
|
|
@@ -346,21 +347,17 @@ export class OPFSStorage extends BaseStorage {
|
|
|
346
347
|
}
|
|
347
348
|
/**
|
|
348
349
|
* Get a verb from storage (internal implementation)
|
|
349
|
-
*
|
|
350
|
+
* v4.0.0: Returns ONLY vector + core relational fields (no metadata field)
|
|
351
|
+
* Base class combines with metadata via getVerb() -> HNSWVerbWithMetadata
|
|
350
352
|
*/
|
|
351
353
|
async getVerb_internal(id) {
|
|
352
|
-
//
|
|
354
|
+
// v4.0.0: Return ONLY vector + core relational data (no metadata field)
|
|
353
355
|
const edge = await this.getEdge(id);
|
|
354
356
|
if (!edge) {
|
|
355
357
|
return null;
|
|
356
358
|
}
|
|
357
|
-
//
|
|
358
|
-
|
|
359
|
-
// Combine into complete verb object
|
|
360
|
-
return {
|
|
361
|
-
...edge,
|
|
362
|
-
metadata: metadata || {}
|
|
363
|
-
};
|
|
359
|
+
// Return pure vector + core fields structure
|
|
360
|
+
return edge;
|
|
364
361
|
}
|
|
365
362
|
/**
|
|
366
363
|
* Get an edge from storage
|
|
@@ -393,7 +390,7 @@ export class OPFSStorage extends BaseStorage {
|
|
|
393
390
|
augmentation: 'unknown',
|
|
394
391
|
version: '1.0'
|
|
395
392
|
};
|
|
396
|
-
//
|
|
393
|
+
// v4.0.0: Return HNSWVerb with core relational fields (NO metadata field)
|
|
397
394
|
return {
|
|
398
395
|
id: data.id,
|
|
399
396
|
vector: data.vector,
|
|
@@ -401,9 +398,9 @@ export class OPFSStorage extends BaseStorage {
|
|
|
401
398
|
// CORE RELATIONAL DATA (read from vector file)
|
|
402
399
|
verb: data.verb,
|
|
403
400
|
sourceId: data.sourceId,
|
|
404
|
-
targetId: data.targetId
|
|
405
|
-
//
|
|
406
|
-
metadata
|
|
401
|
+
targetId: data.targetId
|
|
402
|
+
// ✅ NO metadata field in v4.0.0
|
|
403
|
+
// User metadata retrieved separately via getVerbMetadata()
|
|
407
404
|
};
|
|
408
405
|
}
|
|
409
406
|
catch (error) {
|
|
@@ -445,7 +442,7 @@ export class OPFSStorage extends BaseStorage {
|
|
|
445
442
|
augmentation: 'unknown',
|
|
446
443
|
version: '1.0'
|
|
447
444
|
};
|
|
448
|
-
//
|
|
445
|
+
// v4.0.0: Include core relational fields (NO metadata field)
|
|
449
446
|
allEdges.push({
|
|
450
447
|
id: data.id,
|
|
451
448
|
vector: data.vector,
|
|
@@ -453,9 +450,9 @@ export class OPFSStorage extends BaseStorage {
|
|
|
453
450
|
// CORE RELATIONAL DATA
|
|
454
451
|
verb: data.verb,
|
|
455
452
|
sourceId: data.sourceId,
|
|
456
|
-
targetId: data.targetId
|
|
457
|
-
//
|
|
458
|
-
metadata
|
|
453
|
+
targetId: data.targetId
|
|
454
|
+
// ✅ NO metadata field in v4.0.0
|
|
455
|
+
// User metadata retrieved separately via getVerbMetadata()
|
|
459
456
|
});
|
|
460
457
|
}
|
|
461
458
|
catch (error) {
|
|
@@ -884,6 +881,108 @@ export class OPFSStorage extends BaseStorage {
|
|
|
884
881
|
};
|
|
885
882
|
}
|
|
886
883
|
}
|
|
884
|
+
/**
|
|
885
|
+
* Get detailed quota status with warnings (v4.0.0)
|
|
886
|
+
* Monitors storage usage and warns when approaching quota limits
|
|
887
|
+
*
|
|
888
|
+
* @returns Promise that resolves to quota status with warning levels
|
|
889
|
+
*
|
|
890
|
+
* @example
|
|
891
|
+
* const status = await storage.getQuotaStatus()
|
|
892
|
+
* if (status.warning) {
|
|
893
|
+
* console.warn(`Storage ${status.usagePercent}% full: ${status.warningMessage}`)
|
|
894
|
+
* }
|
|
895
|
+
*/
|
|
896
|
+
async getQuotaStatus() {
|
|
897
|
+
this.lastQuotaCheck = Date.now();
|
|
898
|
+
try {
|
|
899
|
+
if (!navigator.storage || !navigator.storage.estimate) {
|
|
900
|
+
return {
|
|
901
|
+
usage: 0,
|
|
902
|
+
quota: null,
|
|
903
|
+
usagePercent: 0,
|
|
904
|
+
remaining: null,
|
|
905
|
+
status: 'ok',
|
|
906
|
+
warning: false
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
const estimate = await navigator.storage.estimate();
|
|
910
|
+
const usage = estimate.usage || 0;
|
|
911
|
+
const quota = estimate.quota || null;
|
|
912
|
+
if (!quota) {
|
|
913
|
+
return {
|
|
914
|
+
usage,
|
|
915
|
+
quota: null,
|
|
916
|
+
usagePercent: 0,
|
|
917
|
+
remaining: null,
|
|
918
|
+
status: 'ok',
|
|
919
|
+
warning: false
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
const usagePercent = (usage / quota) * 100;
|
|
923
|
+
const remaining = quota - usage;
|
|
924
|
+
// Determine status
|
|
925
|
+
let status = 'ok';
|
|
926
|
+
let warning = false;
|
|
927
|
+
let warningMessage;
|
|
928
|
+
if (usagePercent >= this.quotaCriticalThreshold * 100) {
|
|
929
|
+
status = 'critical';
|
|
930
|
+
warning = true;
|
|
931
|
+
warningMessage = `Critical: Storage ${usagePercent.toFixed(1)}% full. Only ${(remaining / 1024 / 1024).toFixed(1)}MB remaining. Please delete old data.`;
|
|
932
|
+
}
|
|
933
|
+
else if (usagePercent >= this.quotaWarningThreshold * 100) {
|
|
934
|
+
status = 'warning';
|
|
935
|
+
warning = true;
|
|
936
|
+
warningMessage = `Warning: Storage ${usagePercent.toFixed(1)}% full. ${(remaining / 1024 / 1024).toFixed(1)}MB remaining.`;
|
|
937
|
+
}
|
|
938
|
+
if (warning) {
|
|
939
|
+
console.warn(`[OPFS Quota] ${warningMessage}`);
|
|
940
|
+
}
|
|
941
|
+
return {
|
|
942
|
+
usage,
|
|
943
|
+
quota,
|
|
944
|
+
usagePercent,
|
|
945
|
+
remaining,
|
|
946
|
+
status,
|
|
947
|
+
warning,
|
|
948
|
+
warningMessage
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
catch (error) {
|
|
952
|
+
console.error('Failed to get quota status:', error);
|
|
953
|
+
return {
|
|
954
|
+
usage: 0,
|
|
955
|
+
quota: null,
|
|
956
|
+
usagePercent: 0,
|
|
957
|
+
remaining: null,
|
|
958
|
+
status: 'ok',
|
|
959
|
+
warning: false
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Monitor quota during operations (v4.0.0)
|
|
965
|
+
* Automatically checks quota at regular intervals and warns if approaching limits
|
|
966
|
+
* Call this before write operations to ensure quota is available
|
|
967
|
+
*
|
|
968
|
+
* @returns Promise that resolves when quota check is complete
|
|
969
|
+
*
|
|
970
|
+
* @example
|
|
971
|
+
* await storage.monitorQuota() // Checks quota if interval has passed
|
|
972
|
+
* await storage.saveNoun(noun) // Proceed with write operation
|
|
973
|
+
*/
|
|
974
|
+
async monitorQuota() {
|
|
975
|
+
const now = Date.now();
|
|
976
|
+
// Only check if interval has passed
|
|
977
|
+
if (now - this.lastQuotaCheck < this.quotaCheckInterval) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
const status = await this.getQuotaStatus();
|
|
981
|
+
// If critical, throw error to prevent data loss
|
|
982
|
+
if (status.status === 'critical' && status.warningMessage) {
|
|
983
|
+
throw new Error(`Storage quota critical: ${status.warningMessage}`);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
887
986
|
/**
|
|
888
987
|
* Get the statistics key for a specific date
|
|
889
988
|
* @param date The date to get the key for
|
|
@@ -1304,22 +1403,25 @@ export class OPFSStorage extends BaseStorage {
|
|
|
1304
1403
|
}
|
|
1305
1404
|
// Get the subset of files for this page
|
|
1306
1405
|
const pageFiles = nounFiles.slice(startIndex, startIndex + limit);
|
|
1307
|
-
// Load nouns from files
|
|
1406
|
+
// v4.0.0: Load nouns from files and combine with metadata
|
|
1308
1407
|
const items = [];
|
|
1309
1408
|
for (const fileName of pageFiles) {
|
|
1310
1409
|
// fileName is in format "shard/uuid.json", extract just the UUID
|
|
1311
1410
|
const id = fileName.split('/')[1].replace('.json', '');
|
|
1312
1411
|
const noun = await this.getNoun_internal(id);
|
|
1313
1412
|
if (noun) {
|
|
1413
|
+
// Load metadata for filtering and combining
|
|
1414
|
+
const metadata = await this.getNounMetadata(id);
|
|
1415
|
+
if (!metadata)
|
|
1416
|
+
continue;
|
|
1314
1417
|
// Apply filters if provided
|
|
1315
1418
|
if (options.filter) {
|
|
1316
|
-
const metadata = await this.getNounMetadata(id);
|
|
1317
1419
|
// Filter by noun type
|
|
1318
1420
|
if (options.filter.nounType) {
|
|
1319
1421
|
const nounTypes = Array.isArray(options.filter.nounType)
|
|
1320
1422
|
? options.filter.nounType
|
|
1321
1423
|
: [options.filter.nounType];
|
|
1322
|
-
if (
|
|
1424
|
+
if (!nounTypes.includes((metadata.type || metadata.noun))) {
|
|
1323
1425
|
continue;
|
|
1324
1426
|
}
|
|
1325
1427
|
}
|
|
@@ -1328,14 +1430,12 @@ export class OPFSStorage extends BaseStorage {
|
|
|
1328
1430
|
const services = Array.isArray(options.filter.service)
|
|
1329
1431
|
? options.filter.service
|
|
1330
1432
|
: [options.filter.service];
|
|
1331
|
-
if (metadata
|
|
1433
|
+
if (!metadata.createdBy?.augmentation || !services.includes(metadata.createdBy.augmentation)) {
|
|
1332
1434
|
continue;
|
|
1333
1435
|
}
|
|
1334
1436
|
}
|
|
1335
1437
|
// Filter by metadata
|
|
1336
1438
|
if (options.filter.metadata) {
|
|
1337
|
-
if (!metadata)
|
|
1338
|
-
continue;
|
|
1339
1439
|
let matches = true;
|
|
1340
1440
|
for (const [key, value] of Object.entries(options.filter.metadata)) {
|
|
1341
1441
|
if (metadata[key] !== value) {
|
|
@@ -1347,7 +1447,15 @@ export class OPFSStorage extends BaseStorage {
|
|
|
1347
1447
|
continue;
|
|
1348
1448
|
}
|
|
1349
1449
|
}
|
|
1350
|
-
|
|
1450
|
+
// v4.0.0: Create HNSWNounWithMetadata by combining noun with metadata
|
|
1451
|
+
const nounWithMetadata = {
|
|
1452
|
+
id: noun.id,
|
|
1453
|
+
vector: [...noun.vector],
|
|
1454
|
+
connections: new Map(noun.connections),
|
|
1455
|
+
level: noun.level || 0,
|
|
1456
|
+
metadata: metadata
|
|
1457
|
+
};
|
|
1458
|
+
items.push(nounWithMetadata);
|
|
1351
1459
|
}
|
|
1352
1460
|
}
|
|
1353
1461
|
// Determine if there are more items
|
|
@@ -1400,69 +1508,82 @@ export class OPFSStorage extends BaseStorage {
|
|
|
1400
1508
|
}
|
|
1401
1509
|
// Get the subset of files for this page
|
|
1402
1510
|
const pageFiles = verbFiles.slice(startIndex, startIndex + limit);
|
|
1403
|
-
// Load verbs from files and
|
|
1511
|
+
// v4.0.0: Load verbs from files and combine with metadata
|
|
1404
1512
|
const items = [];
|
|
1405
1513
|
for (const fileName of pageFiles) {
|
|
1406
1514
|
// fileName is in format "shard/uuid.json", extract just the UUID
|
|
1407
1515
|
const id = fileName.split('/')[1].replace('.json', '');
|
|
1408
1516
|
const hnswVerb = await this.getVerb_internal(id);
|
|
1409
1517
|
if (hnswVerb) {
|
|
1410
|
-
//
|
|
1411
|
-
const
|
|
1412
|
-
if (
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1518
|
+
// Load metadata for filtering and combining
|
|
1519
|
+
const metadata = await this.getVerbMetadata(id);
|
|
1520
|
+
if (!metadata)
|
|
1521
|
+
continue;
|
|
1522
|
+
// Apply filters if provided
|
|
1523
|
+
if (options.filter) {
|
|
1524
|
+
// Filter by verb type
|
|
1525
|
+
// v4.0.0: verb field is in HNSWVerb structure (NOT in metadata)
|
|
1526
|
+
if (options.filter.verbType) {
|
|
1527
|
+
const verbTypes = Array.isArray(options.filter.verbType)
|
|
1528
|
+
? options.filter.verbType
|
|
1529
|
+
: [options.filter.verbType];
|
|
1530
|
+
if (!hnswVerb.verb || !verbTypes.includes(hnswVerb.verb)) {
|
|
1531
|
+
continue;
|
|
1423
1532
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1533
|
+
}
|
|
1534
|
+
// Filter by source ID
|
|
1535
|
+
// v4.0.0: sourceId field is in HNSWVerb structure (NOT in metadata)
|
|
1536
|
+
if (options.filter.sourceId) {
|
|
1537
|
+
const sourceIds = Array.isArray(options.filter.sourceId)
|
|
1538
|
+
? options.filter.sourceId
|
|
1539
|
+
: [options.filter.sourceId];
|
|
1540
|
+
if (!hnswVerb.sourceId || !sourceIds.includes(hnswVerb.sourceId)) {
|
|
1541
|
+
continue;
|
|
1432
1542
|
}
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1543
|
+
}
|
|
1544
|
+
// Filter by target ID
|
|
1545
|
+
// v4.0.0: targetId field is in HNSWVerb structure (NOT in metadata)
|
|
1546
|
+
if (options.filter.targetId) {
|
|
1547
|
+
const targetIds = Array.isArray(options.filter.targetId)
|
|
1548
|
+
? options.filter.targetId
|
|
1549
|
+
: [options.filter.targetId];
|
|
1550
|
+
if (!hnswVerb.targetId || !targetIds.includes(hnswVerb.targetId)) {
|
|
1551
|
+
continue;
|
|
1441
1552
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1553
|
+
}
|
|
1554
|
+
// Filter by service
|
|
1555
|
+
if (options.filter.service) {
|
|
1556
|
+
const services = Array.isArray(options.filter.service)
|
|
1557
|
+
? options.filter.service
|
|
1558
|
+
: [options.filter.service];
|
|
1559
|
+
if (!metadata.createdBy?.augmentation || !services.includes(metadata.createdBy.augmentation)) {
|
|
1560
|
+
continue;
|
|
1450
1561
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1562
|
+
}
|
|
1563
|
+
// Filter by metadata
|
|
1564
|
+
if (options.filter.metadata) {
|
|
1565
|
+
let matches = true;
|
|
1566
|
+
for (const [key, value] of Object.entries(options.filter.metadata)) {
|
|
1567
|
+
if (metadata[key] !== value) {
|
|
1568
|
+
matches = false;
|
|
1569
|
+
break;
|
|
1459
1570
|
}
|
|
1460
|
-
if (!matches)
|
|
1461
|
-
continue;
|
|
1462
1571
|
}
|
|
1572
|
+
if (!matches)
|
|
1573
|
+
continue;
|
|
1463
1574
|
}
|
|
1464
|
-
items.push(graphVerb);
|
|
1465
1575
|
}
|
|
1576
|
+
// v4.0.0: Create HNSWVerbWithMetadata by combining verb with metadata
|
|
1577
|
+
const verbWithMetadata = {
|
|
1578
|
+
id: hnswVerb.id,
|
|
1579
|
+
vector: [...hnswVerb.vector],
|
|
1580
|
+
connections: new Map(hnswVerb.connections),
|
|
1581
|
+
verb: hnswVerb.verb,
|
|
1582
|
+
sourceId: hnswVerb.sourceId,
|
|
1583
|
+
targetId: hnswVerb.targetId,
|
|
1584
|
+
metadata: metadata
|
|
1585
|
+
};
|
|
1586
|
+
items.push(verbWithMetadata);
|
|
1466
1587
|
}
|
|
1467
1588
|
}
|
|
1468
1589
|
// Determine if there are more items
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Based on latest GCS and S3 implementations with R2-specific enhancements
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
14
|
+
import { HNSWNoun, HNSWVerb, HNSWNounWithMetadata, HNSWVerbWithMetadata, StatisticsData } from '../../coreTypes.js';
|
|
15
15
|
import { BaseStorage } from '../baseStorage.js';
|
|
16
16
|
type HNSWNode = HNSWNoun;
|
|
17
17
|
type Edge = HNSWVerb;
|
|
@@ -130,6 +130,8 @@ export declare class R2Storage extends BaseStorage {
|
|
|
130
130
|
private saveNodeDirect;
|
|
131
131
|
/**
|
|
132
132
|
* Get a noun from storage (internal implementation)
|
|
133
|
+
* v4.0.0: Returns ONLY vector data (no metadata field)
|
|
134
|
+
* Base class combines with metadata via getNoun() -> HNSWNounWithMetadata
|
|
133
135
|
*/
|
|
134
136
|
protected getNoun_internal(id: string): Promise<HNSWNoun | null>;
|
|
135
137
|
/**
|
|
@@ -159,6 +161,11 @@ export declare class R2Storage extends BaseStorage {
|
|
|
159
161
|
protected saveVerb_internal(verb: HNSWVerb): Promise<void>;
|
|
160
162
|
protected saveEdge(edge: Edge): Promise<void>;
|
|
161
163
|
private saveEdgeDirect;
|
|
164
|
+
/**
|
|
165
|
+
* Get a verb from storage (internal implementation)
|
|
166
|
+
* v4.0.0: Returns ONLY vector + core relational fields (no metadata field)
|
|
167
|
+
* Base class combines with metadata via getVerb() -> HNSWVerbWithMetadata
|
|
168
|
+
*/
|
|
162
169
|
protected getVerb_internal(id: string): Promise<HNSWVerb | null>;
|
|
163
170
|
protected getEdge(id: string): Promise<Edge | null>;
|
|
164
171
|
protected deleteVerb_internal(id: string): Promise<void>;
|
|
@@ -200,14 +207,14 @@ export declare class R2Storage extends BaseStorage {
|
|
|
200
207
|
metadata?: Record<string, any>;
|
|
201
208
|
};
|
|
202
209
|
}): Promise<{
|
|
203
|
-
items:
|
|
210
|
+
items: HNSWNounWithMetadata[];
|
|
204
211
|
totalCount?: number;
|
|
205
212
|
hasMore: boolean;
|
|
206
213
|
nextCursor?: string;
|
|
207
214
|
}>;
|
|
208
215
|
protected getNounsByNounType_internal(nounType: string): Promise<HNSWNoun[]>;
|
|
209
|
-
protected getVerbsBySource_internal(sourceId: string): Promise<
|
|
210
|
-
protected getVerbsByTarget_internal(targetId: string): Promise<
|
|
211
|
-
protected getVerbsByType_internal(type: string): Promise<
|
|
216
|
+
protected getVerbsBySource_internal(sourceId: string): Promise<HNSWVerbWithMetadata[]>;
|
|
217
|
+
protected getVerbsByTarget_internal(targetId: string): Promise<HNSWVerbWithMetadata[]>;
|
|
218
|
+
protected getVerbsByType_internal(type: string): Promise<HNSWVerbWithMetadata[]>;
|
|
212
219
|
}
|
|
213
220
|
export {};
|
|
@@ -326,17 +326,17 @@ export class R2Storage extends BaseStorage {
|
|
|
326
326
|
}
|
|
327
327
|
/**
|
|
328
328
|
* Get a noun from storage (internal implementation)
|
|
329
|
+
* v4.0.0: Returns ONLY vector data (no metadata field)
|
|
330
|
+
* Base class combines with metadata via getNoun() -> HNSWNounWithMetadata
|
|
329
331
|
*/
|
|
330
332
|
async getNoun_internal(id) {
|
|
333
|
+
// v4.0.0: Return ONLY vector data (no metadata field)
|
|
331
334
|
const node = await this.getNode(id);
|
|
332
335
|
if (!node) {
|
|
333
336
|
return null;
|
|
334
337
|
}
|
|
335
|
-
|
|
336
|
-
return
|
|
337
|
-
...node,
|
|
338
|
-
metadata: metadata || {}
|
|
339
|
-
};
|
|
338
|
+
// Return pure vector structure
|
|
339
|
+
return node;
|
|
340
340
|
}
|
|
341
341
|
/**
|
|
342
342
|
* Get a node from storage
|
|
@@ -589,16 +589,19 @@ export class R2Storage extends BaseStorage {
|
|
|
589
589
|
throw new Error(`Failed to save edge ${edge.id}: ${error}`);
|
|
590
590
|
}
|
|
591
591
|
}
|
|
592
|
+
/**
|
|
593
|
+
* Get a verb from storage (internal implementation)
|
|
594
|
+
* v4.0.0: Returns ONLY vector + core relational fields (no metadata field)
|
|
595
|
+
* Base class combines with metadata via getVerb() -> HNSWVerbWithMetadata
|
|
596
|
+
*/
|
|
592
597
|
async getVerb_internal(id) {
|
|
598
|
+
// v4.0.0: Return ONLY vector + core relational data (no metadata field)
|
|
593
599
|
const edge = await this.getEdge(id);
|
|
594
600
|
if (!edge) {
|
|
595
601
|
return null;
|
|
596
602
|
}
|
|
597
|
-
|
|
598
|
-
return
|
|
599
|
-
...edge,
|
|
600
|
-
metadata: metadata || {}
|
|
601
|
-
};
|
|
603
|
+
// Return pure vector + core fields structure
|
|
604
|
+
return edge;
|
|
602
605
|
}
|
|
603
606
|
async getEdge(id) {
|
|
604
607
|
await this.ensureInitialized();
|
|
@@ -620,7 +623,7 @@ export class R2Storage extends BaseStorage {
|
|
|
620
623
|
for (const [level, verbIds] of Object.entries(data.connections || {})) {
|
|
621
624
|
connections.set(Number(level), new Set(verbIds));
|
|
622
625
|
}
|
|
623
|
-
//
|
|
626
|
+
// v4.0.0: Return HNSWVerb with core relational fields (NO metadata field)
|
|
624
627
|
const edge = {
|
|
625
628
|
id: data.id,
|
|
626
629
|
vector: data.vector,
|
|
@@ -628,9 +631,9 @@ export class R2Storage extends BaseStorage {
|
|
|
628
631
|
// CORE RELATIONAL DATA (read from vector file)
|
|
629
632
|
verb: data.verb,
|
|
630
633
|
sourceId: data.sourceId,
|
|
631
|
-
targetId: data.targetId
|
|
632
|
-
//
|
|
633
|
-
metadata
|
|
634
|
+
targetId: data.targetId
|
|
635
|
+
// ✅ NO metadata field in v4.0.0
|
|
636
|
+
// User metadata retrieved separately via getVerbMetadata()
|
|
634
637
|
};
|
|
635
638
|
this.verbCacheManager.set(id, edge);
|
|
636
639
|
this.releaseBackpressure(true, requestId);
|
|
@@ -860,7 +863,52 @@ export class R2Storage extends BaseStorage {
|
|
|
860
863
|
continue;
|
|
861
864
|
const noun = await this.getNoun_internal(id);
|
|
862
865
|
if (noun) {
|
|
863
|
-
|
|
866
|
+
// v4.0.0: Load metadata and combine with noun to create HNSWNounWithMetadata
|
|
867
|
+
const metadata = await this.getNounMetadata(id);
|
|
868
|
+
if (!metadata)
|
|
869
|
+
continue;
|
|
870
|
+
// Apply filters if provided
|
|
871
|
+
if (options.filter) {
|
|
872
|
+
// Filter by noun type
|
|
873
|
+
if (options.filter.nounType) {
|
|
874
|
+
const nounTypes = Array.isArray(options.filter.nounType)
|
|
875
|
+
? options.filter.nounType
|
|
876
|
+
: [options.filter.nounType];
|
|
877
|
+
if (!nounTypes.includes((metadata.type || metadata.noun))) {
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
// Filter by service
|
|
882
|
+
if (options.filter.service) {
|
|
883
|
+
const services = Array.isArray(options.filter.service)
|
|
884
|
+
? options.filter.service
|
|
885
|
+
: [options.filter.service];
|
|
886
|
+
if (!metadata.createdBy?.augmentation || !services.includes(metadata.createdBy.augmentation)) {
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
// Filter by metadata
|
|
891
|
+
if (options.filter.metadata) {
|
|
892
|
+
let matches = true;
|
|
893
|
+
for (const [key, value] of Object.entries(options.filter.metadata)) {
|
|
894
|
+
if (metadata[key] !== value) {
|
|
895
|
+
matches = false;
|
|
896
|
+
break;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
if (!matches)
|
|
900
|
+
continue;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
// v4.0.0: Create HNSWNounWithMetadata by combining noun with metadata
|
|
904
|
+
const nounWithMetadata = {
|
|
905
|
+
id: noun.id,
|
|
906
|
+
vector: [...noun.vector],
|
|
907
|
+
connections: new Map(noun.connections),
|
|
908
|
+
level: noun.level || 0,
|
|
909
|
+
metadata: metadata
|
|
910
|
+
};
|
|
911
|
+
items.push(nounWithMetadata);
|
|
864
912
|
}
|
|
865
913
|
}
|
|
866
914
|
return {
|