@aws/ml-container-creator 0.2.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 (143) hide show
  1. package/LICENSE +202 -0
  2. package/LICENSE-THIRD-PARTY +68620 -0
  3. package/NOTICE +2 -0
  4. package/README.md +106 -0
  5. package/bin/cli.js +365 -0
  6. package/config/defaults.json +32 -0
  7. package/config/presets/transformers-djl.json +26 -0
  8. package/config/presets/transformers-gpu.json +24 -0
  9. package/config/presets/transformers-lmi.json +27 -0
  10. package/package.json +129 -0
  11. package/servers/README.md +419 -0
  12. package/servers/base-image-picker/catalogs/model-servers.json +1191 -0
  13. package/servers/base-image-picker/catalogs/python-slim.json +38 -0
  14. package/servers/base-image-picker/catalogs/triton-backends.json +51 -0
  15. package/servers/base-image-picker/catalogs/triton.json +38 -0
  16. package/servers/base-image-picker/index.js +495 -0
  17. package/servers/base-image-picker/manifest.json +17 -0
  18. package/servers/base-image-picker/package.json +15 -0
  19. package/servers/hyperpod-cluster-picker/LICENSE +202 -0
  20. package/servers/hyperpod-cluster-picker/index.js +424 -0
  21. package/servers/hyperpod-cluster-picker/manifest.json +14 -0
  22. package/servers/hyperpod-cluster-picker/package.json +17 -0
  23. package/servers/instance-recommender/LICENSE +202 -0
  24. package/servers/instance-recommender/catalogs/instances.json +852 -0
  25. package/servers/instance-recommender/index.js +284 -0
  26. package/servers/instance-recommender/manifest.json +16 -0
  27. package/servers/instance-recommender/package.json +15 -0
  28. package/servers/lib/LICENSE +202 -0
  29. package/servers/lib/bedrock-client.js +160 -0
  30. package/servers/lib/custom-validators.js +46 -0
  31. package/servers/lib/dynamic-resolver.js +36 -0
  32. package/servers/lib/package.json +11 -0
  33. package/servers/lib/schemas/image-catalog.schema.json +185 -0
  34. package/servers/lib/schemas/instances.schema.json +124 -0
  35. package/servers/lib/schemas/manifest.schema.json +64 -0
  36. package/servers/lib/schemas/model-catalog.schema.json +91 -0
  37. package/servers/lib/schemas/regions.schema.json +26 -0
  38. package/servers/lib/schemas/triton-backends.schema.json +51 -0
  39. package/servers/model-picker/catalogs/jumpstart-public.json +66 -0
  40. package/servers/model-picker/catalogs/popular-diffusors.json +88 -0
  41. package/servers/model-picker/catalogs/popular-transformers.json +226 -0
  42. package/servers/model-picker/index.js +1693 -0
  43. package/servers/model-picker/manifest.json +18 -0
  44. package/servers/model-picker/package.json +20 -0
  45. package/servers/region-picker/LICENSE +202 -0
  46. package/servers/region-picker/catalogs/regions.json +263 -0
  47. package/servers/region-picker/index.js +230 -0
  48. package/servers/region-picker/manifest.json +16 -0
  49. package/servers/region-picker/package.json +15 -0
  50. package/src/app.js +1007 -0
  51. package/src/copy-tpl.js +77 -0
  52. package/src/lib/accelerator-validator.js +39 -0
  53. package/src/lib/asset-manager.js +385 -0
  54. package/src/lib/aws-profile-parser.js +181 -0
  55. package/src/lib/bootstrap-command-handler.js +1647 -0
  56. package/src/lib/bootstrap-config.js +238 -0
  57. package/src/lib/ci-register-helpers.js +124 -0
  58. package/src/lib/ci-report-helpers.js +158 -0
  59. package/src/lib/ci-stage-helpers.js +268 -0
  60. package/src/lib/cli-handler.js +529 -0
  61. package/src/lib/comment-generator.js +544 -0
  62. package/src/lib/community-reports-validator.js +91 -0
  63. package/src/lib/config-manager.js +2106 -0
  64. package/src/lib/configuration-exporter.js +204 -0
  65. package/src/lib/configuration-manager.js +695 -0
  66. package/src/lib/configuration-matcher.js +221 -0
  67. package/src/lib/cpu-validator.js +36 -0
  68. package/src/lib/cuda-validator.js +57 -0
  69. package/src/lib/deployment-config-resolver.js +103 -0
  70. package/src/lib/deployment-entry-schema.js +125 -0
  71. package/src/lib/deployment-registry.js +598 -0
  72. package/src/lib/docker-introspection-validator.js +51 -0
  73. package/src/lib/engine-prefix-resolver.js +60 -0
  74. package/src/lib/huggingface-client.js +172 -0
  75. package/src/lib/key-value-parser.js +37 -0
  76. package/src/lib/known-flags-validator.js +200 -0
  77. package/src/lib/manifest-cli.js +280 -0
  78. package/src/lib/mcp-client.js +303 -0
  79. package/src/lib/mcp-command-handler.js +532 -0
  80. package/src/lib/neuron-validator.js +80 -0
  81. package/src/lib/parameter-schema-validator.js +284 -0
  82. package/src/lib/prompt-runner.js +1349 -0
  83. package/src/lib/prompts.js +1138 -0
  84. package/src/lib/registry-command-handler.js +519 -0
  85. package/src/lib/registry-loader.js +198 -0
  86. package/src/lib/rocm-validator.js +80 -0
  87. package/src/lib/schema-validator.js +157 -0
  88. package/src/lib/sensitive-redactor.js +59 -0
  89. package/src/lib/template-engine.js +156 -0
  90. package/src/lib/template-manager.js +341 -0
  91. package/src/lib/validation-engine.js +314 -0
  92. package/src/prompt-adapter.js +63 -0
  93. package/templates/Dockerfile +300 -0
  94. package/templates/IAM_PERMISSIONS.md +84 -0
  95. package/templates/MIGRATION.md +488 -0
  96. package/templates/PROJECT_README.md +439 -0
  97. package/templates/TEMPLATE_SYSTEM.md +243 -0
  98. package/templates/buildspec.yml +64 -0
  99. package/templates/code/chat_template.jinja +1 -0
  100. package/templates/code/flask/gunicorn_config.py +35 -0
  101. package/templates/code/flask/wsgi.py +10 -0
  102. package/templates/code/model_handler.py +387 -0
  103. package/templates/code/serve +300 -0
  104. package/templates/code/serve.py +175 -0
  105. package/templates/code/serving.properties +105 -0
  106. package/templates/code/start_server.py +39 -0
  107. package/templates/code/start_server.sh +39 -0
  108. package/templates/diffusors/Dockerfile +72 -0
  109. package/templates/diffusors/patch_image_api.py +35 -0
  110. package/templates/diffusors/serve +115 -0
  111. package/templates/diffusors/start_server.sh +114 -0
  112. package/templates/do/.gitkeep +1 -0
  113. package/templates/do/README.md +541 -0
  114. package/templates/do/build +83 -0
  115. package/templates/do/ci +681 -0
  116. package/templates/do/clean +811 -0
  117. package/templates/do/config +260 -0
  118. package/templates/do/deploy +1560 -0
  119. package/templates/do/export +306 -0
  120. package/templates/do/logs +319 -0
  121. package/templates/do/manifest +12 -0
  122. package/templates/do/push +119 -0
  123. package/templates/do/register +580 -0
  124. package/templates/do/run +113 -0
  125. package/templates/do/submit +417 -0
  126. package/templates/do/test +1147 -0
  127. package/templates/hyperpod/configmap.yaml +24 -0
  128. package/templates/hyperpod/deployment.yaml +71 -0
  129. package/templates/hyperpod/pvc.yaml +42 -0
  130. package/templates/hyperpod/service.yaml +17 -0
  131. package/templates/nginx-diffusors.conf +74 -0
  132. package/templates/nginx-predictors.conf +47 -0
  133. package/templates/nginx-tensorrt.conf +74 -0
  134. package/templates/requirements.txt +61 -0
  135. package/templates/sample_model/test_inference.py +123 -0
  136. package/templates/sample_model/train_abalone.py +252 -0
  137. package/templates/test/test_endpoint.sh +79 -0
  138. package/templates/test/test_local_image.sh +80 -0
  139. package/templates/test/test_model_handler.py +180 -0
  140. package/templates/triton/Dockerfile +128 -0
  141. package/templates/triton/config.pbtxt +163 -0
  142. package/templates/triton/model.py +130 -0
  143. package/templates/triton/requirements.txt +11 -0
