@soulcraft/brainy 3.50.1 ā 4.0.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.
- package/CHANGELOG.md +242 -0
- package/README.md +358 -658
- package/dist/api/ConfigAPI.js +56 -19
- package/dist/api/DataAPI.js +24 -18
- package/dist/augmentations/storageAugmentations.d.ts +24 -0
- package/dist/augmentations/storageAugmentations.js +22 -0
- package/dist/brainy.js +32 -9
- package/dist/cli/commands/core.d.ts +20 -10
- package/dist/cli/commands/core.js +384 -82
- package/dist/cli/commands/import.d.ts +41 -0
- package/dist/cli/commands/import.js +456 -0
- package/dist/cli/commands/insights.d.ts +34 -0
- package/dist/cli/commands/insights.js +300 -0
- package/dist/cli/commands/neural.d.ts +6 -12
- package/dist/cli/commands/neural.js +113 -10
- package/dist/cli/commands/nlp.d.ts +28 -0
- package/dist/cli/commands/nlp.js +246 -0
- package/dist/cli/commands/storage.d.ts +64 -0
- package/dist/cli/commands/storage.js +730 -0
- package/dist/cli/index.js +210 -24
- package/dist/coreTypes.d.ts +206 -34
- package/dist/distributed/configManager.js +8 -6
- package/dist/distributed/shardMigration.js +2 -0
- package/dist/distributed/storageDiscovery.js +6 -4
- package/dist/embeddings/EmbeddingManager.d.ts +2 -2
- package/dist/embeddings/EmbeddingManager.js +5 -1
- package/dist/graph/lsm/LSMTree.js +32 -20
- package/dist/hnsw/typeAwareHNSWIndex.js +6 -2
- package/dist/storage/adapters/azureBlobStorage.d.ts +545 -0
- package/dist/storage/adapters/azureBlobStorage.js +1809 -0
- package/dist/storage/adapters/baseStorageAdapter.d.ts +16 -13
- package/dist/storage/adapters/fileSystemStorage.d.ts +21 -9
- package/dist/storage/adapters/fileSystemStorage.js +204 -127
- package/dist/storage/adapters/gcsStorage.d.ts +119 -9
- package/dist/storage/adapters/gcsStorage.js +317 -62
- package/dist/storage/adapters/memoryStorage.d.ts +30 -18
- package/dist/storage/adapters/memoryStorage.js +99 -94
- package/dist/storage/adapters/opfsStorage.d.ts +48 -10
- package/dist/storage/adapters/opfsStorage.js +201 -80
- package/dist/storage/adapters/r2Storage.d.ts +12 -5
- package/dist/storage/adapters/r2Storage.js +63 -15
- package/dist/storage/adapters/s3CompatibleStorage.d.ts +164 -17
- package/dist/storage/adapters/s3CompatibleStorage.js +472 -80
- package/dist/storage/adapters/typeAwareStorageAdapter.d.ts +38 -6
- package/dist/storage/adapters/typeAwareStorageAdapter.js +218 -39
- package/dist/storage/baseStorage.d.ts +41 -38
- package/dist/storage/baseStorage.js +110 -134
- package/dist/storage/storageFactory.d.ts +29 -2
- package/dist/storage/storageFactory.js +30 -1
- package/dist/utils/entityIdMapper.js +5 -2
- package/dist/utils/fieldTypeInference.js +8 -1
- package/dist/utils/metadataFilter.d.ts +3 -2
- package/dist/utils/metadataFilter.js +1 -0
- package/dist/utils/metadataIndex.d.ts +2 -1
- package/dist/utils/metadataIndex.js +9 -1
- package/dist/utils/metadataIndexChunking.js +9 -4
- package/dist/utils/periodicCleanup.js +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Insights & Analytics Commands
|
|
3
|
+
*
|
|
4
|
+
* Database insights, field statistics, and query optimization
|
|
5
|
+
*/
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
import inquirer from 'inquirer';
|
|
9
|
+
import Table from 'cli-table3';
|
|
10
|
+
import { Brainy } from '../../brainy.js';
|
|
11
|
+
let brainyInstance = null;
|
|
12
|
+
const getBrainy = () => {
|
|
13
|
+
if (!brainyInstance) {
|
|
14
|
+
brainyInstance = new Brainy();
|
|
15
|
+
}
|
|
16
|
+
return brainyInstance;
|
|
17
|
+
};
|
|
18
|
+
const formatOutput = (data, options) => {
|
|
19
|
+
if (options.json) {
|
|
20
|
+
console.log(options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data));
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
export const insightsCommands = {
|
|
24
|
+
/**
|
|
25
|
+
* Get comprehensive database insights
|
|
26
|
+
*/
|
|
27
|
+
async insights(options) {
|
|
28
|
+
const spinner = ora('Analyzing database...').start();
|
|
29
|
+
try {
|
|
30
|
+
const brain = getBrainy();
|
|
31
|
+
// Get insights from Brainy
|
|
32
|
+
const insights = await brain.insights();
|
|
33
|
+
spinner.succeed('Analysis complete');
|
|
34
|
+
if (!options.json) {
|
|
35
|
+
console.log(chalk.cyan('\nš Database Insights:\n'));
|
|
36
|
+
// Overview - using actual insights return type
|
|
37
|
+
console.log(chalk.bold('Overview:'));
|
|
38
|
+
console.log(` Total Entities: ${chalk.yellow(insights.entities)}`);
|
|
39
|
+
console.log(` Total Relationships: ${chalk.yellow(insights.relationships)}`);
|
|
40
|
+
console.log(` Unique Types: ${chalk.yellow(Object.keys(insights.types).length)}`);
|
|
41
|
+
console.log(` Active Services: ${chalk.yellow(insights.services.join(', '))}`);
|
|
42
|
+
console.log(` Graph Density: ${chalk.yellow((insights.density * 100).toFixed(2))}%`);
|
|
43
|
+
// Entity types breakdown
|
|
44
|
+
const typeEntries = Object.entries(insights.types).sort((a, b) => b[1] - a[1]);
|
|
45
|
+
if (typeEntries.length > 0) {
|
|
46
|
+
console.log(chalk.bold('\nš Entities by Type:'));
|
|
47
|
+
const typeTable = new Table({
|
|
48
|
+
head: [chalk.cyan('Type'), chalk.cyan('Count'), chalk.cyan('Percentage')],
|
|
49
|
+
colWidths: [25, 12, 15]
|
|
50
|
+
});
|
|
51
|
+
typeEntries.slice(0, 10).forEach(([type, count]) => {
|
|
52
|
+
const percentage = insights.entities > 0 ? (count / insights.entities * 100) : 0;
|
|
53
|
+
typeTable.push([
|
|
54
|
+
type,
|
|
55
|
+
count.toString(),
|
|
56
|
+
`${percentage.toFixed(1)}%`
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
console.log(typeTable.toString());
|
|
60
|
+
if (typeEntries.length > 10) {
|
|
61
|
+
console.log(chalk.dim(`\n... and ${typeEntries.length - 10} more types`));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Recommendations based on actual data
|
|
65
|
+
console.log(chalk.bold('\nš” Recommendations:'));
|
|
66
|
+
if (insights.entities === 0) {
|
|
67
|
+
console.log(` ${chalk.yellow('ā')} Database is empty - add entities to get started`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
if (insights.density < 0.1) {
|
|
71
|
+
console.log(` ${chalk.yellow('ā')} Low graph density - consider adding more relationships`);
|
|
72
|
+
}
|
|
73
|
+
if (insights.relationships === 0) {
|
|
74
|
+
console.log(` ${chalk.yellow('ā')} No relationships yet - use 'brainy relate' to connect entities`);
|
|
75
|
+
}
|
|
76
|
+
if (Object.keys(insights.types).length === 1) {
|
|
77
|
+
console.log(` ${chalk.yellow('ā')} Only one entity type - consider adding diverse types for better organization`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
formatOutput(insights, options);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
spinner.fail('Failed to get insights');
|
|
87
|
+
console.error(chalk.red('Insights failed:', error.message));
|
|
88
|
+
if (options.verbose) {
|
|
89
|
+
console.error(chalk.dim(error.stack));
|
|
90
|
+
}
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
/**
|
|
95
|
+
* Get available fields across all entities
|
|
96
|
+
*/
|
|
97
|
+
async fields(options) {
|
|
98
|
+
const spinner = ora('Analyzing fields...').start();
|
|
99
|
+
try {
|
|
100
|
+
const brain = getBrainy();
|
|
101
|
+
// Get available fields from metadata index
|
|
102
|
+
const fields = await brain.getAvailableFields();
|
|
103
|
+
spinner.succeed(`Found ${fields.length} fields`);
|
|
104
|
+
if (!options.json) {
|
|
105
|
+
if (fields.length === 0) {
|
|
106
|
+
console.log(chalk.yellow('\nNo metadata fields found'));
|
|
107
|
+
console.log(chalk.dim('Add entities with metadata to see field statistics'));
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.log(chalk.cyan(`\nš Available Fields (${fields.length}):\n`));
|
|
111
|
+
// Get statistics for each field
|
|
112
|
+
const statistics = await brain.getFieldStatistics();
|
|
113
|
+
const table = new Table({
|
|
114
|
+
head: [chalk.cyan('Field'), chalk.cyan('Occurrences'), chalk.cyan('Unique Values')],
|
|
115
|
+
colWidths: [30, 15, 20]
|
|
116
|
+
});
|
|
117
|
+
for (const field of fields.slice(0, 50)) {
|
|
118
|
+
const stats = statistics.get(field);
|
|
119
|
+
table.push([
|
|
120
|
+
field,
|
|
121
|
+
stats?.count || 0,
|
|
122
|
+
stats?.uniqueValues || 0
|
|
123
|
+
]);
|
|
124
|
+
}
|
|
125
|
+
console.log(table.toString());
|
|
126
|
+
if (fields.length > 50) {
|
|
127
|
+
console.log(chalk.dim(`\n... and ${fields.length - 50} more fields`));
|
|
128
|
+
}
|
|
129
|
+
console.log(chalk.dim('\nš” Use --json to see all fields'));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const statistics = await brain.getFieldStatistics();
|
|
134
|
+
const fieldsWithStats = fields.map(field => ({
|
|
135
|
+
field,
|
|
136
|
+
...Object.fromEntries(statistics.get(field) || [])
|
|
137
|
+
}));
|
|
138
|
+
formatOutput(fieldsWithStats, options);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
spinner.fail('Failed to get fields');
|
|
143
|
+
console.error(chalk.red('Fields analysis failed:', error.message));
|
|
144
|
+
if (options.verbose) {
|
|
145
|
+
console.error(chalk.dim(error.stack));
|
|
146
|
+
}
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
/**
|
|
151
|
+
* Get field values for a specific field
|
|
152
|
+
*/
|
|
153
|
+
async fieldValues(field, options) {
|
|
154
|
+
let spinner = null;
|
|
155
|
+
try {
|
|
156
|
+
// Interactive mode if no field provided
|
|
157
|
+
if (!field) {
|
|
158
|
+
spinner = ora('Getting available fields...').start();
|
|
159
|
+
const brain = getBrainy();
|
|
160
|
+
const availableFields = await brain.getAvailableFields();
|
|
161
|
+
spinner.stop();
|
|
162
|
+
const answer = await inquirer.prompt([{
|
|
163
|
+
type: 'list',
|
|
164
|
+
name: 'field',
|
|
165
|
+
message: 'Select field:',
|
|
166
|
+
choices: availableFields.slice(0, 50),
|
|
167
|
+
pageSize: 15
|
|
168
|
+
}]);
|
|
169
|
+
field = answer.field;
|
|
170
|
+
}
|
|
171
|
+
spinner = ora(`Getting values for field: ${field}...`).start();
|
|
172
|
+
const brain = getBrainy();
|
|
173
|
+
const values = await brain.getFieldValues(field);
|
|
174
|
+
const limit = options.limit ? parseInt(options.limit) : 100;
|
|
175
|
+
spinner.succeed(`Found ${values.length} unique values`);
|
|
176
|
+
if (!options.json) {
|
|
177
|
+
console.log(chalk.cyan(`\nš Values for field "${chalk.bold(field)}":\n`));
|
|
178
|
+
if (values.length === 0) {
|
|
179
|
+
console.log(chalk.yellow('No values found for this field'));
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// Group by value and count
|
|
183
|
+
const valueCounts = values.reduce((acc, val) => {
|
|
184
|
+
acc[val] = (acc[val] || 0) + 1;
|
|
185
|
+
return acc;
|
|
186
|
+
}, {});
|
|
187
|
+
const sorted = Object.entries(valueCounts)
|
|
188
|
+
.sort((a, b) => b[1] - a[1])
|
|
189
|
+
.slice(0, limit);
|
|
190
|
+
const table = new Table({
|
|
191
|
+
head: [chalk.cyan('Value'), chalk.cyan('Count')],
|
|
192
|
+
colWidths: [50, 12]
|
|
193
|
+
});
|
|
194
|
+
sorted.forEach(([value, count]) => {
|
|
195
|
+
table.push([value, count.toString()]);
|
|
196
|
+
});
|
|
197
|
+
console.log(table.toString());
|
|
198
|
+
if (values.length > limit) {
|
|
199
|
+
console.log(chalk.dim(`\n... and ${values.length - limit} more values (use --limit to show more)`));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
formatOutput({ field, values, count: values.length }, options);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
if (spinner)
|
|
209
|
+
spinner.fail('Failed to get field values');
|
|
210
|
+
console.error(chalk.red('Field values failed:', error.message));
|
|
211
|
+
if (options.verbose) {
|
|
212
|
+
console.error(chalk.dim(error.stack));
|
|
213
|
+
}
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
/**
|
|
218
|
+
* Get optimal query plan for filters
|
|
219
|
+
*/
|
|
220
|
+
async queryPlan(options) {
|
|
221
|
+
let spinner = null;
|
|
222
|
+
try {
|
|
223
|
+
let filters = {};
|
|
224
|
+
// Interactive mode if no filters provided
|
|
225
|
+
if (!options.filters) {
|
|
226
|
+
const answer = await inquirer.prompt([{
|
|
227
|
+
type: 'editor',
|
|
228
|
+
name: 'filters',
|
|
229
|
+
message: 'Enter filter JSON (e.g., {"status": "active", "priority": {"$gte": 5}}):',
|
|
230
|
+
validate: (input) => {
|
|
231
|
+
if (!input.trim())
|
|
232
|
+
return 'Filters cannot be empty';
|
|
233
|
+
try {
|
|
234
|
+
JSON.parse(input);
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
return 'Invalid JSON format';
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}]);
|
|
242
|
+
filters = JSON.parse(answer.filters);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
try {
|
|
246
|
+
filters = JSON.parse(options.filters);
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
console.error(chalk.red('Invalid JSON in --filters'));
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
spinner = ora('Analyzing optimal query plan...').start();
|
|
254
|
+
const brain = getBrainy();
|
|
255
|
+
const plan = await brain.getOptimalQueryPlan(filters);
|
|
256
|
+
spinner.succeed('Query plan generated');
|
|
257
|
+
if (!options.json) {
|
|
258
|
+
console.log(chalk.cyan('\nšÆ Optimal Query Plan:\n'));
|
|
259
|
+
console.log(chalk.bold('Filters:'));
|
|
260
|
+
console.log(JSON.stringify(filters, null, 2));
|
|
261
|
+
console.log(chalk.bold('\nš Query Execution Plan:'));
|
|
262
|
+
console.log(` Strategy: ${chalk.yellow(plan.strategy)}`);
|
|
263
|
+
console.log(` Estimated Cost: ${chalk.yellow(plan.estimatedCost)}`);
|
|
264
|
+
if (plan.fieldOrder && plan.fieldOrder.length > 0) {
|
|
265
|
+
console.log(chalk.bold('\nš Field Processing Order (Optimized):'));
|
|
266
|
+
plan.fieldOrder.forEach((field, index) => {
|
|
267
|
+
console.log(` ${index + 1}. ${chalk.green(field)}`);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
console.log(chalk.bold('\nš” Strategy Explanation:'));
|
|
271
|
+
if (plan.strategy === 'exact') {
|
|
272
|
+
console.log(` ${chalk.yellow('ā')} Using exact-match indexing for fast lookups`);
|
|
273
|
+
}
|
|
274
|
+
else if (plan.strategy === 'range') {
|
|
275
|
+
console.log(` ${chalk.yellow('ā')} Using range-based scanning for numeric/date filters`);
|
|
276
|
+
}
|
|
277
|
+
else if (plan.strategy === 'hybrid') {
|
|
278
|
+
console.log(` ${chalk.yellow('ā')} Using hybrid approach combining multiple index types`);
|
|
279
|
+
}
|
|
280
|
+
console.log(chalk.bold('\nā” Performance Tips:'));
|
|
281
|
+
console.log(` ${chalk.yellow('ā')} Lower estimated cost means faster queries`);
|
|
282
|
+
console.log(` ${chalk.yellow('ā')} Fields are processed in optimal order`);
|
|
283
|
+
console.log(` ${chalk.yellow('ā')} Consider adding indexes for frequently used fields`);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
formatOutput({ filters, plan }, options);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
if (spinner)
|
|
291
|
+
spinner.fail('Failed to generate query plan');
|
|
292
|
+
console.error(chalk.red('Query plan failed:', error.message));
|
|
293
|
+
if (options.verbose) {
|
|
294
|
+
console.error(chalk.dim(error.stack));
|
|
295
|
+
}
|
|
296
|
+
process.exit(1);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
//# sourceMappingURL=insights.js.map
|
|
@@ -22,18 +22,12 @@ export declare const neuralCommand: {
|
|
|
22
22
|
builder: (yargs: any) => any;
|
|
23
23
|
handler: (argv: CommandArguments) => Promise<void>;
|
|
24
24
|
};
|
|
25
|
-
declare function handleSimilarCommand(neural: any, argv: CommandArguments): Promise<void>;
|
|
26
|
-
declare function handleClustersCommand(neural: any, argv: CommandArguments): Promise<void>;
|
|
27
|
-
declare function handleHierarchyCommand(neural: any, argv: CommandArguments): Promise<void>;
|
|
28
|
-
declare function handleNeighborsCommand(neural: any, argv: CommandArguments): Promise<void>;
|
|
29
|
-
declare function handleOutliersCommand(neural: any, argv: CommandArguments): Promise<void>;
|
|
30
|
-
declare function handleVisualizeCommand(neural: any, argv: CommandArguments): Promise<void>;
|
|
31
25
|
export declare const neuralCommands: {
|
|
32
|
-
similar:
|
|
33
|
-
cluster:
|
|
34
|
-
hierarchy:
|
|
35
|
-
related:
|
|
36
|
-
outliers:
|
|
37
|
-
visualize:
|
|
26
|
+
similar(a?: string, b?: string, options?: any): Promise<void>;
|
|
27
|
+
cluster(options?: any): Promise<void>;
|
|
28
|
+
hierarchy(id?: string, options?: any): Promise<void>;
|
|
29
|
+
related(id?: string, options?: any): Promise<void>;
|
|
30
|
+
outliers(options?: any): Promise<void>;
|
|
31
|
+
visualize(options?: any): Promise<void>;
|
|
38
32
|
};
|
|
39
33
|
export default neuralCommand;
|
|
@@ -205,13 +205,49 @@ async function handleSimilarCommand(neural, argv) {
|
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
async function handleClustersCommand(neural, argv) {
|
|
208
|
-
|
|
208
|
+
let spinner = null;
|
|
209
209
|
try {
|
|
210
|
-
|
|
210
|
+
let options = {
|
|
211
211
|
algorithm: argv.algorithm,
|
|
212
212
|
threshold: argv.threshold,
|
|
213
213
|
maxClusters: argv.limit
|
|
214
214
|
};
|
|
215
|
+
// Interactive mode if no algorithm specified or using defaults
|
|
216
|
+
if (!argv.algorithm || argv.algorithm === 'hierarchical') {
|
|
217
|
+
const answers = await inquirer.prompt([
|
|
218
|
+
{
|
|
219
|
+
type: 'list',
|
|
220
|
+
name: 'algorithm',
|
|
221
|
+
message: 'Choose clustering algorithm:',
|
|
222
|
+
default: argv.algorithm || 'hierarchical',
|
|
223
|
+
choices: [
|
|
224
|
+
{ name: 'š³ Hierarchical (Tree-based, best for natural grouping)', value: 'hierarchical' },
|
|
225
|
+
{ name: 'š K-Means (Fixed number of clusters)', value: 'kmeans' },
|
|
226
|
+
{ name: 'šÆ DBSCAN (Density-based, finds arbitrary shapes)', value: 'dbscan' }
|
|
227
|
+
]
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
type: 'number',
|
|
231
|
+
name: 'maxClusters',
|
|
232
|
+
message: 'Maximum number of clusters:',
|
|
233
|
+
default: argv.limit || 5,
|
|
234
|
+
when: (answers) => answers.algorithm === 'kmeans'
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
type: 'number',
|
|
238
|
+
name: 'threshold',
|
|
239
|
+
message: 'Similarity threshold (0-1):',
|
|
240
|
+
default: argv.threshold || 0.7,
|
|
241
|
+
validate: (input) => (input >= 0 && input <= 1) || 'Must be between 0 and 1'
|
|
242
|
+
}
|
|
243
|
+
]);
|
|
244
|
+
options = {
|
|
245
|
+
algorithm: answers.algorithm,
|
|
246
|
+
threshold: answers.threshold,
|
|
247
|
+
maxClusters: answers.maxClusters || options.maxClusters
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
const spinner = ora('šÆ Finding semantic clusters...').start();
|
|
215
251
|
const clusters = await neural.clusters(argv.query || options);
|
|
216
252
|
spinner.succeed(`ā
Found ${clusters.length} clusters`);
|
|
217
253
|
if (argv.format === 'json') {
|
|
@@ -242,7 +278,8 @@ async function handleClustersCommand(neural, argv) {
|
|
|
242
278
|
}
|
|
243
279
|
}
|
|
244
280
|
catch (error) {
|
|
245
|
-
spinner
|
|
281
|
+
if (spinner)
|
|
282
|
+
spinner.fail('š„ Failed to find clusters');
|
|
246
283
|
throw error;
|
|
247
284
|
}
|
|
248
285
|
}
|
|
@@ -505,14 +542,80 @@ function showHelp() {
|
|
|
505
542
|
console.log(' --explain, -e Include explanations');
|
|
506
543
|
console.log('');
|
|
507
544
|
}
|
|
545
|
+
// Commander-compatible wrappers
|
|
508
546
|
export const neuralCommands = {
|
|
509
|
-
similar
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
547
|
+
async similar(a, b, options) {
|
|
548
|
+
const brain = new Brainy();
|
|
549
|
+
const neural = brain.neural();
|
|
550
|
+
// Build argv-style object for handler
|
|
551
|
+
const argv = {
|
|
552
|
+
_: ['neural', 'similar', a || '', b || ''].filter(x => x),
|
|
553
|
+
id: a,
|
|
554
|
+
query: b,
|
|
555
|
+
explain: options?.explain,
|
|
556
|
+
...options
|
|
557
|
+
};
|
|
558
|
+
await handleSimilarCommand(neural, argv);
|
|
559
|
+
},
|
|
560
|
+
async cluster(options) {
|
|
561
|
+
const brain = new Brainy();
|
|
562
|
+
const neural = brain.neural();
|
|
563
|
+
const argv = {
|
|
564
|
+
_: ['neural', 'cluster'],
|
|
565
|
+
algorithm: options?.algorithm || 'hierarchical',
|
|
566
|
+
threshold: options?.threshold ? parseFloat(options.threshold) : 0.7,
|
|
567
|
+
limit: options?.maxClusters ? parseInt(options.maxClusters) : 10,
|
|
568
|
+
query: options?.near,
|
|
569
|
+
...options
|
|
570
|
+
};
|
|
571
|
+
await handleClustersCommand(neural, argv);
|
|
572
|
+
},
|
|
573
|
+
async hierarchy(id, options) {
|
|
574
|
+
const brain = new Brainy();
|
|
575
|
+
const neural = brain.neural();
|
|
576
|
+
const argv = {
|
|
577
|
+
_: ['neural', 'hierarchy', id || ''].filter(x => x),
|
|
578
|
+
id,
|
|
579
|
+
...options
|
|
580
|
+
};
|
|
581
|
+
await handleHierarchyCommand(neural, argv);
|
|
582
|
+
},
|
|
583
|
+
async related(id, options) {
|
|
584
|
+
const brain = new Brainy();
|
|
585
|
+
const neural = brain.neural();
|
|
586
|
+
const argv = {
|
|
587
|
+
_: ['neural', 'related', id || ''].filter(x => x),
|
|
588
|
+
id,
|
|
589
|
+
limit: options?.limit ? parseInt(options.limit) : 10,
|
|
590
|
+
threshold: options?.radius ? parseFloat(options.radius) : 0.3,
|
|
591
|
+
...options
|
|
592
|
+
};
|
|
593
|
+
await handleNeighborsCommand(neural, argv);
|
|
594
|
+
},
|
|
595
|
+
async outliers(options) {
|
|
596
|
+
const brain = new Brainy();
|
|
597
|
+
const neural = brain.neural();
|
|
598
|
+
const argv = {
|
|
599
|
+
_: ['neural', 'outliers'],
|
|
600
|
+
threshold: options?.threshold ? parseFloat(options.threshold) : 0.3,
|
|
601
|
+
explain: options?.explain,
|
|
602
|
+
...options
|
|
603
|
+
};
|
|
604
|
+
await handleOutliersCommand(neural, argv);
|
|
605
|
+
},
|
|
606
|
+
async visualize(options) {
|
|
607
|
+
const brain = new Brainy();
|
|
608
|
+
const neural = brain.neural();
|
|
609
|
+
const argv = {
|
|
610
|
+
_: ['neural', 'visualize'],
|
|
611
|
+
format: options?.format || 'json',
|
|
612
|
+
dimensions: options?.dimensions ? parseInt(options.dimensions) : 2,
|
|
613
|
+
limit: options?.maxNodes ? parseInt(options.maxNodes) : 500,
|
|
614
|
+
output: options?.output,
|
|
615
|
+
...options
|
|
616
|
+
};
|
|
617
|
+
await handleVisualizeCommand(neural, argv);
|
|
618
|
+
}
|
|
516
619
|
};
|
|
517
620
|
export default neuralCommand;
|
|
518
621
|
//# sourceMappingURL=neural.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NLP Commands - Natural Language Processing
|
|
3
|
+
*
|
|
4
|
+
* Extract entities, concepts, and insights from text using Brainy's neural NLP
|
|
5
|
+
*/
|
|
6
|
+
interface NLPOptions {
|
|
7
|
+
verbose?: boolean;
|
|
8
|
+
json?: boolean;
|
|
9
|
+
pretty?: boolean;
|
|
10
|
+
quiet?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare const nlpCommands: {
|
|
13
|
+
/**
|
|
14
|
+
* Extract entities from text
|
|
15
|
+
*/
|
|
16
|
+
extract(text: string | undefined, options: NLPOptions): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Extract concepts from text
|
|
19
|
+
*/
|
|
20
|
+
extractConcepts(text: string | undefined, options: NLPOptions & {
|
|
21
|
+
threshold?: string;
|
|
22
|
+
}): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Analyze text with full NLP pipeline
|
|
25
|
+
*/
|
|
26
|
+
analyze(text: string | undefined, options: NLPOptions): Promise<void>;
|
|
27
|
+
};
|
|
28
|
+
export {};
|