@soulcraft/brainy 3.5.0 → 3.6.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.
@@ -7,9 +7,7 @@
7
7
  * - Runtime updates
8
8
  * - Default values from schema
9
9
  */
10
- import { existsSync, readFileSync } from 'node:fs';
11
- import { join } from 'node:path';
12
- import { homedir } from 'node:os';
10
+ import { isNode } from '../utils/environment.js';
13
11
  /**
14
12
  * Configuration source priority (highest to lowest)
15
13
  */
@@ -29,14 +27,28 @@ export class AugmentationConfigResolver {
29
27
  this.options = options;
30
28
  this.sources = [];
31
29
  this.resolved = {};
30
+ // Default config paths - include home directory paths only in Node.js
31
+ const defaultConfigPaths = [
32
+ '.brainyrc',
33
+ '.brainyrc.json',
34
+ 'brainy.config.json'
35
+ ];
36
+ // Add home directory paths in Node.js environments
37
+ if (isNode()) {
38
+ try {
39
+ const nodeRequire = typeof require !== 'undefined' ? require : null;
40
+ if (nodeRequire) {
41
+ const path = nodeRequire('node:path');
42
+ const os = nodeRequire('node:os');
43
+ defaultConfigPaths.push(path.join(os.homedir(), '.brainy', 'config.json'), path.join(os.homedir(), '.brainyrc'));
44
+ }
45
+ }
46
+ catch {
47
+ // Silently continue without home directory paths
48
+ }
49
+ }
32
50
  this.options = {
33
- configPaths: [
34
- '.brainyrc',
35
- '.brainyrc.json',
36
- 'brainy.config.json',
37
- join(homedir(), '.brainy', 'config.json'),
38
- join(homedir(), '.brainyrc')
39
- ],
51
+ configPaths: defaultConfigPaths,
40
52
  envPrefix: `BRAINY_AUG_${options.augmentationId.toUpperCase()}_`,
41
53
  allowUndefined: true,
42
54
  ...options
@@ -98,30 +110,39 @@ export class AugmentationConfigResolver {
98
110
  */
99
111
  loadFromFiles() {
100
112
  // Skip in browser environment
101
- if (typeof process === 'undefined' || typeof window !== 'undefined') {
113
+ if (!isNode()) {
102
114
  return;
103
115
  }
104
- for (const configPath of this.options.configPaths || []) {
105
- try {
106
- if (existsSync(configPath)) {
107
- const content = readFileSync(configPath, 'utf8');
108
- const config = this.parseConfigFile(content, configPath);
109
- // Extract augmentation-specific configuration
110
- const augConfig = this.extractAugmentationConfig(config);
111
- if (augConfig && Object.keys(augConfig).length > 0) {
112
- this.sources.push({
113
- priority: ConfigPriority.FILE,
114
- source: `file:${configPath}`,
115
- config: augConfig
116
- });
117
- break; // Use first found config file
116
+ try {
117
+ const nodeRequire = typeof require !== 'undefined' ? require : null;
118
+ if (!nodeRequire)
119
+ return;
120
+ const fs = nodeRequire('node:fs');
121
+ for (const configPath of this.options.configPaths || []) {
122
+ try {
123
+ if (fs.existsSync(configPath)) {
124
+ const content = fs.readFileSync(configPath, 'utf8');
125
+ const config = this.parseConfigFile(content, configPath);
126
+ // Extract augmentation-specific configuration
127
+ const augConfig = this.extractAugmentationConfig(config);
128
+ if (augConfig && Object.keys(augConfig).length > 0) {
129
+ this.sources.push({
130
+ priority: ConfigPriority.FILE,
131
+ source: `file:${configPath}`,
132
+ config: augConfig
133
+ });
134
+ break; // Use first found config file
135
+ }
118
136
  }
119
137
  }
138
+ catch (error) {
139
+ // Silently ignore file errors
140
+ console.debug(`Failed to load config from ${configPath}:`, error);
141
+ }
120
142
  }
121
- catch (error) {
122
- // Silently ignore file errors
123
- console.debug(`Failed to load config from ${configPath}:`, error);
124
- }
143
+ }
144
+ catch {
145
+ // Silently continue if require fails
125
146
  }
126
147
  }
127
148
  /**
@@ -162,7 +183,7 @@ export class AugmentationConfigResolver {
162
183
  */
163
184
  loadFromEnvironment() {
164
185
  // Skip in browser environment
165
- if (typeof process === 'undefined' || !process.env) {
186
+ if (!isNode() || !process.env) {
166
187
  return;
167
188
  }
168
189
  const prefix = this.options.envPrefix;
@@ -376,7 +397,7 @@ export class AugmentationConfigResolver {
376
397
  */
377
398
  async saveToFile(filepath, format = 'json') {
378
399
  // Skip in browser environment
379
- if (typeof process === 'undefined' || typeof window !== 'undefined') {
400
+ if (!isNode()) {
380
401
  throw new Error('Cannot save configuration files in browser environment');
381
402
  }
382
403
  const fs = await import('node:fs');
@@ -63,8 +63,12 @@ export declare class EmbeddingManager {
63
63
  getEmbeddingFunction(): EmbeddingFunction;
64
64
  /**
65
65
  * Get models directory path
66
+ * Note: In browser environments, returns a simple default path
67
+ * In Node.js, checks multiple locations for the models directory
66
68
  */
67
69
  private getModelsPath;
70
+ private modelsPathCache;
71
+ private resolveModelsPathSync;
68
72
  /**
69
73
  * Get memory usage in MB
70
74
  */
@@ -16,8 +16,7 @@
16
16
  * hybridModelManager, universalMemoryManager, and more.
17
17
  */
18
18
  import { pipeline, env } from '@huggingface/transformers';
19
- import { existsSync } from 'node:fs';
20
- import { join } from 'node:path';
19
+ import { isNode } from '../utils/environment.js';
21
20
  // Global state for true singleton across entire process
22
21
  let globalInstance = null;
23
22
  let globalInitPromise = null;
@@ -32,6 +31,7 @@ export class EmbeddingManager {
32
31
  this.initTime = null;
33
32
  this.embedCount = 0;
34
33
  this.locked = false;
34
+ this.modelsPathCache = null;
35
35
  // Always use Q8 for optimal size/performance (99% accuracy, 75% smaller)
36
36
  this.precision = 'q8';
37
37
  console.log(`🎯 EmbeddingManager: Using Q8 precision`);
@@ -95,11 +95,23 @@ export class EmbeddingManager {
95
95
  env.cacheDir = modelsPath;
96
96
  env.allowLocalModels = true;
97
97
  env.useFSCache = true;
98
- // Check if models exist locally
99
- const modelPath = join(modelsPath, ...this.modelName.split('/'));
100
- const hasLocalModels = existsSync(modelPath);
101
- if (hasLocalModels) {
102
- console.log('✅ Using cached models from:', modelPath);
98
+ // Check if models exist locally (only in Node.js)
99
+ if (isNode()) {
100
+ try {
101
+ const nodeRequire = typeof require !== 'undefined' ? require : null;
102
+ if (nodeRequire) {
103
+ const path = nodeRequire('node:path');
104
+ const fs = nodeRequire('node:fs');
105
+ const modelPath = path.join(modelsPath, ...this.modelName.split('/'));
106
+ const hasLocalModels = fs.existsSync(modelPath);
107
+ if (hasLocalModels) {
108
+ console.log('✅ Using cached models from:', modelPath);
109
+ }
110
+ }
111
+ }
112
+ catch {
113
+ // Silently continue if require fails
114
+ }
103
115
  }
104
116
  // Configure pipeline options for the selected precision
105
117
  const pipelineOptions = {
@@ -206,22 +218,52 @@ export class EmbeddingManager {
206
218
  }
207
219
  /**
208
220
  * Get models directory path
221
+ * Note: In browser environments, returns a simple default path
222
+ * In Node.js, checks multiple locations for the models directory
209
223
  */
210
224
  getModelsPath() {
211
- // Check various possible locations
212
- const paths = [
213
- process.env.BRAINY_MODELS_PATH,
214
- './models',
215
- join(process.cwd(), 'models'),
216
- join(process.env.HOME || '', '.brainy', 'models')
217
- ];
218
- for (const path of paths) {
219
- if (path && existsSync(path)) {
220
- return path;
225
+ // In browser environments, use a default path
226
+ if (!isNode()) {
227
+ return './models';
228
+ }
229
+ // Node.js-specific model path resolution
230
+ // Cache the result for performance
231
+ if (!this.modelsPathCache) {
232
+ this.modelsPathCache = this.resolveModelsPathSync();
233
+ }
234
+ return this.modelsPathCache;
235
+ }
236
+ resolveModelsPathSync() {
237
+ // For Node.js environments, we can safely assume these modules exist
238
+ // TypeScript will handle the imports at build time
239
+ // At runtime, these will only be called if isNode() is true
240
+ // Default fallback path
241
+ const defaultPath = './models';
242
+ try {
243
+ // Create a conditional require function that only works in Node
244
+ const nodeRequire = typeof require !== 'undefined' ? require : null;
245
+ if (!nodeRequire)
246
+ return defaultPath;
247
+ const fs = nodeRequire('node:fs');
248
+ const path = nodeRequire('node:path');
249
+ const paths = [
250
+ process.env.BRAINY_MODELS_PATH,
251
+ './models',
252
+ path.join(process.cwd(), 'models'),
253
+ path.join(process.env.HOME || '', '.brainy', 'models')
254
+ ];
255
+ for (const p of paths) {
256
+ if (p && fs.existsSync(p)) {
257
+ return p;
258
+ }
221
259
  }
260
+ // Default Node.js path
261
+ return path.join(process.cwd(), 'models');
262
+ }
263
+ catch {
264
+ // Fallback if require fails
265
+ return defaultPath;
222
266
  }
223
- // Default
224
- return join(process.cwd(), 'models');
225
267
  }
226
268
  /**
227
269
  * Get memory usage in MB
@@ -9,7 +9,9 @@ let nodeFs = null;
9
9
  if (isNode()) {
10
10
  try {
11
11
  // Use node: protocol to prevent bundler polyfilling (requires Node 22+)
12
- nodeFs = await import('node:fs/promises');
12
+ // Import main module and access promises to avoid subpath issues with bundlers
13
+ const fs = await import('node:fs');
14
+ nodeFs = fs.promises;
13
15
  }
14
16
  catch {
15
17
  // Ignore import errors in non-Node environments
@@ -3,9 +3,17 @@
3
3
  */
4
4
  /**
5
5
  * Get the current Brainy package version
6
+ * In Node.js, attempts to read from package.json
7
+ * In browser, returns the default version
6
8
  * @returns The current version string
7
9
  */
8
10
  export declare function getBrainyVersion(): string;
11
+ /**
12
+ * Get the current Brainy package version asynchronously
13
+ * Guaranteed to attempt loading from package.json in Node.js
14
+ * @returns Promise resolving to the current version string
15
+ */
16
+ export declare function getBrainyVersionAsync(): Promise<string>;
9
17
  /**
10
18
  * Get version information for augmentation metadata
11
19
  * @param service The service/augmentation name
@@ -1,30 +1,77 @@
1
1
  /**
2
2
  * Version utilities for Brainy
3
3
  */
4
- import { readFileSync } from 'node:fs';
5
- import { join, dirname } from 'node:path';
6
- import { fileURLToPath } from 'node:url';
7
- // Get package.json path relative to this file
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = dirname(__filename);
10
- const packageJsonPath = join(__dirname, '../../package.json');
4
+ import { isNode } from './environment.js';
5
+ // Default version - this should be replaced at build time
6
+ const DEFAULT_VERSION = '3.5.1';
11
7
  let cachedVersion = null;
8
+ let versionPromise = null;
9
+ /**
10
+ * Load version from package.json in Node.js environment
11
+ */
12
+ async function loadVersionFromPackageJson() {
13
+ if (!isNode()) {
14
+ return DEFAULT_VERSION;
15
+ }
16
+ try {
17
+ // Dynamic imports for Node.js modules - modern approach
18
+ const [{ readFileSync }, { join, dirname }, { fileURLToPath }] = await Promise.all([
19
+ import('node:fs'),
20
+ import('node:path'),
21
+ import('node:url')
22
+ ]);
23
+ const __filename = fileURLToPath(import.meta.url);
24
+ const __dirname = dirname(__filename);
25
+ const packageJsonPath = join(__dirname, '../../package.json');
26
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
27
+ return packageJson.version || DEFAULT_VERSION;
28
+ }
29
+ catch (error) {
30
+ // Silently fall back to default version
31
+ return DEFAULT_VERSION;
32
+ }
33
+ }
12
34
  /**
13
35
  * Get the current Brainy package version
36
+ * In Node.js, attempts to read from package.json
37
+ * In browser, returns the default version
14
38
  * @returns The current version string
15
39
  */
16
40
  export function getBrainyVersion() {
17
- if (!cachedVersion) {
18
- try {
19
- const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
20
- cachedVersion = packageJson.version;
21
- }
22
- catch {
23
- // Fallback version if package.json can't be read
24
- cachedVersion = '2.7.1';
25
- }
26
- }
27
- return cachedVersion;
41
+ if (cachedVersion) {
42
+ return cachedVersion;
43
+ }
44
+ // In browser or if we need immediate response, return default
45
+ if (!isNode()) {
46
+ cachedVersion = DEFAULT_VERSION;
47
+ return cachedVersion;
48
+ }
49
+ // For Node.js, try to load synchronously first time
50
+ // This is a compromise for backward compatibility
51
+ if (!versionPromise) {
52
+ versionPromise = loadVersionFromPackageJson();
53
+ versionPromise.then(version => {
54
+ cachedVersion = version;
55
+ });
56
+ }
57
+ // Return default while loading
58
+ return cachedVersion || DEFAULT_VERSION;
59
+ }
60
+ /**
61
+ * Get the current Brainy package version asynchronously
62
+ * Guaranteed to attempt loading from package.json in Node.js
63
+ * @returns Promise resolving to the current version string
64
+ */
65
+ export async function getBrainyVersionAsync() {
66
+ if (cachedVersion) {
67
+ return cachedVersion;
68
+ }
69
+ if (!versionPromise) {
70
+ versionPromise = loadVersionFromPackageJson();
71
+ }
72
+ const version = await versionPromise;
73
+ cachedVersion = version;
74
+ return version;
28
75
  }
29
76
  /**
30
77
  * Get version information for augmentation metadata
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulcraft/brainy",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
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",