@aws/ml-container-creator 0.10.0 → 0.10.3
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/LICENSE-THIRD-PARTY +9304 -0
- package/bin/cli.js +2 -0
- package/config/bootstrap-e2e-stack.json +341 -0
- package/config/bootstrap-stack.json +40 -3
- package/config/parameter-schema-v2.json +5 -21
- package/config/tune-catalog.json +1781 -0
- package/infra/ci-harness/buildspec.yml +1 -0
- package/infra/ci-harness/lambda/path-prover/brain.ts +306 -0
- package/infra/ci-harness/lambda/path-prover/write-results.ts +152 -0
- package/infra/ci-harness/lib/ci-harness-stack.ts +837 -7
- package/infra/ci-harness/state-machines/path-prover.asl.json +496 -0
- package/package.json +51 -66
- package/servers/base-image-picker/index.js +121 -121
- package/servers/e2e-status/index.js +297 -0
- package/servers/e2e-status/manifest.json +14 -0
- package/servers/e2e-status/package.json +15 -0
- package/servers/endpoint-picker/LICENSE +202 -0
- package/servers/endpoint-picker/index.js +536 -0
- package/servers/endpoint-picker/manifest.json +14 -0
- package/servers/endpoint-picker/package.json +18 -0
- package/servers/hyperpod-cluster-picker/index.js +125 -125
- package/servers/instance-sizer/index.js +138 -138
- package/servers/instance-sizer/lib/instance-ranker.js +76 -76
- package/servers/instance-sizer/lib/model-resolver.js +61 -61
- package/servers/instance-sizer/lib/quota-resolver.js +113 -113
- package/servers/instance-sizer/lib/vram-estimator.js +31 -31
- package/servers/lib/bedrock-client.js +38 -38
- package/servers/lib/catalogs/model-servers.json +201 -3
- package/servers/lib/custom-validators.js +13 -13
- package/servers/lib/dynamic-resolver.js +4 -4
- package/servers/marketplace-picker/index.js +342 -0
- package/servers/marketplace-picker/manifest.json +14 -0
- package/servers/marketplace-picker/package.json +18 -0
- package/servers/model-picker/index.js +382 -382
- package/servers/region-picker/index.js +56 -56
- package/servers/workload-picker/LICENSE +202 -0
- package/servers/workload-picker/catalogs/workload-profiles.json +67 -0
- package/servers/workload-picker/index.js +171 -0
- package/servers/workload-picker/manifest.json +16 -0
- package/servers/workload-picker/package.json +16 -0
- package/src/app.js +4 -2
- package/src/lib/bootstrap-command-handler.js +579 -14
- package/src/lib/bootstrap-config.js +36 -0
- package/src/lib/bootstrap-profile-manager.js +48 -41
- package/src/lib/ci-register-helpers.js +74 -0
- package/src/lib/config-loader.js +3 -0
- package/src/lib/config-manager.js +7 -0
- package/src/lib/cuda-resolver.js +17 -8
- package/src/lib/generated/cli-options.js +315 -315
- package/src/lib/generated/parameter-matrix.js +661 -661
- package/src/lib/generated/validation-rules.js +71 -71
- package/src/lib/path-prover-brain.js +607 -0
- package/src/lib/prompts/project-prompts.js +12 -0
- package/src/lib/template-variable-resolver.js +25 -1
- package/src/lib/tune-catalog-validator.js +37 -4
- package/templates/Dockerfile +9 -0
- package/templates/code/adapter_sidecar.py +444 -0
- package/templates/code/serve +6 -0
- package/templates/code/serve.d/vllm.ejs +1 -1
- package/templates/do/.benchmark_writer.py +1476 -0
- package/templates/do/.tune_helper.py +982 -57
- package/templates/do/__pycache__/.benchmark_writer.cpython-312.pyc +0 -0
- package/templates/do/adapter +149 -0
- package/templates/do/benchmark +639 -85
- package/templates/do/config +108 -5
- package/templates/do/deploy.d/managed-inference.ejs +192 -11
- package/templates/do/optimize +106 -37
- package/templates/do/register +89 -0
- package/templates/do/test +13 -0
- package/templates/do/tune +378 -59
- package/templates/do/validate +44 -4
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Marketplace Picker MCP Server
|
|
7
|
+
*
|
|
8
|
+
* A bundled MCP server that discovers active AWS Marketplace model package
|
|
9
|
+
* subscriptions for deployment via the ml-container-creator generator.
|
|
10
|
+
*
|
|
11
|
+
* Uses ListModelPackages with ModelPackageType='Marketplace' to find subscribed
|
|
12
|
+
* packages, then DescribeModelPackage for each to extract InferenceSpecification
|
|
13
|
+
* details (supported instance types, content types).
|
|
14
|
+
*
|
|
15
|
+
* Tool: get_marketplace_subscriptions
|
|
16
|
+
* Accepts: { region?: string, limit?: number }
|
|
17
|
+
* Returns: { subscriptions: [...], message: string }
|
|
18
|
+
*
|
|
19
|
+
* Environment variables:
|
|
20
|
+
* AWS_REGION - AWS region for SageMaker API calls (default: us-east-1)
|
|
21
|
+
* AWS_PROFILE - AWS profile to use for credentials
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
25
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
26
|
+
import { z } from 'zod';
|
|
27
|
+
import { fileURLToPath } from 'node:url';
|
|
28
|
+
import { resolve } from 'node:path';
|
|
29
|
+
import { readFileSync } from 'node:fs';
|
|
30
|
+
import { homedir } from 'node:os';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Log to stderr so it doesn't interfere with MCP stdio protocol on stdout.
|
|
34
|
+
*/
|
|
35
|
+
function log(message) {
|
|
36
|
+
process.stderr.write(`[marketplace-picker] ${message}\n`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ── AWS SDK lazy loading ─────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
let _SageMakerClient = null;
|
|
42
|
+
let _ListModelPackagesCommand = null;
|
|
43
|
+
let _DescribeModelPackageCommand = null;
|
|
44
|
+
let _fromIni = null;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Lazily load the AWS SDK SageMaker client classes.
|
|
48
|
+
*/
|
|
49
|
+
async function _ensureSdkLoaded() {
|
|
50
|
+
if (_SageMakerClient) return;
|
|
51
|
+
const sdk = await import('@aws-sdk/client-sagemaker');
|
|
52
|
+
_SageMakerClient = sdk.SageMakerClient;
|
|
53
|
+
_ListModelPackagesCommand = sdk.ListModelPackagesCommand;
|
|
54
|
+
_DescribeModelPackageCommand = sdk.DescribeModelPackageCommand;
|
|
55
|
+
try {
|
|
56
|
+
const credentialProviders = await import('@aws-sdk/credential-providers');
|
|
57
|
+
_fromIni = credentialProviders.fromIni;
|
|
58
|
+
} catch {
|
|
59
|
+
// credential-providers not available — profile-based fallback won't work
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create a SageMaker client for the given region using default credential chain.
|
|
65
|
+
*/
|
|
66
|
+
function _createClient(region) {
|
|
67
|
+
return new _SageMakerClient({ region });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create a SageMaker client using a named AWS profile via fromIni.
|
|
72
|
+
*/
|
|
73
|
+
function _createClientWithProfile(region, profile) {
|
|
74
|
+
if (!_fromIni) {
|
|
75
|
+
throw new Error('Cannot use profile-based credentials: @aws-sdk/credential-providers not available');
|
|
76
|
+
}
|
|
77
|
+
return new _SageMakerClient({
|
|
78
|
+
region,
|
|
79
|
+
credentials: _fromIni({ profile })
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Detect available AWS profile names from ~/.aws/credentials and ~/.aws/config.
|
|
85
|
+
*/
|
|
86
|
+
function _detectAwsProfiles() {
|
|
87
|
+
const profiles = new Set();
|
|
88
|
+
try {
|
|
89
|
+
const credsPath = resolve(homedir(), '.aws/credentials');
|
|
90
|
+
const creds = readFileSync(credsPath, 'utf8');
|
|
91
|
+
for (const match of creds.matchAll(/^\[(.+)\]$/gm)) {
|
|
92
|
+
profiles.add(match[1]);
|
|
93
|
+
}
|
|
94
|
+
} catch { /* no credentials file */ }
|
|
95
|
+
try {
|
|
96
|
+
const configPath = resolve(homedir(), '.aws/config');
|
|
97
|
+
const config = readFileSync(configPath, 'utf8');
|
|
98
|
+
for (const match of config.matchAll(/^\[profile\s+(.+)\]$/gm)) {
|
|
99
|
+
profiles.add(match[1]);
|
|
100
|
+
}
|
|
101
|
+
} catch { /* no config file */ }
|
|
102
|
+
return [...profiles];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ── Core logic ───────────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Fetch marketplace model package subscriptions from SageMaker.
|
|
109
|
+
*
|
|
110
|
+
* Lists model packages with ModelPackageType='Marketplace', then describes
|
|
111
|
+
* each to extract InferenceSpecification details.
|
|
112
|
+
*
|
|
113
|
+
* @param {object} client - SageMaker client instance
|
|
114
|
+
* @param {object} options - { limit }
|
|
115
|
+
* @returns {Promise<Array<object>>} Array of subscription info objects
|
|
116
|
+
*/
|
|
117
|
+
async function fetchMarketplaceSubscriptions(client, { limit = 20 } = {}) {
|
|
118
|
+
const subscriptions = [];
|
|
119
|
+
let nextToken;
|
|
120
|
+
|
|
121
|
+
// Paginate ListModelPackages — Marketplace type only
|
|
122
|
+
const collectedArns = [];
|
|
123
|
+
do {
|
|
124
|
+
const params = {
|
|
125
|
+
ModelPackageType: 'Marketplace',
|
|
126
|
+
MaxResults: Math.min(limit, 100)
|
|
127
|
+
};
|
|
128
|
+
if (nextToken) params.NextToken = nextToken;
|
|
129
|
+
|
|
130
|
+
const command = new _ListModelPackagesCommand(params);
|
|
131
|
+
const response = await client.send(command);
|
|
132
|
+
|
|
133
|
+
const summaries = response.ModelPackageSummaryList || [];
|
|
134
|
+
for (const summary of summaries) {
|
|
135
|
+
collectedArns.push(summary.ModelPackageArn);
|
|
136
|
+
if (collectedArns.length >= limit) break;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
nextToken = response.NextToken;
|
|
140
|
+
} while (nextToken && collectedArns.length < limit);
|
|
141
|
+
|
|
142
|
+
// Describe each model package to get InferenceSpecification details
|
|
143
|
+
for (const arn of collectedArns) {
|
|
144
|
+
try {
|
|
145
|
+
const describeCmd = new _DescribeModelPackageCommand({
|
|
146
|
+
ModelPackageName: arn
|
|
147
|
+
});
|
|
148
|
+
const detail = await client.send(describeCmd);
|
|
149
|
+
|
|
150
|
+
const inferenceSpec = detail.InferenceSpecification || {};
|
|
151
|
+
const supportedInstanceTypes = inferenceSpec.SupportedRealtimeInferenceInstanceTypes || [];
|
|
152
|
+
const supportedContentTypes = inferenceSpec.SupportedContentTypes || [];
|
|
153
|
+
|
|
154
|
+
// Extract model name from the ARN (last segment before version)
|
|
155
|
+
const arnParts = arn.split('/');
|
|
156
|
+
const modelName = arnParts.length >= 2 ? arnParts[arnParts.length - 2] : arn;
|
|
157
|
+
|
|
158
|
+
// Extract vendor from model package description or source
|
|
159
|
+
const vendor = detail.ModelPackageDescription
|
|
160
|
+
? detail.ModelPackageDescription.split(' ')[0]
|
|
161
|
+
: 'Unknown';
|
|
162
|
+
|
|
163
|
+
subscriptions.push({
|
|
164
|
+
arn,
|
|
165
|
+
modelName,
|
|
166
|
+
vendor,
|
|
167
|
+
supportedInstanceTypes,
|
|
168
|
+
supportedContentTypes,
|
|
169
|
+
status: detail.ModelPackageStatus || 'Unknown'
|
|
170
|
+
});
|
|
171
|
+
} catch (err) {
|
|
172
|
+
if (err.name === 'AccessDeniedException' || err.Code === 'AccessDeniedException') {
|
|
173
|
+
log(`AccessDeniedException for package "${arn}" — skipping`);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
log(`Warning: could not describe package "${arn}": ${err.message}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return subscriptions;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Build the MCP response from a list of discovered subscriptions.
|
|
185
|
+
*
|
|
186
|
+
* @param {Array} subscriptions - Array of subscription objects from fetchMarketplaceSubscriptions
|
|
187
|
+
* @returns {{ subscriptions: Array, message: string }}
|
|
188
|
+
*/
|
|
189
|
+
function buildResponse(subscriptions) {
|
|
190
|
+
if (!subscriptions || subscriptions.length === 0) {
|
|
191
|
+
return {
|
|
192
|
+
subscriptions: [],
|
|
193
|
+
message: 'No active AWS Marketplace model package subscriptions found in this region. Subscribe to models at https://aws.amazon.com/marketplace/solutions/machine-learning'
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
subscriptions,
|
|
199
|
+
message: `Found ${subscriptions.length} Marketplace model package subscription(s).`
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ── MCP Server ───────────────────────────────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
const server = new McpServer({
|
|
206
|
+
name: 'marketplace-picker',
|
|
207
|
+
version: '1.0.0'
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Register the get_marketplace_subscriptions tool
|
|
211
|
+
server.tool(
|
|
212
|
+
'get_marketplace_subscriptions',
|
|
213
|
+
'Discovers active AWS Marketplace model package subscriptions with supported instance types and content types',
|
|
214
|
+
{
|
|
215
|
+
region: z.string().optional().describe('AWS region to query (defaults to AWS_REGION env var or us-east-1)'),
|
|
216
|
+
limit: z.number().int().positive().default(20).describe('Maximum number of subscriptions to return')
|
|
217
|
+
},
|
|
218
|
+
async ({ region, limit }) => {
|
|
219
|
+
const effectiveRegion = region || process.env.AWS_REGION || 'us-east-1';
|
|
220
|
+
const profile = process.env.AWS_PROFILE || null;
|
|
221
|
+
log(`Querying Marketplace subscriptions in region: ${effectiveRegion}${profile ? ` (profile: ${profile})` : ''}`);
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
await _ensureSdkLoaded();
|
|
225
|
+
|
|
226
|
+
let subscriptions = null;
|
|
227
|
+
let lastError = null;
|
|
228
|
+
|
|
229
|
+
// Strategy 1: If a specific profile was requested, use it directly
|
|
230
|
+
if (profile) {
|
|
231
|
+
try {
|
|
232
|
+
log(`Trying explicit profile: ${profile}`);
|
|
233
|
+
const client = _createClientWithProfile(effectiveRegion, profile);
|
|
234
|
+
subscriptions = await fetchMarketplaceSubscriptions(client, { limit });
|
|
235
|
+
} catch (err) {
|
|
236
|
+
log(`Profile "${profile}" failed: ${err.message}`);
|
|
237
|
+
lastError = err;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Strategy 2: Try the default credential chain
|
|
242
|
+
if (!subscriptions) {
|
|
243
|
+
try {
|
|
244
|
+
log('Trying default credential chain');
|
|
245
|
+
const client = _createClient(effectiveRegion);
|
|
246
|
+
subscriptions = await fetchMarketplaceSubscriptions(client, { limit });
|
|
247
|
+
} catch (err) {
|
|
248
|
+
log(`Default credential chain failed: ${err.message}`);
|
|
249
|
+
lastError = err;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Strategy 3: Detect available AWS profiles and try each
|
|
254
|
+
if (!subscriptions && _fromIni) {
|
|
255
|
+
const profiles = _detectAwsProfiles();
|
|
256
|
+
if (profiles.length > 0) {
|
|
257
|
+
log(`Default credentials failed, trying ${profiles.length} detected profile(s): ${profiles.join(', ')}`);
|
|
258
|
+
for (const p of profiles) {
|
|
259
|
+
try {
|
|
260
|
+
const client = _createClientWithProfile(effectiveRegion, p);
|
|
261
|
+
subscriptions = await fetchMarketplaceSubscriptions(client, { limit });
|
|
262
|
+
log(`Profile "${p}" succeeded`);
|
|
263
|
+
break;
|
|
264
|
+
} catch (err) {
|
|
265
|
+
log(`Profile "${p}" failed: ${err.message}`);
|
|
266
|
+
lastError = err;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// If all strategies failed, throw the last error
|
|
273
|
+
if (!subscriptions) {
|
|
274
|
+
throw lastError || new Error('No AWS credentials available');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const result = buildResponse(subscriptions);
|
|
278
|
+
|
|
279
|
+
if (subscriptions.length > 0) {
|
|
280
|
+
log(`Found ${subscriptions.length} Marketplace subscription(s)`);
|
|
281
|
+
} else {
|
|
282
|
+
log('No Marketplace subscriptions found');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
content: [{
|
|
287
|
+
type: 'text',
|
|
288
|
+
text: JSON.stringify(result)
|
|
289
|
+
}]
|
|
290
|
+
};
|
|
291
|
+
} catch (err) {
|
|
292
|
+
log(`Error querying Marketplace subscriptions: ${err.message}`);
|
|
293
|
+
|
|
294
|
+
// Handle AccessDeniedException gracefully
|
|
295
|
+
if (err.name === 'AccessDeniedException' || err.Code === 'AccessDeniedException') {
|
|
296
|
+
log('AccessDeniedException — returning empty result');
|
|
297
|
+
return {
|
|
298
|
+
content: [{
|
|
299
|
+
type: 'text',
|
|
300
|
+
text: JSON.stringify({
|
|
301
|
+
subscriptions: [],
|
|
302
|
+
message: 'Access denied when querying Marketplace subscriptions. Check IAM permissions for sagemaker:ListModelPackages and sagemaker:DescribeModelPackage.'
|
|
303
|
+
})
|
|
304
|
+
}]
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const errorResult = {
|
|
309
|
+
subscriptions: [],
|
|
310
|
+
error: err.message,
|
|
311
|
+
message: `Failed to query Marketplace subscriptions: ${err.message}`
|
|
312
|
+
};
|
|
313
|
+
return {
|
|
314
|
+
content: [{
|
|
315
|
+
type: 'text',
|
|
316
|
+
text: JSON.stringify(errorResult)
|
|
317
|
+
}]
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
// Export for testing
|
|
324
|
+
export {
|
|
325
|
+
fetchMarketplaceSubscriptions,
|
|
326
|
+
buildResponse,
|
|
327
|
+
_ensureSdkLoaded,
|
|
328
|
+
_createClient,
|
|
329
|
+
_createClientWithProfile,
|
|
330
|
+
_detectAwsProfiles
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// Guard MCP transport — only connect when run as main module
|
|
334
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
335
|
+
const isMain = process.argv[1] && resolve(process.argv[1]) === __filename;
|
|
336
|
+
|
|
337
|
+
if (isMain) {
|
|
338
|
+
log('Starting Marketplace Picker MCP server');
|
|
339
|
+
await _ensureSdkLoaded();
|
|
340
|
+
const transport = new StdioServerTransport();
|
|
341
|
+
await server.connect(transport);
|
|
342
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@amzn/ml-container-creator-marketplace-picker",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Discovers active AWS Marketplace model package subscriptions for deployment.",
|
|
5
|
+
"modes": {
|
|
6
|
+
"static": false,
|
|
7
|
+
"smart": false,
|
|
8
|
+
"discover": true
|
|
9
|
+
},
|
|
10
|
+
"catalogs": {},
|
|
11
|
+
"tool": {
|
|
12
|
+
"name": "get_marketplace_subscriptions"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@amzn/ml-container-creator-marketplace-picker",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "MCP server that discovers active AWS Marketplace model package subscriptions.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"license": "Apache-2.0",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node test.js"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@aws-sdk/client-sagemaker": "^3.700.0",
|
|
14
|
+
"@aws-sdk/credential-providers": "^3.700.0",
|
|
15
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
16
|
+
"zod": "^3.22.0"
|
|
17
|
+
}
|
|
18
|
+
}
|