@juspay/neurolink 7.26.0 → 7.26.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.
@@ -1,779 +0,0 @@
1
- /**
2
- * SageMaker CLI Commands
3
- *
4
- * Provides comprehensive command-line interface for Amazon SageMaker operations
5
- * including configuration management, endpoint testing, and model deployment.
6
- */
7
- import chalk from "chalk";
8
- import ora from "ora";
9
- import inquirer from "inquirer";
10
- import { SageMakerClient, ListEndpointsCommand, } from "@aws-sdk/client-sagemaker";
11
- import { logger } from "../../lib/utils/logger.js";
12
- import { checkSageMakerConfiguration, getSageMakerConfig, getConfigurationSummary, clearConfigurationCache, } from "../../lib/providers/sagemaker/config.js";
13
- import { AmazonSageMakerProvider } from "../../lib/providers/sagemaker/index.js";
14
- import { runQuickDiagnostics, formatDiagnosticReport, } from "../../lib/providers/sagemaker/diagnostics.js";
15
- /**
16
- * In-memory secure credential store (cleared after validation)
17
- */
18
- const secureCredentialStore = new Map();
19
- /**
20
- * Create secure configuration without exposing credentials to process.env
21
- */
22
- function createSecureConfiguration(config) {
23
- const sessionId = `sagemaker_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
24
- const secureConfig = {
25
- ...config,
26
- sessionId,
27
- createdAt: Date.now(),
28
- };
29
- // Store temporarily in secure memory store
30
- secureCredentialStore.set(sessionId, secureConfig);
31
- // Auto-cleanup after 5 minutes for security
32
- setTimeout(() => {
33
- secureCredentialStore.delete(sessionId);
34
- }, 5 * 60 * 1000);
35
- return secureConfig;
36
- }
37
- /**
38
- * Validate secure configuration without exposing credentials
39
- */
40
- function validateSecureConfiguration(secureConfig) {
41
- // Create temporary AWS SDK client with secure credentials
42
- const tempClient = new SageMakerClient({
43
- region: secureConfig.region,
44
- credentials: {
45
- accessKeyId: secureConfig.accessKeyId,
46
- secretAccessKey: secureConfig.secretAccessKey,
47
- },
48
- });
49
- // Test basic connectivity (this will throw if credentials are invalid)
50
- // Note: We're not actually making a call here, just validating the client can be created
51
- if (!tempClient ||
52
- !secureConfig.accessKeyId ||
53
- !secureConfig.secretAccessKey) {
54
- throw new Error("Invalid AWS credentials provided");
55
- }
56
- // Basic validation of configuration values
57
- if (!secureConfig.region || secureConfig.region.length < 3) {
58
- throw new Error("Invalid AWS region provided");
59
- }
60
- if (!secureConfig.endpointName || secureConfig.endpointName.length < 1) {
61
- throw new Error("Invalid SageMaker endpoint name provided");
62
- }
63
- if (secureConfig.timeout < 1000 || secureConfig.timeout > 300000) {
64
- throw new Error("Timeout must be between 1000ms and 300000ms");
65
- }
66
- if (secureConfig.maxRetries < 0 || secureConfig.maxRetries > 10) {
67
- throw new Error("Max retries must be between 0 and 10");
68
- }
69
- }
70
- /**
71
- * Clear secure credentials from memory
72
- */
73
- function clearSecureCredentials(sessionId) {
74
- secureCredentialStore.delete(sessionId);
75
- }
76
- /**
77
- * Add SageMaker commands to the CLI
78
- */
79
- export function addSageMakerCommands(cli) {
80
- cli.command("sagemaker <command>", "Manage Amazon SageMaker AI models and endpoints", (yargs) => {
81
- return yargs
82
- .command("status", "Check SageMaker configuration and connectivity", {}, statusHandler)
83
- .command("test <endpoint>", "Test connectivity to a SageMaker endpoint", {
84
- endpoint: {
85
- describe: "SageMaker endpoint name to test",
86
- type: "string",
87
- demandOption: true,
88
- },
89
- model: {
90
- describe: "Model name for the endpoint",
91
- type: "string",
92
- default: "sagemaker-model",
93
- },
94
- prompt: {
95
- describe: "Test prompt to send",
96
- type: "string",
97
- default: "Hello, world!",
98
- },
99
- }, testEndpointHandler)
100
- .command("list-endpoints", "List available SageMaker endpoints", {}, listEndpointsHandler)
101
- .command("config", "Show current SageMaker configuration", {
102
- format: {
103
- describe: "Output format",
104
- choices: ["json", "table", "yaml"],
105
- default: "table",
106
- },
107
- }, configHandler)
108
- .command("setup", "Interactive SageMaker configuration setup", {}, setupHandler)
109
- .command("validate", "Validate SageMaker configuration and credentials", {
110
- endpoint: {
111
- describe: "Optional endpoint name to validate",
112
- type: "string",
113
- },
114
- }, validateHandler)
115
- .command("benchmark <endpoint>", "Run performance benchmark against SageMaker endpoint", {
116
- endpoint: {
117
- describe: "SageMaker endpoint name to benchmark",
118
- type: "string",
119
- demandOption: true,
120
- },
121
- requests: {
122
- describe: "Number of requests to send",
123
- type: "number",
124
- default: 10,
125
- },
126
- concurrency: {
127
- describe: "Number of concurrent requests",
128
- type: "number",
129
- default: 1,
130
- },
131
- maxTokens: {
132
- describe: "Maximum tokens per request",
133
- type: "number",
134
- default: 50,
135
- },
136
- }, benchmarkHandler)
137
- .command("clear-cache", "Clear SageMaker configuration cache", {}, clearCacheHandler)
138
- .command("diagnose [endpoint]", "Run comprehensive streaming diagnostics", {
139
- endpoint: {
140
- describe: "SageMaker endpoint name to diagnose",
141
- type: "string",
142
- },
143
- quick: {
144
- describe: "Run quick diagnostics only",
145
- type: "boolean",
146
- default: false,
147
- },
148
- full: {
149
- describe: "Run full diagnostic suite including performance tests",
150
- type: "boolean",
151
- default: false,
152
- },
153
- connectivity: {
154
- describe: "Test connectivity only",
155
- type: "boolean",
156
- default: false,
157
- },
158
- streaming: {
159
- describe: "Test streaming capability only",
160
- type: "boolean",
161
- default: false,
162
- },
163
- timeout: {
164
- describe: "Timeout for diagnostic tests in milliseconds",
165
- type: "number",
166
- default: 30000,
167
- },
168
- }, diagnoseHandler)
169
- .demandCommand(1, "Please specify a SageMaker command");
170
- }, () => { });
171
- }
172
- /**
173
- * Handler for checking SageMaker status
174
- */
175
- async function statusHandler() {
176
- const spinner = ora("Checking SageMaker configuration...").start();
177
- try {
178
- const status = checkSageMakerConfiguration();
179
- spinner.stop();
180
- logger.always(chalk.blue("\nšŸ” SageMaker Configuration Status\n"));
181
- if (status.configured) {
182
- logger.always(chalk.green("āœ… Configuration: Valid"));
183
- }
184
- else {
185
- logger.always(chalk.red("āŒ Configuration: Invalid"));
186
- }
187
- if (status.issues.length > 0) {
188
- logger.always(chalk.yellow("\nāš ļø Issues found:"));
189
- status.issues.forEach((issue) => {
190
- logger.always(` • ${issue}`);
191
- });
192
- }
193
- // Show configuration summary (safe for display)
194
- if (status.summary) {
195
- logger.always(chalk.blue("\nšŸ“‹ Configuration Summary:"));
196
- if (typeof status.summary === "object" && status.summary.aws) {
197
- const aws = status.summary.aws;
198
- logger.always(` Region: ${aws.region}`);
199
- logger.always(` Access Key: ${aws.accessKeyId}`);
200
- logger.always(` Timeout: ${aws.timeout}ms`);
201
- logger.always(` Max Retries: ${aws.maxRetries}`);
202
- }
203
- }
204
- process.exit(status.configured ? 0 : 1);
205
- }
206
- catch (error) {
207
- spinner.fail("Failed to check SageMaker configuration");
208
- logger.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
209
- process.exit(1);
210
- }
211
- }
212
- /**
213
- * Handler for testing SageMaker endpoint connectivity
214
- */
215
- async function testEndpointHandler(argv) {
216
- const { endpoint, model, prompt } = argv;
217
- const spinner = ora(`Testing connectivity to endpoint: ${endpoint}...`).start();
218
- try {
219
- // First check configuration
220
- const status = checkSageMakerConfiguration();
221
- if (!status.configured) {
222
- spinner.fail("SageMaker configuration is invalid");
223
- logger.error(chalk.red("Please run 'neurolink sagemaker setup' first"));
224
- process.exit(1);
225
- }
226
- // Create provider and test connectivity
227
- const provider = new AmazonSageMakerProvider(model, endpoint);
228
- const languageModel = await provider.getModel();
229
- spinner.text = "Testing endpoint connectivity...";
230
- const testResult = await provider.testConnectivity();
231
- if (testResult.success) {
232
- spinner.succeed(`āœ… Endpoint '${endpoint}' is accessible`);
233
- // Run a simple generation test
234
- spinner.start("Testing text generation...");
235
- try {
236
- const result = await languageModel.doGenerate({
237
- inputFormat: "messages",
238
- mode: { type: "regular" },
239
- prompt: [
240
- {
241
- role: "user",
242
- content: [{ type: "text", text: prompt || "Hello" }],
243
- },
244
- ],
245
- maxTokens: 50,
246
- });
247
- spinner.succeed("āœ… Text generation test successful");
248
- logger.always(chalk.blue("\nšŸ“ Test Response:"));
249
- logger.always(` Input: "${prompt}"`);
250
- logger.always(` Output: "${result.text?.substring(0, 100)}${result.text && result.text.length > 100 ? "..." : ""}"`);
251
- logger.always(` Tokens: ${result.usage.promptTokens} → ${result.usage.completionTokens} (${result.usage.totalTokens ?? result.usage.promptTokens + result.usage.completionTokens} total)`);
252
- logger.always(` Finish Reason: ${result.finishReason}`);
253
- }
254
- catch (genError) {
255
- spinner.fail("āŒ Text generation test failed");
256
- logger.error(chalk.red(`Generation Error: ${genError instanceof Error ? genError.message : String(genError)}`));
257
- }
258
- }
259
- else {
260
- spinner.fail(`āŒ Endpoint '${endpoint}' is not accessible`);
261
- logger.error(chalk.red(`Error: ${testResult.error}`));
262
- process.exit(1);
263
- }
264
- }
265
- catch (error) {
266
- spinner.fail("Failed to test SageMaker endpoint");
267
- logger.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
268
- process.exit(1);
269
- }
270
- }
271
- /**
272
- * Handler for listing SageMaker endpoints
273
- */
274
- async function listEndpointsHandler() {
275
- const spinner = ora("Listing SageMaker endpoints...").start();
276
- try {
277
- // Use AWS SDK directly for better security and error handling
278
- try {
279
- const config = await getSageMakerConfig();
280
- const sagemakerClient = new SageMakerClient({
281
- region: config.region,
282
- credentials: {
283
- accessKeyId: config.accessKeyId,
284
- secretAccessKey: config.secretAccessKey,
285
- ...(config.sessionToken && { sessionToken: config.sessionToken }),
286
- },
287
- });
288
- // List endpoints using AWS SDK
289
- const command = new ListEndpointsCommand({});
290
- const response = await sagemakerClient.send(command);
291
- const endpoints = { Endpoints: response.Endpoints || [] };
292
- spinner.stop();
293
- if (endpoints.Endpoints && endpoints.Endpoints.length > 0) {
294
- logger.always(chalk.blue("\nšŸ”— Available SageMaker Endpoints:\n"));
295
- endpoints.Endpoints.forEach((endpoint, index) => {
296
- logger.always(`${index + 1}. ${chalk.green(endpoint.EndpointName)}`);
297
- logger.always(` Status: ${endpoint.EndpointStatus}`);
298
- logger.always(` Created: ${endpoint.CreationTime?.toLocaleDateString() ?? "Unknown"}`);
299
- if (endpoint.LastModifiedTime) {
300
- logger.always(` Modified: ${endpoint.LastModifiedTime.toLocaleDateString()}`);
301
- }
302
- logger.always();
303
- });
304
- }
305
- else {
306
- logger.always(chalk.yellow("No SageMaker endpoints found"));
307
- }
308
- }
309
- catch (_awsError) {
310
- spinner.fail("Failed to list endpoints");
311
- logger.error(chalk.red("AWS SDK credentials error or insufficient permissions"));
312
- logger.always(chalk.yellow("\nTo list endpoints, please:"));
313
- logger.always("1. Set AWS_ACCESS_KEY_ID environment variable");
314
- logger.always("2. Set AWS_SECRET_ACCESS_KEY environment variable");
315
- logger.always("3. Set AWS_REGION environment variable (or use default)");
316
- logger.always("4. Ensure you have sagemaker:ListEndpoints permission");
317
- }
318
- }
319
- catch (error) {
320
- spinner.fail("Failed to list SageMaker endpoints");
321
- logger.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
322
- process.exit(1);
323
- }
324
- }
325
- /**
326
- * Handler for showing configuration
327
- */
328
- async function configHandler(args) {
329
- const format = args.format;
330
- const spinner = ora("Loading SageMaker configuration...").start();
331
- try {
332
- const summary = getConfigurationSummary();
333
- spinner.stop();
334
- logger.always(chalk.blue("\nāš™ļø SageMaker Configuration\n"));
335
- if (format === "json") {
336
- logger.always(JSON.stringify(summary, null, 2));
337
- }
338
- else if (format === "yaml") {
339
- // Simple YAML-like output
340
- function printYaml(obj, indent = 0) {
341
- const spaces = " ".repeat(indent);
342
- for (const [key, value] of Object.entries(obj)) {
343
- if (typeof value === "object" && value !== null) {
344
- logger.always(`${spaces}${key}:`);
345
- printYaml(value, indent + 2);
346
- }
347
- else {
348
- logger.always(`${spaces}${key}: ${value}`);
349
- }
350
- }
351
- }
352
- printYaml(summary);
353
- }
354
- else {
355
- // Table format (default)
356
- if (typeof summary === "object" && summary.aws) {
357
- const aws = summary.aws;
358
- const sagemaker = (summary.sagemaker ||
359
- {});
360
- const environment = (summary.environment ||
361
- {});
362
- logger.always(chalk.green("AWS Configuration:"));
363
- logger.always(` Region: ${aws.region}`);
364
- logger.always(` Access Key: ${aws.accessKeyId}`);
365
- logger.always(` Secret Key: ${aws.secretAccessKey}`);
366
- logger.always(` Session Token: ${aws.sessionToken}`);
367
- logger.always(` Timeout: ${aws.timeout}ms`);
368
- logger.always(` Max Retries: ${aws.maxRetries}`);
369
- logger.always(` Custom Endpoint: ${aws.endpoint || "None"}`);
370
- logger.always(chalk.blue("\nSageMaker Configuration:"));
371
- logger.always(` Default Endpoint: ${sagemaker.defaultEndpoint}`);
372
- logger.always(` Model Name: ${sagemaker.model}`);
373
- if (sagemaker.modelConfig) {
374
- const modelConfig = sagemaker.modelConfig;
375
- logger.always(` Model Type: ${modelConfig.modelType}`);
376
- logger.always(` Content Type: ${modelConfig.contentType}`);
377
- logger.always(` Accept: ${modelConfig.accept}`);
378
- }
379
- logger.always(chalk.yellow("\nEnvironment:"));
380
- logger.always(` Node Environment: ${environment.nodeEnv}`);
381
- logger.always(` SageMaker Configured: ${environment.sagemakerConfigured ? "Yes" : "No"}`);
382
- logger.always(` AWS Configured: ${environment.awsConfigured ? "Yes" : "No"}`);
383
- }
384
- }
385
- }
386
- catch (error) {
387
- spinner.fail("Failed to load SageMaker configuration");
388
- logger.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
389
- process.exit(1);
390
- }
391
- }
392
- /**
393
- * Handler for interactive setup
394
- */
395
- async function setupHandler() {
396
- logger.always(chalk.blue("\nšŸš€ SageMaker Interactive Setup\n"));
397
- // Pre-setup security advisory
398
- logger.always(chalk.yellow.bold("šŸ”’ SECURITY NOTICE: You will be prompted to enter AWS credentials.\n" +
399
- "These credentials will be stored temporarily in memory only.\n" +
400
- "For production use, consider using AWS credential files or IAM roles.\n"));
401
- // Ask for user confirmation before proceeding
402
- const { confirmSetup } = await inquirer.prompt([
403
- {
404
- type: "confirm",
405
- name: "confirmSetup",
406
- message: "Do you understand the security implications and want to proceed?",
407
- default: false,
408
- },
409
- ]);
410
- if (!confirmSetup) {
411
- logger.always(chalk.blue("\nSetup cancelled. Consider using alternative credential methods:"));
412
- logger.always("• AWS credential files: ~/.aws/credentials");
413
- logger.always("• Environment variables in .env file");
414
- logger.always("• AWS CLI configuration: aws configure");
415
- logger.always("• IAM roles for production environments");
416
- return;
417
- }
418
- try {
419
- const answers = await inquirer.prompt([
420
- {
421
- type: "input",
422
- name: "accessKeyId",
423
- message: "AWS Access Key ID:",
424
- validate: (input) => input.trim().length > 0 || "Access Key ID is required",
425
- },
426
- {
427
- type: "password",
428
- name: "secretAccessKey",
429
- message: "AWS Secret Access Key:",
430
- validate: (input) => input.trim().length > 0 || "Secret Access Key is required",
431
- },
432
- {
433
- type: "input",
434
- name: "region",
435
- message: "AWS Region:",
436
- default: "us-east-1",
437
- },
438
- {
439
- type: "input",
440
- name: "endpointName",
441
- message: "Default SageMaker Endpoint Name:",
442
- validate: (input) => input.trim().length > 0 || "Endpoint name is required",
443
- },
444
- {
445
- type: "number",
446
- name: "timeout",
447
- message: "Request timeout (ms):",
448
- default: 30000,
449
- },
450
- {
451
- type: "number",
452
- name: "maxRetries",
453
- message: "Maximum retry attempts:",
454
- default: 3,
455
- },
456
- ]);
457
- const spinner = ora("Setting up SageMaker configuration...").start();
458
- // Enhanced security warnings for credential handling
459
- spinner.stop();
460
- logger.always(chalk.red.bold("\nšŸ”’ CRITICAL SECURITY WARNINGS\n"));
461
- logger.always(chalk.yellow.bold("āš ļø CREDENTIAL PERSISTENCE: AWS credentials will only be set for this session.\n" +
462
- " They will NOT persist after you exit the CLI.\n\n" +
463
- "šŸ” SECURE STORAGE OPTIONS:\n" +
464
- " • Use environment variables in a secure .env file (never commit to git)\n" +
465
- " • Use AWS credential files (~/.aws/credentials) with proper permissions\n" +
466
- " • Use AWS credential managers (AWS CLI, AWS SSO, IAM roles)\n" +
467
- " • Use cloud provider credential chains for production\n\n" +
468
- "🚫 SECURITY BEST PRACTICES:\n" +
469
- " • NEVER share or expose your AWS credentials in plain text\n" +
470
- " • NEVER commit credentials to version control systems\n" +
471
- " • Use least-privilege IAM policies (only SageMaker permissions needed)\n" +
472
- " • Rotate credentials regularly and revoke unused access keys\n" +
473
- " • Monitor AWS CloudTrail for unexpected API usage\n\n" +
474
- "šŸ“‹ REQUIRED IAM PERMISSIONS:\n" +
475
- " • sagemaker:InvokeEndpoint (for model inference)\n" +
476
- " • sagemaker:ListEndpoints (for endpoint discovery)\n" +
477
- " • sagemaker:DescribeEndpoint (for status checks)\n\n" +
478
- "🌐 PRODUCTION RECOMMENDATIONS:\n" +
479
- " • Use IAM roles instead of access keys in production\n" +
480
- " • Implement credential rotation policies\n" +
481
- " • Use AWS Systems Manager Parameter Store for secrets\n" +
482
- " • Consider AWS Secrets Manager for automated rotation\n\n" +
483
- "šŸ“– Learn more: https://docs.aws.amazon.com/general/latest/gr/aws-access-keys-best-practices.html\n"));
484
- spinner.start("Setting up SageMaker configuration...");
485
- // Secure credential management without process.env exposure
486
- const secureConfig = createSecureConfiguration({
487
- accessKeyId: answers.accessKeyId,
488
- secretAccessKey: answers.secretAccessKey,
489
- region: answers.region,
490
- endpointName: answers.endpointName,
491
- timeout: answers.timeout,
492
- maxRetries: answers.maxRetries,
493
- });
494
- // Clear cache and test configuration with secure config
495
- clearConfigurationCache();
496
- try {
497
- validateSecureConfiguration(secureConfig); // Validate configuration is loadable
498
- spinner.succeed("āœ… Configuration validated successfully");
499
- logger.always(chalk.green("\nšŸŽ‰ SageMaker setup complete!"));
500
- logger.always(chalk.yellow("\nšŸ’” Next steps:"));
501
- logger.always("1. Test your endpoint: neurolink sagemaker test <endpoint-name>");
502
- logger.always("2. Check status: neurolink sagemaker status");
503
- logger.always("3. List endpoints: neurolink sagemaker list-endpoints");
504
- logger.always(chalk.blue("\nšŸ”’ Secure configuration validated:"));
505
- logger.always(" āœ“ AWS credentials verified");
506
- logger.always(" āœ“ AWS region validated");
507
- logger.always(" āœ“ SageMaker endpoint configured");
508
- logger.always(" āœ“ Timeout and retry settings applied");
509
- logger.always(chalk.yellow("\nāš ļø For persistent configuration, add these to your .env file:\n" +
510
- " AWS_ACCESS_KEY_ID=your_access_key\n" +
511
- " AWS_SECRET_ACCESS_KEY=your_secret_key\n" +
512
- " AWS_REGION=" +
513
- secureConfig.region +
514
- "\n" +
515
- " SAGEMAKER_DEFAULT_ENDPOINT=" +
516
- secureConfig.endpointName +
517
- "\n\n" +
518
- "šŸ”’ SECURITY REMINDER:\n" +
519
- " • Add .env to .gitignore to prevent credential exposure\n" +
520
- " • Set restrictive file permissions (600) on credential files\n" +
521
- " • Never share or commit these credentials to version control\n" +
522
- " • Consider using AWS credential rotation policies\n" +
523
- " • Monitor AWS CloudTrail for unauthorized access attempts"));
524
- // Clear secure credentials from memory after successful setup
525
- clearSecureCredentials(secureConfig.sessionId);
526
- }
527
- catch (configError) {
528
- spinner.fail("āŒ Configuration validation failed");
529
- logger.error(chalk.red(`Error: ${configError instanceof Error ? configError.message : String(configError)}`));
530
- // Clear secure credentials from memory on error
531
- clearSecureCredentials(secureConfig.sessionId);
532
- process.exit(1);
533
- }
534
- }
535
- catch (error) {
536
- logger.error(chalk.red(`Setup failed: ${error instanceof Error ? error.message : String(error)}`));
537
- process.exit(1);
538
- }
539
- }
540
- /**
541
- * Handler for configuration validation
542
- */
543
- async function validateHandler() {
544
- const spinner = ora("Validating SageMaker configuration...").start();
545
- try {
546
- const status = checkSageMakerConfiguration();
547
- spinner.stop();
548
- logger.always(chalk.blue("\nšŸ” Configuration Validation Results\n"));
549
- if (status.configured) {
550
- logger.always(chalk.green("āœ… All checks passed"));
551
- logger.always(chalk.blue("šŸš€ SageMaker is ready to use"));
552
- }
553
- else {
554
- logger.always(chalk.red("āŒ Configuration validation failed"));
555
- if (status.issues.length > 0) {
556
- logger.always(chalk.yellow("\nšŸ”§ Issues to fix:"));
557
- status.issues.forEach((issue, index) => {
558
- logger.always(`${index + 1}. ${issue}`);
559
- });
560
- }
561
- logger.always(chalk.blue("\nšŸ’” How to fix:"));
562
- logger.always("Run: neurolink sagemaker setup");
563
- process.exit(1);
564
- }
565
- }
566
- catch (error) {
567
- spinner.fail("Validation failed");
568
- logger.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
569
- process.exit(1);
570
- }
571
- }
572
- /**
573
- * Handler for performance benchmarking
574
- */
575
- async function benchmarkHandler(argv) {
576
- const { endpoint, requests = 10, concurrency = 2, maxTokens = 100 } = argv;
577
- logger.always(chalk.blue(`\n⚔ SageMaker Performance Benchmark\n`));
578
- logger.always(`Endpoint: ${endpoint}`);
579
- logger.always(`Requests: ${requests}`);
580
- logger.always(`Concurrency: ${concurrency}`);
581
- logger.always(`Max Tokens: ${maxTokens}\n`);
582
- const spinner = ora("Setting up benchmark...").start();
583
- try {
584
- // Check configuration first
585
- const status = checkSageMakerConfiguration();
586
- if (!status.configured) {
587
- spinner.fail("SageMaker configuration is invalid");
588
- logger.error(chalk.red("Please run 'neurolink sagemaker setup' first"));
589
- process.exit(1);
590
- }
591
- const provider = new AmazonSageMakerProvider(undefined, endpoint);
592
- const model = await provider.getModel();
593
- spinner.text = "Running connectivity test...";
594
- const connectivityTest = await provider.testConnectivity();
595
- if (!connectivityTest.success) {
596
- spinner.fail(`Endpoint '${endpoint}' is not accessible`);
597
- logger.error(chalk.red(`Error: ${connectivityTest.error}`));
598
- process.exit(1);
599
- }
600
- spinner.text = "Starting benchmark...";
601
- const results = [];
602
- const startTime = Date.now();
603
- // Run requests in batches based on concurrency
604
- for (let batch = 0; batch < Math.ceil(requests / concurrency); batch++) {
605
- const batchSize = Math.min(concurrency, requests - batch * concurrency);
606
- const batchPromises = [];
607
- for (let i = 0; i < batchSize; i++) {
608
- const requestStart = Date.now();
609
- batchPromises.push((async () => {
610
- try {
611
- const result = await model.doGenerate({
612
- inputFormat: "messages",
613
- mode: { type: "regular" },
614
- prompt: [
615
- {
616
- role: "user",
617
- content: [
618
- {
619
- type: "text",
620
- text: `Benchmark request ${batch * concurrency + i + 1}`,
621
- },
622
- ],
623
- },
624
- ],
625
- maxTokens,
626
- });
627
- return {
628
- duration: Date.now() - requestStart,
629
- tokens: result.usage.totalTokens ??
630
- result.usage.promptTokens + result.usage.completionTokens,
631
- success: true,
632
- };
633
- }
634
- catch (error) {
635
- return {
636
- duration: Date.now() - requestStart,
637
- tokens: 0,
638
- success: false,
639
- error: error instanceof Error ? error.message : String(error),
640
- };
641
- }
642
- })());
643
- }
644
- const batchResults = await Promise.all(batchPromises);
645
- results.push(...batchResults);
646
- spinner.text = `Progress: ${results.length}/${requests} requests completed`;
647
- }
648
- const totalTime = Date.now() - startTime;
649
- spinner.succeed("Benchmark completed");
650
- // Calculate statistics
651
- const successful = results.filter((r) => r.success);
652
- const failed = results.filter((r) => !r.success);
653
- const durations = successful.map((r) => r.duration);
654
- const totalTokens = successful.reduce((sum, r) => sum + r.tokens, 0);
655
- logger.always(chalk.green("\nšŸ“Š Benchmark Results\n"));
656
- logger.always(`Total Time: ${totalTime}ms`);
657
- logger.always(`Successful Requests: ${successful.length}/${requests}`);
658
- logger.always(`Failed Requests: ${failed.length}`);
659
- logger.always(`Success Rate: ${((successful.length / requests) * 100).toFixed(1)}%`);
660
- if (successful.length > 0) {
661
- logger.always(`\nLatency Statistics:`);
662
- logger.always(` Average: ${(durations.reduce((a, b) => a + b, 0) / durations.length).toFixed(0)}ms`);
663
- logger.always(` Minimum: ${Math.min(...durations)}ms`);
664
- logger.always(` Maximum: ${Math.max(...durations)}ms`);
665
- logger.always(` Median: ${durations.sort((a, b) => a - b)[Math.floor(durations.length / 2)]}ms`);
666
- logger.always(`\nThroughput:`);
667
- logger.always(` Requests/sec: ${(successful.length / (totalTime / 1000)).toFixed(2)}`);
668
- logger.always(` Tokens/sec: ${(totalTokens / (totalTime / 1000)).toFixed(2)}`);
669
- logger.always(` Average tokens/request: ${(totalTokens / successful.length).toFixed(1)}`);
670
- }
671
- if (failed.length > 0) {
672
- logger.always(chalk.red(`\nāŒ Failed Requests (${failed.length}):`));
673
- failed.slice(0, 5).forEach((failure, index) => {
674
- logger.always(` ${index + 1}. ${failure.error}`);
675
- });
676
- if (failed.length > 5) {
677
- logger.always(` ... and ${failed.length - 5} more`);
678
- }
679
- }
680
- }
681
- catch (error) {
682
- spinner.fail("Benchmark failed");
683
- logger.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
684
- process.exit(1);
685
- }
686
- }
687
- /**
688
- * Handler for clearing configuration cache
689
- */
690
- async function clearCacheHandler() {
691
- const spinner = ora("Clearing SageMaker configuration cache...").start();
692
- try {
693
- clearConfigurationCache();
694
- spinner.succeed("āœ… Configuration cache cleared");
695
- logger.always(chalk.blue("Configuration will be reloaded on next use"));
696
- }
697
- catch (error) {
698
- spinner.fail("Failed to clear cache");
699
- logger.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
700
- process.exit(1);
701
- }
702
- }
703
- /**
704
- * Handler for running streaming diagnostics
705
- */
706
- async function diagnoseHandler(argv) {
707
- const { endpoint, quick, full, timeout } = argv;
708
- logger.always(chalk.blue(`\nšŸ” SageMaker Streaming Diagnostics\n`));
709
- if (endpoint) {
710
- logger.always(`Endpoint: ${endpoint}`);
711
- }
712
- else {
713
- logger.always("Endpoint: Not specified (configuration tests only)");
714
- }
715
- logger.always(`Mode: ${quick ? "Quick" : full ? "Full" : "Standard"}`);
716
- logger.always(`Timeout: ${timeout}ms\n`);
717
- const spinner = ora("Starting diagnostics...").start();
718
- try {
719
- // Run diagnostics (simplified - advanced streaming diagnostics removed)
720
- const report = await runQuickDiagnostics(endpoint);
721
- spinner.stop();
722
- // Display results
723
- const formatted = formatDiagnosticReport(report);
724
- logger.always(formatted);
725
- // Additional insights based on results
726
- if (report.overallStatus === "critical") {
727
- logger.always(chalk.red("🚨 Critical Issues Detected"));
728
- logger.always(chalk.red(" Your streaming configuration has serious problems that need immediate attention."));
729
- logger.always(chalk.yellow(" See the recommendations above for resolution steps.\n"));
730
- }
731
- else if (report.overallStatus === "issues") {
732
- logger.always(chalk.yellow("āš ļø Issues Detected"));
733
- logger.always(chalk.yellow(" Your streaming setup works but has some issues that could affect performance."));
734
- logger.always(chalk.blue(" Consider addressing the recommendations above.\n"));
735
- }
736
- else {
737
- logger.always(chalk.green("āœ… All Systems Go"));
738
- logger.always(chalk.green(" Your SageMaker streaming configuration looks healthy!"));
739
- if (endpoint) {
740
- logger.always(chalk.blue(" You can now use streaming features with confidence.\n"));
741
- logger.always(chalk.dim(" Try: neurolink sagemaker stream " + endpoint));
742
- }
743
- }
744
- // Show additional help based on findings
745
- const failedTests = report.results.filter((r) => r.status === "fail");
746
- if (failedTests.length > 0) {
747
- logger.always(chalk.blue("šŸ“š Additional Resources:"));
748
- const hasConnectivityIssues = failedTests.some((t) => t.category === "connectivity");
749
- const hasStreamingIssues = failedTests.some((t) => t.category === "streaming");
750
- const hasConfigIssues = failedTests.some((t) => t.category === "configuration");
751
- if (hasConfigIssues) {
752
- logger.always(" • Configuration: neurolink sagemaker setup");
753
- }
754
- if (hasConnectivityIssues) {
755
- logger.always(" • Connectivity: neurolink sagemaker test " +
756
- (endpoint || "your-endpoint"));
757
- }
758
- if (hasStreamingIssues) {
759
- logger.always(" • Streaming Guide: docs/providers/sagemaker/streaming-troubleshooting.md");
760
- }
761
- logger.always(" • Full Diagnostics: neurolink sagemaker diagnose " +
762
- (endpoint || "") +
763
- " --full");
764
- logger.always();
765
- }
766
- // Exit with appropriate code
767
- process.exit(report.overallStatus === "critical" ? 1 : 0);
768
- }
769
- catch (error) {
770
- spinner.fail("Diagnostics failed");
771
- logger.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
772
- logger.always(chalk.yellow("\nšŸ’” Diagnostic troubleshooting:"));
773
- logger.always(" • Check your SageMaker configuration: neurolink sagemaker status");
774
- logger.always(" • Verify AWS credentials and permissions");
775
- logger.always(" • Try with a specific endpoint: neurolink sagemaker diagnose your-endpoint");
776
- logger.always(" • Run quick mode: neurolink sagemaker diagnose --quick");
777
- process.exit(1);
778
- }
779
- }