@aws/ml-container-creator 0.9.1 → 0.10.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.
Files changed (32) hide show
  1. package/config/parameter-schema-v2.json +2065 -0
  2. package/package.json +4 -4
  3. package/servers/lib/catalogs/jumpstart-public.json +101 -16
  4. package/servers/lib/catalogs/models.json +182 -26
  5. package/src/app.js +1 -389
  6. package/src/lib/bootstrap-command-handler.js +75 -1078
  7. package/src/lib/bootstrap-profile-manager.js +634 -0
  8. package/src/lib/bootstrap-provisioners.js +421 -0
  9. package/src/lib/config-loader.js +405 -0
  10. package/src/lib/config-manager.js +59 -1685
  11. package/src/lib/config-mcp-client.js +118 -0
  12. package/src/lib/config-validator.js +634 -0
  13. package/src/lib/cuda-resolver.js +140 -0
  14. package/src/lib/e2e-catalog-validator.js +251 -3
  15. package/src/lib/e2e-ci-recorder.js +103 -0
  16. package/src/lib/generated/cli-options.js +8 -4
  17. package/src/lib/generated/parameter-matrix.js +671 -0
  18. package/src/lib/generated/validation-rules.js +2 -2
  19. package/src/lib/marketplace-flow.js +276 -0
  20. package/src/lib/mcp-query-runner.js +768 -0
  21. package/src/lib/parameter-schema-validator.js +62 -18
  22. package/src/lib/prompt-runner.js +41 -1504
  23. package/src/lib/prompts/feature-prompts.js +172 -0
  24. package/src/lib/prompts/index.js +48 -0
  25. package/src/lib/prompts/infrastructure-prompts.js +690 -0
  26. package/src/lib/prompts/model-prompts.js +552 -0
  27. package/src/lib/prompts/project-prompts.js +70 -0
  28. package/src/lib/prompts.js +2 -1446
  29. package/src/lib/registry-command-handler.js +135 -3
  30. package/src/lib/secrets-prompt-runner.js +251 -0
  31. package/src/lib/template-variable-resolver.js +398 -0
  32. package/config/parameter-schema.json +0 -88
