@juspay/neurolink 7.25.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.
- package/CHANGELOG.md +8 -0
- package/dist/cli/factories/commandFactory.d.ts +8 -0
- package/dist/cli/factories/commandFactory.js +14 -0
- package/dist/cli/factories/ollamaCommandFactory.d.ts +38 -0
- package/dist/cli/factories/ollamaCommandFactory.js +375 -0
- package/dist/cli/factories/sagemakerCommandFactory.d.ts +62 -0
- package/dist/cli/factories/sagemakerCommandFactory.js +783 -0
- package/dist/cli/index.js +5 -7
- package/dist/core/baseProvider.js +42 -0
- package/dist/core/evaluationProviders.d.ts +26 -0
- package/dist/core/evaluationProviders.js +160 -0
- package/dist/lib/core/baseProvider.js +42 -0
- package/dist/lib/core/evaluationProviders.d.ts +26 -0
- package/dist/lib/core/evaluationProviders.js +160 -0
- package/package.json +1 -1
- package/dist/cli/commands/ollama.d.ts +0 -3
- package/dist/cli/commands/ollama.js +0 -339
- package/dist/cli/commands/sagemaker.d.ts +0 -11
- package/dist/cli/commands/sagemaker.js +0 -779
|
@@ -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
|
-
}
|