@soulcraft/brainy 2.1.0 → 2.3.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/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
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Periodic Cleanup for Soft-Deleted Items
|
|
3
|
+
*
|
|
4
|
+
* SAFETY-FIRST APPROACH:
|
|
5
|
+
* - Maintains durability guarantees (storage-first)
|
|
6
|
+
* - Coordinates HNSW and metadata index consistency
|
|
7
|
+
* - Isolated from live operations
|
|
8
|
+
* - Graceful failure handling
|
|
9
|
+
*/
|
|
10
|
+
import { prodLog } from './logger.js';
|
|
11
|
+
import { isDeleted } from './metadataNamespace.js';
|
|
12
|
+
/**
|
|
13
|
+
* Coordinates safe cleanup of old soft-deleted items across all indexes
|
|
14
|
+
*
|
|
15
|
+
* CRITICAL SAFETY FEATURES:
|
|
16
|
+
* 1. Storage-first deletion (durability)
|
|
17
|
+
* 2. Index consistency coordination
|
|
18
|
+
* 3. Batch processing with limits
|
|
19
|
+
* 4. Error isolation and recovery
|
|
20
|
+
*/
|
|
21
|
+
export class PeriodicCleanup {
|
|
22
|
+
constructor(storage, hnswIndex, metadataIndex, config = {}) {
|
|
23
|
+
this.cleanupTimer = null;
|
|
24
|
+
this.running = false;
|
|
25
|
+
this.storage = storage;
|
|
26
|
+
this.hnswIndex = hnswIndex;
|
|
27
|
+
this.metadataIndex = metadataIndex;
|
|
28
|
+
// Default: clean up items deleted more than 1 hour ago
|
|
29
|
+
this.config = {
|
|
30
|
+
maxAge: config.maxAge ?? 60 * 60 * 1000, // 1 hour
|
|
31
|
+
batchSize: config.batchSize ?? 100, // 100 items max per batch
|
|
32
|
+
cleanupInterval: config.cleanupInterval ?? 15 * 60 * 1000, // Every 15 minutes
|
|
33
|
+
enabled: config.enabled ?? true
|
|
34
|
+
};
|
|
35
|
+
this.stats = {
|
|
36
|
+
itemsProcessed: 0,
|
|
37
|
+
itemsDeleted: 0,
|
|
38
|
+
errors: 0,
|
|
39
|
+
lastRun: 0,
|
|
40
|
+
nextRun: 0
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Start periodic cleanup
|
|
45
|
+
*/
|
|
46
|
+
start() {
|
|
47
|
+
if (!this.config.enabled || this.cleanupTimer) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
prodLog.info(`Starting periodic cleanup: maxAge=${this.config.maxAge}, batchSize=${this.config.batchSize}, interval=${this.config.cleanupInterval}`);
|
|
51
|
+
this.scheduleNext();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Stop periodic cleanup
|
|
55
|
+
*/
|
|
56
|
+
stop() {
|
|
57
|
+
if (this.cleanupTimer) {
|
|
58
|
+
clearTimeout(this.cleanupTimer);
|
|
59
|
+
this.cleanupTimer = null;
|
|
60
|
+
}
|
|
61
|
+
prodLog.info('Stopped periodic cleanup');
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Run cleanup manually
|
|
65
|
+
*/
|
|
66
|
+
async runNow() {
|
|
67
|
+
if (this.running) {
|
|
68
|
+
throw new Error('Cleanup already running');
|
|
69
|
+
}
|
|
70
|
+
return this.performCleanup();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get current cleanup statistics
|
|
74
|
+
*/
|
|
75
|
+
getStats() {
|
|
76
|
+
return { ...this.stats };
|
|
77
|
+
}
|
|
78
|
+
scheduleNext() {
|
|
79
|
+
const nextRun = Date.now() + this.config.cleanupInterval;
|
|
80
|
+
this.stats.nextRun = nextRun;
|
|
81
|
+
this.cleanupTimer = setTimeout(async () => {
|
|
82
|
+
await this.performCleanup();
|
|
83
|
+
this.scheduleNext();
|
|
84
|
+
}, this.config.cleanupInterval);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* CRITICAL: Coordinated cleanup across all indexes
|
|
88
|
+
*
|
|
89
|
+
* SAFETY PROTOCOL:
|
|
90
|
+
* 1. Find eligible items (old + soft-deleted)
|
|
91
|
+
* 2. Remove from storage FIRST (durability)
|
|
92
|
+
* 3. Remove from HNSW (graph consistency)
|
|
93
|
+
* 4. Remove from metadata index (search consistency)
|
|
94
|
+
* 5. Track stats and errors
|
|
95
|
+
*/
|
|
96
|
+
async performCleanup() {
|
|
97
|
+
if (this.running) {
|
|
98
|
+
prodLog.warn('Cleanup already running, skipping');
|
|
99
|
+
return this.stats;
|
|
100
|
+
}
|
|
101
|
+
this.running = true;
|
|
102
|
+
const startTime = Date.now();
|
|
103
|
+
this.stats.lastRun = startTime;
|
|
104
|
+
try {
|
|
105
|
+
prodLog.debug(`Starting cleanup run: maxAge=${this.config.maxAge}, cutoffTime=${startTime - this.config.maxAge}`);
|
|
106
|
+
// Step 1: Find eligible items for cleanup
|
|
107
|
+
const eligibleItems = await this.findEligibleItems(startTime);
|
|
108
|
+
if (eligibleItems.length === 0) {
|
|
109
|
+
prodLog.debug('No items eligible for cleanup');
|
|
110
|
+
return this.stats;
|
|
111
|
+
}
|
|
112
|
+
prodLog.info(`Found ${eligibleItems.length} items eligible for cleanup`);
|
|
113
|
+
// Step 2: Process in batches for safety
|
|
114
|
+
let processed = 0;
|
|
115
|
+
let deleted = 0;
|
|
116
|
+
let errors = 0;
|
|
117
|
+
for (let i = 0; i < eligibleItems.length; i += this.config.batchSize) {
|
|
118
|
+
const batch = eligibleItems.slice(i, i + this.config.batchSize);
|
|
119
|
+
const batchResult = await this.processBatch(batch);
|
|
120
|
+
processed += batchResult.processed;
|
|
121
|
+
deleted += batchResult.deleted;
|
|
122
|
+
errors += batchResult.errors;
|
|
123
|
+
// Small delay between batches to avoid overwhelming the system
|
|
124
|
+
if (i + this.config.batchSize < eligibleItems.length) {
|
|
125
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Update stats
|
|
129
|
+
this.stats.itemsProcessed += processed;
|
|
130
|
+
this.stats.itemsDeleted += deleted;
|
|
131
|
+
this.stats.errors += errors;
|
|
132
|
+
prodLog.info(`Cleanup run completed: processed=${processed}, deleted=${deleted}, errors=${errors}, duration=${Date.now() - startTime}ms`);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
prodLog.error(`Cleanup run failed: ${error}`);
|
|
136
|
+
this.stats.errors++;
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
this.running = false;
|
|
140
|
+
}
|
|
141
|
+
return this.stats;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Find items eligible for cleanup (old + soft-deleted)
|
|
145
|
+
*/
|
|
146
|
+
async findEligibleItems(currentTime) {
|
|
147
|
+
const cutoffTime = currentTime - this.config.maxAge;
|
|
148
|
+
const eligibleItems = [];
|
|
149
|
+
try {
|
|
150
|
+
// Get all nouns from storage (using pagination to avoid memory issues)
|
|
151
|
+
const nounsResult = await this.storage.getNouns({
|
|
152
|
+
pagination: { limit: 1000 } // Process in chunks
|
|
153
|
+
});
|
|
154
|
+
for (const noun of nounsResult.items) {
|
|
155
|
+
try {
|
|
156
|
+
if (!noun.metadata || !isDeleted(noun.metadata)) {
|
|
157
|
+
continue; // Not deleted, skip
|
|
158
|
+
}
|
|
159
|
+
// Check if old enough for cleanup
|
|
160
|
+
const deletedTime = noun.metadata._brainy?.updated || 0;
|
|
161
|
+
if (deletedTime && (currentTime - deletedTime) > this.config.maxAge) {
|
|
162
|
+
eligibleItems.push(noun.id);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
prodLog.warn(`Failed to check item ${noun.id} for cleanup eligibility: ${error}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
prodLog.error(`Failed to find eligible items: ${error}`);
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
return eligibleItems;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Process a batch of items for cleanup
|
|
178
|
+
*
|
|
179
|
+
* CRITICAL: This maintains the durability-first approach:
|
|
180
|
+
* Storage → HNSW → Metadata Index
|
|
181
|
+
*/
|
|
182
|
+
async processBatch(itemIds) {
|
|
183
|
+
let processed = 0;
|
|
184
|
+
let deleted = 0;
|
|
185
|
+
let errors = 0;
|
|
186
|
+
for (const id of itemIds) {
|
|
187
|
+
processed++;
|
|
188
|
+
try {
|
|
189
|
+
// STEP 1: Remove from storage FIRST (durability guarantee)
|
|
190
|
+
try {
|
|
191
|
+
await this.storage.deleteNoun(id);
|
|
192
|
+
}
|
|
193
|
+
catch (storageError) {
|
|
194
|
+
prodLog.warn(`Failed to delete ${id} from storage: ${storageError}`);
|
|
195
|
+
errors++;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
// STEP 2: Remove from HNSW index (vector search consistency)
|
|
199
|
+
const hnswResult = this.hnswIndex.removeItem(id);
|
|
200
|
+
if (!hnswResult) {
|
|
201
|
+
prodLog.warn(`Failed to remove ${id} from HNSW index (may not have been indexed)`);
|
|
202
|
+
// Not a critical error - item might not have been in vector index
|
|
203
|
+
}
|
|
204
|
+
// STEP 3: Remove from metadata index (faceted search consistency)
|
|
205
|
+
if (this.metadataIndex) {
|
|
206
|
+
await this.metadataIndex.removeFromIndex(id);
|
|
207
|
+
}
|
|
208
|
+
deleted++;
|
|
209
|
+
prodLog.debug(`Successfully cleaned up item ${id}`);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
errors++;
|
|
213
|
+
prodLog.error(`Failed to cleanup item ${id}: ${error}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return { processed, deleted, errors };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=periodicCleanup.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Universal Knowledge Protocol™ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. 31 nouns × 40 verbs for infinite expressiveness.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -77,7 +77,12 @@
|
|
|
77
77
|
"lint": "eslint --ext .ts,.js src/",
|
|
78
78
|
"lint:fix": "eslint --ext .ts,.js src/ --fix",
|
|
79
79
|
"format": "prettier --write \"src/**/*.{ts,js}\"",
|
|
80
|
-
"format:check": "prettier --check \"src/**/*.{ts,js}\""
|
|
80
|
+
"format:check": "prettier --check \"src/**/*.{ts,js}\"",
|
|
81
|
+
"release": "standard-version",
|
|
82
|
+
"release:patch": "standard-version --release-as patch",
|
|
83
|
+
"release:minor": "standard-version --release-as minor",
|
|
84
|
+
"release:major": "standard-version --release-as major",
|
|
85
|
+
"release:dry": "standard-version --dry-run"
|
|
81
86
|
},
|
|
82
87
|
"keywords": [
|
|
83
88
|
"ai-database",
|
|
@@ -130,13 +135,14 @@
|
|
|
130
135
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
131
136
|
"@typescript-eslint/parser": "^8.0.0",
|
|
132
137
|
"@vitest/coverage-v8": "^3.2.4",
|
|
138
|
+
"standard-version": "^9.5.0",
|
|
133
139
|
"tsx": "^4.19.2",
|
|
134
140
|
"typescript": "^5.4.5",
|
|
135
141
|
"vitest": "^3.2.4"
|
|
136
142
|
},
|
|
137
143
|
"dependencies": {
|
|
138
144
|
"@aws-sdk/client-s3": "^3.540.0",
|
|
139
|
-
"@huggingface/transformers": "^3.
|
|
145
|
+
"@huggingface/transformers": "^3.7.2",
|
|
140
146
|
"boxen": "^8.0.1",
|
|
141
147
|
"chalk": "^5.3.0",
|
|
142
148
|
"cli-table3": "^0.6.5",
|