@soulcraft/brainy 0.9.11 → 0.9.13

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/README.md CHANGED
@@ -3,10 +3,10 @@
3
3
  <br/><br/>
4
4
 
5
5
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
6
- [![Node.js](https://img.shields.io/badge/node-%3E%3D23.11.0-brightgreen.svg)](https://nodejs.org/)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D24.0.0-brightgreen.svg)](https://nodejs.org/)
7
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.1.6-blue.svg)](https://www.typescriptlang.org/)
8
8
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
9
- [![npm](https://img.shields.io/badge/npm-v0.9.11-blue.svg)](https://www.npmjs.com/package/@soulcraft/brainy)
9
+ [![npm](https://img.shields.io/badge/npm-v0.9.13-blue.svg)](https://www.npmjs.com/package/@soulcraft/brainy)
10
10
 
11
11
  [//]: # ([![Cartographer]&#40;https://img.shields.io/badge/Cartographer-Official%20Standard-brightgreen&#41;]&#40;https://github.com/sodal-project/cartographer&#41;)
12
12
 
@@ -1060,8 +1060,9 @@ The repository includes a comprehensive demo that showcases Brainy's main featur
1060
1060
  which automatically deploys when pushing to the main branch or can be manually triggered
1061
1061
  - To use a custom domain (like www.soulcraft.com):
1062
1062
  1. A CNAME file is already included in the demo directory
1063
- 2. In your GitHub repository settings, go to Pages > Custom domain and enter your domain
1064
- 3. Configure your domain's DNS settings to point to GitHub Pages:
1063
+ 2. In your GitHub repository settings, go to Pages > Custom domain and enter your domain
1064
+ 3. Configure your domain's DNS settings to point to GitHub Pages:
1065
+
1065
1066
  - Add a CNAME record for www pointing to `<username>.github.io` (e.g., `soulcraft-research.github.io`)
1066
1067
  - Or for an apex domain (soulcraft.com), add A records pointing to GitHub Pages IP addresses
1067
1068
 
@@ -1263,7 +1264,27 @@ comprehensive [Scaling Strategy](scalingStrategy.md) document.
1263
1264
 
1264
1265
  ## Requirements
1265
1266
 
1266
- - Node.js >= 23.11.0
1267
+ - Node.js >= 24.0.0
1268
+
1269
+ ### Node.js 24 Optimizations
1270
+
1271
+ Brainy takes advantage of several optimizations available in Node.js 24:
1272
+
1273
+ 1. **Improved Worker Threads Performance**: The multithreading system has been completely rewritten to leverage Node.js 24's enhanced Worker Threads API, resulting in better performance for compute-intensive operations like embedding generation and vector similarity calculations.
1274
+
1275
+ 2. **Worker Pool Management**: A sophisticated worker pool system reuses worker threads to minimize the overhead of creating and destroying threads, leading to more efficient resource utilization.
1276
+
1277
+ 3. **Dynamic Module Imports**: Uses the new `node:` protocol prefix for importing core modules, which provides better performance and more reliable module resolution.
1278
+
1279
+ 4. **ES Modules Optimizations**: Takes advantage of Node.js 24's improved ESM implementation for faster module loading and execution.
1280
+
1281
+ 5. **Enhanced Error Handling**: Implements more robust error handling patterns available in Node.js 24 for better stability and debugging.
1282
+
1283
+ These optimizations are particularly beneficial for:
1284
+ - Large-scale vector operations
1285
+ - Batch processing of embeddings
1286
+ - Real-time data processing pipelines
1287
+ - High-throughput search operations
1267
1288
 
1268
1289
  ## Contributing
1269
1290
 
package/dist/brainy.js CHANGED
@@ -2672,33 +2672,213 @@ async function calculateDistancesBatch(queryVector, vectors, distanceFunction =
2672
2672
  }
2673
2673
 
2674
2674
  /**
2675
- * Utility functions for executing functions (without Web Workers or Worker Threads)
2676
- * This is a replacement for the original multithreaded implementation
2675
+ * Utility functions for environment detection
2676
+ */
2677
+ /**
2678
+ * Check if code is running in a browser environment
2679
+ */
2680
+ function isBrowser$1() {
2681
+ return typeof window !== 'undefined' && typeof document !== 'undefined';
2682
+ }
2683
+ /**
2684
+ * Check if code is running in a Node.js environment
2685
+ */
2686
+ function isNode() {
2687
+ return typeof process !== 'undefined' &&
2688
+ process.versions != null &&
2689
+ process.versions.node != null;
2690
+ }
2691
+ /**
2692
+ * Check if Web Workers are available in the current environment
2677
2693
  */
2694
+ function areWebWorkersAvailable() {
2695
+ return isBrowser$1() && typeof Worker !== 'undefined';
2696
+ }
2697
+ /**
2698
+ * Check if Worker Threads are available in the current environment (Node.js)
2699
+ */
2700
+ function areWorkerThreadsAvailable() {
2701
+ if (!isNode())
2702
+ return false;
2703
+ try {
2704
+ // Dynamic import to avoid errors in browser environments
2705
+ require('worker_threads');
2706
+ return true;
2707
+ }
2708
+ catch (e) {
2709
+ return false;
2710
+ }
2711
+ }
2678
2712
  /**
2679
- * Execute a function directly in the main thread
2713
+ * Determine if threading is available in the current environment
2714
+ * Returns true if either Web Workers (browser) or Worker Threads (Node.js) are available
2715
+ */
2716
+ function isThreadingAvailable() {
2717
+ return areWebWorkersAvailable() || areWorkerThreadsAvailable();
2718
+ }
2719
+
2720
+ /**
2721
+ * Utility functions for executing functions in Worker Threads (Node.js) or Web Workers (Browser)
2722
+ * This implementation leverages Node.js 24's improved Worker Threads API for better performance
2723
+ */
2724
+ // Worker pool to reuse workers
2725
+ const workerPool = new Map();
2726
+ const MAX_POOL_SIZE = 4; // Adjust based on system capabilities
2727
+ /**
2728
+ * Execute a function in a separate thread
2680
2729
  *
2681
2730
  * @param fnString The function to execute as a string
2682
2731
  * @param args The arguments to pass to the function
2683
2732
  * @returns A promise that resolves with the result of the function
2684
2733
  */
2685
2734
  function executeInThread(fnString, args) {
2686
- try {
2687
- // Parse the function from string and execute it
2688
- const fn = new Function('return ' + fnString)();
2689
- return Promise.resolve(fn(args));
2735
+ if (isNode()) {
2736
+ return executeInNodeWorker(fnString, args);
2690
2737
  }
2691
- catch (error) {
2692
- return Promise.reject(error);
2738
+ else if (isBrowser$1() && typeof window !== 'undefined' && window.Worker) {
2739
+ return executeInWebWorker(fnString, args);
2740
+ }
2741
+ else {
2742
+ // Fallback to main thread execution
2743
+ try {
2744
+ const fn = new Function('return ' + fnString)();
2745
+ return Promise.resolve(fn(args));
2746
+ }
2747
+ catch (error) {
2748
+ return Promise.reject(error);
2749
+ }
2693
2750
  }
2694
2751
  }
2695
2752
  /**
2696
- * No-op function for backward compatibility
2697
- * This function does nothing since there are no worker pools to clean up
2753
+ * Execute a function in a Node.js Worker Thread
2754
+ * Optimized for Node.js 24 with improved Worker Threads performance
2755
+ */
2756
+ function executeInNodeWorker(fnString, args) {
2757
+ return new Promise((resolve, reject) => {
2758
+ try {
2759
+ // Dynamically import worker_threads (Node.js only)
2760
+ import('node:worker_threads').then(({ Worker, isMainThread, parentPort, workerData }) => {
2761
+ if (!isMainThread && parentPort) {
2762
+ // We're inside a worker, execute the function
2763
+ const fn = new Function('return ' + workerData.fnString)();
2764
+ const result = fn(workerData.args);
2765
+ parentPort.postMessage({ result });
2766
+ return;
2767
+ }
2768
+ // Get a worker from the pool or create a new one
2769
+ const workerId = `worker-${Math.random().toString(36).substring(2, 9)}`;
2770
+ let worker;
2771
+ if (workerPool.size < MAX_POOL_SIZE) {
2772
+ // Create a new worker
2773
+ worker = new Worker(`
2774
+ import { parentPort, workerData } from 'node:worker_threads';
2775
+ const fn = new Function('return ' + workerData.fnString)();
2776
+ const result = fn(workerData.args);
2777
+ parentPort.postMessage({ result });
2778
+ `, {
2779
+ eval: true,
2780
+ workerData: { fnString, args }
2781
+ });
2782
+ workerPool.set(workerId, worker);
2783
+ }
2784
+ else {
2785
+ // Reuse an existing worker
2786
+ const poolKeys = Array.from(workerPool.keys());
2787
+ const randomKey = poolKeys[Math.floor(Math.random() * poolKeys.length)];
2788
+ worker = workerPool.get(randomKey);
2789
+ // Terminate and recreate if the worker is busy
2790
+ if (worker._busy) {
2791
+ worker.terminate();
2792
+ worker = new Worker(`
2793
+ import { parentPort, workerData } from 'node:worker_threads';
2794
+ const fn = new Function('return ' + workerData.fnString)();
2795
+ const result = fn(workerData.args);
2796
+ parentPort.postMessage({ result });
2797
+ `, {
2798
+ eval: true,
2799
+ workerData: { fnString, args }
2800
+ });
2801
+ workerPool.set(randomKey, worker);
2802
+ }
2803
+ worker._busy = true;
2804
+ }
2805
+ worker.on('message', (message) => {
2806
+ worker._busy = false;
2807
+ resolve(message.result);
2808
+ });
2809
+ worker.on('error', (err) => {
2810
+ worker._busy = false;
2811
+ reject(err);
2812
+ });
2813
+ worker.on('exit', (code) => {
2814
+ if (code !== 0) {
2815
+ worker._busy = false;
2816
+ reject(new Error(`Worker stopped with exit code ${code}`));
2817
+ }
2818
+ });
2819
+ }).catch(reject);
2820
+ }
2821
+ catch (error) {
2822
+ reject(error);
2823
+ }
2824
+ });
2825
+ }
2826
+ /**
2827
+ * Execute a function in a Web Worker (Browser environment)
2828
+ */
2829
+ function executeInWebWorker(fnString, args) {
2830
+ return new Promise((resolve, reject) => {
2831
+ try {
2832
+ const workerCode = `
2833
+ self.onmessage = function(e) {
2834
+ try {
2835
+ const fn = new Function('return ' + e.data.fnString)();
2836
+ const result = fn(e.data.args);
2837
+ self.postMessage({ result: result });
2838
+ } catch (error) {
2839
+ self.postMessage({ error: error.message });
2840
+ }
2841
+ };
2842
+ `;
2843
+ const blob = new Blob([workerCode], { type: 'application/javascript' });
2844
+ const url = URL.createObjectURL(blob);
2845
+ const worker = new Worker(url);
2846
+ worker.onmessage = function (e) {
2847
+ if (e.data.error) {
2848
+ reject(new Error(e.data.error));
2849
+ }
2850
+ else {
2851
+ resolve(e.data.result);
2852
+ }
2853
+ worker.terminate();
2854
+ URL.revokeObjectURL(url);
2855
+ };
2856
+ worker.onerror = function (e) {
2857
+ reject(new Error(`Worker error: ${e.message}`));
2858
+ worker.terminate();
2859
+ URL.revokeObjectURL(url);
2860
+ };
2861
+ worker.postMessage({ fnString, args });
2862
+ }
2863
+ catch (error) {
2864
+ reject(error);
2865
+ }
2866
+ });
2867
+ }
2868
+ /**
2869
+ * Clean up all worker pools
2870
+ * This should be called when the application is shutting down
2698
2871
  */
2699
2872
  function cleanupWorkerPools() {
2700
- // No-op function
2701
- console.log('Worker pools cleanup called (no-op in non-threaded version)');
2873
+ if (isNode()) {
2874
+ import('node:worker_threads').then(({ Worker }) => {
2875
+ for (const worker of workerPool.values()) {
2876
+ worker.terminate();
2877
+ }
2878
+ workerPool.clear();
2879
+ console.log('Worker pools cleaned up');
2880
+ }).catch(console.error);
2881
+ }
2702
2882
  }
2703
2883
 
2704
2884
  /**
@@ -2967,7 +3147,8 @@ function findUSELoadFunction(sentenceEncoderModule) {
2967
3147
  }
2968
3148
  }
2969
3149
  // Also check nested objects
2970
- else if (typeof sentenceEncoderModule[key] === 'object' && sentenceEncoderModule[key] !== null) {
3150
+ else if (typeof sentenceEncoderModule[key] === 'object' &&
3151
+ sentenceEncoderModule[key] !== null) {
2971
3152
  for (const nestedKey in sentenceEncoderModule[key]) {
2972
3153
  if (typeof sentenceEncoderModule[key][nestedKey] === 'function') {
2973
3154
  const fnName = sentenceEncoderModule[key][nestedKey].name || nestedKey;
@@ -7501,7 +7682,7 @@ class BrainyData {
7501
7682
  console.log('Retrying Universal Sentence Encoder initialization...');
7502
7683
  try {
7503
7684
  // Wait a moment before retrying
7504
- await new Promise(resolve => setTimeout(resolve, 1000));
7685
+ await new Promise((resolve) => setTimeout(resolve, 1000));
7505
7686
  // Try again with a different approach - use the non-threaded version
7506
7687
  // This is a fallback in case the threaded version fails
7507
7688
  const { createTensorFlowEmbeddingFunction } = await Promise.resolve().then(function () { return embedding; });
@@ -7764,19 +7945,25 @@ class BrainyData {
7764
7945
  }
7765
7946
  });
7766
7947
  // Process vector items (already embedded)
7767
- const vectorPromises = vectorItems.map(item => this.add(item.vectorOrData, item.metadata, options));
7948
+ const vectorPromises = vectorItems.map((item) => this.add(item.vectorOrData, item.metadata, options));
7768
7949
  // Process text items in a single batch embedding operation
7769
7950
  let textPromises = [];
7770
7951
  if (textItems.length > 0) {
7771
7952
  // Extract just the text for batch embedding
7772
- const texts = textItems.map(item => item.text);
7953
+ const texts = textItems.map((item) => item.text);
7773
7954
  // Perform batch embedding
7774
7955
  const embeddings = await defaultBatchEmbeddingFunction(texts);
7775
7956
  // Add each item with its embedding
7776
- textPromises = textItems.map((item, i) => this.add(embeddings[i], item.metadata, { ...options, forceEmbed: false }));
7957
+ textPromises = textItems.map((item, i) => this.add(embeddings[i], item.metadata, {
7958
+ ...options,
7959
+ forceEmbed: false
7960
+ }));
7777
7961
  }
7778
7962
  // Combine all promises
7779
- const batchResults = await Promise.all([...vectorPromises, ...textPromises]);
7963
+ const batchResults = await Promise.all([
7964
+ ...vectorPromises,
7965
+ ...textPromises
7966
+ ]);
7780
7967
  // Add the results to our ids array
7781
7968
  ids.push(...batchResults);
7782
7969
  }
@@ -8714,8 +8901,10 @@ class BrainyData {
8714
8901
  let attempts = 0;
8715
8902
  const maxAttempts = 100; // Prevent infinite loop
8716
8903
  const delay = 50; // ms
8717
- while (this.isInitializing && !this.isInitialized && attempts < maxAttempts) {
8718
- await new Promise(resolve => setTimeout(resolve, delay));
8904
+ while (this.isInitializing &&
8905
+ !this.isInitialized &&
8906
+ attempts < maxAttempts) {
8907
+ await new Promise((resolve) => setTimeout(resolve, delay));
8719
8908
  attempts++;
8720
8909
  }
8721
8910
  if (!this.isInitialized) {
@@ -10847,31 +11036,6 @@ var s3CompatibleStorage = /*#__PURE__*/Object.freeze({
10847
11036
  S3CompatibleStorage: S3CompatibleStorage
10848
11037
  });
10849
11038
 
10850
- /**
10851
- * Utility functions for environment detection
10852
- */
10853
- /**
10854
- * Check if code is running in a browser environment
10855
- */
10856
- function isBrowser$1() {
10857
- return typeof window !== 'undefined' && typeof document !== 'undefined';
10858
- }
10859
- /**
10860
- * Check if code is running in a Node.js environment
10861
- */
10862
- function isNode() {
10863
- return typeof process !== 'undefined' &&
10864
- process.versions != null &&
10865
- process.versions.node != null;
10866
- }
10867
- /**
10868
- * Determine if threading is available in the current environment
10869
- * Always returns false since multithreading has been removed
10870
- */
10871
- function isThreadingAvailable() {
10872
- return false;
10873
- }
10874
-
10875
11039
  /**
10876
11040
  * Augmentation Registry
10877
11041
  *
@@ -87027,5 +87191,5 @@ var _child_processShim = /*#__PURE__*/Object.freeze({
87027
87191
  __proto__: null
87028
87192
  });
87029
87193
 
87030
- export { AugmentationType, BrainyData, BrainyMCPAdapter, BrainyMCPService, ExecutionMode$1 as ExecutionMode, FileSystemStorage, FileSystemStorageAugmentation, HNSWIndex, HNSWIndexOptimized, MCPAugmentationToolset, MCPRequestType, MCP_VERSION, MemoryStorage, MemoryStorageAugmentation, NounType, OPFSStorage, OPFSStorageAugmentation, Pipeline, S3CompatibleStorage as R2Storage, S3CompatibleStorage, SequentialPipeline, ServerSearchActivationAugmentation, ServerSearchConduitAugmentation, StreamlinedExecutionMode, UniversalSentenceEncoder$1 as UniversalSentenceEncoder, VerbType, WebRTCConduitAugmentation, WebSocketConduitAugmentation, addWebSocketSupport, augmentationPipeline$1 as augmentationPipeline, availableAugmentations, cosineDistance$1 as cosineDistance, createAugmentationRegistryPlugin, createAugmentationRegistryRollupPlugin, createConduitAugmentation, createEmbeddingFunction, createMemoryAugmentation, createPipeline, createSenseAugmentation, createServerSearchAugmentations, createStorage, createStreamingPipeline, createTensorFlowEmbeddingFunction, createThreadedEmbeddingFunction, defaultEmbeddingFunction, dotProductDistance, environment, euclideanDistance, executeAugmentation, executeByType, executeSingle, executeStreamlined, getAugmentationsByType, initializeAugmentationPipeline, loadAugmentationModule, loadAugmentationsFromModules, manhattanDistance, pipeline, processStaticData, processStreamingData, registerAugmentation, sequentialPipeline, setAugmentationEnabled };
87194
+ export { AugmentationType, BrainyData, BrainyMCPAdapter, BrainyMCPService, ExecutionMode$1 as ExecutionMode, FileSystemStorage, FileSystemStorageAugmentation, HNSWIndex, HNSWIndexOptimized, MCPAugmentationToolset, MCPRequestType, MCP_VERSION, MemoryStorage, MemoryStorageAugmentation, NounType, OPFSStorage, OPFSStorageAugmentation, Pipeline, S3CompatibleStorage as R2Storage, S3CompatibleStorage, SequentialPipeline, ServerSearchActivationAugmentation, ServerSearchConduitAugmentation, StreamlinedExecutionMode, UniversalSentenceEncoder$1 as UniversalSentenceEncoder, VerbType, WebRTCConduitAugmentation, WebSocketConduitAugmentation, addWebSocketSupport, augmentationPipeline$1 as augmentationPipeline, availableAugmentations, cleanupWorkerPools, cosineDistance$1 as cosineDistance, createAugmentationRegistryPlugin, createAugmentationRegistryRollupPlugin, createConduitAugmentation, createEmbeddingFunction, createMemoryAugmentation, createPipeline, createSenseAugmentation, createServerSearchAugmentations, createStorage, createStreamingPipeline, createTensorFlowEmbeddingFunction, createThreadedEmbeddingFunction, defaultEmbeddingFunction, dotProductDistance, environment, euclideanDistance, executeAugmentation, executeByType, executeInThread, executeSingle, executeStreamlined, getAugmentationsByType, initializeAugmentationPipeline, loadAugmentationModule, loadAugmentationsFromModules, manhattanDistance, pipeline, processStaticData, processStreamingData, registerAugmentation, sequentialPipeline, setAugmentationEnabled };
87031
87195
  //# sourceMappingURL=brainy.js.map