@soulcraft/brainy 2.1.0 โ 3.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/dist/augmentations/AugmentationMetadataContract.d.ts +94 -0
- package/dist/augmentations/AugmentationMetadataContract.js +306 -0
- package/dist/augmentations/apiServerAugmentation.d.ts +1 -0
- package/dist/augmentations/apiServerAugmentation.js +1 -0
- package/dist/augmentations/batchProcessingAugmentation.d.ts +1 -0
- package/dist/augmentations/batchProcessingAugmentation.js +1 -0
- package/dist/augmentations/brainyAugmentation.d.ts +16 -0
- package/dist/augmentations/cacheAugmentation.d.ts +1 -0
- package/dist/augmentations/cacheAugmentation.js +1 -0
- package/dist/augmentations/conduitAugmentations.d.ts +1 -0
- package/dist/augmentations/conduitAugmentations.js +1 -0
- package/dist/augmentations/connectionPoolAugmentation.d.ts +1 -0
- package/dist/augmentations/connectionPoolAugmentation.js +1 -0
- package/dist/augmentations/entityRegistryAugmentation.d.ts +2 -0
- package/dist/augmentations/entityRegistryAugmentation.js +2 -0
- package/dist/augmentations/indexAugmentation.d.ts +1 -0
- package/dist/augmentations/indexAugmentation.js +1 -0
- package/dist/augmentations/intelligentVerbScoringAugmentation.d.ts +4 -0
- package/dist/augmentations/intelligentVerbScoringAugmentation.js +4 -0
- package/dist/augmentations/metadataEnforcer.d.ts +20 -0
- package/dist/augmentations/metadataEnforcer.js +171 -0
- package/dist/augmentations/metricsAugmentation.d.ts +2 -7
- package/dist/augmentations/metricsAugmentation.js +1 -0
- package/dist/augmentations/monitoringAugmentation.d.ts +1 -0
- package/dist/augmentations/monitoringAugmentation.js +1 -0
- package/dist/augmentations/neuralImport.d.ts +4 -0
- package/dist/augmentations/neuralImport.js +4 -0
- package/dist/augmentations/requestDeduplicatorAugmentation.d.ts +1 -0
- package/dist/augmentations/requestDeduplicatorAugmentation.js +1 -0
- package/dist/augmentations/serverSearchAugmentations.d.ts +2 -0
- package/dist/augmentations/serverSearchAugmentations.js +2 -0
- package/dist/augmentations/storageAugmentation.d.ts +1 -0
- package/dist/augmentations/storageAugmentation.js +1 -0
- package/dist/augmentations/synapseAugmentation.d.ts +4 -0
- package/dist/augmentations/synapseAugmentation.js +4 -0
- package/dist/augmentations/walAugmentation.d.ts +1 -0
- package/dist/augmentations/walAugmentation.js +1 -0
- package/dist/brainyData.d.ts +28 -1
- package/dist/brainyData.js +229 -83
- package/dist/embeddings/model-manager.d.ts +9 -8
- package/dist/embeddings/model-manager.js +105 -85
- package/dist/triple/TripleIntelligence.d.ts +4 -0
- package/dist/triple/TripleIntelligence.js +39 -9
- package/dist/utils/deletedItemsIndex.d.ts +59 -0
- package/dist/utils/deletedItemsIndex.js +98 -0
- package/dist/utils/ensureDeleted.d.ts +38 -0
- package/dist/utils/ensureDeleted.js +79 -0
- package/dist/utils/metadataFilter.js +5 -0
- package/dist/utils/metadataIndex.d.ts +4 -0
- package/dist/utils/metadataIndex.js +45 -0
- package/dist/utils/metadataNamespace.d.ts +113 -0
- package/dist/utils/metadataNamespace.js +162 -0
- package/dist/utils/periodicCleanup.d.ts +87 -0
- package/dist/utils/periodicCleanup.js +219 -0
- package/package.json +9 -3
package/dist/brainyData.js
CHANGED
|
@@ -8,6 +8,8 @@ import { HNSWIndexOptimized } from './hnsw/hnswIndexOptimized.js';
|
|
|
8
8
|
import { cosineDistance, defaultEmbeddingFunction, cleanupWorkerPools, batchEmbed } from './utils/index.js';
|
|
9
9
|
import { getAugmentationVersion } from './utils/version.js';
|
|
10
10
|
import { matchesMetadataFilter } from './utils/metadataFilter.js';
|
|
11
|
+
import { createNamespacedMetadata, updateNamespacedMetadata, markDeleted, markRestored, isDeleted, getUserMetadata } from './utils/metadataNamespace.js';
|
|
12
|
+
import { PeriodicCleanup } from './utils/periodicCleanup.js';
|
|
11
13
|
import { NounType, VerbType } from './types/graphTypes.js';
|
|
12
14
|
import { createServerSearchAugmentations } from './augmentations/serverSearchAugmentations.js';
|
|
13
15
|
import { augmentationPipeline } from './augmentationPipeline.js';
|
|
@@ -94,6 +96,8 @@ export class BrainyData {
|
|
|
94
96
|
* Handles WAL, connection pooling, batching, streaming, and intelligent scoring
|
|
95
97
|
*/
|
|
96
98
|
this.augmentations = new AugmentationRegistry();
|
|
99
|
+
// Periodic cleanup for soft-deleted items
|
|
100
|
+
this.periodicCleanup = null;
|
|
97
101
|
// Timeout and retry configuration
|
|
98
102
|
this.timeoutConfig = {};
|
|
99
103
|
this.retryConfig = {};
|
|
@@ -397,6 +401,41 @@ export class BrainyData {
|
|
|
397
401
|
if (this.loggingConfig?.verbose) {
|
|
398
402
|
console.log('๐ New augmentation system initialized successfully');
|
|
399
403
|
}
|
|
404
|
+
// Initialize periodic cleanup system
|
|
405
|
+
await this.initializePeriodicCleanup();
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Initialize periodic cleanup system for old soft-deleted items
|
|
409
|
+
* SAFETY-CRITICAL: Coordinates with both HNSW and metadata indexes
|
|
410
|
+
*/
|
|
411
|
+
async initializePeriodicCleanup() {
|
|
412
|
+
if (!this.storage) {
|
|
413
|
+
throw new Error('Cannot initialize periodic cleanup: storage not available');
|
|
414
|
+
}
|
|
415
|
+
// Skip cleanup if in read-only or frozen mode
|
|
416
|
+
if (this.readOnly || this.frozen) {
|
|
417
|
+
if (this.loggingConfig?.verbose) {
|
|
418
|
+
console.log('๐งน Periodic cleanup disabled: database is read-only or frozen');
|
|
419
|
+
}
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
// Get cleanup config with safe defaults
|
|
423
|
+
const cleanupConfig = this.config.cleanup || {};
|
|
424
|
+
// Create cleanup system with all required dependencies
|
|
425
|
+
this.periodicCleanup = new PeriodicCleanup(this.storage, this.hnswIndex, this.metadataIndex, // Can be null, cleanup will handle gracefully
|
|
426
|
+
{
|
|
427
|
+
enabled: cleanupConfig.enabled !== false, // Enabled by default
|
|
428
|
+
maxAge: cleanupConfig.maxAge || 60 * 60 * 1000, // 1 hour default
|
|
429
|
+
batchSize: cleanupConfig.batchSize || 100, // 100 items per batch
|
|
430
|
+
cleanupInterval: cleanupConfig.cleanupInterval || 15 * 60 * 1000 // 15 minutes
|
|
431
|
+
});
|
|
432
|
+
// Start cleanup if enabled
|
|
433
|
+
if (this.periodicCleanup && cleanupConfig.enabled !== false) {
|
|
434
|
+
this.periodicCleanup.start();
|
|
435
|
+
if (this.loggingConfig?.verbose) {
|
|
436
|
+
console.log('๐งน Periodic cleanup system initialized and started');
|
|
437
|
+
}
|
|
438
|
+
}
|
|
400
439
|
}
|
|
401
440
|
checkReadOnly() {
|
|
402
441
|
if (this.readOnly) {
|
|
@@ -1362,46 +1401,42 @@ export class BrainyData {
|
|
|
1362
1401
|
// Always update updatedAt
|
|
1363
1402
|
graphNoun.updatedAt = timestamp;
|
|
1364
1403
|
}
|
|
1365
|
-
// Create
|
|
1366
|
-
let metadataToSave = metadata;
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
if (
|
|
1374
|
-
|
|
1375
|
-
|
|
1404
|
+
// Create properly namespaced metadata for new items
|
|
1405
|
+
let metadataToSave = createNamespacedMetadata(metadata);
|
|
1406
|
+
// Add domain metadata if distributed mode is enabled
|
|
1407
|
+
if (this.domainDetector) {
|
|
1408
|
+
// First check if domain is already in metadata
|
|
1409
|
+
if (metadataToSave.domain) {
|
|
1410
|
+
// Domain already specified, keep it
|
|
1411
|
+
const domainInfo = this.domainDetector.detectDomain(metadataToSave);
|
|
1412
|
+
if (domainInfo.domainMetadata) {
|
|
1413
|
+
;
|
|
1414
|
+
metadataToSave.domainMetadata =
|
|
1415
|
+
domainInfo.domainMetadata;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
else {
|
|
1419
|
+
// Try to detect domain from the data
|
|
1420
|
+
const dataToAnalyze = Array.isArray(vectorOrData)
|
|
1421
|
+
? metadata
|
|
1422
|
+
: vectorOrData;
|
|
1423
|
+
const domainInfo = this.domainDetector.detectDomain(dataToAnalyze);
|
|
1424
|
+
if (domainInfo.domain) {
|
|
1425
|
+
;
|
|
1426
|
+
metadataToSave.domain = domainInfo.domain;
|
|
1376
1427
|
if (domainInfo.domainMetadata) {
|
|
1377
1428
|
;
|
|
1378
1429
|
metadataToSave.domainMetadata =
|
|
1379
1430
|
domainInfo.domainMetadata;
|
|
1380
1431
|
}
|
|
1381
1432
|
}
|
|
1382
|
-
else {
|
|
1383
|
-
// Try to detect domain from the data
|
|
1384
|
-
const dataToAnalyze = Array.isArray(vectorOrData)
|
|
1385
|
-
? metadata
|
|
1386
|
-
: vectorOrData;
|
|
1387
|
-
const domainInfo = this.domainDetector.detectDomain(dataToAnalyze);
|
|
1388
|
-
if (domainInfo.domain) {
|
|
1389
|
-
;
|
|
1390
|
-
metadataToSave.domain = domainInfo.domain;
|
|
1391
|
-
if (domainInfo.domainMetadata) {
|
|
1392
|
-
;
|
|
1393
|
-
metadataToSave.domainMetadata =
|
|
1394
|
-
domainInfo.domainMetadata;
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
// Add partition information if distributed mode is enabled
|
|
1400
|
-
if (this.partitioner) {
|
|
1401
|
-
const partition = this.partitioner.getPartition(id);
|
|
1402
|
-
metadataToSave.partition = partition;
|
|
1403
1433
|
}
|
|
1404
1434
|
}
|
|
1435
|
+
// Add partition information if distributed mode is enabled
|
|
1436
|
+
if (this.partitioner) {
|
|
1437
|
+
const partition = this.partitioner.getPartition(id);
|
|
1438
|
+
metadataToSave.partition = partition;
|
|
1439
|
+
}
|
|
1405
1440
|
await this.storage.saveMetadata(id, metadataToSave);
|
|
1406
1441
|
// Update metadata index (write-only mode should build indices!)
|
|
1407
1442
|
if (this.index && !this.frozen) {
|
|
@@ -2077,10 +2112,11 @@ export class BrainyData {
|
|
|
2077
2112
|
// This preserves pure vector searches without metadata
|
|
2078
2113
|
if (metadataFilter && Object.keys(metadataFilter).length > 0) {
|
|
2079
2114
|
// If no explicit deleted filter is provided, exclude soft-deleted items
|
|
2080
|
-
|
|
2115
|
+
// Use namespaced field for O(1) performance
|
|
2116
|
+
if (!metadataFilter['_brainy.deleted'] && !metadataFilter.anyOf) {
|
|
2081
2117
|
metadataFilter = {
|
|
2082
2118
|
...metadataFilter,
|
|
2083
|
-
deleted:
|
|
2119
|
+
['_brainy.deleted']: false // O(1) positive match instead of notEquals
|
|
2084
2120
|
};
|
|
2085
2121
|
}
|
|
2086
2122
|
}
|
|
@@ -2264,7 +2300,8 @@ export class BrainyData {
|
|
|
2264
2300
|
if (result.metadata && typeof result.metadata === 'object') {
|
|
2265
2301
|
const metadata = result.metadata;
|
|
2266
2302
|
// Exclude deleted items from search results (soft delete)
|
|
2267
|
-
|
|
2303
|
+
// Check namespaced field
|
|
2304
|
+
if (metadata._brainy?.deleted === true) {
|
|
2268
2305
|
return false;
|
|
2269
2306
|
}
|
|
2270
2307
|
// Exclude placeholder nouns from search results
|
|
@@ -2877,9 +2914,9 @@ export class BrainyData {
|
|
|
2877
2914
|
finalWeight = options.weight;
|
|
2878
2915
|
}
|
|
2879
2916
|
}
|
|
2880
|
-
// Create complete verb metadata
|
|
2881
|
-
//
|
|
2882
|
-
const
|
|
2917
|
+
// Create complete verb metadata with proper namespace
|
|
2918
|
+
// First combine user metadata with verb-specific metadata
|
|
2919
|
+
const userAndVerbMetadata = {
|
|
2883
2920
|
sourceId: sourceId,
|
|
2884
2921
|
targetId: targetId,
|
|
2885
2922
|
source: sourceId,
|
|
@@ -2899,6 +2936,8 @@ export class BrainyData {
|
|
|
2899
2936
|
...(options.metadata || {}),
|
|
2900
2937
|
data: options.metadata // Also store in data field for backwards compatibility
|
|
2901
2938
|
};
|
|
2939
|
+
// Now wrap with namespace for internal fields
|
|
2940
|
+
const verbMetadata = createNamespacedMetadata(userAndVerbMetadata);
|
|
2902
2941
|
// Add to index
|
|
2903
2942
|
await this.index.addItem({ id, vector: verbVector });
|
|
2904
2943
|
// Get the noun from the index
|
|
@@ -2978,6 +3017,12 @@ export class BrainyData {
|
|
|
2978
3017
|
const metadata = await this.storage.getVerbMetadata(id);
|
|
2979
3018
|
if (!metadata) {
|
|
2980
3019
|
console.warn(`Verb ${id} found but no metadata - creating minimal GraphVerb`);
|
|
3020
|
+
}
|
|
3021
|
+
else if (isDeleted(metadata)) {
|
|
3022
|
+
// Check if verb is soft-deleted
|
|
3023
|
+
return null;
|
|
3024
|
+
}
|
|
3025
|
+
if (!metadata) {
|
|
2981
3026
|
// Return minimal GraphVerb if metadata is missing
|
|
2982
3027
|
return {
|
|
2983
3028
|
id: hnswVerb.id,
|
|
@@ -3226,50 +3271,65 @@ export class BrainyData {
|
|
|
3226
3271
|
// Check if database is in read-only mode
|
|
3227
3272
|
this.checkReadOnly();
|
|
3228
3273
|
try {
|
|
3229
|
-
//
|
|
3230
|
-
// The MetadataIndex
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
deleted: true,
|
|
3236
|
-
deletedAt: new Date().toISOString(),
|
|
3237
|
-
deletedBy: options.service || '2.0-api'
|
|
3238
|
-
});
|
|
3239
|
-
// Update MetadataIndex for O(log n) filtering
|
|
3240
|
-
if (this.metadataIndex) {
|
|
3241
|
-
await this.metadataIndex.updateIndex(id, { deleted: true });
|
|
3242
|
-
}
|
|
3243
|
-
return true;
|
|
3244
|
-
}
|
|
3245
|
-
catch (error) {
|
|
3246
|
-
// If verb doesn't exist, return false (not an error)
|
|
3274
|
+
// CONSISTENT: Always use soft delete for graph integrity and recoverability
|
|
3275
|
+
// The MetadataIndex efficiently filters out deleted items with O(1) performance
|
|
3276
|
+
try {
|
|
3277
|
+
const existing = await this.storage.getVerb(id);
|
|
3278
|
+
if (!existing || !existing.metadata) {
|
|
3279
|
+
// Verb doesn't exist, return false (not an error)
|
|
3247
3280
|
return false;
|
|
3248
3281
|
}
|
|
3282
|
+
const updatedMetadata = markDeleted(existing.metadata);
|
|
3283
|
+
await this.storage.saveVerbMetadata(id, updatedMetadata);
|
|
3284
|
+
// Update MetadataIndex for O(1) filtering
|
|
3285
|
+
if (this.metadataIndex) {
|
|
3286
|
+
await this.metadataIndex.removeFromIndex(id, existing.metadata);
|
|
3287
|
+
await this.metadataIndex.addToIndex(id, updatedMetadata);
|
|
3288
|
+
}
|
|
3289
|
+
return true;
|
|
3249
3290
|
}
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
// Remove from index
|
|
3253
|
-
const removed = this.index.removeItem(id);
|
|
3254
|
-
if (!removed) {
|
|
3291
|
+
catch (error) {
|
|
3292
|
+
// If verb doesn't exist, return false (not an error)
|
|
3255
3293
|
return false;
|
|
3256
3294
|
}
|
|
3257
|
-
// Remove from metadata index
|
|
3258
|
-
if (this.index && existingMetadata) {
|
|
3259
|
-
await this.metadataIndex?.removeFromIndex?.(id, existingMetadata);
|
|
3260
|
-
}
|
|
3261
|
-
// Remove from storage
|
|
3262
|
-
await this.storage.deleteVerb(id);
|
|
3263
|
-
// Track deletion statistics
|
|
3264
|
-
const service = this.getServiceName(options);
|
|
3265
|
-
await this.storage.decrementStatistic('verb', service);
|
|
3266
|
-
return true;
|
|
3267
3295
|
}
|
|
3268
3296
|
catch (error) {
|
|
3269
3297
|
console.error(`Failed to delete verb ${id}:`, error);
|
|
3270
3298
|
throw new Error(`Failed to delete verb ${id}: ${error}`);
|
|
3271
3299
|
}
|
|
3272
3300
|
}
|
|
3301
|
+
/**
|
|
3302
|
+
* Restore a soft-deleted verb (complement to consistent soft delete)
|
|
3303
|
+
* @param id The verb ID to restore
|
|
3304
|
+
* @param options Options for the restore operation
|
|
3305
|
+
* @returns Promise<boolean> True if restored, false if not found or not deleted
|
|
3306
|
+
*/
|
|
3307
|
+
async restoreVerb(id, options = {}) {
|
|
3308
|
+
await this.ensureInitialized();
|
|
3309
|
+
// Check if database is in read-only mode
|
|
3310
|
+
this.checkReadOnly();
|
|
3311
|
+
try {
|
|
3312
|
+
const existing = await this.storage.getVerb(id);
|
|
3313
|
+
if (!existing || !existing.metadata) {
|
|
3314
|
+
return false; // Verb doesn't exist
|
|
3315
|
+
}
|
|
3316
|
+
if (!isDeleted(existing.metadata)) {
|
|
3317
|
+
return false; // Verb not deleted, nothing to restore
|
|
3318
|
+
}
|
|
3319
|
+
const restoredMetadata = markRestored(existing.metadata);
|
|
3320
|
+
await this.storage.saveVerbMetadata(id, restoredMetadata);
|
|
3321
|
+
// Update MetadataIndex
|
|
3322
|
+
if (this.metadataIndex) {
|
|
3323
|
+
await this.metadataIndex.removeFromIndex(id, existing.metadata);
|
|
3324
|
+
await this.metadataIndex.addToIndex(id, restoredMetadata);
|
|
3325
|
+
}
|
|
3326
|
+
return true;
|
|
3327
|
+
}
|
|
3328
|
+
catch (error) {
|
|
3329
|
+
console.error(`Failed to restore verb ${id}:`, error);
|
|
3330
|
+
throw new Error(`Failed to restore verb ${id}: ${error}`);
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3273
3333
|
/**
|
|
3274
3334
|
* Get the number of vectors in the database
|
|
3275
3335
|
*/
|
|
@@ -5474,8 +5534,8 @@ export class BrainyData {
|
|
|
5474
5534
|
metadata = {};
|
|
5475
5535
|
}
|
|
5476
5536
|
else if (typeof metadata === 'object') {
|
|
5477
|
-
// Check if this item is soft-deleted
|
|
5478
|
-
if (metadata
|
|
5537
|
+
// Check if this item is soft-deleted using namespace
|
|
5538
|
+
if (isDeleted(metadata)) {
|
|
5479
5539
|
// Return null for soft-deleted items to match expected API behavior
|
|
5480
5540
|
return null;
|
|
5481
5541
|
}
|
|
@@ -5527,17 +5587,34 @@ export class BrainyData {
|
|
|
5527
5587
|
}
|
|
5528
5588
|
}
|
|
5529
5589
|
// For 2.0 API safety, we default to soft delete
|
|
5530
|
-
// Soft delete:
|
|
5590
|
+
// Soft delete: mark as deleted using namespace for O(1) filtering
|
|
5531
5591
|
try {
|
|
5532
|
-
await this.
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
}
|
|
5592
|
+
const existing = await this.getNoun(actualId);
|
|
5593
|
+
if (!existing) {
|
|
5594
|
+
// Item doesn't exist, return false (per API contract)
|
|
5595
|
+
return false;
|
|
5596
|
+
}
|
|
5597
|
+
if (existing.metadata) {
|
|
5598
|
+
// Directly save the metadata with deleted flag set
|
|
5599
|
+
const metadata = existing.metadata;
|
|
5600
|
+
const metadataWithNamespace = metadata._brainy
|
|
5601
|
+
? metadata
|
|
5602
|
+
: createNamespacedMetadata(metadata);
|
|
5603
|
+
const updatedMetadata = markDeleted(metadataWithNamespace);
|
|
5604
|
+
// Save to storage
|
|
5605
|
+
await this.storage.saveMetadata(actualId, updatedMetadata);
|
|
5606
|
+
// CRITICAL: Update the metadata index for O(1) soft delete filtering
|
|
5607
|
+
if (this.metadataIndex) {
|
|
5608
|
+
// Remove old metadata from index
|
|
5609
|
+
await this.metadataIndex.removeFromIndex(actualId, metadataWithNamespace);
|
|
5610
|
+
// Add updated metadata with deleted flag
|
|
5611
|
+
await this.metadataIndex.addToIndex(actualId, updatedMetadata);
|
|
5612
|
+
}
|
|
5613
|
+
}
|
|
5537
5614
|
return true;
|
|
5538
5615
|
}
|
|
5539
5616
|
catch (error) {
|
|
5540
|
-
// If
|
|
5617
|
+
// If an actual error occurs, return false
|
|
5541
5618
|
return false;
|
|
5542
5619
|
}
|
|
5543
5620
|
}
|
|
@@ -5546,6 +5623,62 @@ export class BrainyData {
|
|
|
5546
5623
|
throw new Error(`Failed to delete vector ${id}: ${error}`);
|
|
5547
5624
|
}
|
|
5548
5625
|
}
|
|
5626
|
+
/**
|
|
5627
|
+
* Restore a soft-deleted noun (complement to consistent soft delete)
|
|
5628
|
+
* @param id The noun ID to restore
|
|
5629
|
+
* @returns Promise<boolean> True if restored, false if not found or not deleted
|
|
5630
|
+
*/
|
|
5631
|
+
async restoreNoun(id) {
|
|
5632
|
+
// Validate id parameter first, before any other logic
|
|
5633
|
+
if (id === null || id === undefined) {
|
|
5634
|
+
throw new Error('ID cannot be null or undefined');
|
|
5635
|
+
}
|
|
5636
|
+
await this.ensureInitialized();
|
|
5637
|
+
// Check if database is in read-only mode
|
|
5638
|
+
this.checkReadOnly();
|
|
5639
|
+
try {
|
|
5640
|
+
// Handle content text vs ID resolution (same as deleteNoun)
|
|
5641
|
+
let actualId = id;
|
|
5642
|
+
if (!this.index.getNouns().has(id)) {
|
|
5643
|
+
// Try to find a noun with matching text content
|
|
5644
|
+
for (const [nounId, noun] of this.index.getNouns().entries()) {
|
|
5645
|
+
if (noun.metadata?.text === id) {
|
|
5646
|
+
actualId = nounId;
|
|
5647
|
+
break;
|
|
5648
|
+
}
|
|
5649
|
+
}
|
|
5650
|
+
}
|
|
5651
|
+
const existing = await this.getNoun(actualId);
|
|
5652
|
+
if (!existing) {
|
|
5653
|
+
return false; // Noun doesn't exist
|
|
5654
|
+
}
|
|
5655
|
+
if (!existing.metadata) {
|
|
5656
|
+
return false; // No metadata
|
|
5657
|
+
}
|
|
5658
|
+
// Ensure metadata has namespace structure before checking if deleted
|
|
5659
|
+
const metadata = existing.metadata;
|
|
5660
|
+
const metadataWithNamespace = metadata._brainy
|
|
5661
|
+
? metadata
|
|
5662
|
+
: createNamespacedMetadata(getUserMetadata(metadata));
|
|
5663
|
+
if (!isDeleted(metadataWithNamespace)) {
|
|
5664
|
+
return false; // Noun not deleted, nothing to restore
|
|
5665
|
+
}
|
|
5666
|
+
// Restore the noun using the namespace-aware metadata
|
|
5667
|
+
const restoredMetadata = markRestored(metadataWithNamespace);
|
|
5668
|
+
// Save to storage
|
|
5669
|
+
await this.storage.saveMetadata(actualId, restoredMetadata);
|
|
5670
|
+
// Update the metadata index
|
|
5671
|
+
if (this.metadataIndex) {
|
|
5672
|
+
await this.metadataIndex.removeFromIndex(actualId, metadataWithNamespace);
|
|
5673
|
+
await this.metadataIndex.addToIndex(actualId, restoredMetadata);
|
|
5674
|
+
}
|
|
5675
|
+
return true;
|
|
5676
|
+
}
|
|
5677
|
+
catch (error) {
|
|
5678
|
+
console.error(`Failed to restore noun ${id}:`, error);
|
|
5679
|
+
throw new Error(`Failed to restore noun ${id}: ${error}`);
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5549
5682
|
/**
|
|
5550
5683
|
* Delete multiple nouns by IDs
|
|
5551
5684
|
* @param ids Array of noun IDs
|
|
@@ -5611,6 +5744,8 @@ export class BrainyData {
|
|
|
5611
5744
|
else if (!metadata && existingMetadata) {
|
|
5612
5745
|
finalMetadata = existingMetadata;
|
|
5613
5746
|
}
|
|
5747
|
+
// Update metadata while preserving namespaces
|
|
5748
|
+
finalMetadata = updateNamespacedMetadata(existingMetadata || {}, finalMetadata);
|
|
5614
5749
|
// Update the noun with new data and vector
|
|
5615
5750
|
const updatedNoun = {
|
|
5616
5751
|
...existingNoun,
|
|
@@ -5668,8 +5803,17 @@ export class BrainyData {
|
|
|
5668
5803
|
if (!noun) {
|
|
5669
5804
|
throw new Error(`Vector with ID ${id} does not exist`);
|
|
5670
5805
|
}
|
|
5806
|
+
// Get existing metadata to preserve namespaces
|
|
5807
|
+
const existing = await this.storage.getMetadata(id) || {};
|
|
5808
|
+
// Update metadata while preserving namespace structure
|
|
5809
|
+
const metadataToSave = updateNamespacedMetadata(existing, metadata);
|
|
5671
5810
|
// Save updated metadata to storage
|
|
5672
|
-
await this.storage.saveMetadata(id,
|
|
5811
|
+
await this.storage.saveMetadata(id, metadataToSave);
|
|
5812
|
+
// Update metadata index for efficient filtering
|
|
5813
|
+
if (this.metadataIndex) {
|
|
5814
|
+
await this.metadataIndex.removeFromIndex(id, existing);
|
|
5815
|
+
await this.metadataIndex.addToIndex(id, metadataToSave);
|
|
5816
|
+
}
|
|
5673
5817
|
// Invalidate search cache since metadata has changed
|
|
5674
5818
|
this.cache?.invalidateOnDataChange('update');
|
|
5675
5819
|
}
|
|
@@ -5803,12 +5947,14 @@ export class BrainyData {
|
|
|
5803
5947
|
else if (offset > 0) {
|
|
5804
5948
|
processedQuery.offset = offset;
|
|
5805
5949
|
}
|
|
5806
|
-
//
|
|
5950
|
+
// Add soft-delete filter using POSITIVE match (O(1) hash lookup)
|
|
5951
|
+
// We use _brainy.deleted to avoid conflicts with user metadata
|
|
5807
5952
|
if (excludeDeleted) {
|
|
5808
5953
|
if (!processedQuery.where) {
|
|
5809
5954
|
processedQuery.where = {};
|
|
5810
5955
|
}
|
|
5811
|
-
|
|
5956
|
+
// Use namespaced field for O(1) hash lookup in metadata index
|
|
5957
|
+
processedQuery.where['_brainy.deleted'] = false; // or { equals: false }
|
|
5812
5958
|
}
|
|
5813
5959
|
// Apply mode-specific optimizations
|
|
5814
5960
|
if (mode !== 'auto') {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Model Manager - Ensures transformer models are available at runtime
|
|
3
3
|
*
|
|
4
|
-
* Strategy:
|
|
5
|
-
* 1. Check local cache first
|
|
6
|
-
* 2. Try
|
|
7
|
-
* 3.
|
|
8
|
-
* 4.
|
|
4
|
+
* Strategy (in order):
|
|
5
|
+
* 1. Check local cache first (instant)
|
|
6
|
+
* 2. Try Soulcraft CDN (fastest when available)
|
|
7
|
+
* 3. Try GitHub release tar.gz with extraction (reliable backup)
|
|
8
|
+
* 4. Fall back to Hugging Face (always works)
|
|
9
|
+
*
|
|
10
|
+
* NO USER CONFIGURATION REQUIRED - Everything is automatic!
|
|
9
11
|
*/
|
|
10
12
|
export declare class ModelManager {
|
|
11
13
|
private static instance;
|
|
@@ -16,9 +18,8 @@ export declare class ModelManager {
|
|
|
16
18
|
private getModelsPath;
|
|
17
19
|
ensureModels(modelName?: string): Promise<boolean>;
|
|
18
20
|
private verifyModelFiles;
|
|
19
|
-
private
|
|
20
|
-
private
|
|
21
|
-
private configureTransformers;
|
|
21
|
+
private tryModelSource;
|
|
22
|
+
private downloadAndExtractFromGitHub;
|
|
22
23
|
/**
|
|
23
24
|
* Pre-download models for deployment
|
|
24
25
|
* This is what npm run download-models calls
|