@soulcraft/brainy 4.3.2 → 4.5.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.
@@ -109,23 +109,37 @@ export const coreCommands = {
109
109
  spinner.text = `Using detected type: ${nounType}`;
110
110
  }
111
111
  // Add with explicit type
112
- const result = await brain.add({
112
+ const addParams = {
113
113
  data: text,
114
114
  type: nounType,
115
115
  metadata
116
- });
116
+ };
117
+ // v4.3.x: Add confidence and weight if provided
118
+ if (options.confidence) {
119
+ addParams.confidence = parseFloat(options.confidence);
120
+ }
121
+ if (options.weight) {
122
+ addParams.weight = parseFloat(options.weight);
123
+ }
124
+ const result = await brain.add(addParams);
117
125
  spinner.succeed('Added successfully');
118
126
  if (!options.json) {
119
127
  console.log(chalk.green(`✓ Added with ID: ${result}`));
120
128
  if (options.type) {
121
129
  console.log(chalk.dim(` Type: ${options.type}`));
122
130
  }
131
+ if (options.confidence) {
132
+ console.log(chalk.dim(` Confidence: ${options.confidence}`));
133
+ }
134
+ if (options.weight) {
135
+ console.log(chalk.dim(` Weight: ${options.weight}`));
136
+ }
123
137
  if (Object.keys(metadata).length > 0) {
124
138
  console.log(chalk.dim(` Metadata: ${JSON.stringify(metadata)}`));
125
139
  }
126
140
  }
127
141
  else {
128
- formatOutput({ id: result, metadata }, options);
142
+ formatOutput({ id: result, metadata, confidence: addParams.confidence, weight: addParams.weight }, options);
129
143
  }
130
144
  }
