@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,695 @@
1
+ /**
2
+ * Configuration Manager
3
+ *
4
+ * Orchestrates configuration loading, matching, and merging for the multi-registry system.
5
+ * Handles framework configurations, model-specific overrides, and HuggingFace API integration.
6
+ *
7
+ * Requirements: 1.7, 2.1, 2.2, 2.3, 2.4, 4.1, 4.2, 13.1, 13.2, 13.3, 13.4, 13.5, 13.6, 13.7, 13.8
8
+ */
9
+
10
+ import RegistryLoader from './registry-loader.js';
11
+ import ConfigurationMatcher from './configuration-matcher.js';
12
+ import ValidationEngine from './validation-engine.js';
13
+ import HuggingFaceClient from './huggingface-client.js';
14
+
15
+ export default class ConfigurationManager {
16
+ constructor(options = {}) {
17
+ this.registryLoader = new RegistryLoader();
18
+ this.validationEngine = new ValidationEngine();
19
+ this.hfClient = new HuggingFaceClient({
20
+ timeout: options.hfTimeout || 5000,
21
+ offline: options.offline || false
22
+ });
23
+
24
+ // Registries (loaded on demand)
25
+ this.frameworkRegistry = null;
26
+ this.modelRegistry = null;
27
+ this.instanceMapping = null;
28
+
29
+ // Configuration matcher (initialized after registries load)
30
+ this.configMatcher = null;
31
+
32
+ // Validation options
33
+ this.validateEnvVars = options.validateEnvVars ?? true;
34
+ }
35
+
36
+ /**
37
+ * Load all registries from disk
38
+ * Gracefully handles missing or invalid registries by using empty objects
39
+ *
40
+ * Requirements: 1.7, 2.8
41
+ */
42
+ async loadRegistries() {
43
+ this.frameworkRegistry = await this.registryLoader.loadFrameworkRegistry();
44
+ this.modelRegistry = await this.registryLoader.loadModelRegistry();
45
+ this.instanceMapping = await this.registryLoader.loadInstanceAcceleratorMapping();
46
+
47
+ // Initialize configuration matcher with loaded registries
48
+ this.configMatcher = new ConfigurationMatcher(
49
+ this.frameworkRegistry,
50
+ this.modelRegistry
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Find best matching configuration for user selections
56
+ * Orchestrates matching across framework, model, and HuggingFace sources
57
+ *
58
+ * @param {Object} userSelections - User's configuration choices
59
+ * @param {string} userSelections.framework - Framework name (e.g., "vllm")
60
+ * @param {string} userSelections.version - Framework version (e.g., "0.3.0")
61
+ * @param {string} [userSelections.modelId] - Optional model ID
62
+ * @param {string} [userSelections.frameworkProfile] - Optional framework profile name
63
+ * @param {string} [userSelections.modelProfile] - Optional model profile name
64
+ * @returns {Promise<Object>} Matched configuration profile
65
+ *
66
+ * Requirements: 2.1, 2.2, 2.3, 5.1, 5.2, 5.3, 12.1, 12.2
67
+ */
68
+ async matchConfiguration(userSelections) {
69
+ // Ensure registries are loaded
70
+ if (!this.configMatcher) {
71
+ await this.loadRegistries();
72
+ }
73
+
74
+ // Match framework configuration
75
+ const frameworkConfig = this.configMatcher.matchFramework(
76
+ userSelections.framework,
77
+ userSelections.version
78
+ );
79
+
80
+ // Get framework profile if specified
81
+ let frameworkProfile = null;
82
+ if (userSelections.frameworkProfile && frameworkConfig?.profiles) {
83
+ frameworkProfile = frameworkConfig.profiles[userSelections.frameworkProfile];
84
+ }
85
+
86
+ // Match model configuration (if model ID provided)
87
+ let modelConfig = null;
88
+ let hfData = null;
89
+
90
+ if (userSelections.modelId) {
91
+ // Try HuggingFace API first
92
+ hfData = await this._fetchHuggingFaceData(userSelections.modelId);
93
+
94
+ // Check model registry for overrides
95
+ modelConfig = this.configMatcher.matchModel(userSelections.modelId);
96
+ }
97
+
98
+ // Get model profile if specified
99
+ let modelProfile = null;
100
+ if (userSelections.modelProfile && modelConfig?.profiles) {
101
+ modelProfile = modelConfig.profiles[userSelections.modelProfile];
102
+ }
103
+
104
+ // Merge all configurations
105
+ return this.mergeConfigurations({
106
+ frameworkConfig,
107
+ frameworkProfile,
108
+ hfData,
109
+ modelConfig,
110
+ modelProfile,
111
+ userSelections
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Merge configurations with correct priority order
117
+ * Priority (highest to lowest): model profile → model config → HF data → framework profile → framework base
118
+ *
119
+ * @param {Object} configs - Configuration sources
120
+ * @param {Object} configs.frameworkConfig - Base framework configuration
121
+ * @param {Object} [configs.frameworkProfile] - Framework profile configuration
122
+ * @param {Object} [configs.hfData] - HuggingFace API data
123
+ * @param {Object} [configs.modelConfig] - Model registry configuration
124
+ * @param {Object} [configs.modelProfile] - Model profile configuration
125
+ * @param {Object} configs.userSelections - User selections
126
+ * @returns {Object} Merged configuration profile
127
+ *
128
+ * Requirements: 1.7, 5.3, 12.6, 12.13
129
+ */
130
+ mergeConfigurations(configs) {
131
+ const {
132
+ frameworkConfig,
133
+ frameworkProfile,
134
+ hfData,
135
+ modelConfig,
136
+ modelProfile,
137
+ userSelections
138
+ } = configs;
139
+
140
+ // Start with empty configuration (graceful degradation)
141
+ const merged = {
142
+ // User selections (always preserved)
143
+ framework: userSelections?.framework || null,
144
+ version: userSelections?.version || null,
145
+ modelId: userSelections?.modelId || null,
146
+
147
+ // Configuration fields (will be populated if sources available)
148
+ baseImage: null,
149
+ accelerator: null,
150
+ envVars: {},
151
+ inferenceAmiVersion: null,
152
+ chatTemplate: null,
153
+ recommendedInstanceTypes: [],
154
+
155
+ // Metadata
156
+ configSources: [],
157
+ validationLevel: 'unknown',
158
+ matchType: null,
159
+ generatedAt: new Date().toISOString()
160
+ };
161
+
162
+ // Apply configurations in priority order (lowest to highest)
163
+
164
+ // 1. Framework base configuration
165
+ if (frameworkConfig) {
166
+ this._applyFrameworkConfig(merged, frameworkConfig);
167
+ merged.configSources.push('Framework_Registry');
168
+ merged.validationLevel = frameworkConfig.validationLevel || 'unknown';
169
+ merged.matchType = frameworkConfig.matchType;
170
+ }
171
+
172
+ // 2. Framework profile
173
+ if (frameworkProfile) {
174
+ this._applyProfileConfig(merged, frameworkProfile);
175
+ merged.configSources.push('Framework_Profile');
176
+ }
177
+
178
+ // 3. HuggingFace API data
179
+ if (hfData) {
180
+ this._applyHuggingFaceData(merged, hfData);
181
+ merged.configSources.push('HuggingFace_Hub_API');
182
+ }
183
+
184
+ // 4. Model registry configuration
185
+ if (modelConfig) {
186
+ this._applyModelConfig(merged, modelConfig);
187
+ merged.configSources.push('Model_Registry');
188
+ // Model registry can override validation level
189
+ if (modelConfig.validationLevel) {
190
+ merged.validationLevel = modelConfig.validationLevel;
191
+ }
192
+ }
193
+
194
+ // 5. Model profile (highest priority)
195
+ if (modelProfile) {
196
+ this._applyProfileConfig(merged, modelProfile);
197
+ merged.configSources.push('Model_Profile');
198
+ }
199
+
200
+ // If no sources provided configuration, mark as default
201
+ if (merged.configSources.length === 0) {
202
+ merged.configSources.push('Default');
203
+ }
204
+
205
+ return merged;
206
+ }
207
+
208
+ /**
209
+ * Validate instance type against framework requirements
210
+ *
211
+ * @param {string} instanceType - Instance type to validate
212
+ * @param {Object} frameworkConfig - Framework configuration with accelerator requirements
213
+ * @returns {Object} Validation result
214
+ * @returns {boolean} result.compatible - Whether instance is compatible
215
+ * @returns {string} [result.error] - Error message if incompatible
216
+ * @returns {string} [result.warning] - Warning message if issues detected
217
+ * @returns {string} [result.info] - Informational message
218
+ * @returns {Array<string>} [result.recommendations] - Recommended instance types
219
+ *
220
+ * Requirements: 4.1, 4.2, 4.7, 4.8, 4.10, 4.11, 4.12, 4.13, 4.14, 4.15
221
+ */
222
+ validateInstanceType(instanceType, frameworkConfig) {
223
+ // Ensure registries are loaded
224
+ if (!this.instanceMapping) {
225
+ return {
226
+ compatible: true,
227
+ warning: 'Instance accelerator mapping not loaded. Proceeding without validation.'
228
+ };
229
+ }
230
+
231
+ // Get instance configuration
232
+ const instanceConfig = this.instanceMapping[instanceType];
233
+
234
+ if (!instanceConfig) {
235
+ return {
236
+ compatible: true,
237
+ warning: `No accelerator data for ${instanceType}. Proceeding with best-effort validation.`
238
+ };
239
+ }
240
+
241
+ // Validate accelerator compatibility
242
+ if (!frameworkConfig?.accelerator) {
243
+ return {
244
+ compatible: true,
245
+ info: 'No accelerator requirements specified for framework.'
246
+ };
247
+ }
248
+
249
+ const validation = this.validationEngine.validateAcceleratorCompatibility(
250
+ frameworkConfig,
251
+ instanceConfig
252
+ );
253
+
254
+ // Add recommendations if incompatible
255
+ if (!validation.compatible && frameworkConfig.accelerator) {
256
+ const recommendations = this.validationEngine.getRecommendedInstanceTypes(
257
+ frameworkConfig,
258
+ this.instanceMapping
259
+ );
260
+ validation.recommendations = recommendations.map(r => r.instanceType);
261
+ }
262
+
263
+ return validation;
264
+ }
265
+
266
+ /**
267
+ * Validate environment variables using validation engine
268
+ *
269
+ * @param {Object} envVars - Environment variables to validate
270
+ * @param {Object} frameworkConfig - Framework configuration
271
+ * @returns {Object} Validation result
272
+ * @returns {Array<Object>} result.errors - Validation errors
273
+ * @returns {Array<Object>} result.warnings - Validation warnings
274
+ * @returns {Array<string>} result.strategiesUsed - Validation strategies used
275
+ *
276
+ * Requirements: 13.1, 13.2, 13.3, 13.4, 13.5, 13.6, 13.7, 13.8
277
+ */
278
+ validateEnvironmentVariables(envVars, frameworkConfig) {
279
+ return this.validationEngine.validateEnvironmentVariables(
280
+ envVars,
281
+ frameworkConfig,
282
+ {
283
+ enabled: this.validateEnvVars,
284
+ useKnownFlags: true,
285
+ useCommunityReports: true,
286
+ useDockerIntrospection: false
287
+ }
288
+ );
289
+ }
290
+
291
+ /**
292
+ * Fetch model data from HuggingFace Hub API
293
+ * Gracefully handles failures by returning null
294
+ *
295
+ * @param {string} modelId - Model ID
296
+ * @returns {Promise<Object|null>} Model data or null
297
+ * @private
298
+ */
299
+ async _fetchHuggingFaceData(modelId) {
300
+ try {
301
+ // Fetch metadata
302
+ const metadata = await this.hfClient.fetchModelMetadata(modelId);
303
+
304
+ // Fetch tokenizer config for chat template
305
+ const tokenizerConfig = await this.hfClient.fetchTokenizerConfig(modelId);
306
+
307
+ // Fetch model config for architecture
308
+ const modelConfig = await this.hfClient.fetchModelConfig(modelId);
309
+
310
+ // Combine data
311
+ if (!metadata && !tokenizerConfig && !modelConfig) {
312
+ return null;
313
+ }
314
+
315
+ return {
316
+ metadata,
317
+ tokenizerConfig,
318
+ modelConfig,
319
+ chatTemplate: tokenizerConfig?.chat_template || null
320
+ };
321
+ } catch (error) {
322
+ // Graceful fallback
323
+ return null;
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Apply framework configuration to merged config
329
+ * @private
330
+ */
331
+ _applyFrameworkConfig(merged, frameworkConfig) {
332
+ if (frameworkConfig.baseImage) {
333
+ merged.baseImage = frameworkConfig.baseImage;
334
+ }
335
+ if (frameworkConfig.accelerator) {
336
+ merged.accelerator = { ...frameworkConfig.accelerator };
337
+ }
338
+ if (frameworkConfig.envVars) {
339
+ merged.envVars = { ...merged.envVars, ...frameworkConfig.envVars };
340
+ }
341
+ if (frameworkConfig.inferenceAmiVersion) {
342
+ merged.inferenceAmiVersion = frameworkConfig.inferenceAmiVersion;
343
+ }
344
+ if (frameworkConfig.recommendedInstanceTypes) {
345
+ merged.recommendedInstanceTypes = [...frameworkConfig.recommendedInstanceTypes];
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Apply profile configuration to merged config
351
+ * @private
352
+ */
353
+ _applyProfileConfig(merged, profileConfig) {
354
+ if (profileConfig.envVars) {
355
+ merged.envVars = { ...merged.envVars, ...profileConfig.envVars };
356
+ }
357
+ if (profileConfig.recommendedInstanceTypes) {
358
+ merged.recommendedInstanceTypes = [...profileConfig.recommendedInstanceTypes];
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Apply HuggingFace data to merged config
364
+ * @private
365
+ */
366
+ _applyHuggingFaceData(merged, hfData) {
367
+ if (hfData.chatTemplate) {
368
+ merged.chatTemplate = hfData.chatTemplate;
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Apply model configuration to merged config
374
+ * @private
375
+ */
376
+ _applyModelConfig(merged, modelConfig) {
377
+ if (modelConfig.chatTemplate) {
378
+ merged.chatTemplate = modelConfig.chatTemplate;
379
+ }
380
+ if (modelConfig.envVars) {
381
+ merged.envVars = { ...merged.envVars, ...modelConfig.envVars };
382
+ }
383
+ if (modelConfig.recommendedInstanceTypes) {
384
+ merged.recommendedInstanceTypes = [...modelConfig.recommendedInstanceTypes];
385
+ }
386
+ }
387
+
388
+ /**
389
+ * Export configuration for community contribution
390
+ * Creates a shareable configuration entry in the correct format for the appropriate registry
391
+ *
392
+ * @param {Object} config - Configuration profile to export
393
+ * @param {Object} options - Export options
394
+ * @param {string} [options.testingNotes] - User's testing notes
395
+ * @param {string} [options.instanceType] - Instance type used for testing
396
+ * @param {boolean} [options.deploymentSuccess] - Whether deployment was successful
397
+ * @param {boolean} [options.inferenceSuccess] - Whether inference worked correctly
398
+ * @param {string} [options.testerName] - Name/handle of tester (optional)
399
+ * @returns {Object} Export result with configuration entry and submission instructions
400
+ * @returns {string} result.registryType - "framework" or "model"
401
+ * @returns {Object} result.configEntry - Configuration entry in registry format
402
+ * @returns {string} result.submissionInstructions - Instructions for submitting
403
+ * @returns {Object} result.metadata - Export metadata
404
+ *
405
+ * Requirements: 7.1, 7.2, 7.3, 7.4, 7.5, 7.6
406
+ */
407
+ exportConfiguration(config, options = {}) {
408
+ const {
409
+ testingNotes = '',
410
+ instanceType = null,
411
+ deploymentSuccess = false,
412
+ inferenceSuccess = false,
413
+ testerName = 'Anonymous'
414
+ } = options;
415
+
416
+ // Determine registry type based on configuration
417
+ const registryType = this._determineRegistryType(config);
418
+
419
+ // Create configuration entry
420
+ const configEntry = this._createConfigEntry(config, {
421
+ testingNotes,
422
+ instanceType,
423
+ deploymentSuccess,
424
+ inferenceSuccess,
425
+ testerName
426
+ });
427
+
428
+ // Generate submission instructions
429
+ const submissionInstructions = this._generateSubmissionInstructions(
430
+ registryType,
431
+ config,
432
+ configEntry
433
+ );
434
+
435
+ // Create metadata
436
+ const metadata = {
437
+ generatorVersion: this._getGeneratorVersion(),
438
+ exportedAt: new Date().toISOString(),
439
+ configSource: config.configSources?.join(', ') || 'Unknown',
440
+ validationLevel: config.validationLevel || 'experimental',
441
+ testerName,
442
+ instanceType,
443
+ deploymentSuccess,
444
+ inferenceSuccess
445
+ };
446
+
447
+ return {
448
+ registryType,
449
+ configEntry,
450
+ submissionInstructions,
451
+ metadata
452
+ };
453
+ }
454
+
455
+ /**
456
+ * Determine which registry type this configuration should be exported to
457
+ * @private
458
+ */
459
+ _determineRegistryType(config) {
460
+ // If model ID is present and model-specific overrides exist, export to Model_Registry
461
+ if (config.modelId && (config.chatTemplate || config.configSources?.includes('Model_Registry'))) {
462
+ return 'model';
463
+ }
464
+
465
+ // Otherwise, export to Framework_Registry
466
+ return 'framework';
467
+ }
468
+
469
+ /**
470
+ * Create configuration entry in registry format
471
+ * @private
472
+ */
473
+ _createConfigEntry(config, testingInfo) {
474
+ if (this._determineRegistryType(config) === 'framework') {
475
+ return this._createFrameworkEntry(config, testingInfo);
476
+ } else {
477
+ return this._createModelEntry(config, testingInfo);
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Create Framework_Registry entry
483
+ * @private
484
+ */
485
+ _createFrameworkEntry(config, testingInfo) {
486
+ const entry = {
487
+ [config.version]: {
488
+ baseImage: config.baseImage || 'REPLACE_WITH_BASE_IMAGE',
489
+ accelerator: config.accelerator || {
490
+ type: 'cuda',
491
+ version: '12.1',
492
+ versionRange: {
493
+ min: '12.0',
494
+ max: '12.2'
495
+ }
496
+ },
497
+ envVars: config.envVars || {},
498
+ inferenceAmiVersion: config.inferenceAmiVersion || 'REPLACE_WITH_AMI_VERSION',
499
+ recommendedInstanceTypes: config.recommendedInstanceTypes?.length > 0
500
+ ? config.recommendedInstanceTypes
501
+ : (testingInfo.instanceType ? [testingInfo.instanceType] : ['ml.g5.xlarge']),
502
+ validationLevel: this._determineValidationLevel(testingInfo),
503
+ notes: this._createTestingNotes(testingInfo)
504
+ }
505
+ };
506
+
507
+ return entry;
508
+ }
509
+
510
+ /**
511
+ * Create Model_Registry entry
512
+ * @private
513
+ */
514
+ _createModelEntry(config, testingInfo) {
515
+ const entry = {
516
+ [config.modelId]: {
517
+ family: this._extractModelFamily(config.modelId),
518
+ chatTemplate: config.chatTemplate || null,
519
+ requiresTemplate: !!config.chatTemplate,
520
+ validationLevel: this._determineValidationLevel(testingInfo),
521
+ frameworkCompatibility: {
522
+ [config.framework]: `>=${config.version}`
523
+ },
524
+ notes: this._createTestingNotes(testingInfo)
525
+ }
526
+ };
527
+
528
+ // Add environment variables if present
529
+ if (config.envVars && Object.keys(config.envVars).length > 0) {
530
+ entry[config.modelId].envVars = config.envVars;
531
+ }
532
+
533
+ // Add recommended instance types if present
534
+ if (config.recommendedInstanceTypes?.length > 0) {
535
+ entry[config.modelId].recommendedInstanceTypes = config.recommendedInstanceTypes;
536
+ } else if (testingInfo.instanceType) {
537
+ entry[config.modelId].recommendedInstanceTypes = [testingInfo.instanceType];
538
+ }
539
+
540
+ return entry;
541
+ }
542
+
543
+ /**
544
+ * Determine validation level based on testing results
545
+ * @private
546
+ */
547
+ _determineValidationLevel(testingInfo) {
548
+ const { deploymentSuccess, inferenceSuccess } = testingInfo;
549
+
550
+ if (deploymentSuccess && inferenceSuccess) {
551
+ return 'community-validated';
552
+ } else if (deploymentSuccess) {
553
+ return 'experimental';
554
+ } else {
555
+ return 'unknown';
556
+ }
557
+ }
558
+
559
+ /**
560
+ * Create testing notes from testing info
561
+ * @private
562
+ */
563
+ _createTestingNotes(testingInfo) {
564
+ const { testingNotes, instanceType, deploymentSuccess, inferenceSuccess, testerName } = testingInfo;
565
+
566
+ const notes = [];
567
+
568
+ if (testingNotes) {
569
+ notes.push(testingNotes);
570
+ }
571
+
572
+ if (instanceType) {
573
+ notes.push(`Tested on ${instanceType}`);
574
+ }
575
+
576
+ if (deploymentSuccess && inferenceSuccess) {
577
+ notes.push('✓ Deployment and inference successful');
578
+ } else if (deploymentSuccess) {
579
+ notes.push('✓ Deployment successful, inference not tested');
580
+ }
581
+
582
+ if (testerName && testerName !== 'Anonymous') {
583
+ notes.push(`Tested by: ${testerName}`);
584
+ }
585
+
586
+ notes.push(`Exported: ${new Date().toISOString().split('T')[0]}`);
587
+
588
+ return notes.join('. ');
589
+ }
590
+
591
+ /**
592
+ * Extract model family from model ID
593
+ * @private
594
+ */
595
+ _extractModelFamily(modelId) {
596
+ if (!modelId) return 'unknown';
597
+
598
+ // Extract family from model ID (e.g., "meta-llama/Llama-2-7b-chat-hf" -> "llama-2")
599
+ const parts = modelId.toLowerCase().split('/');
600
+ const modelName = parts[parts.length - 1];
601
+
602
+ // Common patterns
603
+ if (modelName.includes('llama-2')) return 'llama-2';
604
+ if (modelName.includes('llama-3')) return 'llama-3';
605
+ if (modelName.includes('llama')) return 'llama';
606
+ if (modelName.includes('mistral')) return 'mistral';
607
+ if (modelName.includes('mixtral')) return 'mixtral';
608
+ if (modelName.includes('gemma')) return 'gemma';
609
+ if (modelName.includes('phi')) return 'phi';
610
+ if (modelName.includes('qwen')) return 'qwen';
611
+
612
+ // Default: use first part of model name
613
+ return modelName.split('-')[0];
614
+ }
615
+
616
+ /**
617
+ * Generate submission instructions
618
+ * @private
619
+ */
620
+ _generateSubmissionInstructions(registryType, config, configEntry) {
621
+ const registryFile = registryType === 'framework'
622
+ ? 'servers/base-image-picker/catalogs/model-servers.json'
623
+ : 'servers/model-picker/catalogs/popular-transformers.json';
624
+
625
+ const registryName = registryType === 'framework'
626
+ ? 'Framework_Catalog'
627
+ : 'Model_Catalog';
628
+
629
+ const key = registryType === 'framework'
630
+ ? config.framework
631
+ : config.modelId;
632
+
633
+ return `
634
+ # Configuration Export for Community Contribution
635
+
636
+ Thank you for testing this configuration! Your contribution helps the community.
637
+
638
+ ## Configuration Details
639
+
640
+ - **Registry Type**: ${registryName}
641
+ - **${registryType === 'framework' ? 'Framework' : 'Model'}**: ${key}
642
+ - **Validation Level**: ${config.validationLevel || 'experimental'}
643
+ - **Tested On**: ${new Date().toISOString().split('T')[0]}
644
+
645
+ ## Submission Instructions
646
+
647
+ ### Option 1: GitHub Issue (Recommended)
648
+
649
+ 1. Go to: https://github.com/YOUR_REPO/issues/new
650
+ 2. Title: "[Config] Add ${registryType === 'framework' ? `${config.framework } ${ config.version}` : config.modelId}"
651
+ 3. Paste the configuration entry below
652
+ 4. Add any additional context about your testing
653
+
654
+ ### Option 2: Pull Request
655
+
656
+ 1. Fork the repository
657
+ 2. Edit: \`${registryFile}\`
658
+ 3. Add the configuration entry to the appropriate section
659
+ 4. Submit a pull request with title: "Add ${registryType === 'framework' ? `${config.framework } ${ config.version}` : config.modelId} configuration"
660
+
661
+ ## Configuration Entry
662
+
663
+ Add this to \`${registryFile}\`:
664
+
665
+ \`\`\`javascript
666
+ ${JSON.stringify(configEntry, null, 2)}
667
+ \`\`\`
668
+
669
+ ## Testing Information
670
+
671
+ - **Instance Type**: ${config.recommendedInstanceTypes?.[0] || 'Not specified'}
672
+ - **Accelerator**: ${config.accelerator?.type || 'Not specified'} ${config.accelerator?.version || ''}
673
+ - **Base Image**: ${config.baseImage || 'Not specified'}
674
+ - **AMI Version**: ${config.inferenceAmiVersion || 'Not specified'}
675
+
676
+ ## Notes
677
+
678
+ ${config.notes || 'No additional notes'}
679
+
680
+ ---
681
+
682
+ Generated by ML Container Creator v${this._getGeneratorVersion()}
683
+ `.trim();
684
+ }
685
+
686
+ /**
687
+ * Get generator version
688
+ * @private
689
+ */
690
+ _getGeneratorVersion() {
691
+ // In a real implementation, this would read from package.json
692
+ // For now, return a placeholder
693
+ return '1.0.0';
694
+ }
695
+ }