@soulcraft/brainy 2.9.0 → 2.10.1

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 CHANGED
@@ -2,6 +2,8 @@
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
+ ## [2.10.0](https://github.com/soulcraftlabs/brainy/compare/v2.9.0...v2.10.0) (2025-08-29)
6
+
5
7
  ## [2.8.0](https://github.com/soulcraftlabs/brainy/compare/v2.7.4...v2.8.0) (2025-08-29)
6
8
 
7
9
  ## [2.7.4] - 2025-08-29
package/README.md CHANGED
@@ -29,15 +29,17 @@
29
29
  - **Perfect Interoperability**: All tools and AI models speak the same language
30
30
  - **Universal Compatibility**: Node.js, Browser, Edge, Workers
31
31
 
32
- ## ⚡ Quick Start
32
+ ## ⚡ Quick Start - Zero Configuration
33
33
 
34
34
  ```bash
35
35
  npm install @soulcraft/brainy
36
36
  ```
37
37
 
38
+ ### 🎯 **True Zero Configuration**
38
39
  ```javascript
39
- import { BrainyData } from 'brainy'
40
+ import { BrainyData } from '@soulcraft/brainy'
40
41
 
42
+ // Just this - auto-detects everything!
41
43
  const brain = new BrainyData()
42
44
  await brain.init()
43
45
 
@@ -109,11 +111,41 @@ await brain.find("Popular JavaScript libraries similar to Vue")
109
111
  await brain.find("Documentation about authentication from last month")
110
112
  ```
111
113
 
112
- ### Zero Configuration Philosophy
113
- - **No API keys required** - Built-in embedding models
114
- - **No external dependencies** - Everything included
115
- - **No complex setup** - Works instantly
116
- - **Smart defaults** - Optimized out of the box
114
+ ### 🎯 Zero Configuration Philosophy
115
+ Brainy 2.9+ automatically configures **everything**:
116
+
117
+ ```javascript
118
+ import { BrainyData, PresetName } from '@soulcraft/brainy'
119
+
120
+ // 1. Pure zero-config - detects everything
121
+ const brain = new BrainyData()
122
+
123
+ // 2. Environment presets (strongly typed)
124
+ const devBrain = new BrainyData(PresetName.DEVELOPMENT) // Memory + verbose
125
+ const prodBrain = new BrainyData(PresetName.PRODUCTION) // Disk + optimized
126
+ const miniBrain = new BrainyData(PresetName.MINIMAL) // Q8 + minimal features
127
+
128
+ // 3. Distributed architecture presets
129
+ const writer = new BrainyData(PresetName.WRITER) // Write-only instance
130
+ const reader = new BrainyData(PresetName.READER) // Read-only + caching
131
+ const ingestion = new BrainyData(PresetName.INGESTION_SERVICE) // High-throughput
132
+ const searchAPI = new BrainyData(PresetName.SEARCH_API) // Low-latency search
133
+
134
+ // 4. Custom zero-config (type-safe)
135
+ const customBrain = new BrainyData({
136
+ mode: 'production',
137
+ model: 'fp32', // or 'q8', 'auto'
138
+ storage: 'cloud', // or 'memory', 'disk', 'auto'
139
+ features: ['core', 'search', 'cache']
140
+ })
141
+ ```
142
+
143
+ **What's Auto-Detected:**
144
+ - **Storage**: S3/GCS/R2 → Filesystem → Memory (priority order)
145
+ - **Models**: FP32 vs Q8 based on memory/performance
146
+ - **Features**: Minimal → Default → Full based on environment
147
+ - **Memory**: Optimal cache sizes and batching
148
+ - **Performance**: Threading, chunking, indexing strategies
117
149
 
118
150
  ### Production Performance
119
151
  - **3ms average search** - Lightning fast queries
@@ -121,29 +153,40 @@ await brain.find("Documentation about authentication from last month")
121
153
  - **Worker-based embeddings** - Non-blocking operations
122
154
  - **Automatic caching** - Intelligent result caching
123
155
 
124
- ### Performance Optimization
156
+ ### 🎛️ Advanced Configuration (When Needed)
125
157
 
126
- **Q8 Quantized Models** - 75% smaller, faster loading (v2.8.0+)
158
+ Most users **never need this** - zero-config handles everything. For advanced use cases:
127
159
 