@@ -0,0 +1,276 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /**
5
+ * Marketplace Flow - Handles the marketplace-specific prompt flow.
6
+ * Uses delegation pattern: receives parent PromptRunner reference to access shared state.
7
+ */
8
+
9
+ import {
10
+ infraAsyncPrompts,
11
+ infraBatchTransformPrompts,
12
+ projectPrompts,
13
+ destinationPrompts
14
+ } from './prompts/index.js';
15
+
16
+ export default class MarketplaceFlow {
17
+ constructor(runner) {
18
+ this.runner = runner;
19
+ }
20
+
21
+ /**
22
+ * Marketplace-specific prompt flow.
23
+ * Skips all container-related prompts and prompts only for:
24
+ * model package ARN, instance type, deployment target, region.
25
+ *
26
+ * Requirements: 2.3, 2.4, 2.5
27
+ */
28
+ async _runMarketplaceFlow(frameworkAnswers, explicitConfig, existingConfig, buildTimestamp) {
29
+ console.log('\nšŸŖ Marketplace Model Package Configuration');
30
+
31
+ // Query marketplace-picker MCP server for subscription discovery
32
+ let mcpSubscriptions = [];
33
+ const cm = this.runner.configManager;
34
+ if (cm && cm.getMcpServerNames && cm.getMcpServerNames().includes('marketplace-picker')) {
35
+ try {
36
+ console.log(' šŸ” Querying marketplace-picker for subscriptions...');
37
+ const result = await cm.queryMcpServer('marketplace-picker', {
38
+ region: explicitConfig.awsRegion || existingConfig.awsRegion || process.env.AWS_REGION || 'us-east-1'
39
+ });
40
+ if (result && result.metadata?.subscriptions?.length > 0) {
41
+ mcpSubscriptions = result.metadata.subscriptions;
42
+ console.log(` āœ… Found ${mcpSubscriptions.length} Marketplace subscription(s)`);
43
+ } else {
44
+ console.log(' ā„¹ļø No Marketplace subscriptions found — enter ARN manually');
45
+ }
46
+ } catch (err) {
47
+ console.log(` āš ļø marketplace-picker unavailable: ${err.message}`);
48
+ console.log(' Falling back to manual ARN entry');
49
+ }
50
+ }
51
+
52
+ // Marketplace-specific prompts: model package ARN
53
+ const marketplacePrompts = [
54
+ {
55
+ type: mcpSubscriptions.length > 0 ? 'list' : 'input',
56
+ name: 'modelPackageArn',
57
+ message: mcpSubscriptions.length > 0
58
+ ? 'Select a Marketplace model package:'
59
+ : 'Model package ARN (arn:aws:sagemaker:<region>:<account>:model-package/<name>/<version>):',
60
+ ...(mcpSubscriptions.length > 0 ? {
61
+ choices: [
62
+ ...mcpSubscriptions.map(sub => ({
63
+ name: `${sub.modelName} (${sub.vendor}) — ${sub.arn}`,
64
+ value: sub.arn,
65
+ short: sub.modelName
66
+ })),
67
+ { type: 'separator', separator: '──────────────' },
68
+ { name: 'Enter ARN manually...', value: '__manual__', short: 'manual' }
69
+ ]
70
+ } : {
71
+ validate: (input) => {
72
+ if (!input || input.trim() === '') {
73
+ return 'Model package ARN is required';
74
+ }
75
+ const arnPattern = /^arn:aws:sagemaker:[a-z0-9-]+:\d{12}:model-package\/[\w-]+\/\d+$/;
76
+ if (!arnPattern.test(input.trim())) {
77
+ return 'Invalid ARN format. Expected: arn:aws:sagemaker:<region>:<account>:model-package/<name>/<version>';
78
+ }
79
+ return true;
80
+ }
81
+ })
82
+ },
83
+ {
84
+ type: 'input',
85
+ name: 'modelPackageArnManual',
86
+ message: 'Model package ARN (arn:aws:sagemaker:<region>:<account>:model-package/<name>/<version>):',
87
+ when: (answers) => answers.modelPackageArn === '__manual__',
88
+ validate: (input) => {
89
+ if (!input || input.trim() === '') {
90
+ return 'Model package ARN is required';
91
+ }
92
+ const arnPattern = /^arn:aws:sagemaker:[a-z0-9-]+:\d{12}:model-package\/[\w-]+\/\d+$/;
93
+ if (!arnPattern.test(input.trim())) {
94
+ return 'Invalid ARN format. Expected: arn:aws:sagemaker:<region>:<account>:model-package/<name>/<version>';
95
+ }
96
+ return true;
97
+ }
98
+ }
99
+ ];
100
+ const marketplaceAnswers = await this.runner._runPhase(marketplacePrompts, { ...frameworkAnswers }, explicitConfig, existingConfig);
101
+
102
+ // Handle manual ARN entry fallback
103
+ if (marketplaceAnswers.modelPackageArn === '__manual__' && marketplaceAnswers.modelPackageArnManual) {
104
+ marketplaceAnswers.modelPackageArn = marketplaceAnswers.modelPackageArnManual;
105
+ delete marketplaceAnswers.modelPackageArnManual;
106
+ }
107
+
108
+ // Infrastructure prompts: region, deployment target, instance type
109
+ console.log('\nšŸ’Ŗ Infrastructure & Deployment');
110
+ const bootstrapRegion = existingConfig.awsRegion || explicitConfig.awsRegion;
111
+ const regionPreviousAnswers = bootstrapRegion ? { _bootstrapRegion: bootstrapRegion } : {};
112
+
113
+ const marketplaceInfraPrompts = [
114
+ {
115
+ type: 'list',
116
+ name: 'awsRegion',
117
+ message: 'Target AWS region?',
118
+ choices: (answers) => {
119
+ const bootstrapReg = answers._bootstrapRegion;
120
+ const choices = ['us-east-1'];
121
+ if (bootstrapReg && bootstrapReg !== 'us-east-1') {
122
+ choices.unshift({ name: `${bootstrapReg} (from bootstrap profile)`, value: bootstrapReg });
123
+ }
124
+ choices.push({ name: 'Custom...', value: 'custom' });
125
+ return choices;
126
+ },
127
+ default: (answers) => answers._bootstrapRegion || 'us-east-1'
128
+ },
129
+ {
130
+ type: 'input',
131
+ name: 'customAwsRegion',
132
+ message: 'Enter AWS region (e.g., us-west-2, eu-west-1):',
133
+ when: answers => answers.awsRegion === 'custom'
134
+ },
135
+ {
136
+ type: 'list',
137
+ name: 'deploymentTarget',
138
+ message: 'Deployment target?',
139
+ choices: [
140
+ { name: 'SageMaker Real-Time Inference', value: 'realtime-inference' },
141
+ { name: 'SageMaker Async Inference', value: 'async-inference' },
142
+ { name: 'SageMaker Batch Transform', value: 'batch-transform' }
143
+ ],
144
+ default: 'realtime-inference'
145
+ },
146
+ {
147
+ type: 'list',
148
+ name: 'instanceType',
149
+ message: 'Instance type for deployment?',
150
+ choices: [
151
+ { name: 'ml.g5.xlarge (1 GPU, 24GB)', value: 'ml.g5.xlarge' },
152
+ { name: 'ml.g5.2xlarge (1 GPU, 24GB)', value: 'ml.g5.2xlarge' },
153
+ { name: 'ml.g5.4xlarge (1 GPU, 24GB)', value: 'ml.g5.4xlarge' },
154
+ { name: 'ml.g5.12xlarge (4 GPUs, 96GB)', value: 'ml.g5.12xlarge' },
155
+ { name: 'ml.p3.2xlarge (1 GPU, 16GB V100)', value: 'ml.p3.2xlarge' },
156
+ { name: 'ml.m5.xlarge (CPU, 16GB)', value: 'ml.m5.xlarge' },
157
+ { name: 'Custom...', value: 'custom' }
158
+ ],
159
+ default: 'ml.g5.xlarge'
160
+ },
161
+ {
162
+ type: 'input',
163
+ name: 'customInstanceType',
164
+ message: 'Enter instance type (e.g., ml.g5.xlarge):',
165
+ validate: (input) => {
166
+ if (!input || input.trim() === '') {
167
+ return 'Instance type is required';
168
+ }
169
+ if (!input.startsWith('ml.')) {
170
+ return 'Instance type must start with "ml." (e.g., ml.g5.xlarge)';
171
+ }
172
+ return true;
173
+ },
174
+ when: answers => answers.instanceType === 'custom'
175
+ }
176
+ ];
177
+ const infraAnswers = await this.runner._runPhase(marketplaceInfraPrompts, { ...frameworkAnswers, ...regionPreviousAnswers }, explicitConfig, existingConfig);
178
+
179
+ // Async-specific prompts
180
+ let asyncAnswers = {};
181
+ if (infraAnswers.deploymentTarget === 'async-inference') {
182
+ asyncAnswers = await this.runner._runPhase(infraAsyncPrompts, { ...infraAnswers }, explicitConfig, existingConfig);
183
+ }
184
+
185
+ // Batch transform-specific prompts
186
+ let batchTransformAnswers = {};
187
+ if (infraAnswers.deploymentTarget === 'batch-transform') {
188
+ batchTransformAnswers = await this.runner._runPhase(
189
+ infraBatchTransformPrompts,
190
+ { ...infraAnswers },
191
+ explicitConfig,
192
+ existingConfig
193
+ );
194
+ }
195
+
196
+ // Role ARN prompt
197
+ const rolePrompts = [
198
+ {
199
+ type: 'input',
200
+ name: 'awsRoleArn',
201
+ message: 'AWS IAM Role ARN for SageMaker execution (optional)?',
202
+ validate: (input) => {
203
+ if (!input || input.trim() === '') {
204
+ return true;
205
+ }
206
+ const arnPattern = /^arn:aws:iam::\d{12}:role\/[\w+=,.@-]+$/;
207
+ if (!arnPattern.test(input)) {
208
+ return 'Invalid ARN format. Expected: arn:aws:iam::123456789012:role/RoleName';
209
+ }
210
+ return true;
211
+ }
212
+ }
213
+ ];
214
+ const roleAnswers = await this.runner._runPhase(rolePrompts, { ...infraAnswers }, explicitConfig, existingConfig);
215
+
216
+ // Project name + destination
217
+ console.log('\nšŸ“‹ Project Configuration');
218
+ const allTechnicalAnswers = {
219
+ ...frameworkAnswers,
220
+ ...marketplaceAnswers,
221
+ ...infraAnswers,
222
+ ...asyncAnswers,
223
+ ...batchTransformAnswers,
224
+ ...roleAnswers
225
+ };
226
+ const projectAnswers = await this.runner._runPhase(projectPrompts, allTechnicalAnswers, explicitConfig, existingConfig);
227
+ const destinationAnswers = await this.runner._runPhase(destinationPrompts,
228
+ { ...allTechnicalAnswers, ...projectAnswers }, explicitConfig, existingConfig);
229
+
230
+ // Combine all marketplace answers
231
+ const combinedAnswers = {
232
+ ...frameworkAnswers,
233
+ ...marketplaceAnswers,
234
+ ...infraAnswers,
235
+ ...asyncAnswers,
236
+ ...batchTransformAnswers,
237
+ ...roleAnswers,
238
+ ...projectAnswers,
239
+ ...destinationAnswers,
240
+ buildTimestamp
241
+ };
242
+
243
+ // Handle custom instance type
244
+ if (combinedAnswers.customInstanceType) {
245
+ combinedAnswers.instanceType = combinedAnswers.customInstanceType;
246
+ delete combinedAnswers.customInstanceType;
247
+ }
248
+
249
+ // Handle custom AWS region
250
+ if (combinedAnswers.customAwsRegion) {
251
+ combinedAnswers.awsRegion = combinedAnswers.customAwsRegion;
252
+ delete combinedAnswers.customAwsRegion;
253
+ }
254
+
255
+ // Map awsRoleArn to roleArn for templates
256
+ if (combinedAnswers.awsRoleArn) {
257
+ combinedAnswers.roleArn = combinedAnswers.awsRoleArn;
258
+ delete combinedAnswers.awsRoleArn;
259
+ }
260
+
261
+ // Ensure CLI-provided values are in combinedAnswers
262
+ if (explicitConfig.modelPackageArn && !combinedAnswers.modelPackageArn) {
263
+ combinedAnswers.modelPackageArn = explicitConfig.modelPackageArn;
264
+ }
265
+
266
+ // Handle marketplace:// prefix from --model-name CLI option
267
+ const modelName = explicitConfig.modelName || combinedAnswers.modelName;
268
+ if (modelName && modelName.startsWith('marketplace://')) {
269
+ const arn = modelName.replace(/^marketplace:\/\//, '');
270
+ combinedAnswers.modelPackageArn = arn;
271
+ delete combinedAnswers.modelName;
272
+ }
273
+
274
+ return combinedAnswers;
275
+ }
276
+ }