@soulcraft/brainy 5.0.0 → 5.1.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 +148 -0
- package/README.md +1 -1
- package/dist/augmentations/cacheAugmentation.js +9 -4
- package/dist/brainy.d.ts +87 -15
- package/dist/brainy.js +266 -27
- package/dist/cli/commands/import.js +1 -1
- package/dist/cli/commands/vfs.js +24 -40
- package/dist/import/ImportHistory.js +3 -3
- package/dist/importers/VFSStructureGenerator.d.ts +1 -1
- package/dist/importers/VFSStructureGenerator.js +3 -3
- package/dist/storage/adapters/memoryStorage.d.ts +6 -0
- package/dist/storage/adapters/memoryStorage.js +39 -14
- package/dist/storage/adapters/typeAwareStorageAdapter.d.ts +31 -1
- package/dist/storage/adapters/typeAwareStorageAdapter.js +272 -43
- package/dist/storage/baseStorage.d.ts +44 -1
- package/dist/storage/baseStorage.js +148 -16
- package/dist/storage/cow/BlobStorage.d.ts +1 -0
- package/dist/storage/cow/BlobStorage.js +5 -3
- package/dist/storage/storageFactory.d.ts +1 -2
- package/dist/storage/storageFactory.js +7 -6
- package/dist/types/brainy.types.d.ts +0 -1
- package/dist/vfs/FSCompat.d.ts +1 -1
- package/dist/vfs/FSCompat.js +1 -1
- package/dist/vfs/VirtualFileSystem.js +5 -6
- package/package.json +1 -1
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
* @since Phase 1 - Type-First Implementation
|
|
37
37
|
*/
|
|
38
38
|
import { BaseStorage } from '../baseStorage.js';
|
|
39
|
-
import { TypeUtils, NOUN_TYPE_COUNT, VERB_TYPE_COUNT } from '../../types/graphTypes.js';
|
|
39
|
+
import { NounType, TypeUtils, NOUN_TYPE_COUNT, VERB_TYPE_COUNT } from '../../types/graphTypes.js';
|
|
40
40
|
import { getShardIdFromUuid } from '../sharding.js';
|
|
41
41
|
/**
|
|
42
42
|
* Type-first storage paths
|
|
@@ -201,8 +201,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
201
201
|
const typeIndex = TypeUtils.getNounIndex(type);
|
|
202
202
|
this.nounCountsByType[typeIndex]++;
|
|
203
203
|
this.nounTypeCache.set(noun.id, type);
|
|
204
|
-
//
|
|
205
|
-
await this.
|
|
204
|
+
// COW-aware write (v5.0.1): Use COW helper for branch isolation
|
|
205
|
+
await this.writeObjectToBranch(path, noun);
|
|
206
206
|
// Periodically save statistics (every 100 saves)
|
|
207
207
|
if (this.nounCountsByType[typeIndex] % 100 === 0) {
|
|
208
208
|
await this.saveTypeStatistics();
|
|
@@ -216,14 +216,16 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
216
216
|
const cachedType = this.nounTypeCache.get(id);
|
|
217
217
|
if (cachedType) {
|
|
218
218
|
const path = getNounVectorPath(cachedType, id);
|
|
219
|
-
|
|
219
|
+
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
220
|
+
return await this.readWithInheritance(path);
|
|
220
221
|
}
|
|
221
222
|
// Need to search across all types (expensive, but cached after first access)
|
|
222
223
|
for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
|
|
223
224
|
const type = TypeUtils.getNounFromIndex(i);
|
|
224
225
|
const path = getNounVectorPath(type, id);
|
|
225
226
|
try {
|
|
226
|
-
|
|
227
|
+
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
228
|
+
const noun = await this.readWithInheritance(path);
|
|
227
229
|
if (noun) {
|
|
228
230
|
// Cache the type for next time
|
|
229
231
|
this.nounTypeCache.set(id, type);
|
|
@@ -242,13 +244,14 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
242
244
|
async getNounsByNounType_internal(nounType) {
|
|
243
245
|
const type = nounType;
|
|
244
246
|
const prefix = `entities/nouns/${type}/vectors/`;
|
|
245
|
-
//
|
|
246
|
-
const paths = await this.
|
|
247
|
+
// COW-aware list (v5.0.1): Use COW helper for branch isolation
|
|
248
|
+
const paths = await this.listObjectsInBranch(prefix);
|
|
247
249
|
// Load all nouns of this type
|
|
248
250
|
const nouns = [];
|
|
249
251
|
for (const path of paths) {
|
|
250
252
|
try {
|
|
251
|
-
|
|
253
|
+
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
254
|
+
const noun = await this.readWithInheritance(path);
|
|
252
255
|
if (noun) {
|
|
253
256
|
nouns.push(noun);
|
|
254
257
|
// Cache the type
|
|
@@ -269,7 +272,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
269
272
|
const cachedType = this.nounTypeCache.get(id);
|
|
270
273
|
if (cachedType) {
|
|
271
274
|
const path = getNounVectorPath(cachedType, id);
|
|
272
|
-
|
|
275
|
+
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
276
|
+
await this.deleteObjectFromBranch(path);
|
|
273
277
|
// Update counts
|
|
274
278
|
const typeIndex = TypeUtils.getNounIndex(cachedType);
|
|
275
279
|
if (this.nounCountsByType[typeIndex] > 0) {
|
|
@@ -283,7 +287,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
283
287
|
const type = TypeUtils.getNounFromIndex(i);
|
|
284
288
|
const path = getNounVectorPath(type, id);
|
|
285
289
|
try {
|
|
286
|
-
|
|
290
|
+
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
291
|
+
await this.deleteObjectFromBranch(path);
|
|
287
292
|
// Update counts
|
|
288
293
|
if (this.nounCountsByType[i] > 0) {
|
|
289
294
|
this.nounCountsByType[i]--;
|
|
@@ -310,8 +315,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
310
315
|
const typeIndex = TypeUtils.getVerbIndex(type);
|
|
311
316
|
this.verbCountsByType[typeIndex]++;
|
|
312
317
|
this.verbTypeCache.set(verb.id, type);
|
|
313
|
-
//
|
|
314
|
-
await this.
|
|
318
|
+
// COW-aware write (v5.0.1): Use COW helper for branch isolation
|
|
319
|
+
await this.writeObjectToBranch(path, verb);
|
|
315
320
|
// Periodically save statistics
|
|
316
321
|
if (this.verbCountsByType[typeIndex] % 100 === 0) {
|
|
317
322
|
await this.saveTypeStatistics();
|
|
@@ -328,7 +333,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
328
333
|
const cachedType = this.verbTypeCache.get(id);
|
|
329
334
|
if (cachedType) {
|
|
330
335
|
const path = getVerbVectorPath(cachedType, id);
|
|
331
|
-
|
|
336
|
+
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
337
|
+
const verb = await this.readWithInheritance(path);
|
|
332
338
|
return verb;
|
|
333
339
|
}
|
|
334
340
|
// Search across all types (only on first access)
|
|
@@ -336,7 +342,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
336
342
|
const type = TypeUtils.getVerbFromIndex(i);
|
|
337
343
|
const path = getVerbVectorPath(type, id);
|
|
338
344
|
try {
|
|
339
|
-
|
|
345
|
+
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
346
|
+
const verb = await this.readWithInheritance(path);
|
|
340
347
|
if (verb) {
|
|
341
348
|
// Cache the type for next time (read from verb.verb field)
|
|
342
349
|
this.verbTypeCache.set(id, verb.verb);
|
|
@@ -353,27 +360,36 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
353
360
|
* Get verbs by source
|
|
354
361
|
*/
|
|
355
362
|
async getVerbsBySource_internal(sourceId) {
|
|
356
|
-
//
|
|
357
|
-
// Previous implementation
|
|
358
|
-
//
|
|
363
|
+
// v5.0.1 COW FIX: Use getVerbsWithPagination which is COW-aware
|
|
364
|
+
// Previous v4.8.1 implementation delegated to underlying storage, which bypasses COW!
|
|
365
|
+
// The underlying storage delegates to GraphAdjacencyIndex, which is shared between forks.
|
|
366
|
+
// This caused getRelations() to return 0 results for fork-created relationships.
|
|
359
367
|
//
|
|
360
|
-
//
|
|
361
|
-
// -
|
|
362
|
-
// -
|
|
363
|
-
// -
|
|
368
|
+
// Now we use getVerbsWithPagination with sourceId filter, which:
|
|
369
|
+
// - Searches across all verb types using COW-aware listObjectsInBranch()
|
|
370
|
+
// - Reads verbs using COW-aware readWithInheritance()
|
|
371
|
+
// - Properly isolates fork data from parent
|
|
364
372
|
//
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
373
|
+
// Performance: Still efficient because sourceId filter reduces iteration
|
|
374
|
+
const result = await this.getVerbsWithPagination({
|
|
375
|
+
limit: 10000, // High limit to get all verbs for this source
|
|
376
|
+
offset: 0,
|
|
377
|
+
filter: { sourceId }
|
|
378
|
+
});
|
|
379
|
+
return result.items;
|
|
369
380
|
}
|
|
370
381
|
/**
|
|
371
382
|
* Get verbs by target
|
|
372
383
|
*/
|
|
373
384
|
async getVerbsByTarget_internal(targetId) {
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
|
|
385
|
+
// v5.0.1 COW FIX: Use getVerbsWithPagination which is COW-aware
|
|
386
|
+
// Same fix as getVerbsBySource_internal - delegating to underlying bypasses COW
|
|
387
|
+
const result = await this.getVerbsWithPagination({
|
|
388
|
+
limit: 10000, // High limit to get all verbs for this target
|
|
389
|
+
offset: 0,
|
|
390
|
+
filter: { targetId }
|
|
391
|
+
});
|
|
392
|
+
return result.items;
|
|
377
393
|
}
|
|
378
394
|
/**
|
|
379
395
|
* Get verbs by type (O(1) with type-first paths!)
|
|
@@ -383,11 +399,13 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
383
399
|
async getVerbsByType_internal(verbType) {
|
|
384
400
|
const type = verbType;
|
|
385
401
|
const prefix = `entities/verbs/${type}/vectors/`;
|
|
386
|
-
|
|
402
|
+
// COW-aware list (v5.0.1): Use COW helper for branch isolation
|
|
403
|
+
const paths = await this.listObjectsInBranch(prefix);
|
|
387
404
|
const verbs = [];
|
|
388
405
|
for (const path of paths) {
|
|
389
406
|
try {
|
|
390
|
-
|
|
407
|
+
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
408
|
+
const hnswVerb = await this.readWithInheritance(path);
|
|
391
409
|
if (!hnswVerb)
|
|
392
410
|
continue;
|
|
393
411
|
// Cache type from HNSWVerb for future O(1) retrievals
|
|
@@ -438,7 +456,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
438
456
|
const cachedType = this.verbTypeCache.get(id);
|
|
439
457
|
if (cachedType) {
|
|
440
458
|
const path = getVerbVectorPath(cachedType, id);
|
|
441
|
-
|
|
459
|
+
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
460
|
+
await this.deleteObjectFromBranch(path);
|
|
442
461
|
const typeIndex = TypeUtils.getVerbIndex(cachedType);
|
|
443
462
|
if (this.verbCountsByType[typeIndex] > 0) {
|
|
444
463
|
this.verbCountsByType[typeIndex]--;
|
|
@@ -451,7 +470,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
451
470
|
const type = TypeUtils.getVerbFromIndex(i);
|
|
452
471
|
const path = getVerbVectorPath(type, id);
|
|
453
472
|
try {
|
|
454
|
-
|
|
473
|
+
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
474
|
+
await this.deleteObjectFromBranch(path);
|
|
455
475
|
if (this.verbCountsByType[i] > 0) {
|
|
456
476
|
this.verbCountsByType[i]--;
|
|
457
477
|
}
|
|
@@ -472,9 +492,9 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
472
492
|
// Extract and cache the type
|
|
473
493
|
const type = (metadata.noun || 'thing');
|
|
474
494
|
this.nounTypeCache.set(id, type);
|
|
475
|
-
//
|
|
495
|
+
// COW-aware write (v5.0.1): Use COW helper for branch isolation
|
|
476
496
|
const path = getNounMetadataPath(type, id);
|
|
477
|
-
await this.
|
|
497
|
+
await this.writeObjectToBranch(path, metadata);
|
|
478
498
|
}
|
|
479
499
|
/**
|
|
480
500
|
* Get noun metadata (override to use type-aware paths)
|
|
@@ -484,14 +504,16 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
484
504
|
const cachedType = this.nounTypeCache.get(id);
|
|
485
505
|
if (cachedType) {
|
|
486
506
|
const path = getNounMetadataPath(cachedType, id);
|
|
487
|
-
|
|
507
|
+
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
508
|
+
return await this.readWithInheritance(path);
|
|
488
509
|
}
|
|
489
510
|
// Search across all types
|
|
490
511
|
for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
|
|
491
512
|
const type = TypeUtils.getNounFromIndex(i);
|
|
492
513
|
const path = getNounMetadataPath(type, id);
|
|
493
514
|
try {
|
|
494
|
-
|
|
515
|
+
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
516
|
+
const metadata = await this.readWithInheritance(path);
|
|
495
517
|
if (metadata) {
|
|
496
518
|
// Cache the type for next time
|
|
497
519
|
const metadataType = (metadata.noun || 'thing');
|
|
@@ -512,7 +534,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
512
534
|
const cachedType = this.nounTypeCache.get(id);
|
|
513
535
|
if (cachedType) {
|
|
514
536
|
const path = getNounMetadataPath(cachedType, id);
|
|
515
|
-
|
|
537
|
+
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
538
|
+
await this.deleteObjectFromBranch(path);
|
|
516
539
|
return;
|
|
517
540
|
}
|
|
518
541
|
// Search across all types
|
|
@@ -520,7 +543,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
520
543
|
const type = TypeUtils.getNounFromIndex(i);
|
|
521
544
|
const path = getNounMetadataPath(type, id);
|
|
522
545
|
try {
|
|
523
|
-
|
|
546
|
+
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
547
|
+
await this.deleteObjectFromBranch(path);
|
|
524
548
|
return;
|
|
525
549
|
}
|
|
526
550
|
catch (error) {
|
|
@@ -550,7 +574,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
550
574
|
}
|
|
551
575
|
// Save to type-aware path
|
|
552
576
|
const path = getVerbMetadataPath(type, id);
|
|
553
|
-
|
|
577
|
+
// COW-aware write (v5.0.1): Use COW helper for branch isolation
|
|
578
|
+
await this.writeObjectToBranch(path, metadata);
|
|
554
579
|
}
|
|
555
580
|
/**
|
|
556
581
|
* Get verb metadata (override to use type-aware paths)
|
|
@@ -560,14 +585,16 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
560
585
|
const cachedType = this.verbTypeCache.get(id);
|
|
561
586
|
if (cachedType) {
|
|
562
587
|
const path = getVerbMetadataPath(cachedType, id);
|
|
563
|
-
|
|
588
|
+
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
589
|
+
return await this.readWithInheritance(path);
|
|
564
590
|
}
|
|
565
591
|
// Search across all types
|
|
566
592
|
for (let i = 0; i < VERB_TYPE_COUNT; i++) {
|
|
567
593
|
const type = TypeUtils.getVerbFromIndex(i);
|
|
568
594
|
const path = getVerbMetadataPath(type, id);
|
|
569
595
|
try {
|
|
570
|
-
|
|
596
|
+
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
597
|
+
const metadata = await this.readWithInheritance(path);
|
|
571
598
|
if (metadata) {
|
|
572
599
|
// Cache the type for next time
|
|
573
600
|
this.verbTypeCache.set(id, type);
|
|
@@ -587,7 +614,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
587
614
|
const cachedType = this.verbTypeCache.get(id);
|
|
588
615
|
if (cachedType) {
|
|
589
616
|
const path = getVerbMetadataPath(cachedType, id);
|
|
590
|
-
|
|
617
|
+
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
618
|
+
await this.deleteObjectFromBranch(path);
|
|
591
619
|
return;
|
|
592
620
|
}
|
|
593
621
|
// Search across all types
|
|
@@ -595,7 +623,8 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
595
623
|
const type = TypeUtils.getVerbFromIndex(i);
|
|
596
624
|
const path = getVerbMetadataPath(type, id);
|
|
597
625
|
try {
|
|
598
|
-
|
|
626
|
+
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
627
|
+
await this.deleteObjectFromBranch(path);
|
|
599
628
|
return;
|
|
600
629
|
}
|
|
601
630
|
catch (error) {
|
|
@@ -742,6 +771,206 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
742
771
|
}
|
|
743
772
|
return null;
|
|
744
773
|
}
|
|
774
|
+
/**
|
|
775
|
+
* Get nouns with pagination (v5.0.1: COW-aware)
|
|
776
|
+
* Required for find() to work with TypeAwareStorage
|
|
777
|
+
*/
|
|
778
|
+
async getNounsWithPagination(options) {
|
|
779
|
+
const limit = options.limit || 100;
|
|
780
|
+
const offset = options.offset || 0;
|
|
781
|
+
const filter = options.filter || {};
|
|
782
|
+
// Determine which types to search
|
|
783
|
+
let typesToSearch;
|
|
784
|
+
if (filter.nounType) {
|
|
785
|
+
typesToSearch = Array.isArray(filter.nounType) ? filter.nounType : [filter.nounType];
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
// Search all 31 types
|
|
789
|
+
typesToSearch = [];
|
|
790
|
+
for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
|
|
791
|
+
const type = TypeUtils.getNounFromIndex(i);
|
|
792
|
+
typesToSearch.push(type);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
// Collect all matching nouns across types (COW-aware!)
|
|
796
|
+
const allNouns = [];
|
|
797
|
+
for (const type of typesToSearch) {
|
|
798
|
+
const prefix = `entities/nouns/${type}/vectors/`;
|
|
799
|
+
// COW-aware list with inheritance (v5.0.1): Fork sees parent's nouns too!
|
|
800
|
+
const paths = await this.listObjectsWithInheritance(prefix);
|
|
801
|
+
for (const path of paths) {
|
|
802
|
+
try {
|
|
803
|
+
// COW-aware read with inheritance
|
|
804
|
+
const noun = await this.readWithInheritance(path);
|
|
805
|
+
if (!noun)
|
|
806
|
+
continue;
|
|
807
|
+
// Get metadata separately
|
|
808
|
+
const metadata = await this.getNounMetadata(noun.id);
|
|
809
|
+
if (!metadata)
|
|
810
|
+
continue;
|
|
811
|
+
// Filter by service if specified
|
|
812
|
+
if (filter.service && metadata.service !== filter.service)
|
|
813
|
+
continue;
|
|
814
|
+
// Filter by custom metadata if specified
|
|
815
|
+
if (filter.metadata) {
|
|
816
|
+
let matches = true;
|
|
817
|
+
for (const [key, value] of Object.entries(filter.metadata)) {
|
|
818
|
+
if (metadata[key] !== value) {
|
|
819
|
+
matches = false;
|
|
820
|
+
break;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
if (!matches)
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
// Extract standard fields from metadata
|
|
827
|
+
const { noun: nounType, createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadata;
|
|
828
|
+
// Create HNSWNounWithMetadata (v4.8.0 format)
|
|
829
|
+
const nounWithMetadata = {
|
|
830
|
+
id: noun.id,
|
|
831
|
+
vector: noun.vector,
|
|
832
|
+
connections: noun.connections,
|
|
833
|
+
level: noun.level || 0,
|
|
834
|
+
type: nounType || NounType.Thing,
|
|
835
|
+
createdAt: createdAt || Date.now(),
|
|
836
|
+
updatedAt: updatedAt || Date.now(),
|
|
837
|
+
confidence,
|
|
838
|
+
weight,
|
|
839
|
+
service,
|
|
840
|
+
data,
|
|
841
|
+
createdBy,
|
|
842
|
+
metadata: customMetadata
|
|
843
|
+
};
|
|
844
|
+
allNouns.push(nounWithMetadata);
|
|
845
|
+
}
|
|
846
|
+
catch (error) {
|
|
847
|
+
// Skip entities with errors
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
// Apply pagination
|
|
853
|
+
const totalCount = allNouns.length;
|
|
854
|
+
const paginatedNouns = allNouns.slice(offset, offset + limit);
|
|
855
|
+
const hasMore = offset + limit < totalCount;
|
|
856
|
+
// Generate cursor if more results exist
|
|
857
|
+
let nextCursor;
|
|
858
|
+
if (hasMore && paginatedNouns.length > 0) {
|
|
859
|
+
nextCursor = paginatedNouns[paginatedNouns.length - 1].id;
|
|
860
|
+
}
|
|
861
|
+
return {
|
|
862
|
+
items: paginatedNouns,
|
|
863
|
+
totalCount,
|
|
864
|
+
hasMore,
|
|
865
|
+
nextCursor
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Get verbs with pagination (v5.0.1: COW-aware)
|
|
870
|
+
* Required for GraphAdjacencyIndex rebuild and find() to work
|
|
871
|
+
*/
|
|
872
|
+
async getVerbsWithPagination(options) {
|
|
873
|
+
const limit = options.limit || 100;
|
|
874
|
+
const offset = options.offset || 0;
|
|
875
|
+
const filter = options.filter || {};
|
|
876
|
+
// Determine which types to search
|
|
877
|
+
let typesToSearch;
|
|
878
|
+
if (filter.verbType) {
|
|
879
|
+
typesToSearch = Array.isArray(filter.verbType) ? filter.verbType : [filter.verbType];
|
|
880
|
+
}
|
|
881
|
+
else {
|
|
882
|
+
// Search all 40 verb types
|
|
883
|
+
typesToSearch = [];
|
|
884
|
+
for (let i = 0; i < VERB_TYPE_COUNT; i++) {
|
|
885
|
+
const type = TypeUtils.getVerbFromIndex(i);
|
|
886
|
+
typesToSearch.push(type);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
// Collect all matching verbs across types (COW-aware!)
|
|
890
|
+
const allVerbs = [];
|
|
891
|
+
for (const type of typesToSearch) {
|
|
892
|
+
const prefix = `entities/verbs/${type}/vectors/`;
|
|
893
|
+
// COW-aware list with inheritance (v5.0.1): Fork sees parent's verbs too!
|
|
894
|
+
const paths = await this.listObjectsWithInheritance(prefix);
|
|
895
|
+
for (const path of paths) {
|
|
896
|
+
try {
|
|
897
|
+
// COW-aware read with inheritance
|
|
898
|
+
const verb = await this.readWithInheritance(path);
|
|
899
|
+
if (!verb)
|
|
900
|
+
continue;
|
|
901
|
+
// Filter by sourceId if specified
|
|
902
|
+
if (filter.sourceId) {
|
|
903
|
+
const sourceIds = Array.isArray(filter.sourceId) ? filter.sourceId : [filter.sourceId];
|
|
904
|
+
if (!sourceIds.includes(verb.sourceId))
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
// Filter by targetId if specified
|
|
908
|
+
if (filter.targetId) {
|
|
909
|
+
const targetIds = Array.isArray(filter.targetId) ? filter.targetId : [filter.targetId];
|
|
910
|
+
if (!targetIds.includes(verb.targetId))
|
|
911
|
+
continue;
|
|
912
|
+
}
|
|
913
|
+
// Get metadata separately
|
|
914
|
+
const metadata = await this.getVerbMetadata(verb.id);
|
|
915
|
+
// Filter by service if specified
|
|
916
|
+
if (filter.service && metadata && metadata.service !== filter.service)
|
|
917
|
+
continue;
|
|
918
|
+
// Filter by custom metadata if specified
|
|
919
|
+
if (filter.metadata && metadata) {
|
|
920
|
+
let matches = true;
|
|
921
|
+
for (const [key, value] of Object.entries(filter.metadata)) {
|
|
922
|
+
if (metadata[key] !== value) {
|
|
923
|
+
matches = false;
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
if (!matches)
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
// Extract standard fields from metadata
|
|
931
|
+
const metadataObj = metadata || {};
|
|
932
|
+
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
933
|
+
// Create HNSWVerbWithMetadata (v4.8.0 format)
|
|
934
|
+
const verbWithMetadata = {
|
|
935
|
+
id: verb.id,
|
|
936
|
+
vector: verb.vector,
|
|
937
|
+
connections: verb.connections,
|
|
938
|
+
verb: verb.verb,
|
|
939
|
+
sourceId: verb.sourceId,
|
|
940
|
+
targetId: verb.targetId,
|
|
941
|
+
createdAt: createdAt || Date.now(),
|
|
942
|
+
updatedAt: updatedAt || Date.now(),
|
|
943
|
+
confidence,
|
|
944
|
+
weight,
|
|
945
|
+
service,
|
|
946
|
+
data,
|
|
947
|
+
createdBy,
|
|
948
|
+
metadata: customMetadata
|
|
949
|
+
};
|
|
950
|
+
allVerbs.push(verbWithMetadata);
|
|
951
|
+
}
|
|
952
|
+
catch (error) {
|
|
953
|
+
// Skip verbs with errors
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
// Apply pagination
|
|
959
|
+
const totalCount = allVerbs.length;
|
|
960
|
+
const paginatedVerbs = allVerbs.slice(offset, offset + limit);
|
|
961
|
+
const hasMore = offset + limit < totalCount;
|
|
962
|
+
// Generate cursor if more results exist
|
|
963
|
+
let nextCursor;
|
|
964
|
+
if (hasMore && paginatedVerbs.length > 0) {
|
|
965
|
+
nextCursor = paginatedVerbs[paginatedVerbs.length - 1].id;
|
|
966
|
+
}
|
|
967
|
+
return {
|
|
968
|
+
items: paginatedVerbs,
|
|
969
|
+
totalCount,
|
|
970
|
+
hasMore,
|
|
971
|
+
nextCursor
|
|
972
|
+
};
|
|
973
|
+
}
|
|
745
974
|
/**
|
|
746
975
|
* Save HNSW system data (entry point, max level)
|
|
747
976
|
*/
|
|
@@ -73,19 +73,62 @@ export declare abstract class BaseStorage extends BaseStorageAdapter {
|
|
|
73
73
|
* Ensure the storage adapter is initialized
|
|
74
74
|
*/
|
|
75
75
|
protected ensureInitialized(): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Lightweight COW enablement - just enables branch-scoped paths
|
|
78
|
+
* Called during init() to ensure all data is stored with branch prefixes from the start
|
|
79
|
+
* RefManager/BlobStorage/CommitLog are lazy-initialized on first fork()
|
|
80
|
+
* @param branch - Branch name to use (default: 'main')
|
|
81
|
+
*/
|
|
82
|
+
enableCOWLightweight(branch?: string): void;
|
|
76
83
|
/**
|
|
77
84
|
* Initialize COW (Copy-on-Write) support
|
|
78
85
|
* Creates RefManager and BlobStorage for instant fork() capability
|
|
79
86
|
*
|
|
87
|
+
* v5.0.1: Now called automatically by storageFactory (zero-config)
|
|
88
|
+
*
|
|
80
89
|
* @param options - COW initialization options
|
|
81
90
|
* @param options.branch - Initial branch name (default: 'main')
|
|
82
91
|
* @param options.enableCompression - Enable zstd compression for blobs (default: true)
|
|
83
92
|
* @returns Promise that resolves when COW is initialized
|
|
84
93
|
*/
|
|
85
|
-
|
|
94
|
+
initializeCOW(options?: {
|
|
86
95
|
branch?: string;
|
|
87
96
|
enableCompression?: boolean;
|
|
88
97
|
}): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Resolve branch-scoped path for COW isolation
|
|
100
|
+
* @protected - Available to subclasses for COW implementation
|
|
101
|
+
*/
|
|
102
|
+
protected resolveBranchPath(basePath: string, branch?: string): string;
|
|
103
|
+
/**
|
|
104
|
+
* Write object to branch-specific path (COW layer)
|
|
105
|
+
* @protected - Available to subclasses for COW implementation
|
|
106
|
+
*/
|
|
107
|
+
protected writeObjectToBranch(path: string, data: any, branch?: string): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Read object with inheritance from parent branches (COW layer)
|
|
110
|
+
* Tries current branch first, then walks commit history
|
|
111
|
+
* @protected - Available to subclasses for COW implementation
|
|
112
|
+
*/
|
|
113
|
+
protected readWithInheritance(path: string, branch?: string): Promise<any | null>;
|
|
114
|
+
/**
|
|
115
|
+
* Delete object from branch-specific path (COW layer)
|
|
116
|
+
* @protected - Available to subclasses for COW implementation
|
|
117
|
+
*/
|
|
118
|
+
protected deleteObjectFromBranch(path: string, branch?: string): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* List objects under path in branch (COW layer)
|
|
121
|
+
* @protected - Available to subclasses for COW implementation
|
|
122
|
+
*/
|
|
123
|
+
protected listObjectsInBranch(prefix: string, branch?: string): Promise<string[]>;
|
|
124
|
+
/**
|
|
125
|
+
* List objects with inheritance (v5.0.1)
|
|
126
|
+
* Lists objects from current branch AND main branch, returns unique paths
|
|
127
|
+
* This enables fork to see parent's data in pagination operations
|
|
128
|
+
*
|
|
129
|
+
* Simplified approach: All branches inherit from main
|
|
130
|
+
*/
|
|
131
|
+
protected listObjectsWithInheritance(prefix: string, branch?: string): Promise<string[]>;
|
|
89
132
|
/**
|
|
90
133
|
* Save a noun to storage (v4.0.0: vector only, metadata saved separately)
|
|
91
134
|
* @param noun Pure HNSW vector data (no metadata)
|