128
160
  ```javascript
129
- // Default: Full precision (fp32) - maximum compatibility
130
- const brain = new BrainyData()
161
+ // Model precision control (auto-detected by default)
162
+ const brain = new BrainyData({
163
+ model: 'fp32' // Full precision - max compatibility
164
+ })
165
+ const fastBrain = new BrainyData({
166
+ model: 'q8' // Quantized - 75% smaller, 99% accuracy
167
+ })
168
+
169
+ // Storage control (auto-detected by default)
170
+ const memoryBrain = new BrainyData({ storage: 'memory' }) // RAM only
171
+ const diskBrain = new BrainyData({ storage: 'disk' }) // Local filesystem
172
+ const cloudBrain = new BrainyData({ storage: 'cloud' }) // S3/GCS/R2
131
173
 
132
- // Optimized: Quantized models (q8) - 75% smaller, 99% accuracy
133
- const brainOptimized = new BrainyData({
134
- embeddingOptions: { dtype: 'q8' }
174
+ // Legacy full config (still supported)
175
+ const legacyBrain = new BrainyData({
176
+ storage: { forceMemoryStorage: true },
177
+ embeddingOptions: { precision: 'fp32' }
135
178
  })
136
179
  ```
137
180
 
138
181
  **Model Comparison:**
139
- - **FP32 (default)**: 90MB, 100% accuracy, maximum compatibility
140
- - **Q8 (optional)**: 23MB, ~99% accuracy, faster loading
182
+ - **FP32**: 90MB, 100% accuracy, maximum compatibility
183
+ - **Q8**: 23MB, ~99% accuracy, faster loading, smaller memory
141
184
 
142
185
  **When to use Q8:**
143
- - ✅ New projects where size/speed matters
144
186
  - ✅ Memory-constrained environments
187
+ - ✅ Faster startup required
145
188
  - ✅ Mobile or edge deployments
146
- - ❌ Existing projects with FP32 data (incompatible embeddings)
189
+ - ❌ Existing FP32 datasets (incompatible embeddings)
147
190
 
148
191
  **Air-gap deployment:**