131
145
  catch (error) {
@@ -260,6 +274,10 @@ export const coreCommands = {
260
274
  if (options.includeRelations) {
261
275
  searchParams.includeRelations = true;
262
276
  }
277
+ // Include VFS files (v4.4.0 - find excludes VFS by default)
278
+ if (options.includeVfs) {
279
+ searchParams.includeVFS = true;
280
+ }
263
281
  // Triple Intelligence Fusion - custom weighting
264
282
  if (options.fusion || options.vectorWeight || options.graphWeight || options.fieldWeight) {
265
283
  searchParams.fusion = {
@@ -120,19 +120,25 @@ export const importCommands = {
120
120
  }]);
121
121
  options.recursive = answer.recursive;
122
122
  }
123
- spinner = ora('Initializing neural import...').start();
123
+ spinner = ora('Initializing import...').start();
124
124
  const brain = getBrainy();
125
- // Load UniversalImportAPI
126
- const { UniversalImportAPI } = await import('../../api/UniversalImportAPI.js');
127
- const universalImport = new UniversalImportAPI(brain);
128
- await universalImport.init();
129
- spinner.text = 'Processing import...';
130
125
  // Handle different source types
131
126
  let result;
132
127
  if (isURL) {
133
- // URL import
128
+ // URL import - fetch first
134
129
  spinner.text = `Fetching from ${source}...`;
135
- result = await universalImport.importFromURL(source);
130
+ const response = await fetch(source);
131
+ const buffer = Buffer.from(await response.arrayBuffer());
132
+ spinner.text = 'Importing...';
133
+ result = await brain.import(buffer, {
134
+ enableNeuralExtraction: options.extractEntities !== false,
135
+ enableRelationshipInference: options.detectRelationships !== false,
136
+ enableConceptExtraction: options.extractConcepts || false,
137
+ confidenceThreshold: options.confidence ? parseFloat(options.confidence) : 0.6,
138
+ onProgress: options.progress ? (p) => {
139
+ spinner.text = `${p.message}${p.entities ? ` (${p.entities} entities)` : ''}`;
140
+ } : undefined
141
+ });
136
142
  }
137
143
  else if (isDirectory) {
138
144
  // Directory import - process each file
@@ -163,31 +169,42 @@ export const importCommands = {
163
169
  };
164
170
  await collectFiles(source);
165
171
  spinner.succeed(`Found ${files.length} files`);
166
- // Process files in batches
167
- const batchSize = options.batchSize ? parseInt(options.batchSize) : 100;
172
+ // Process files with progress
168
173
  let totalEntities = 0;
169
174
  let totalRelationships = 0;
170
175
  let filesProcessed = 0;
171
- for (let i = 0; i < files.length; i += batchSize) {
172
- const batch = files.slice(i, i + batchSize);
173
- if (options.progress) {
174
- spinner = ora(`Processing batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(files.length / batchSize)} (${filesProcessed}/${files.length} files)...`).start();
175
- }
176
- for (const file of batch) {
177
- try {
178
- const fileResult = await universalImport.importFromFile(file);
179
- totalEntities += fileResult.stats.entitiesCreated;
180
- totalRelationships += fileResult.stats.relationshipsCreated;
181
- filesProcessed++;
176
+ for (const file of files) {
177
+ try {
178
+ if (options.progress) {
179
+ spinner = ora(`[${filesProcessed + 1}/${files.length}] Importing ${file}...`).start();
182
180
  }
183
- catch (error) {
184
- if (options.verbose) {
185
- console.log(chalk.yellow(`⚠️ Failed to import ${file}: ${error.message}`));
186
- }
181
+ const fileResult = await brain.import(file, {
182
+ enableNeuralExtraction: options.extractEntities !== false,
183
+ enableRelationshipInference: options.detectRelationships !== false,
184
+ enableConceptExtraction: options.extractConcepts || false,
185
+ confidenceThreshold: options.confidence ? parseFloat(options.confidence) : 0.6,
186
+ onProgress: options.progress ? (p) => {
187
+ spinner.text = `[${filesProcessed + 1}/${files.length}] ${p.message}`;
188
+ } : undefined
189
+ });
190
+ totalEntities += fileResult.entities.length;
191
+ totalRelationships += fileResult.relationships.length;
192
+ filesProcessed++;
193
+ if (options.progress) {
194
+ spinner.succeed(`[${filesProcessed}/${files.length}] ${file}`);
195
+ }
196
+ }
197
+ catch (error) {
198
+ if (options.verbose) {
199
+ if (spinner)
200
+ spinner.fail(`Failed: ${file}`);
201
+ console.log(chalk.yellow(`⚠️ ${error.message}`));
187
202
  }
188
203
  }
189
204
  }
190
205
  result = {
206
+ entities: [],
207
+ relationships: [],
191
208
  stats: {
192
209
  filesProcessed,
193
210
  entitiesCreated: totalEntities,
@@ -195,11 +212,23 @@ export const importCommands = {
195
212
  totalProcessed: filesProcessed
196
213
  }
197
214
  };
198
- spinner.succeed('Directory import complete');
215
+ spinner = ora().succeed(`Directory import complete: ${filesProcessed} files`);
199
216
  }
200
217
  else {
201
- // File import
202
- result = await universalImport.importFromFile(source);
218
+ // File import with progress
219
+ result = await brain.import(source, {
220
+ format: options.format,
221
+ enableNeuralExtraction: options.extractEntities !== false,
222
+ enableRelationshipInference: options.detectRelationships !== false,
223
+ enableConceptExtraction: options.extractConcepts || false,
224
+ confidenceThreshold: options.confidence ? parseFloat(options.confidence) : 0.6,
225
+ onProgress: options.progress ? (p) => {
226
+ spinner.text = `${p.message}${p.entities ? ` (${p.entities} entities, ${p.relationships || 0} relationships)` : ''}`;
227
+ if (p.throughput && p.eta) {
228
+ spinner.text += ` - ${p.throughput.toFixed(1)}/sec, ETA: ${Math.round(p.eta / 1000)}s`;
229
+ }
230
+ } : undefined
231
+ });
203
232
  }
204
233
  spinner.succeed('Import complete');
205
234
  // Post-processing: extract concepts if requested
@@ -270,15 +299,21 @@ export const importCommands = {
270
299
  if (!options.json && !options.quiet) {
271
300
  console.log(chalk.cyan('\n📊 Import Results:\n'));
272
301
  console.log(chalk.bold('Statistics:'));
273
- console.log(` Entities created: ${chalk.green(result.stats.entitiesCreated)}`);
274
- if (result.stats.relationshipsCreated > 0) {
275
- console.log(` Relationships created: ${chalk.green(result.stats.relationshipsCreated)}`);
302
+ const entitiesCount = result.stats?.entitiesCreated || result.entities?.length || 0;
303
+ const relationshipsCount = result.stats?.relationshipsCreated || result.relationships?.length || 0;
304
+ console.log(` Entities created: ${chalk.green(entitiesCount)}`);
305
+ if (relationshipsCount > 0) {
306
+ console.log(` Relationships created: ${chalk.green(relationshipsCount)}`);
276
307
  }
277
- if (result.stats.filesProcessed) {
308
+ if (result.stats?.filesProcessed) {
278
309
  console.log(` Files processed: ${chalk.green(result.stats.filesProcessed)}`);
279
310
  }
280
- console.log(` Average confidence: ${chalk.yellow((result.stats.averageConfidence * 100).toFixed(1))}%`);
281
- console.log(` Processing time: ${chalk.dim(result.stats.processingTimeMs)}ms`);
311
+ if (result.stats?.averageConfidence) {
312
+ console.log(` Average confidence: ${chalk.yellow((result.stats.averageConfidence * 100).toFixed(1))}%`);
313
+ }
314
+ if (result.stats?.processingTimeMs) {
315
+ console.log(` Processing time: ${chalk.dim(result.stats.processingTimeMs)}ms`);
316
+ }
282
317
  if (options.verbose && result.entities && result.entities.length > 0) {
283
318
  console.log(chalk.bold('\n📦 Imported Entities (first 10):'));
284
319
  result.entities.slice(0, 10).forEach((entity, i) => {
@@ -53,10 +53,44 @@ export class SmartCSVImporter {
53
53
  ...options
54
54
  };
55
55
  // Parse CSV using existing handler
56
+ // v4.5.0: Pass progress hooks to handler for file parsing progress
56
57
  const processedData = await this.csvHandler.process(buffer, {
57
58
  ...options,
58
59
  csvDelimiter: opts.csvDelimiter,
59
- csvHeaders: opts.csvHeaders
60
+ csvHeaders: opts.csvHeaders,
61
+ totalBytes: buffer.length,
62
+ progressHooks: {
63
+ onBytesProcessed: (bytes) => {
64
+ // Handler reports bytes processed during parsing
65
+ opts.onProgress?.({
66
+ processed: 0,
67
+ total: 0,
68
+ entities: 0,
69
+ relationships: 0,
70
+ phase: `Parsing CSV (${Math.round((bytes / buffer.length) * 100)}%)`
71
+ });
72
+ },
73
+ onCurrentItem: (message) => {
74
+ // Handler reports current processing step
75
+ opts.onProgress?.({
76
+ processed: 0,
77
+ total: 0,
78
+ entities: 0,
79
+ relationships: 0,
80
+ phase: message
81
+ });
82
+ },
83
+ onDataExtracted: (count, total) => {
84
+ // Handler reports rows extracted
85
+ opts.onProgress?.({
86
+ processed: 0,
87
+ total: total || count,
88
+ entities: 0,
89
+ relationships: 0,
90
+ phase: `Extracted ${count} rows`
91
+ });
92
+ }
93
+ }
60
94
  });
61
95
  const rows = processedData.data;
62
96
  if (rows.length === 0) {
@@ -54,10 +54,22 @@ export class SmartDOCXImporter {
54
54
  if (!this.mammothLoaded) {
55
55
  await this.init();
56
56
  }
57
+ // v4.5.0: Report parsing start
58
+ options.onProgress?.({
59
+ processed: 0,
60
+ entities: 0,
61
+ relationships: 0
62
+ });
57
63
  // Extract raw text for entity extraction
58
64
  const textResult = await mammoth.extractRawText({ buffer });
59
65
  // Extract HTML for structure analysis (headings, tables)
60
66
  const htmlResult = await mammoth.convertToHtml({ buffer });
67
+ // v4.5.0: Report parsing complete
68
+ options.onProgress?.({
69
+ processed: 0,
70
+ entities: 0,
71
+ relationships: 0
72
+ });
61
73
  // Process the document
62
74
  const result = await this.extractFromContent(textResult.value, htmlResult.value, options);
63
75
  result.processingTime = Date.now() - startTime;
@@ -61,7 +61,43 @@ export class SmartExcelImporter {
61
61
  ...options
62
62
  };
63
63
  // Parse Excel using existing handler
64
- const processedData = await this.excelHandler.process(buffer, options);
64
+ // v4.5.0: Pass progress hooks to handler for file parsing progress
65
+ const processedData = await this.excelHandler.process(buffer, {
66
+ ...options,
67
+ totalBytes: buffer.length,
68
+ progressHooks: {
69
+ onBytesProcessed: (bytes) => {
70
+ // Handler reports bytes processed during parsing
71
+ opts.onProgress?.({
72
+ processed: 0,
73
+ total: 0,
74
+ entities: 0,
75
+ relationships: 0,
76
+ phase: `Parsing Excel (${Math.round((bytes / buffer.length) * 100)}%)`
77
+ });
78
+ },
79
+ onCurrentItem: (message) => {
80
+ // Handler reports current processing step (e.g., "Reading sheet: Sales (1/3)")
81
+ opts.onProgress?.({
82
+ processed: 0,
83
+ total: 0,
84
+ entities: 0,
85
+ relationships: 0,
86
+ phase: message
87
+ });
88
+ },
89
+ onDataExtracted: (count, total) => {
90
+ // Handler reports rows extracted
91
+ opts.onProgress?.({
92
+ processed: 0,
93
+ total: total || count,
94
+ entities: 0,
95
+ relationships: 0,
96
+ phase: `Extracted ${count} rows from Excel`
97
+ });
98
+ }
99
+ }
100
+ });
65
101
  const rows = processedData.data;
66
102
  if (rows.length === 0) {
67
103
  return this.emptyResult(startTime);
@@ -48,6 +48,12 @@ export class SmartJSONImporter {
48
48
  onProgress: () => { },
49
49
  ...options
50
50
  };
51
+ // v4.5.0: Report parsing start
52
+ opts.onProgress({
53
+ processed: 0,
54
+ entities: 0,
55
+ relationships: 0
56
+ });
51
57
  // Parse JSON if string
52
58
  let jsonData;
53
59
  if (typeof data === 'string') {
@@ -61,6 +67,12 @@ export class SmartJSONImporter {
61
67
  else {
62
68
  jsonData = data;
63
69
  }
70
+ // v4.5.0: Report parsing complete, starting traversal
71
+ opts.onProgress({
72
+ processed: 0,
73
+ entities: 0,
74
+ relationships: 0
75
+ });
64
76
  // Traverse and extract
65
77
  const entities = [];
66
78
  const relationships = [];
@@ -82,6 +94,12 @@ export class SmartJSONImporter {
82
94
  });
83
95
  }
84
96
  });
97
+ // v4.5.0: Report completion
98
+ opts.onProgress({
99
+ processed: nodesProcessed,
100
+ entities: entities.length,
101
+ relationships: relationships.length
102
+ });
85
103
  return {
86
104
  nodesProcessed,
87
105
  entitiesExtracted: entities.length,
@@ -46,8 +46,22 @@ export class SmartMarkdownImporter {
46
46
  onProgress: () => { },
47
47
  ...options
48
48
  };
49
+ // v4.5.0: Report parsing start
50
+ opts.onProgress({
51
+ processed: 0,
52
+ total: 0,
53
+ entities: 0,
54
+ relationships: 0
55
+ });
49
56
  // Parse markdown into sections
50
57
  const parsedSections = this.parseMarkdown(markdown, opts);
58
+ // v4.5.0: Report parsing complete
59
+ opts.onProgress({
60
+ processed: 0,
61
+ total: parsedSections.length,
62
+ entities: 0,
63
+ relationships: 0
64
+ });
51
65
  // Process each section
52
66
  const sections = [];
53
67
  const entityMap = new Map();
@@ -69,10 +83,19 @@ export class SmartMarkdownImporter {
69
83
  relationships: sections.reduce((sum, s) => sum + s.relationships.length, 0)
70
84
  });
71
85
  }
86
+ // v4.5.0: Report completion
87
+ const totalEntities = sections.reduce((sum, s) => sum + s.entities.length, 0);
88
+ const totalRelationships = sections.reduce((sum, s) => sum + s.relationships.length, 0);
89
+ opts.onProgress({
90
+ processed: sections.length,
91
+ total: sections.length,
92
+ entities: totalEntities,
93
+ relationships: totalRelationships
94
+ });
72
95
  return {
73
96
  sectionsProcessed: sections.length,
74
- entitiesExtracted: sections.reduce((sum, s) => sum + s.entities.length, 0),
75
- relationshipsInferred: sections.reduce((sum, s) => sum + s.relationships.length, 0),
97
+ entitiesExtracted: totalEntities,
98
+ relationshipsInferred: totalRelationships,
76
99
  sections,
77
100
  entityMap,
78
101
  processingTime: Date.now() - startTime,
@@ -49,7 +49,43 @@ export class SmartPDFImporter {
49
49
  ...options
50
50
  };
51
51
  // Parse PDF using existing handler
52
- const processedData = await this.pdfHandler.process(buffer, options);
52
+ // v4.5.0: Pass progress hooks to handler for file parsing progress
53
+ const processedData = await this.pdfHandler.process(buffer, {
54
+ ...options,
55
+ totalBytes: buffer.length,
56
+ progressHooks: {
57
+ onBytesProcessed: (bytes) => {
58
+ // Handler reports bytes processed during parsing
59
+ opts.onProgress?.({
60
+ processed: 0,
61
+ total: 0,
62
+ entities: 0,
63
+ relationships: 0,
64
+ phase: `Parsing PDF (${Math.round((bytes / buffer.length) * 100)}%)`
65
+ });
66
+ },
67
+ onCurrentItem: (message) => {
68
+ // Handler reports current processing step (e.g., "Processing page 5 of 23")
69
+ opts.onProgress?.({
70
+ processed: 0,
71
+ total: 0,
72
+ entities: 0,
73
+ relationships: 0,
74
+ phase: message
75
+ });
76
+ },
77
+ onDataExtracted: (count, total) => {
78
+ // Handler reports items extracted (paragraphs + tables)
79
+ opts.onProgress?.({
80
+ processed: 0,
81
+ total: total || count,
82
+ entities: 0,
83
+ relationships: 0,
84
+ phase: `Extracted ${count} items from PDF`
85
+ });
86
+ }
87
+ }
88
+ });
53
89
  const data = processedData.data;
54
90
  const pdfMetadata = processedData.metadata.additionalInfo?.pdfMetadata || {};
55
91
  if (data.length === 0) {
@@ -37,6 +37,12 @@ export class SmartYAMLImporter {
37
37
  */
38
38
  async extract(yamlContent, options = {}) {
39
39
  const startTime = Date.now();
40
+ // v4.5.0: Report parsing start
41
+ options.onProgress?.({
42
+ processed: 0,
43
+ entities: 0,
44
+ relationships: 0
45
+ });
40
46
  // Parse YAML to JavaScript object
41
47
  const yamlString = typeof yamlContent === 'string'
42
48
  ? yamlContent
@@ -48,6 +54,12 @@ export class SmartYAMLImporter {
48
54
  catch (error) {
49
55
  throw new Error(`Failed to parse YAML: ${error.message}`);
50
56
  }
57
+ // v4.5.0: Report parsing complete
58
+ options.onProgress?.({
59
+ processed: 0,
60
+ entities: 0,
61
+ relationships: 0
62
+ });
51
63
  // Process as JSON-like structure
52
64
  const result = await this.extractFromData(data, options);
53
65
  result.processingTime = Date.now() - startTime;
@@ -146,6 +146,7 @@ export interface FindParams<T = any> {
146
146
  mode?: SearchMode;
147
147
  explain?: boolean;
148
148
  includeRelations?: boolean;
149
+ includeVFS?: boolean;
149
150
  service?: string;
150
151
  fusion?: {
151
152
  strategy?: 'adaptive' | 'weighted' | 'progressive';
@@ -183,6 +184,7 @@ export interface SimilarParams<T = any> {
183
184
  type?: NounType | NounType[];
184
185
  where?: Partial<T>;
185
186
  service?: string;
187
+ includeVFS?: boolean;
186
188
  }
187
189
  /**
188
190
  * Parameters for getting relationships
@@ -317,6 +319,102 @@ export interface BatchResult<T = any> {
317
319
  total: number;
318
320
  duration: number;
319
321
  }
322
+ /**
323
+ * Import stage enumeration
324
+ */
325
+ export type ImportStage = 'detecting' | 'reading' | 'parsing' | 'extracting' | 'indexing' | 'completing';
326
+ /**
327
+ * Overall import status
328
+ */
329
+ export type ImportStatus = 'starting' | 'processing' | 'completing' | 'done';
330
+ /**
331
+ * Comprehensive import progress information
332
+ *
333
+ * Provides multi-dimensional progress tracking:
334
+ * - Bytes processed (always deterministic)
335
+ * - Entities extracted and indexed
336
+ * - Stage-specific progress
337
+ * - Time estimates
338
+ * - Performance metrics
339
+ *
340
+ * @since v4.5.0
341
+ */
342
+ export interface ImportProgress {
343
+ overall_progress: number;
344
+ overall_status: ImportStatus;
345
+ stage: ImportStage;
346
+ stage_progress: number;
347
+ stage_message: string;
348
+ bytes_processed: number;
349
+ total_bytes: number;
350
+ bytes_percentage: number;
351
+ bytes_per_second?: number;
352
+ entities_extracted: number;
353
+ entities_indexed: number;
354
+ entities_per_second?: number;
355
+ estimated_total_entities?: number;
356
+ estimation_confidence?: number;
357
+ elapsed_ms: number;
358
+ estimated_remaining_ms?: number;
359
+ estimated_total_ms?: number;
360
+ current_item?: string;
361
+ current_file?: string;
362
+ file_number?: number;
363
+ total_files?: number;
364
+ metrics?: {
365
+ parsing_rate_mbps?: number;
366
+ extraction_rate_entities_per_sec?: number;
367
+ indexing_rate_entities_per_sec?: number;
368
+ memory_usage_mb?: number;
369
+ peak_memory_mb?: number;
370
+ };
371
+ current: number;
372
+ total: number;
373
+ }
374
+ /**
375
+ * Import progress callback - backwards compatible
376
+ *
377
+ * Supports both legacy (current, total) and new (ImportProgress object) signatures
378
+ */
379
+ export type ImportProgressCallback = ((progress: ImportProgress) => void) | ((current: number, total: number) => void);
380
+ /**
381
+ * Stage weight configuration for overall progress calculation
382
+ *
383
+ * These weights reflect the typical time distribution across stages.
384
+ * Extraction is typically the slowest stage (60% of time).
385
+ */
386
+ export interface StageWeights {
387
+ detecting: number;
388
+ reading: number;
389
+ parsing: number;
390
+ extracting: number;
391
+ indexing: number;
392
+ completing: number;
393
+ }
394
+ /**
395
+ * Import result statistics
396
+ */
397
+ export interface ImportStats {
398
+ graphNodesCreated: number;
399
+ graphEdgesCreated: number;
400
+ vfsFilesCreated: number;
401
+ duration: number;
402
+ bytesProcessed: number;
403
+ averageRate: number;
404
+ peakMemoryMB?: number;
405
+ }
406
+ /**
407
+ * Import operation result
408
+ */
409
+ export interface ImportResult {
410
+ success: boolean;
411
+ stats: ImportStats;
412
+ errors?: Array<{
413
+ stage: ImportStage;
414
+ message: string;
415
+ error?: any;
416
+ }>;
417
+ }
320
418
  /**
321
419
  * Graph traversal parameters
322
420
  */