@soulcraft/brainy 3.37.6 → 3.37.7

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.
@@ -146,6 +146,12 @@ export class GcsStorage extends BaseStorage {
146
146
  });
147
147
  // Initialize counts from storage
148
148
  await this.initializeCounts();
149
+ // CRITICAL FIX (v3.37.7): Clear any stale cache entries from previous runs
150
+ // This prevents cache poisoning from causing silent failures on container restart
151
+ prodLog.info('🧹 Clearing cache from previous run to prevent cache poisoning');
152
+ this.nounCacheManager.clear();
153
+ this.verbCacheManager.clear();
154
+ prodLog.info('✅ Cache cleared - starting fresh');
149
155
  this.isInitialized = true;
150
156
  }
151
157
  catch (error) {
@@ -380,12 +386,27 @@ export class GcsStorage extends BaseStorage {
380
386
  */
381
387
  async getNode(id) {
382
388
  await this.ensureInitialized();
383
- // Check cache first
389
+ // Check cache first WITH LOGGING
384
390
  const cached = this.nounCacheManager.get(id);
385
- if (cached) {
391
+ // DIAGNOSTIC LOGGING: Reveal cache poisoning
392
+ prodLog.info(`[getNode] 🔍 Cache check for ${id.substring(0, 8)}...:`, {
393
+ hasCached: cached !== undefined,
394
+ isNull: cached === null,
395
+ isObject: cached !== null && typeof cached === 'object',
396
+ type: typeof cached
397
+ });
398
+ // CRITICAL FIX: Only return cached value if it's valid (not null/undefined)
399
+ if (cached !== undefined && cached !== null) {
400
+ prodLog.info(`[getNode] ✅ Cache HIT - returning cached node for ${id.substring(0, 8)}...`);
386
401
  this.logger.trace(`Cache hit for noun ${id}`);
387
402
  return cached;
388
403
  }
404
+ else if (cached === null) {
405
+ prodLog.warn(`[getNode] ⚠️ Cache contains NULL for ${id.substring(0, 8)}... - ignoring and loading from GCS`);
406
+ }
407
+ else {
408
+ prodLog.info(`[getNode] ❌ Cache MISS - loading from GCS for ${id.substring(0, 8)}...`);
409
+ }
389
410
  // Apply backpressure
390
411
  const requestId = await this.applyBackpressure();
391
412
  try {
@@ -420,8 +441,14 @@ export class GcsStorage extends BaseStorage {
420
441
  level: data.level || 0
421
442
  // NO metadata field - retrieved separately for scalability
422
443
  };
423
- // Update cache
424
- this.nounCacheManager.set(id, node);
444
+ // CRITICAL FIX: Only cache valid nodes (never cache null)
445
+ if (node && node.id && node.vector && Array.isArray(node.vector)) {
446
+ this.nounCacheManager.set(id, node);
447
+ prodLog.info(`[getNode] 💾 Cached node ${id.substring(0, 8)}... successfully`);
448
+ }
449
+ else {
450
+ prodLog.warn(`[getNode] ⚠️ NOT caching invalid node for ${id.substring(0, 8)}...`);
451
+ }
425
452
  this.logger.trace(`Successfully retrieved node ${id}`);
426
453
  this.releaseBackpressure(true, requestId);
427
454
  return node;
@@ -440,7 +467,8 @@ export class GcsStorage extends BaseStorage {
440
467
  prodLog.error(`[getNode] Error object:`, JSON.stringify(error, null, 2));
441
468
  // Check if this is a "not found" error
442
469
  if (error.code === 404) {
443
- prodLog.warn(`[getNode] Identified as 404 error - returning null`);
470
+ prodLog.warn(`[getNode] Identified as 404 error - returning null WITHOUT caching`);
471
+ // CRITICAL FIX: Do NOT cache null values
444
472
  return null;
445
473
  }
446
474
  // Handle throttling
@@ -237,6 +237,16 @@ export class S3CompatibleStorage extends BaseStorage {
237
237
  await this.cleanupLegacyIndexFolder();
238
238
  // Initialize counts from storage
239
239
  await this.initializeCounts();
240
+ // CRITICAL FIX (v3.37.7): Clear any stale cache entries from previous runs
241
+ // This prevents cache poisoning from causing silent failures on container restart
242
+ const nodeCacheSize = this.nodeCache?.size || 0;
243
+ if (nodeCacheSize > 0) {
244
+ prodLog.info(`🧹 Clearing ${nodeCacheSize} cached node entries from previous run`);
245
+ this.nodeCache.clear();
246
+ }
247
+ else {
248
+ prodLog.info('🧹 Node cache is empty - starting fresh');
249
+ }
240
250
  this.isInitialized = true;
241
251
  this.logger.info(`Initialized ${this.serviceType} storage with bucket ${this.bucketName}`);
242
252
  }
@@ -808,6 +818,27 @@ export class S3CompatibleStorage extends BaseStorage {
808
818
  */
809
819
  async getNode(id) {
810
820
  await this.ensureInitialized();
821
+ // Check cache first WITH LOGGING
822
+ const cached = this.nodeCache.get(id);
823
+ // DIAGNOSTIC LOGGING: Reveal cache poisoning
824
+ prodLog.info(`[getNode] 🔍 Cache check for ${id.substring(0, 8)}...:`, {
825
+ hasCached: cached !== undefined,
826
+ isNull: cached === null,
827
+ isObject: cached !== null && typeof cached === 'object',
828
+ type: typeof cached
829
+ });
830
+ // CRITICAL FIX: Only return cached value if it's valid (not null/undefined)
831
+ if (cached !== undefined && cached !== null) {
832
+ prodLog.info(`[getNode] ✅ Cache HIT - returning cached node for ${id.substring(0, 8)}...`);
833
+ this.logger.trace(`Cache hit for node ${id}`);
834
+ return cached;
835
+ }
836
+ else if (cached === null) {
837
+ prodLog.warn(`[getNode] ⚠️ Cache contains NULL for ${id.substring(0, 8)}... - ignoring and loading from S3`);
838
+ }
839
+ else {
840
+ prodLog.info(`[getNode] ❌ Cache MISS - loading from S3 for ${id.substring(0, 8)}...`);
841
+ }
811
842
  try {
812
843
  // Import the GetObjectCommand only when needed
813
844
  const { GetObjectCommand } = await import('@aws-sdk/client-s3');
@@ -858,6 +889,14 @@ export class S3CompatibleStorage extends BaseStorage {
858
889
  connections,
859
890
  level: parsedNode.level || 0
860
891
  };
892
+ // CRITICAL FIX: Only cache valid nodes (never cache null)
893
+ if (node && node.id && node.vector && Array.isArray(node.vector)) {
894
+ this.nodeCache.set(id, node);
895
+ prodLog.info(`[getNode] 💾 Cached node ${id.substring(0, 8)}... successfully`);
896
+ }
897
+ else {
898
+ prodLog.warn(`[getNode] ⚠️ NOT caching invalid node for ${id.substring(0, 8)}...`);
899
+ }
861
900
  this.logger.trace(`Successfully retrieved node ${id}`);
862
901
  return node;
863
902
  }
@@ -876,7 +915,8 @@ export class S3CompatibleStorage extends BaseStorage {
876
915
  prodLog.error(`[getNode] Error object:`, JSON.stringify(error, null, 2));
877
916
  // Check if this is a "not found" error (S3 uses "NoSuchKey")
878
917
  if (error?.name === 'NoSuchKey' || error?.Code === 'NoSuchKey' || error?.$metadata?.httpStatusCode === 404) {
879
- prodLog.warn(`[getNode] Identified as 404/NoSuchKey error - returning null`);
918
+ prodLog.warn(`[getNode] Identified as 404/NoSuchKey error - returning null WITHOUT caching`);
919
+ // CRITICAL FIX: Do NOT cache null values
880
920
  return null;
881
921
  }
882
922
  // Handle throttling
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulcraft/brainy",
3
- "version": "3.37.6",
3
+ "version": "3.37.7",
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",