@soulcraft/brainy 5.6.2 → 5.7.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 +12 -0
- package/README.md +29 -4
- package/dist/graph/graphAdjacencyIndex.d.ts +33 -1
- package/dist/graph/graphAdjacencyIndex.js +110 -18
- package/dist/import/BackgroundDeduplicator.d.ts +93 -0
- package/dist/import/BackgroundDeduplicator.js +359 -0
- package/dist/import/ImportCoordinator.d.ts +1 -1
- package/dist/import/ImportCoordinator.js +14 -21
- package/dist/import/index.d.ts +2 -0
- package/dist/import/index.js +1 -0
- package/dist/storage/baseStorage.d.ts +9 -2
- package/dist/storage/baseStorage.js +116 -111
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@ import { getShardIdFromUuid } from './sharding.js';
|
|
|
10
10
|
import { RefManager } from './cow/RefManager.js';
|
|
11
11
|
import { BlobStorage } from './cow/BlobStorage.js';
|
|
12
12
|
import { CommitLog } from './cow/CommitLog.js';
|
|
13
|
+
import { prodLog } from '../utils/logger.js';
|
|
13
14
|
// Clean directory structure (v4.7.2+)
|
|
14
15
|
// All storage adapters use this consistent structure
|
|
15
16
|
export const NOUNS_METADATA_DIR = 'entities/nouns/metadata';
|
|
@@ -118,7 +119,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
118
119
|
// UUID validation for entity keys
|
|
119
120
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
120
121
|
if (!uuidRegex.test(id)) {
|
|
121
|
-
|
|
122
|
+
prodLog.warn(`[Storage] Unknown key format: ${id} - treating as system resource`);
|
|
122
123
|
return {
|
|
123
124
|
original: id,
|
|
124
125
|
isEntity: false,
|
|
@@ -472,7 +473,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
472
473
|
// Load metadata
|
|
473
474
|
const metadata = await this.getNounMetadata(id);
|
|
474
475
|
if (!metadata) {
|
|
475
|
-
|
|
476
|
+
prodLog.warn(`[Storage] Noun ${id} has vector but no metadata - this should not happen in v4.0.0`);
|
|
476
477
|
return null;
|
|
477
478
|
}
|
|
478
479
|
// Combine into HNSWNounWithMetadata - v4.8.0: Extract standard fields to top-level
|
|
@@ -541,7 +542,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
541
542
|
}
|
|
542
543
|
catch (error) {
|
|
543
544
|
// Ignore if metadata file doesn't exist
|
|
544
|
-
|
|
545
|
+
prodLog.debug(`No metadata file to delete for noun ${id}`);
|
|
545
546
|
}
|
|
546
547
|
}
|
|
547
548
|
/**
|
|
@@ -572,7 +573,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
572
573
|
// Load metadata
|
|
573
574
|
const metadata = await this.getVerbMetadata(id);
|
|
574
575
|
if (!metadata) {
|
|
575
|
-
|
|
576
|
+
prodLog.warn(`[Storage] Verb ${id} has vector but no metadata - this should not happen in v4.0.0`);
|
|
576
577
|
return null;
|
|
577
578
|
}
|
|
578
579
|
// Combine into HNSWVerbWithMetadata - v4.8.0: Extract standard fields to top-level
|
|
@@ -650,7 +651,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
650
651
|
};
|
|
651
652
|
}
|
|
652
653
|
catch (error) {
|
|
653
|
-
|
|
654
|
+
prodLog.error(`Failed to convert HNSWVerb to GraphVerb for ${hnswVerb.id}:`, error);
|
|
654
655
|
return null;
|
|
655
656
|
}
|
|
656
657
|
}
|
|
@@ -778,7 +779,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
778
779
|
}
|
|
779
780
|
catch (countError) {
|
|
780
781
|
// Ignore errors from count method, it's optional
|
|
781
|
-
|
|
782
|
+
prodLog.warn('Error getting noun count:', countError);
|
|
782
783
|
}
|
|
783
784
|
// Check if the adapter has a paginated method for getting nouns
|
|
784
785
|
if (typeof this.getNounsWithPagination === 'function') {
|
|
@@ -799,7 +800,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
799
800
|
// If adapter forgets to return totalCount, log warning and use pre-calculated count
|
|
800
801
|
let finalTotalCount = result.totalCount || totalCount;
|
|
801
802
|
if (result.totalCount === undefined && this.totalNounCount > 0) {
|
|
802
|
-
|
|
803
|
+
prodLog.warn(`⚠️ Storage adapter missing totalCount in getNounsWithPagination result! ` +
|
|
803
804
|
`Using pre-calculated count (${this.totalNounCount}) as fallback. ` +
|
|
804
805
|
`Please ensure your storage adapter returns totalCount: this.totalNounCount`);
|
|
805
806
|
finalTotalCount = this.totalNounCount;
|
|
@@ -812,7 +813,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
812
813
|
};
|
|
813
814
|
}
|
|
814
815
|
// Storage adapter does not support pagination
|
|
815
|
-
|
|
816
|
+
prodLog.error('Storage adapter does not support pagination. The deprecated getAllNouns_internal() method has been removed. Please implement getNounsWithPagination() in your storage adapter.');
|
|
816
817
|
return {
|
|
817
818
|
items: [],
|
|
818
819
|
totalCount: 0,
|
|
@@ -820,7 +821,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
820
821
|
};
|
|
821
822
|
}
|
|
822
823
|
catch (error) {
|
|
823
|
-
|
|
824
|
+
prodLog.error('Error getting nouns with pagination:', error);
|
|
824
825
|
return {
|
|
825
826
|
items: [],
|
|
826
827
|
totalCount: 0,
|
|
@@ -1158,7 +1159,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1158
1159
|
}
|
|
1159
1160
|
catch (countError) {
|
|
1160
1161
|
// Ignore errors from count method, it's optional
|
|
1161
|
-
|
|
1162
|
+
prodLog.warn('Error getting verb count:', countError);
|
|
1162
1163
|
}
|
|
1163
1164
|
// Check if the adapter has a paginated method for getting verbs
|
|
1164
1165
|
if (typeof this.getVerbsWithPagination === 'function') {
|
|
@@ -1180,7 +1181,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1180
1181
|
// If adapter forgets to return totalCount, log warning and use pre-calculated count
|
|
1181
1182
|
let finalTotalCount = result.totalCount || totalCount;
|
|
1182
1183
|
if (result.totalCount === undefined && this.totalVerbCount > 0) {
|
|
1183
|
-
|
|
1184
|
+
prodLog.warn(`⚠️ Storage adapter missing totalCount in getVerbsWithPagination result! ` +
|
|
1184
1185
|
`Using pre-calculated count (${this.totalVerbCount}) as fallback. ` +
|
|
1185
1186
|
`Please ensure your storage adapter returns totalCount: this.totalVerbCount`);
|
|
1186
1187
|
finalTotalCount = this.totalVerbCount;
|
|
@@ -1194,7 +1195,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1194
1195
|
}
|
|
1195
1196
|
// UNIVERSAL FALLBACK: Iterate through verb types with early termination (billion-scale safe)
|
|
1196
1197
|
// This approach works for ALL storage adapters without requiring adapter-specific pagination
|
|
1197
|
-
|
|
1198
|
+
prodLog.warn('Using universal type-iteration strategy for getVerbs(). ' +
|
|
1198
1199
|
'This works for all adapters but may be slower than native pagination. ' +
|
|
1199
1200
|
'For optimal performance at scale, storage adapters can implement getVerbsWithPagination().');
|
|
1200
1201
|
const collectedVerbs = [];
|
|
@@ -1273,7 +1274,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1273
1274
|
};
|
|
1274
1275
|
}
|
|
1275
1276
|
catch (error) {
|
|
1276
|
-
|
|
1277
|
+
prodLog.error('Error getting verbs with pagination:', error);
|
|
1277
1278
|
return {
|
|
1278
1279
|
items: [],
|
|
1279
1280
|
totalCount: 0,
|
|
@@ -1294,22 +1295,45 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1294
1295
|
}
|
|
1295
1296
|
catch (error) {
|
|
1296
1297
|
// Ignore if metadata file doesn't exist
|
|
1297
|
-
|
|
1298
|
+
prodLog.debug(`No metadata file to delete for verb ${id}`);
|
|
1298
1299
|
}
|
|
1299
1300
|
}
|
|
1300
1301
|
/**
|
|
1301
|
-
* Get graph index (lazy initialization)
|
|
1302
|
+
* Get graph index (lazy initialization with concurrent access protection)
|
|
1303
|
+
* v5.7.1: Fixed race condition where concurrent calls could trigger multiple rebuilds
|
|
1302
1304
|
*/
|
|
1303
1305
|
async getGraphIndex() {
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
this.graphIndex
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1306
|
+
// If already initialized, return immediately
|
|
1307
|
+
if (this.graphIndex) {
|
|
1308
|
+
return this.graphIndex;
|
|
1309
|
+
}
|
|
1310
|
+
// If initialization in progress, wait for it
|
|
1311
|
+
if (this.graphIndexPromise) {
|
|
1312
|
+
return this.graphIndexPromise;
|
|
1313
|
+
}
|
|
1314
|
+
// Start initialization (only first caller reaches here)
|
|
1315
|
+
this.graphIndexPromise = this._initializeGraphIndex();
|
|
1316
|
+
try {
|
|
1317
|
+
const index = await this.graphIndexPromise;
|
|
1318
|
+
return index;
|
|
1319
|
+
}
|
|
1320
|
+
finally {
|
|
1321
|
+
// Clear promise after completion (success or failure)
|
|
1322
|
+
this.graphIndexPromise = undefined;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Internal method to initialize graph index (called once by getGraphIndex)
|
|
1327
|
+
* @private
|
|
1328
|
+
*/
|
|
1329
|
+
async _initializeGraphIndex() {
|
|
1330
|
+
prodLog.info('Initializing GraphAdjacencyIndex...');
|
|
1331
|
+
this.graphIndex = new GraphAdjacencyIndex(this);
|
|
1332
|
+
// Check if we need to rebuild from existing data
|
|
1333
|
+
const sampleVerbs = await this.getVerbs({ pagination: { limit: 1 } });
|
|
1334
|
+
if (sampleVerbs.items.length > 0) {
|
|
1335
|
+
prodLog.info('Found existing verbs, rebuilding graph index...');
|
|
1336
|
+
await this.graphIndex.rebuild();
|
|
1313
1337
|
}
|
|
1314
1338
|
return this.graphIndex;
|
|
1315
1339
|
}
|
|
@@ -1592,7 +1616,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1592
1616
|
* Ensures verbCountsByType is always accurate for reliable pagination
|
|
1593
1617
|
*/
|
|
1594
1618
|
async rebuildTypeCounts() {
|
|
1595
|
-
|
|
1619
|
+
prodLog.info('[BaseStorage] Rebuilding type counts from storage...');
|
|
1596
1620
|
// Rebuild verb counts by checking each type directory
|
|
1597
1621
|
for (let i = 0; i < VERB_TYPE_COUNT; i++) {
|
|
1598
1622
|
const type = TypeUtils.getVerbFromIndex(i);
|
|
@@ -1623,7 +1647,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1623
1647
|
await this.saveTypeStatistics();
|
|
1624
1648
|
const totalVerbs = this.verbCountsByType.reduce((sum, count) => sum + count, 0);
|
|
1625
1649
|
const totalNouns = this.nounCountsByType.reduce((sum, count) => sum + count, 0);
|
|
1626
|
-
|
|
1650
|
+
prodLog.info(`[BaseStorage] Rebuilt counts: ${totalNouns} nouns, ${totalVerbs} verbs`);
|
|
1627
1651
|
}
|
|
1628
1652
|
/**
|
|
1629
1653
|
* Get noun type from cache or metadata
|
|
@@ -1637,7 +1661,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1637
1661
|
}
|
|
1638
1662
|
// Default to 'thing' if unknown
|
|
1639
1663
|
// This should only happen if saveNoun_internal is called before saveNounMetadata
|
|
1640
|
-
|
|
1664
|
+
prodLog.warn(`[BaseStorage] Unknown noun type for ${noun.id}, defaulting to 'thing'`);
|
|
1641
1665
|
return 'thing';
|
|
1642
1666
|
}
|
|
1643
1667
|
/**
|
|
@@ -1654,7 +1678,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1654
1678
|
return verb.type;
|
|
1655
1679
|
}
|
|
1656
1680
|
// This should never happen with current data
|
|
1657
|
-
|
|
1681
|
+
prodLog.warn(`[BaseStorage] Verb missing type field for ${verb.id}, defaulting to 'relatedTo'`);
|
|
1658
1682
|
return 'relatedTo';
|
|
1659
1683
|
}
|
|
1660
1684
|
// ============================================================================
|
|
@@ -1729,7 +1753,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1729
1753
|
}
|
|
1730
1754
|
}
|
|
1731
1755
|
catch (error) {
|
|
1732
|
-
|
|
1756
|
+
prodLog.warn(`[BaseStorage] Failed to load noun from ${path}:`, error);
|
|
1733
1757
|
}
|
|
1734
1758
|
}
|
|
1735
1759
|
return nouns;
|
|
@@ -1784,6 +1808,25 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1784
1808
|
this.verbTypeCache.set(verb.id, type);
|
|
1785
1809
|
// COW-aware write (v5.0.1): Use COW helper for branch isolation
|
|
1786
1810
|
await this.writeObjectToBranch(path, verb);
|
|
1811
|
+
// v5.7.0: Update GraphAdjacencyIndex incrementally for billion-scale optimization
|
|
1812
|
+
// CRITICAL: Only update if index already initialized to avoid circular dependency
|
|
1813
|
+
// Index is lazy-loaded on first query, then maintained incrementally
|
|
1814
|
+
if (this.graphIndex && this.graphIndex.isInitialized) {
|
|
1815
|
+
// Fast incremental update - no rebuild needed
|
|
1816
|
+
await this.graphIndex.addVerb({
|
|
1817
|
+
id: verb.id,
|
|
1818
|
+
sourceId: verb.sourceId,
|
|
1819
|
+
targetId: verb.targetId,
|
|
1820
|
+
vector: verb.vector,
|
|
1821
|
+
source: verb.sourceId,
|
|
1822
|
+
target: verb.targetId,
|
|
1823
|
+
verb: verb.verb,
|
|
1824
|
+
type: verb.verb,
|
|
1825
|
+
createdAt: { seconds: Math.floor(Date.now() / 1000), nanoseconds: 0 },
|
|
1826
|
+
updatedAt: { seconds: Math.floor(Date.now() / 1000), nanoseconds: 0 },
|
|
1827
|
+
createdBy: { augmentation: 'storage', version: '5.7.0' }
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1787
1830
|
// Periodically save statistics
|
|
1788
1831
|
if (this.verbCountsByType[typeIndex] % 100 === 0) {
|
|
1789
1832
|
await this.saveTypeStatistics();
|
|
@@ -1825,109 +1868,71 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1825
1868
|
* v5.4.0: Fixed to directly list verb files instead of directories
|
|
1826
1869
|
*/
|
|
1827
1870
|
async getVerbsBySource_internal(sourceId) {
|
|
1828
|
-
// v5.
|
|
1829
|
-
//
|
|
1871
|
+
// v5.7.0: BILLION-SCALE OPTIMIZATION - Use GraphAdjacencyIndex for O(log n) lookup
|
|
1872
|
+
// Previous: O(total_verbs) - scanned all 127 verb types
|
|
1873
|
+
// Now: O(log n) LSM-tree lookup + O(verbs_for_source) load
|
|
1830
1874
|
await this.ensureInitialized();
|
|
1875
|
+
const startTime = performance.now();
|
|
1876
|
+
// Get GraphAdjacencyIndex (lazy-initialized)
|
|
1877
|
+
const graphIndex = await this.getGraphIndex();
|
|
1878
|
+
// O(log n) lookup with bloom filter optimization
|
|
1879
|
+
const verbIds = await graphIndex.getVerbIdsBySource(sourceId);
|
|
1880
|
+
// Load each verb by ID (uses existing optimized getVerb())
|
|
1831
1881
|
const results = [];
|
|
1832
|
-
|
|
1833
|
-
for (let i = 0; i < VERB_TYPE_COUNT; i++) {
|
|
1834
|
-
const type = TypeUtils.getVerbFromIndex(i);
|
|
1835
|
-
const typeDir = `entities/verbs/${type}/vectors`;
|
|
1882
|
+
for (const verbId of verbIds) {
|
|
1836
1883
|
try {
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
for (const verbPath of verbFiles) {
|
|
1841
|
-
// Skip if not a .json file
|
|
1842
|
-
if (!verbPath.endsWith('.json'))
|
|
1843
|
-
continue;
|
|
1844
|
-
try {
|
|
1845
|
-
const verb = await this.readWithInheritance(verbPath);
|
|
1846
|
-
if (verb && verb.sourceId === sourceId) {
|
|
1847
|
-
// v5.4.0: Use proper path helper instead of string replacement
|
|
1848
|
-
const metadataPath = getVerbMetadataPath(type, verb.id);
|
|
1849
|
-
const metadata = await this.readWithInheritance(metadataPath);
|
|
1850
|
-
// v5.4.0: Extract standard fields from metadata to top-level (like nouns)
|
|
1851
|
-
results.push({
|
|
1852
|
-
...verb,
|
|
1853
|
-
weight: metadata?.weight,
|
|
1854
|
-
confidence: metadata?.confidence,
|
|
1855
|
-
createdAt: metadata?.createdAt
|
|
1856
|
-
? (typeof metadata.createdAt === 'number' ? metadata.createdAt : metadata.createdAt.seconds * 1000)
|
|
1857
|
-
: Date.now(),
|
|
1858
|
-
updatedAt: metadata?.updatedAt
|
|
1859
|
-
? (typeof metadata.updatedAt === 'number' ? metadata.updatedAt : metadata.updatedAt.seconds * 1000)
|
|
1860
|
-
: Date.now(),
|
|
1861
|
-
service: metadata?.service,
|
|
1862
|
-
createdBy: metadata?.createdBy,
|
|
1863
|
-
metadata: metadata || {}
|
|
1864
|
-
});
|
|
1865
|
-
}
|
|
1866
|
-
}
|
|
1867
|
-
catch (error) {
|
|
1868
|
-
// Skip verbs that fail to load
|
|
1869
|
-
}
|
|
1884
|
+
const verb = await this.getVerb(verbId);
|
|
1885
|
+
if (verb) {
|
|
1886
|
+
results.push(verb);
|
|
1870
1887
|
}
|
|
1871
1888
|
}
|
|
1872
1889
|
catch (error) {
|
|
1873
|
-
// Skip
|
|
1890
|
+
// Skip verbs that fail to load (handles deleted/corrupted verbs gracefully)
|
|
1874
1891
|
}
|
|
1875
1892
|
}
|
|
1893
|
+
const elapsed = performance.now() - startTime;
|
|
1894
|
+
// Performance monitoring - should be 100-10,000x faster than old O(n) scan
|
|
1895
|
+
if (elapsed > 50.0) {
|
|
1896
|
+
prodLog.warn(`getVerbsBySource_internal: Slow query for ${sourceId} ` +
|
|
1897
|
+
`(${verbIds.length} verbs, ${elapsed.toFixed(2)}ms). ` +
|
|
1898
|
+
`Expected <50ms with index optimization.`);
|
|
1899
|
+
}
|
|
1876
1900
|
return results;
|
|
1877
1901
|
}
|
|
1878
1902
|
/**
|
|
1879
1903
|
* Get verbs by target (COW-aware implementation)
|
|
1880
|
-
* v5.
|
|
1904
|
+
* v5.7.0: BILLION-SCALE OPTIMIZATION - Use GraphAdjacencyIndex for O(log n) lookup
|
|
1881
1905
|
*/
|
|
1882
1906
|
async getVerbsByTarget_internal(targetId) {
|
|
1883
|
-
// v5.
|
|
1884
|
-
//
|
|
1907
|
+
// v5.7.0: BILLION-SCALE OPTIMIZATION - Use GraphAdjacencyIndex for O(log n) lookup
|
|
1908
|
+
// Previous: O(total_verbs) - scanned all 127 verb types
|
|
1909
|
+
// Now: O(log n) LSM-tree lookup + O(verbs_for_target) load
|
|
1885
1910
|
await this.ensureInitialized();
|
|
1911
|
+
const startTime = performance.now();
|
|
1912
|
+
// Get GraphAdjacencyIndex (lazy-initialized)
|
|
1913
|
+
const graphIndex = await this.getGraphIndex();
|
|
1914
|
+
// O(log n) lookup with bloom filter optimization
|
|
1915
|
+
const verbIds = await graphIndex.getVerbIdsByTarget(targetId);
|
|
1916
|
+
// Load each verb by ID (uses existing optimized getVerb())
|
|
1886
1917
|
const results = [];
|
|
1887
|
-
|
|
1888
|
-
for (let i = 0; i < VERB_TYPE_COUNT; i++) {
|
|
1889
|
-
const type = TypeUtils.getVerbFromIndex(i);
|
|
1890
|
-
const typeDir = `entities/verbs/${type}/vectors`;
|
|
1918
|
+
for (const verbId of verbIds) {
|
|
1891
1919
|
try {
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
for (const verbPath of verbFiles) {
|
|
1896
|
-
// Skip if not a .json file
|
|
1897
|
-
if (!verbPath.endsWith('.json'))
|
|
1898
|
-
continue;
|
|
1899
|
-
try {
|
|
1900
|
-
const verb = await this.readWithInheritance(verbPath);
|
|
1901
|
-
if (verb && verb.targetId === targetId) {
|
|
1902
|
-
// v5.4.0: Use proper path helper instead of string replacement
|
|
1903
|
-
const metadataPath = getVerbMetadataPath(type, verb.id);
|
|
1904
|
-
const metadata = await this.readWithInheritance(metadataPath);
|
|
1905
|
-
// v5.4.0: Extract standard fields from metadata to top-level (like nouns)
|
|
1906
|
-
results.push({
|
|
1907
|
-
...verb,
|
|
1908
|
-
weight: metadata?.weight,
|
|
1909
|
-
confidence: metadata?.confidence,
|
|
1910
|
-
createdAt: metadata?.createdAt
|
|
1911
|
-
? (typeof metadata.createdAt === 'number' ? metadata.createdAt : metadata.createdAt.seconds * 1000)
|
|
1912
|
-
: Date.now(),
|
|
1913
|
-
updatedAt: metadata?.updatedAt
|
|
1914
|
-
? (typeof metadata.updatedAt === 'number' ? metadata.updatedAt : metadata.updatedAt.seconds * 1000)
|
|
1915
|
-
: Date.now(),
|
|
1916
|
-
service: metadata?.service,
|
|
1917
|
-
createdBy: metadata?.createdBy,
|
|
1918
|
-
metadata: metadata || {}
|
|
1919
|
-
});
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
catch (error) {
|
|
1923
|
-
// Skip verbs that fail to load
|
|
1924
|
-
}
|
|
1920
|
+
const verb = await this.getVerb(verbId);
|
|
1921
|
+
if (verb) {
|
|
1922
|
+
results.push(verb);
|
|
1925
1923
|
}
|
|
1926
1924
|
}
|
|
1927
1925
|
catch (error) {
|
|
1928
|
-
// Skip
|
|
1926
|
+
// Skip verbs that fail to load (handles deleted/corrupted verbs gracefully)
|
|
1929
1927
|
}
|
|
1930
1928
|
}
|
|
1929
|
+
const elapsed = performance.now() - startTime;
|
|
1930
|
+
// Performance monitoring - should be 100-10,000x faster than old O(n) scan
|
|
1931
|
+
if (elapsed > 50.0) {
|
|
1932
|
+
prodLog.warn(`getVerbsByTarget_internal: Slow query for ${targetId} ` +
|
|
1933
|
+
`(${verbIds.length} verbs, ${elapsed.toFixed(2)}ms). ` +
|
|
1934
|
+
`Expected <50ms with index optimization.`);
|
|
1935
|
+
}
|
|
1931
1936
|
return results;
|
|
1932
1937
|
}
|
|
1933
1938
|
/**
|
|
@@ -1980,7 +1985,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1980
1985
|
verbs.push(verbWithMetadata);
|
|
1981
1986
|
}
|
|
1982
1987
|
catch (error) {
|
|
1983
|
-
|
|
1988
|
+
prodLog.warn(`[BaseStorage] Failed to load verb from ${path}:`, error);
|
|
1984
1989
|
}
|
|
1985
1990
|
}
|
|
1986
1991
|
return verbs;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.7.0",
|
|
4
4
|
"description": "Universal Knowledge Protocol™ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. Stage 3 CANONICAL: 42 nouns × 127 verbs covering 96-97% of all human knowledge.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|