@@ -0,0 +1,303 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /**
5
+ * MCP Client
6
+ *
7
+ * Spawns an MCP server as a child process via stdio transport,
8
+ * performs the protocol handshake, calls the configured tool,
9
+ * and returns parsed configuration values.
10
+ *
11
+ * All errors are caught and returned as null with a diagnostic
12
+ * message — never thrown.
13
+ */
14
+
15
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
16
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
17
+
18
+ const DEFAULT_TOOL_NAME = 'get_ml_config';
19
+ const DEFAULT_LIMIT = 10;
20
+ const DEFAULT_TIMEOUT = 10000; // 10 seconds
21
+ // const KILL_GRACE_PERIOD = 2000; // reserved for future graceful-shutdown logic
22
+
23
+ class McpClient {
24
+ /**
25
+ * @param {object} serverConfig - { command, args, env, toolName, limit }
26
+ * @param {object} options - { timeout, parameterMatrix }
27
+ */
28
+ constructor(serverConfig, options = {}) {
29
+ this.serverConfig = serverConfig;
30
+ this.toolName = serverConfig.toolName || DEFAULT_TOOL_NAME;
31
+ this.limit = serverConfig.limit || DEFAULT_LIMIT;
32
+ this.timeout = options.timeout || DEFAULT_TIMEOUT;
33
+ this.parameterMatrix = options.parameterMatrix || {};
34
+ this.smart = options.smart || false;
35
+ this.discover = options.discover || false;
36
+ this._transport = null;
37
+ this._client = null;
38
+ this._diagnosticMessage = null;
39
+ }
40
+
41
+ /**
42
+ * Connect to the MCP server, perform handshake, query for config values.
43
+ * @returns {Promise<{ values: Record<string, any>, choices: Record<string, string[]> } | null>}
44
+ */
45
+ async query() {
46
+ try {
47
+ return await this._queryWithTimeout();
48
+ } catch (err) {
49
+ this._diagnosticMessage = `MCP query failed: ${err.message}`;
50
+ return null;
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Wraps the actual query logic with a timeout that covers the
56
+ * entire lifecycle: spawn + handshake + tool call + response parsing.
57
+ */
58
+ async _queryWithTimeout() {
59
+ return new Promise((resolve, _reject) => {
60
+ let settled = false;
61
+ const timer = setTimeout(() => {
62
+ if (!settled) {
63
+ settled = true;
64
+ this._diagnosticMessage = `MCP server timed out after ${this.timeout}ms`;
65
+ this.close().catch(() => {});
66
+ resolve(null);
67
+ }
68
+ }, this.timeout);
69
+
70
+ this._executeQuery()
71
+ .then(result => {
72
+ if (!settled) {
73
+ settled = true;
74
+ clearTimeout(timer);
75
+ resolve(result);
76
+ }
77
+ })
78
+ .catch(err => {
79
+ if (!settled) {
80
+ settled = true;
81
+ clearTimeout(timer);
82
+ this._diagnosticMessage = `MCP query failed: ${err.message}`;
83
+ resolve(null);
84
+ }
85
+ });
86
+ });
87
+ }
88
+
89
+ /**
90
+ * Performs the actual MCP protocol flow:
91
+ * 1. Spawn server via StdioClientTransport
92
+ * 2. Connect Client (performs initialize handshake + initialized notification)
93
+ * 3. Call the configured tool with unbounded parameter names and limit
94
+ * 4. Parse and return the response
95
+ */
96
+ async _executeQuery() {
97
+ const { command, args = [], env } = this.serverConfig;
98
+
99
+ // Build environment: merge process.env with server-specific env
100
+ // When --smart flag is active, inject BEDROCK_SMART=true for this run
101
+ // When --discover flag is active, inject MCP_DISCOVER=true for this run
102
+ // Always pass process.env so child processes inherit AWS credentials, profiles, etc.
103
+ const smartEnv = this.smart ? { BEDROCK_SMART: 'true' } : {};
104
+ const discoverEnv = this.discover ? { MCP_DISCOVER: 'true' } : {};
105
+ const serverEnv = env && Object.keys(env).length > 0 ? env : {};
106
+ const spawnEnv = { ...process.env, ...smartEnv, ...discoverEnv, ...serverEnv };
107
+
108
+ // Create stdio transport — spawns the server process
109
+ this._transport = new StdioClientTransport({
110
+ command,
111
+ args,
112
+ env: spawnEnv,
113
+ stderr: 'pipe'
114
+ });
115
+
116
+ // Create MCP client
117
+ this._client = new Client(
118
+ { name: 'ml-container-creator', version: '1.0.0' },
119
+ { capabilities: {} }
120
+ );
121
+
122
+ // Connect performs the initialize handshake automatically
123
+ await this._client.connect(this._transport);
124
+
125
+ // Build the list of unbounded parameter names
126
+ const unboundedParams = this._getUnboundedParameterNames();
127
+
128
+ // Build context from bounded parameters that have defaults
129
+ const context = this._buildContext();
130
+
131
+ // Call the configured tool
132
+ const result = await this._client.callTool({
133
+ name: this.toolName,
134
+ arguments: {
135
+ parameters: unboundedParams,
136
+ limit: this.limit,
137
+ context
138
+ }
139
+ });
140
+
141
+ // Parse the response
142
+ return this._parseResponse(result);
143
+ }
144
+
145
+ /**
146
+ * Extract unbounded parameter names from the parameter matrix.
147
+ * @returns {string[]}
148
+ */
149
+ _getUnboundedParameterNames() {
150
+ const names = [];
151
+ for (const [name, config] of Object.entries(this.parameterMatrix)) {
152
+ if (config.valueSpace === 'unbounded' && config.mcp === true) {
153
+ names.push(name);
154
+ }
155
+ }
156
+ return names;
157
+ }
158
+
159
+ /**
160
+ * Build context object from bounded parameters with known defaults.
161
+ * This gives the MCP server context about the current configuration.
162
+ * @returns {object}
163
+ */
164
+ _buildContext() {
165
+ const context = {};
166
+ for (const [name, config] of Object.entries(this.parameterMatrix)) {
167
+ if (config.valueSpace === 'bounded' && config.default !== null && config.default !== undefined) {
168
+ context[name] = config.default;
169
+ }
170
+ }
171
+ return context;
172
+ }
173
+
174
+ /**
175
+ * Parse the MCP tool call response into { values, choices }.
176
+ * The response content is an array of content blocks; we look for
177
+ * a text block containing JSON with values and/or choices.
178
+ * @param {object} result - The callTool result
179
+ * @returns {{ values: Record<string, any>, choices: Record<string, string[]> } | null}
180
+ */
181
+ _parseResponse(result) {
182
+ if (!result) {
183
+ this._diagnosticMessage = 'MCP server returned empty result';
184
+ return null;
185
+ }
186
+
187
+ // Check for error flag
188
+ if (result.isError) {
189
+ const errorText = this._extractTextContent(result);
190
+ this._diagnosticMessage = `MCP server returned error: ${errorText || 'unknown error'}`;
191
+ return null;
192
+ }
193
+
194
+ // Extract text content from the response
195
+ const textContent = this._extractTextContent(result);
196
+ if (!textContent) {
197
+ this._diagnosticMessage = 'MCP server returned no text content';
198
+ return null;
199
+ }
200
+
201
+ // Parse JSON from text content
202
+ let parsed;
203
+ try {
204
+ parsed = JSON.parse(textContent);
205
+ } catch (err) {
206
+ this._diagnosticMessage = `MCP server returned malformed JSON: ${err.message}`;
207
+ return null;
208
+ }
209
+
210
+ // Validate structure
211
+ if (typeof parsed !== 'object' || parsed === null) {
212
+ this._diagnosticMessage = 'MCP server returned non-object response';
213
+ return null;
214
+ }
215
+
216
+ const values = {};
217
+ const choices = {};
218
+ const metadata = {};
219
+
220
+ // Extract values — only for unbounded parameters
221
+ if (parsed.values && typeof parsed.values === 'object') {
222
+ for (const [key, value] of Object.entries(parsed.values)) {
223
+ values[key] = value;
224
+ }
225
+ }
226
+
227
+ // Extract choices — only for unbounded parameters
228
+ if (parsed.choices && typeof parsed.choices === 'object') {
229
+ for (const [key, choiceList] of Object.entries(parsed.choices)) {
230
+ if (Array.isArray(choiceList)) {
231
+ choices[key] = choiceList;
232
+ }
233
+ }
234
+ }
235
+
236
+ // Extract metadata — pass through for rich display (e.g., ImageEntry objects)
237
+ if (parsed.metadata && typeof parsed.metadata === 'object') {
238
+ for (const [key, value] of Object.entries(parsed.metadata)) {
239
+ metadata[key] = value;
240
+ }
241
+ }
242
+
243
+ const response = { values, choices, message: parsed.message || null };
244
+ if (Object.keys(metadata).length > 0) {
245
+ response.metadata = metadata;
246
+ }
247
+ return response;
248
+ }
249
+
250
+ /**
251
+ * Extract text content from an MCP tool result.
252
+ * @param {object} result
253
+ * @returns {string | null}
254
+ */
255
+ _extractTextContent(result) {
256
+ if (!result.content || !Array.isArray(result.content)) {
257
+ return null;
258
+ }
259
+
260
+ const textBlock = result.content.find(block => block.type === 'text');
261
+ return textBlock ? textBlock.text : null;
262
+ }
263
+
264
+ /**
265
+ * Get the diagnostic message from the last operation.
266
+ * @returns {string | null}
267
+ */
268
+ getDiagnosticMessage() {
269
+ return this._diagnosticMessage;
270
+ }
271
+
272
+ /**
273
+ * Cleanly terminate the server process.
274
+ * Sends SIGTERM first, then SIGKILL after grace period.
275
+ */
276
+ async close() {
277
+ try {
278
+ if (this._client) {
279
+ await this._client.close();
280
+ this._client = null;
281
+ }
282
+ if (this._transport) {
283
+ await this._transport.close();
284
+ this._transport = null;
285
+ }
286
+ } catch (err) {
287
+ // If graceful close fails, force kill via transport
288
+ if (this._transport) {
289
+ try {
290
+ // The transport's close() handles SIGTERM + SIGKILL
291
+ await this._transport.close();
292
+ } catch (_) {
293
+ // Ignore — process may already be dead
294
+ }
295
+ this._transport = null;
296
+ }
297
+ this._client = null;
298
+ }
299
+ }
300
+ }
301
+
302
+ export default McpClient;
303
+ export { McpClient, DEFAULT_TOOL_NAME, DEFAULT_LIMIT, DEFAULT_TIMEOUT };