@soulcraft/brainy 5.7.9 → 5.7.11
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 +7 -0
- package/dist/hnsw/hnswIndex.js +3 -3
- package/dist/hnsw/typeAwareHNSWIndex.js +3 -3
- package/dist/storage/adapters/optimizedS3Search.js +2 -2
- package/dist/storage/baseStorage.d.ts +24 -0
- package/dist/storage/baseStorage.js +86 -38
- package/dist/utils/rebuildCounts.js +12 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [5.7.11](https://github.com/soulcraftlabs/brainy/compare/v5.7.10...v5.7.11) (2025-11-13)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### 🐛 Bug Fixes
|
|
9
|
+
|
|
10
|
+
* resolve critical 378x pagination infinite loop bug (v5.7.11) ([e86f765](https://github.com/soulcraftlabs/brainy/commit/e86f765f3d30be41707e2ef7d07bb5c92d4ca3da))
|
|
11
|
+
|
|
5
12
|
### [5.7.9](https://github.com/soulcraftlabs/brainy/compare/v5.7.8...v5.7.9) (2025-11-13)
|
|
6
13
|
|
|
7
14
|
- fix: implement exists: false and missing operators in MetadataIndexManager (b0f72ef)
|
package/dist/hnsw/hnswIndex.js
CHANGED
|
@@ -858,12 +858,12 @@ export class HNSWIndex {
|
|
|
858
858
|
// Cloud storage: Use pagination with native cloud APIs
|
|
859
859
|
prodLog.info(`HNSW: Using cloud pagination strategy (${storageType})`);
|
|
860
860
|
let hasMore = true;
|
|
861
|
-
let
|
|
861
|
+
let offset = 0; // v5.7.11: Use offset-based pagination instead of cursor (bug fix for infinite loop)
|
|
862
862
|
while (hasMore) {
|
|
863
863
|
// Fetch batch of nouns from storage (cast needed as method is not in base interface)
|
|
864
864
|
const result = await this.storage.getNounsWithPagination({
|
|
865
865
|
limit: batchSize,
|
|
866
|
-
cursor
|
|
866
|
+
offset // v5.7.11: Pass offset for proper pagination (previously passed cursor which was ignored)
|
|
867
867
|
});
|
|
868
868
|
// Set total count on first batch
|
|
869
869
|
if (totalCount === undefined && result.totalCount !== undefined) {
|
|
@@ -912,7 +912,7 @@ export class HNSWIndex {
|
|
|
912
912
|
}
|
|
913
913
|
// Check for more data
|
|
914
914
|
hasMore = result.hasMore;
|
|
915
|
-
|
|
915
|
+
offset += batchSize; // v5.7.11: Increment offset for next page
|
|
916
916
|
}
|
|
917
917
|
}
|
|
918
918
|
const cacheInfo = shouldPreload
|
|
@@ -342,14 +342,14 @@ export class TypeAwareHNSWIndex {
|
|
|
342
342
|
}
|
|
343
343
|
// Load ALL nouns ONCE and route to correct type indexes
|
|
344
344
|
// This is O(N) instead of O(42*N) from the previous parallel approach
|
|
345
|
-
let
|
|
345
|
+
let offset = 0; // v5.7.11: Use offset-based pagination instead of cursor (bug fix for infinite loop)
|
|
346
346
|
let hasMore = true;
|
|
347
347
|
let totalLoaded = 0;
|
|
348
348
|
const loadedByType = new Map();
|
|
349
349
|
while (hasMore) {
|
|
350
350
|
const result = await this.storage.getNounsWithPagination({
|
|
351
351
|
limit: batchSize,
|
|
352
|
-
cursor
|
|
352
|
+
offset // v5.7.11: Pass offset for proper pagination (previously passed cursor which was ignored)
|
|
353
353
|
});
|
|
354
354
|
// Route each noun to its type index
|
|
355
355
|
for (const nounData of result.items) {
|
|
@@ -407,7 +407,7 @@ export class TypeAwareHNSWIndex {
|
|
|
407
407
|
}
|
|
408
408
|
}
|
|
409
409
|
hasMore = result.hasMore;
|
|
410
|
-
|
|
410
|
+
offset += batchSize; // v5.7.11: Increment offset for next page
|
|
411
411
|
// Progress logging
|
|
412
412
|
if (totalLoaded % 1000 === 0) {
|
|
413
413
|
prodLog.info(`Progress: ${totalLoaded.toLocaleString()} entities loaded...`);
|
|
@@ -48,7 +48,7 @@ export class OptimizedS3Search {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
// Determine if there are more items
|
|
51
|
-
const hasMore = listResult.hasMore || nouns.length
|
|
51
|
+
const hasMore = listResult.hasMore || nouns.length > limit; // v5.7.11: Fixed >= to > (was causing infinite loop)
|
|
52
52
|
// Set next cursor
|
|
53
53
|
let nextCursor;
|
|
54
54
|
if (hasMore && nouns.length > 0) {
|
|
@@ -114,7 +114,7 @@ export class OptimizedS3Search {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
// Determine if there are more items
|
|
117
|
-
const hasMore = listResult.hasMore || verbs.length
|
|
117
|
+
const hasMore = listResult.hasMore || verbs.length > limit; // v5.7.11: Fixed >= to > (was causing infinite loop)
|
|
118
118
|
// Set next cursor
|
|
119
119
|
let nextCursor;
|
|
120
120
|
if (hasMore && verbs.length > 0) {
|
|
@@ -458,6 +458,30 @@ export declare abstract class BaseStorage extends BaseStorageAdapter {
|
|
|
458
458
|
* Verb type is a required field in HNSWVerb
|
|
459
459
|
*/
|
|
460
460
|
protected getVerbType(verb: HNSWVerb | GraphVerb): VerbType;
|
|
461
|
+
/**
|
|
462
|
+
* Deserialize HNSW connections from JSON storage format
|
|
463
|
+
*
|
|
464
|
+
* Converts plain object { "0": ["id1"], "1": ["id2"] }
|
|
465
|
+
* into Map<number, Set<string>>
|
|
466
|
+
*
|
|
467
|
+
* v5.7.10: Central helper to fix serialization bug across all code paths
|
|
468
|
+
* Root cause: JSON.stringify(Map) = {} (empty object), must reconstruct on read
|
|
469
|
+
*/
|
|
470
|
+
protected deserializeConnections(connections: any): Map<number, Set<string>>;
|
|
471
|
+
/**
|
|
472
|
+
* Deserialize HNSWNoun from JSON storage format
|
|
473
|
+
*
|
|
474
|
+
* v5.7.10: Ensures connections are properly reconstructed from Map → object → Map
|
|
475
|
+
* Fixes: "TypeError: noun.connections.entries is not a function"
|
|
476
|
+
*/
|
|
477
|
+
protected deserializeNoun(data: any): HNSWNoun;
|
|
478
|
+
/**
|
|
479
|
+
* Deserialize HNSWVerb from JSON storage format
|
|
480
|
+
*
|
|
481
|
+
* v5.7.10: Ensures connections are properly reconstructed from Map → object → Map
|
|
482
|
+
* Fixes same serialization bug for verbs
|
|
483
|
+
*/
|
|
484
|
+
protected deserializeVerb(data: any): HNSWVerb;
|
|
461
485
|
/**
|
|
462
486
|
* Save a noun to storage (type-first path)
|
|
463
487
|
*/
|
|
@@ -879,7 +879,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
879
879
|
*/
|
|
880
880
|
async getNounsWithPagination(options) {
|
|
881
881
|
await this.ensureInitialized();
|
|
882
|
-
const { limit, offset = 0, filter } = options;
|
|
882
|
+
const { limit, offset = 0, filter } = options; // cursor intentionally not extracted (not yet implemented)
|
|
883
883
|
const collectedNouns = [];
|
|
884
884
|
const targetCount = offset + limit; // Early termination target
|
|
885
885
|
// v5.5.0 BUG FIX: Only use optimization if counts are reliable
|
|
@@ -912,8 +912,11 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
912
912
|
if (!nounPath.endsWith('.json'))
|
|
913
913
|
continue;
|
|
914
914
|
try {
|
|
915
|
-
const
|
|
916
|
-
if (
|
|
915
|
+
const rawNoun = await this.readWithInheritance(nounPath);
|
|
916
|
+
if (rawNoun) {
|
|
917
|
+
// v5.7.10: Deserialize connections Map from JSON storage format
|
|
918
|
+
// Replaces v5.7.8 manual deserialization (removed 13 lines at 1156-1168)
|
|
919
|
+
const noun = this.deserializeNoun(rawNoun);
|
|
917
920
|
// Load metadata
|
|
918
921
|
const metadataPath = getNounMetadataPath(type, noun.id);
|
|
919
922
|
const metadata = await this.readWithInheritance(metadataPath);
|
|
@@ -925,24 +928,10 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
925
928
|
continue;
|
|
926
929
|
}
|
|
927
930
|
}
|
|
928
|
-
// v5.7.8: Convert connections from plain object to Map (JSON deserialization fix)
|
|
929
|
-
// When loaded from JSON, Map becomes plain object - must reconstruct
|
|
930
|
-
const connections = new Map();
|
|
931
|
-
if (noun.connections && typeof noun.connections === 'object') {
|
|
932
|
-
for (const [levelStr, ids] of Object.entries(noun.connections)) {
|
|
933
|
-
if (Array.isArray(ids)) {
|
|
934
|
-
connections.set(parseInt(levelStr, 10), new Set(ids));
|
|
935
|
-
}
|
|
936
|
-
else if (ids && typeof ids === 'object') {
|
|
937
|
-
// Handle if it's already an array-like or Set-like object
|
|
938
|
-
connections.set(parseInt(levelStr, 10), new Set(Object.values(ids)));
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
931
|
// Combine noun + metadata (v5.4.0: Extract standard fields to top-level)
|
|
943
932
|
collectedNouns.push({
|
|
944
933
|
...noun,
|
|
945
|
-
|
|
934
|
+
// v5.7.10: connections already deserialized by deserializeNoun()
|
|
946
935
|
type: metadata.noun || type, // Required: Extract type from metadata
|
|
947
936
|
confidence: metadata.confidence,
|
|
948
937
|
weight: metadata.weight,
|
|
@@ -971,7 +960,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
971
960
|
}
|
|
972
961
|
// Apply pagination (v5.5.0: Efficient slicing after early termination)
|
|
973
962
|
const paginatedNouns = collectedNouns.slice(offset, offset + limit);
|
|
974
|
-
const hasMore = collectedNouns.length
|
|
963
|
+
const hasMore = collectedNouns.length > targetCount; // v5.7.11: Fixed >= to > (was causing infinite loop)
|
|
975
964
|
return {
|
|
976
965
|
items: paginatedNouns,
|
|
977
966
|
totalCount: collectedNouns.length, // Accurate count of collected results
|
|
@@ -998,7 +987,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
998
987
|
*/
|
|
999
988
|
async getVerbsWithPagination(options) {
|
|
1000
989
|
await this.ensureInitialized();
|
|
1001
|
-
const { limit, offset = 0, filter } = options;
|
|
990
|
+
const { limit, offset = 0, filter } = options; // cursor intentionally not extracted (not yet implemented)
|
|
1002
991
|
const collectedVerbs = [];
|
|
1003
992
|
const targetCount = offset + limit; // Early termination target
|
|
1004
993
|
// v5.5.0 BUG FIX: Only use optimization if counts are reliable
|
|
@@ -1057,7 +1046,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1057
1046
|
}
|
|
1058
1047
|
// Apply pagination (v5.5.0: Efficient slicing after early termination)
|
|
1059
1048
|
const paginatedVerbs = collectedVerbs.slice(offset, offset + limit);
|
|
1060
|
-
const hasMore = collectedVerbs.length
|
|
1049
|
+
const hasMore = collectedVerbs.length > targetCount; // v5.7.11: Fixed >= to > (was causing infinite loop)
|
|
1061
1050
|
return {
|
|
1062
1051
|
items: paginatedVerbs,
|
|
1063
1052
|
totalCount: collectedVerbs.length, // Accurate count of collected results
|
|
@@ -1312,7 +1301,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1312
1301
|
}
|
|
1313
1302
|
// Apply pagination (slice for offset)
|
|
1314
1303
|
const paginatedVerbs = collectedVerbs.slice(offset, offset + limit);
|
|
1315
|
-
const hasMore = collectedVerbs.length
|
|
1304
|
+
const hasMore = collectedVerbs.length > targetCount; // v5.7.11: Fixed >= to > (was causing infinite loop)
|
|
1316
1305
|
return {
|
|
1317
1306
|
items: paginatedVerbs,
|
|
1318
1307
|
totalCount: collectedVerbs.length, // Accurate count of filtered results
|
|
@@ -1731,6 +1720,64 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1731
1720
|
return 'relatedTo';
|
|
1732
1721
|
}
|
|
1733
1722
|
// ============================================================================
|
|
1723
|
+
// DESERIALIZATION HELPERS (v5.7.10)
|
|
1724
|
+
// Centralized Map/Set reconstruction from JSON storage format
|
|
1725
|
+
// ============================================================================
|
|
1726
|
+
/**
|
|
1727
|
+
* Deserialize HNSW connections from JSON storage format
|
|
1728
|
+
*
|
|
1729
|
+
* Converts plain object { "0": ["id1"], "1": ["id2"] }
|
|
1730
|
+
* into Map<number, Set<string>>
|
|
1731
|
+
*
|
|
1732
|
+
* v5.7.10: Central helper to fix serialization bug across all code paths
|
|
1733
|
+
* Root cause: JSON.stringify(Map) = {} (empty object), must reconstruct on read
|
|
1734
|
+
*/
|
|
1735
|
+
deserializeConnections(connections) {
|
|
1736
|
+
const result = new Map();
|
|
1737
|
+
if (!connections || typeof connections !== 'object') {
|
|
1738
|
+
return result;
|
|
1739
|
+
}
|
|
1740
|
+
// Already a Map (in-memory, not from JSON)
|
|
1741
|
+
if (connections instanceof Map) {
|
|
1742
|
+
return connections;
|
|
1743
|
+
}
|
|
1744
|
+
// Deserialize from plain object
|
|
1745
|
+
for (const [levelStr, ids] of Object.entries(connections)) {
|
|
1746
|
+
if (Array.isArray(ids)) {
|
|
1747
|
+
result.set(parseInt(levelStr, 10), new Set(ids));
|
|
1748
|
+
}
|
|
1749
|
+
else if (ids && typeof ids === 'object') {
|
|
1750
|
+
// Handle Set-like or array-like objects
|
|
1751
|
+
result.set(parseInt(levelStr, 10), new Set(Object.values(ids)));
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
return result;
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Deserialize HNSWNoun from JSON storage format
|
|
1758
|
+
*
|
|
1759
|
+
* v5.7.10: Ensures connections are properly reconstructed from Map → object → Map
|
|
1760
|
+
* Fixes: "TypeError: noun.connections.entries is not a function"
|
|
1761
|
+
*/
|
|
1762
|
+
deserializeNoun(data) {
|
|
1763
|
+
return {
|
|
1764
|
+
...data,
|
|
1765
|
+
connections: this.deserializeConnections(data.connections)
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Deserialize HNSWVerb from JSON storage format
|
|
1770
|
+
*
|
|
1771
|
+
* v5.7.10: Ensures connections are properly reconstructed from Map → object → Map
|
|
1772
|
+
* Fixes same serialization bug for verbs
|
|
1773
|
+
*/
|
|
1774
|
+
deserializeVerb(data) {
|
|
1775
|
+
return {
|
|
1776
|
+
...data,
|
|
1777
|
+
connections: this.deserializeConnections(data.connections)
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
// ============================================================================
|
|
1734
1781
|
// ABSTRACT METHOD IMPLEMENTATIONS (v5.4.0)
|
|
1735
1782
|
// Converted from abstract to concrete - all adapters now have built-in type-aware
|
|
1736
1783
|
// ============================================================================
|
|
@@ -1760,7 +1807,9 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1760
1807
|
if (cachedType) {
|
|
1761
1808
|
const path = getNounVectorPath(cachedType, id);
|
|
1762
1809
|
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
1763
|
-
|
|
1810
|
+
const data = await this.readWithInheritance(path);
|
|
1811
|
+
// v5.7.10: Deserialize connections Map from JSON storage format
|
|
1812
|
+
return data ? this.deserializeNoun(data) : null;
|
|
1764
1813
|
}
|
|
1765
1814
|
// Need to search across all types (expensive, but cached after first access)
|
|
1766
1815
|
for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
|
|
@@ -1772,7 +1821,8 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1772
1821
|
if (noun) {
|
|
1773
1822
|
// Cache the type for next time
|
|
1774
1823
|
this.nounTypeCache.set(id, type);
|
|
1775
|
-
|
|
1824
|
+
// v5.7.10: Deserialize connections Map from JSON storage format
|
|
1825
|
+
return this.deserializeNoun(noun);
|
|
1776
1826
|
}
|
|
1777
1827
|
}
|
|
1778
1828
|
catch (error) {
|
|
@@ -1796,7 +1846,8 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1796
1846
|
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
1797
1847
|
const noun = await this.readWithInheritance(path);
|
|
1798
1848
|
if (noun) {
|
|
1799
|
-
|
|
1849
|
+
// v5.7.10: Deserialize connections Map from JSON storage format
|
|
1850
|
+
nouns.push(this.deserializeNoun(noun));
|
|
1800
1851
|
// Cache the type
|
|
1801
1852
|
this.nounTypeCache.set(noun.id, type);
|
|
1802
1853
|
}
|
|
@@ -1891,7 +1942,8 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1891
1942
|
const path = getVerbVectorPath(cachedType, id);
|
|
1892
1943
|
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
1893
1944
|
const verb = await this.readWithInheritance(path);
|
|
1894
|
-
|
|
1945
|
+
// v5.7.10: Deserialize connections Map from JSON storage format
|
|
1946
|
+
return verb ? this.deserializeVerb(verb) : null;
|
|
1895
1947
|
}
|
|
1896
1948
|
// Search across all types (only on first access)
|
|
1897
1949
|
for (let i = 0; i < VERB_TYPE_COUNT; i++) {
|
|
@@ -1903,7 +1955,8 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
1903
1955
|
if (verb) {
|
|
1904
1956
|
// Cache the type for next time (read from verb.verb field)
|
|
1905
1957
|
this.verbTypeCache.set(id, verb.verb);
|
|
1906
|
-
|
|
1958
|
+
// v5.7.10: Deserialize connections Map from JSON storage format
|
|
1959
|
+
return this.deserializeVerb(verb);
|
|
1907
1960
|
}
|
|
1908
1961
|
}
|
|
1909
1962
|
catch (error) {
|
|
@@ -2040,29 +2093,24 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
2040
2093
|
for (const path of paths) {
|
|
2041
2094
|
try {
|
|
2042
2095
|
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
2043
|
-
const
|
|
2044
|
-
if (!
|
|
2096
|
+
const rawVerb = await this.readWithInheritance(path);
|
|
2097
|
+
if (!rawVerb)
|
|
2045
2098
|
continue;
|
|
2099
|
+
// v5.7.10: Deserialize connections Map from JSON storage format
|
|
2100
|
+
// Replaces v5.7.8 manual deserialization (lines 2599-2605)
|
|
2101
|
+
const hnswVerb = this.deserializeVerb(rawVerb);
|
|
2046
2102
|
// Cache type from HNSWVerb for future O(1) retrievals
|
|
2047
2103
|
this.verbTypeCache.set(hnswVerb.id, hnswVerb.verb);
|
|
2048
2104
|
// Load metadata separately (optional in v4.0.0!)
|
|
2049
2105
|
// FIX: Don't skip verbs without metadata - metadata is optional!
|
|
2050
2106
|
const metadata = await this.getVerbMetadata(hnswVerb.id);
|
|
2051
|
-
// Create HNSWVerbWithMetadata (verbs don't have level field)
|
|
2052
|
-
// Convert connections from plain object to Map<number, Set<string>>
|
|
2053
|
-
const connectionsMap = new Map();
|
|
2054
|
-
if (hnswVerb.connections && typeof hnswVerb.connections === 'object') {
|
|
2055
|
-
for (const [level, ids] of Object.entries(hnswVerb.connections)) {
|
|
2056
|
-
connectionsMap.set(Number(level), new Set(ids));
|
|
2057
|
-
}
|
|
2058
|
-
}
|
|
2059
2107
|
// v4.8.0: Extract standard fields from metadata to top-level
|
|
2060
2108
|
const metadataObj = (metadata || {});
|
|
2061
2109
|
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
2062
2110
|
const verbWithMetadata = {
|
|
2063
2111
|
id: hnswVerb.id,
|
|
2064
2112
|
vector: [...hnswVerb.vector],
|
|
2065
|
-
connections:
|
|
2113
|
+
connections: hnswVerb.connections, // v5.7.10: Already deserialized
|
|
2066
2114
|
verb: hnswVerb.verb,
|
|
2067
2115
|
sourceId: hnswVerb.sourceId,
|
|
2068
2116
|
targetId: hnswVerb.targetId,
|
|
@@ -39,9 +39,12 @@ export async function rebuildCounts(storage) {
|
|
|
39
39
|
throw new Error('Storage adapter does not support getNounsWithPagination');
|
|
40
40
|
}
|
|
41
41
|
let hasMore = true;
|
|
42
|
-
let cursor
|
|
42
|
+
let offset = 0; // v5.7.11: Use offset-based pagination instead of cursor (bug fix for infinite loop)
|
|
43
43
|
while (hasMore) {
|
|
44
|
-
const result = await storageWithPagination.getNounsWithPagination({
|
|
44
|
+
const result = await storageWithPagination.getNounsWithPagination({
|
|
45
|
+
limit: 100,
|
|
46
|
+
offset // v5.7.11: Pass offset for proper pagination (previously passed cursor which was ignored)
|
|
47
|
+
});
|
|
45
48
|
for (const noun of result.items) {
|
|
46
49
|
const metadata = await storage.getNounMetadata(noun.id);
|
|
47
50
|
if (metadata?.noun) {
|
|
@@ -51,7 +54,7 @@ export async function rebuildCounts(storage) {
|
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
hasMore = result.hasMore;
|
|
54
|
-
|
|
57
|
+
offset += 100; // v5.7.11: Increment offset for next page
|
|
55
58
|
}
|
|
56
59
|
console.log(` Found ${totalNouns} entities across ${entityCounts.size} types`);
|
|
57
60
|
// Scan all verbs using pagination
|
|
@@ -60,9 +63,12 @@ export async function rebuildCounts(storage) {
|
|
|
60
63
|
throw new Error('Storage adapter does not support getVerbsWithPagination');
|
|
61
64
|
}
|
|
62
65
|
hasMore = true;
|
|
63
|
-
|
|
66
|
+
offset = 0; // v5.7.11: Reset offset for verbs pagination
|
|
64
67
|
while (hasMore) {
|
|
65
|
-
const result = await storageWithPagination.getVerbsWithPagination({
|
|
68
|
+
const result = await storageWithPagination.getVerbsWithPagination({
|
|
69
|
+
limit: 100,
|
|
70
|
+
offset // v5.7.11: Pass offset for proper pagination (previously passed cursor which was ignored)
|
|
71
|
+
});
|
|
66
72
|
for (const verb of result.items) {
|
|
67
73
|
if (verb.verb) {
|
|
68
74
|
const verbType = verb.verb;
|
|
@@ -71,7 +77,7 @@ export async function rebuildCounts(storage) {
|
|
|
71
77
|
}
|
|
72
78
|
}
|
|
73
79
|
hasMore = result.hasMore;
|
|
74
|
-
|
|
80
|
+
offset += 100; // v5.7.11: Increment offset for next page
|
|
75
81
|
}
|
|
76
82
|
console.log(` Found ${totalVerbs} relationships across ${verbCounts.size} types`);
|
|
77
83
|
// Update storage adapter's in-memory counts FIRST
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "5.7.
|
|
3
|
+
"version": "5.7.11",
|
|
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",
|