149
192
  ```bash
@@ -416,6 +416,7 @@ export declare class BrainyData<T = any> implements BrainyDataInterface<T> {
416
416
  private allowDirectReads;
417
417
  private storageConfig;
418
418
  private config;
419
+ private rawConfig;
419
420
  private useOptimizedIndex;
420
421
  private _dimensions;
421
422
  private loggingConfig;
@@ -472,8 +473,10 @@ export declare class BrainyData<T = any> implements BrainyDataInterface<T> {
472
473
  get initialized(): boolean;
473
474
  /**
474
475
  * Create a new vector database
476
+ * @param config - Zero-config string ('production', 'development', 'minimal'),
477
+ * simplified config object, or legacy full config
475
478
  */
476
- constructor(config?: BrainyDataConfig);
479
+ constructor(config?: BrainyDataConfig | string | any);
477
480
  /**
478
481
  * Check if the database is in read-only mode and throw an error if it is
479
482
  * @throws Error if the database is in read-only mode
@@ -8,6 +8,7 @@ import { HNSWIndexOptimized } from './hnsw/hnswIndexOptimized.js';
8
8
  import { cosineDistance, defaultEmbeddingFunction, cleanupWorkerPools, batchEmbed } from './utils/index.js';
9
9
  import { getAugmentationVersion } from './utils/version.js';
10
10
  import { matchesMetadataFilter } from './utils/metadataFilter.js';
11
+ import { enforceNodeVersion } from './utils/nodeVersionCheck.js';
11
12
  import { createNamespacedMetadata, updateNamespacedMetadata, markDeleted, markRestored, isDeleted, getUserMetadata } from './utils/metadataNamespace.js';
12
13
  import { PeriodicCleanup } from './utils/periodicCleanup.js';
13
14
  import { NounType, VerbType } from './types/graphTypes.js';
@@ -80,6 +81,8 @@ export class BrainyData {
80
81
  }
81
82
  /**
82
83
  * Create a new vector database
84
+ * @param config - Zero-config string ('production', 'development', 'minimal'),
85
+ * simplified config object, or legacy full config
83
86
  */
84
87
  constructor(config = {}) {
85
88
  this.storage = null;
@@ -123,72 +126,85 @@ export class BrainyData {
123
126
  this.partitioner = null;
124
127
  this.operationalMode = null;
125
128
  this.domainDetector = null;
126
- // Store config
127
- this.config = config;
129
+ // Enforce Node.js version requirement for ONNX stability
130
+ if (typeof process !== 'undefined' && process.version) {
131
+ enforceNodeVersion();
132
+ }
133
+ // Store raw config for processing in init()
134
+ this.rawConfig = config;
135
+ // For now, process as legacy config if it's an object
136
+ // The actual zero-config processing will happen in init() since it's async
137
+ if (typeof config === 'object') {
138
+ this.config = config;
139
+ }
140
+ else {
141
+ // String preset or simplified config - use minimal defaults for now
142
+ this.config = {};
143
+ }
128
144
  // Set dimensions to fixed value of 384 (all-MiniLM-L6-v2 dimension)
129
145
  this._dimensions = 384;
130
146
  // Set distance function
131
- this.distanceFunction = config.distanceFunction || cosineDistance;
147
+ this.distanceFunction = this.config.distanceFunction || cosineDistance;
132
148
  // Always use the optimized HNSW index implementation
133
149
  // Configure HNSW with disk-based storage when a storage adapter is provided
134
- const hnswConfig = config.hnsw || {};
135
- if (config.storageAdapter) {
150
+ const hnswConfig = this.config.hnsw || {};
151
+ if (this.config.storageAdapter) {
136
152
  hnswConfig.useDiskBasedIndex = true;
137
153
  }
138
154
  // Temporarily use base HNSW index for metadata filtering
139
155
  this.hnswIndex = new HNSWIndex(hnswConfig, this.distanceFunction);
140
156
  this.useOptimizedIndex = false;
141
157
  // Set storage if provided, otherwise it will be initialized in init()
142
- this.storage = config.storageAdapter || null;
158
+ this.storage = this.config.storageAdapter || null;
143
159
  // Store logging configuration
144
- if (config.logging !== undefined) {
160
+ if (this.config.logging !== undefined) {
145
161
  this.loggingConfig = {
146
162
  ...this.loggingConfig,
147
- ...config.logging
163
+ ...this.config.logging
148
164
  };
149
165
  }
150
166
  // Set embedding function if provided, otherwise create one with the appropriate verbose setting
151
- if (config.embeddingFunction) {
152
- this.embeddingFunction = config.embeddingFunction;
167
+ if (this.config.embeddingFunction) {
168
+ this.embeddingFunction = this.config.embeddingFunction;
153
169
  }
154
170
  else {
155
171
  this.embeddingFunction = defaultEmbeddingFunction;
156
172
  }
157
173
  // Set persistent storage request flag
158
174
  this.requestPersistentStorage =
159
- config.storage?.requestPersistentStorage || false;
175
+ this.config.storage?.requestPersistentStorage || false;
160
176
  // Set read-only flag
161
- this.readOnly = config.readOnly || false;
177
+ this.readOnly = this.config.readOnly || false;
162
178
  // Set frozen flag (defaults to false to allow optimizations in readOnly mode)
163
- this.frozen = config.frozen || false;
179
+ this.frozen = this.config.frozen || false;
164
180
  // Set lazy loading in read-only mode flag
165
- this.lazyLoadInReadOnlyMode = config.lazyLoadInReadOnlyMode || false;
181
+ this.lazyLoadInReadOnlyMode = this.config.lazyLoadInReadOnlyMode || false;
166
182
  // Set write-only flag
167
- this.writeOnly = config.writeOnly || false;
183
+ this.writeOnly = this.config.writeOnly || false;
168
184
  // Set allowDirectReads flag
169
- this.allowDirectReads = config.allowDirectReads || false;
185
+ this.allowDirectReads = this.config.allowDirectReads || false;
170
186
  // Validate that readOnly and writeOnly are not both true
171
187
  if (this.readOnly && this.writeOnly) {
172
188
  throw new Error('Database cannot be both read-only and write-only');
173
189
  }
174
190
  // Set default service name if provided
175
- if (config.defaultService) {
176
- this.defaultService = config.defaultService;
191
+ if (this.config.defaultService) {
192
+ this.defaultService = this.config.defaultService;
177
193
  }
178
194
  // Store storage configuration for later use in init()
179
- this.storageConfig = config.storage || {};
195
+ this.storageConfig = this.config.storage || {};
180
196
  // Store timeout and retry configuration
181
- this.timeoutConfig = config.timeouts || {};
182
- this.retryConfig = config.retryPolicy || {};
197
+ this.timeoutConfig = this.config.timeouts || {};
198
+ this.retryConfig = this.config.retryPolicy || {};
183
199
  // Store remote server configuration if provided
184
- if (config.remoteServer) {
185
- this.remoteServerConfig = config.remoteServer;
200
+ if (this.config.remoteServer) {
201
+ this.remoteServerConfig = this.config.remoteServer;
186
202
  }
187
203
  // Initialize real-time update configuration if provided
188
- if (config.realtimeUpdates) {
204
+ if (this.config.realtimeUpdates) {
189
205
  this.realtimeUpdateConfig = {
190
206
  ...this.realtimeUpdateConfig,
191
- ...config.realtimeUpdates
207
+ ...this.config.realtimeUpdates
192
208
  };
193
209
  }
194
210
  // Initialize cache configuration with intelligent defaults
@@ -206,15 +222,15 @@ export class BrainyData {
206
222
  }
207
223
  };
208
224
  // Override defaults with user-provided configuration if available
209
- if (config.cache) {
225
+ if (this.config.cache) {
210
226
  this.cacheConfig = {
211
227
  ...this.cacheConfig,
212
- ...config.cache
228
+ ...this.config.cache
213
229
  };
214
230
  }
215
231
  // Store distributed configuration
216
- if (config.distributed) {
217
- if (typeof config.distributed === 'boolean') {
232
+ if (this.config.distributed) {
233
+ if (typeof this.config.distributed === 'boolean') {
218
234
  // Auto-mode enabled
219
235
  this.distributedConfig = {
220
236
  enabled: true
@@ -222,7 +238,7 @@ export class BrainyData {
222
238
  }
223
239
  else {
224
240
  // Explicit configuration
225
- this.distributedConfig = config.distributed;
241
+ this.distributedConfig = this.config.distributed;
226
242
  }
227
243
  }
228
244
  // Initialize cache auto-configurator first
@@ -350,16 +366,28 @@ export class BrainyData {
350
366
  console.warn('Ignoring s3Storage configuration due to missing required fields');
351
367
  }
352
368
  }
353
- // Check if specific storage is configured
369
+ // Check if specific storage is configured (legacy and new formats)
354
370
  if (storageOptions.s3Storage || storageOptions.r2Storage ||
355
371
  storageOptions.gcsStorage || storageOptions.forceMemoryStorage ||
356
- storageOptions.forceFileSystemStorage) {
357
- // Create storage from config
358
- const { createStorage } = await import('./storage/storageFactory.js');
359
- this.storage = await createStorage(storageOptions);
360
- // Wrap in augmentation for consistency
361
- const wrapper = new DynamicStorageAugmentation(this.storage);
362
- this.augmentations.register(wrapper);
372
+ storageOptions.forceFileSystemStorage ||
373
+ typeof storageOptions === 'string') {
374
+ // Handle string storage types (new zero-config)
375
+ if (typeof storageOptions === 'string') {
376
+ const { createAutoStorageAugmentation } = await import('./augmentations/storageAugmentations.js');
377
+ // For now, use auto-detection - TODO: extend to support preferred types
378
+ const autoAug = await createAutoStorageAugmentation({
379
+ rootDirectory: './brainy-data'
380
+ });
381
+ this.augmentations.register(autoAug);
382
+ }
383
+ else {
384
+ // Legacy object config
385
+ const { createStorage } = await import('./storage/storageFactory.js');
386
+ this.storage = await createStorage(storageOptions);
387
+ // Wrap in augmentation for consistency
388
+ const wrapper = new DynamicStorageAugmentation(this.storage);
389
+ this.augmentations.register(wrapper);
390
+ }
363
391
  }
364
392
  else {
365
393
  // Zero-config: auto-select based on environment
@@ -878,6 +906,34 @@ export class BrainyData {
878
906
  return;
879
907
  }
880
908
  this.isInitializing = true;
909
+ // Process zero-config if needed
910
+ if (this.rawConfig !== undefined) {
911
+ try {
912
+ const { applyZeroConfig } = await import('./config/index.js');
913
+ const processedConfig = await applyZeroConfig(this.rawConfig);
914
+ // Apply processed config if it's different from raw
915
+ if (processedConfig !== this.rawConfig) {
916
+ // Log if verbose
917
+ if (processedConfig.logging?.verbose) {
918
+ console.log('🤖 Zero-config applied successfully');
919
+ }
920
+ // Update config with processed values
921
+ this.config = processedConfig;
922
+ // Update relevant properties from processed config
923
+ this.storageConfig = processedConfig.storage || {};
924
+ this.loggingConfig = processedConfig.logging || { verbose: false };
925
+ // Update embedding function if precision was specified
926
+ if (processedConfig.embeddingOptions?.precision) {
927
+ const { createEmbeddingFunctionWithPrecision } = await import('./config/index.js');
928
+ this.embeddingFunction = await createEmbeddingFunctionWithPrecision(processedConfig.embeddingOptions.precision);
929
+ }
930
+ }
931
+ }
932
+ catch (error) {
933
+ console.warn('Zero-config processing failed, using defaults:', error);
934
+ // Continue with existing config
935
+ }
936
+ }
881
937
  // CRITICAL: Initialize universal memory manager ONLY for default embedding function
882
938
  // This preserves custom embedding functions (like test mocks)
883
939
  if (typeof this.embeddingFunction === 'function' && this.embeddingFunction === defaultEmbeddingFunction) {
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Extended Distributed Configuration Presets
3
+ * Common patterns for distributed and multi-service architectures
4
+ * All strongly typed with enums for compile-time safety
5
+ */
6
+ /**
7
+ * Strongly typed enum for preset names
8
+ */
9
+ export declare enum PresetName {
10
+ PRODUCTION = "production",
11
+ DEVELOPMENT = "development",
12
+ MINIMAL = "minimal",
13
+ ZERO = "zero",
14
+ WRITER = "writer",
15
+ READER = "reader",
16
+ INGESTION_SERVICE = "ingestion-service",
17
+ SEARCH_API = "search-api",
18
+ ANALYTICS_SERVICE = "analytics-service",
19
+ EDGE_CACHE = "edge-cache",
20
+ BATCH_PROCESSOR = "batch-processor",
21
+ STREAMING_SERVICE = "streaming-service",
22
+ ML_TRAINING = "ml-training",
23
+ SIDECAR = "sidecar"
24
+ }
25
+ /**
26
+ * Preset categories for organization
27
+ */
28
+ export declare enum PresetCategory {
29
+ BASIC = "basic",
30
+ DISTRIBUTED = "distributed",
31
+ SERVICE = "service"
32
+ }
33
+ /**
34
+ * Model precision options
35
+ */
36
+ export declare enum ModelPrecision {
37
+ FP32 = "fp32",
38
+ Q8 = "q8",
39
+ AUTO = "auto",
40
+ FAST = "fast",
41
+ SMALL = "small"
42
+ }
43
+ /**
44
+ * Storage options
45
+ */
46
+ export declare enum StorageOption {
47
+ AUTO = "auto",
48
+ MEMORY = "memory",
49
+ DISK = "disk",
50
+ CLOUD = "cloud"
51
+ }
52
+ /**
53
+ * Feature set options
54
+ */
55
+ export declare enum FeatureSet {
56
+ MINIMAL = "minimal",
57
+ DEFAULT = "default",
58
+ FULL = "full",
59
+ CUSTOM = "custom"
60
+ }
61
+ /**
62
+ * Distributed role options
63
+ */
64
+ export declare enum DistributedRole {
65
+ WRITER = "writer",
66
+ READER = "reader",
67
+ HYBRID = "hybrid"
68
+ }
69
+ /**
70
+ * Preset configuration interface
71
+ */
72
+ export interface PresetConfig {
73
+ storage: StorageOption;
74
+ model: ModelPrecision;
75
+ features: FeatureSet | string[];
76
+ distributed: boolean;
77
+ role?: DistributedRole;
78
+ readOnly?: boolean;
79
+ writeOnly?: boolean;
80
+ allowDirectReads?: boolean;
81
+ lazyLoadInReadOnlyMode?: boolean;
82
+ cache?: {
83
+ hotCacheMaxSize?: number;
84
+ autoTune?: boolean;
85
+ batchSize?: number;
86
+ };
87
+ verbose?: boolean;
88
+ description: string;
89
+ category: PresetCategory;
90
+ }
91
+ /**
92
+ * Strongly typed preset configurations
93
+ */
94
+ export declare const PRESET_CONFIGS: Readonly<Record<PresetName, PresetConfig>>;
95
+ /**
96
+ * Type-safe preset getter
97
+ */
98
+ export declare function getPreset(name: PresetName): PresetConfig;
99
+ /**
100
+ * Check if a string is a valid preset name
101
+ */
102
+ export declare function isValidPreset(name: string): name is PresetName;
103
+ /**
104
+ * Get presets by category
105
+ */
106
+ export declare function getPresetsByCategory(category: PresetCategory): PresetName[];
107
+ /**
108
+ * Get all preset names
109
+ */
110
+ export declare function getAllPresetNames(): PresetName[];
111
+ /**
112
+ * Get preset description
113
+ */
114
+ export declare function getPresetDescription(name: PresetName): string;
115
+ /**
116
+ * Convert preset config to Brainy config
117
+ */
118
+ export declare function presetToBrainyConfig(preset: PresetConfig): any;