@aws/ml-container-creator 0.2.1 → 0.2.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.
Files changed (36) hide show
  1. package/bin/cli.js +88 -86
  2. package/config/bootstrap-stack.json +211 -0
  3. package/config/parameter-schema.json +88 -0
  4. package/infra/ci-harness/bin/ci-harness.ts +26 -0
  5. package/infra/ci-harness/buildspec.yml +352 -0
  6. package/infra/ci-harness/cdk.json +27 -0
  7. package/infra/ci-harness/lambda/scanner/index.ts +199 -0
  8. package/infra/ci-harness/lib/ci-harness-stack.ts +609 -0
  9. package/infra/ci-harness/package-lock.json +3979 -0
  10. package/infra/ci-harness/package.json +32 -0
  11. package/infra/ci-harness/tsconfig.json +38 -0
  12. package/package.json +13 -3
  13. package/src/app.js +318 -318
  14. package/src/copy-tpl.js +19 -19
  15. package/src/lib/asset-manager.js +74 -74
  16. package/src/lib/aws-profile-parser.js +45 -45
  17. package/src/lib/bootstrap-command-handler.js +560 -547
  18. package/src/lib/bootstrap-config.js +45 -45
  19. package/src/lib/ci-register-helpers.js +19 -19
  20. package/src/lib/ci-report-helpers.js +37 -37
  21. package/src/lib/ci-stage-helpers.js +49 -49
  22. package/src/lib/comment-generator.js +4 -4
  23. package/src/lib/config-manager.js +105 -105
  24. package/src/lib/deployment-config-resolver.js +10 -10
  25. package/src/lib/deployment-registry.js +153 -153
  26. package/src/lib/engine-prefix-resolver.js +8 -8
  27. package/src/lib/key-value-parser.js +6 -6
  28. package/src/lib/manifest-cli.js +108 -108
  29. package/src/lib/prompt-runner.js +224 -224
  30. package/src/lib/prompts.js +121 -121
  31. package/src/lib/registry-command-handler.js +174 -174
  32. package/src/lib/registry-loader.js +52 -52
  33. package/src/lib/sensitive-redactor.js +9 -9
  34. package/src/lib/template-engine.js +1 -1
  35. package/src/lib/template-manager.js +62 -62
  36. package/src/prompt-adapter.js +18 -18
@@ -60,9 +60,9 @@ export default class PromptRunner {
60
60
  const buildTimestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
61
61
 
62
62
  // Load catalog data via Registry_Loader
63
- const registryLoader = new RegistryLoader()
64
- this._tritonBackends = await registryLoader.loadTritonBackends()
65
- this._instanceAcceleratorMapping = await registryLoader.loadInstanceAcceleratorMapping()
63
+ const registryLoader = new RegistryLoader();
64
+ this._tritonBackends = await registryLoader.loadTritonBackends();
65
+ this._instanceAcceleratorMapping = await registryLoader.loadInstanceAcceleratorMapping();
66
66
 
67
67
  // Get existing configuration to use as defaults
68
68
  const existingConfig = this.baseConfig || {};
@@ -77,8 +77,8 @@ export default class PromptRunner {
77
77
 
78
78
  // 1a. Query region MCP, then prompt for region + deployment target
79
79
  await this._queryMcpForRegion({}, explicitConfig);
80
- const bootstrapRegion = existingConfig.awsRegion || explicitConfig.awsRegion
81
- const regionPreviousAnswers = bootstrapRegion ? { _bootstrapRegion: bootstrapRegion } : {}
80
+ const bootstrapRegion = existingConfig.awsRegion || explicitConfig.awsRegion;
81
+ const regionPreviousAnswers = bootstrapRegion ? { _bootstrapRegion: bootstrapRegion } : {};
82
82
  const regionAndTargetAnswers = await this._runPhase(infraRegionAndTargetPrompts, regionPreviousAnswers, explicitConfig, existingConfig);
83
83
 
84
84
  // 1b. Instance type — query MCP and prompt for managed-inference, async-inference, batch-transform, and hyperpod-eks
@@ -171,18 +171,18 @@ export default class PromptRunner {
171
171
 
172
172
  // Query base-image-picker MCP server for base image choices
173
173
  // Requirements: 5.1, 5.2, 5.3
174
- await this._queryMcpForBaseImage(frameworkAnswers, explicitConfig)
174
+ await this._queryMcpForBaseImage(frameworkAnswers, explicitConfig);
175
175
  const baseImagePreviousAnswers = {
176
176
  ...frameworkAnswers,
177
177
  ...engineAnswers,
178
178
  ...(this._mcpBaseImageChoices ? { _mcpBaseImageChoices: this._mcpBaseImageChoices } : {})
179
- }
179
+ };
180
180
  const baseImageAnswers = await this._runPhase(
181
181
  baseImagePrompts,
182
182
  baseImagePreviousAnswers,
183
183
  explicitConfig,
184
184
  existingConfig
185
- )
185
+ );
186
186
 
187
187
  // Populate framework version choices from registry
188
188
  const frameworkVersionChoices = this._getFrameworkVersionChoices(frameworkAnswers.framework);
@@ -211,10 +211,10 @@ export default class PromptRunner {
211
211
  );
212
212
 
213
213
  // Query model-picker MCP server for model choices
214
- this._queryMcpForModels(frameworkAnswers.architecture)
214
+ this._queryMcpForModels(frameworkAnswers.architecture);
215
215
  if (this._mcpModelChoices) {
216
- console.log(` šŸ” Querying model-picker...`)
217
- console.log(` āœ“ ${this._mcpModelChoices.length} model(s) available from catalog`)
216
+ console.log(' šŸ” Querying model-picker...');
217
+ console.log(` āœ“ ${this._mcpModelChoices.length} model(s) available from catalog`);
218
218
  }
219
219
  const modelFormatPreviousAnswers = {
220
220
  ...frameworkAnswers,
@@ -222,7 +222,7 @@ export default class PromptRunner {
222
222
  ...frameworkVersionAnswers,
223
223
  ...frameworkProfileAnswers,
224
224
  ...(this._mcpModelChoices ? { _mcpModelChoices: this._mcpModelChoices } : {})
225
- }
225
+ };
226
226
  const modelFormatAnswers = await this._runPhase(
227
227
  modelFormatPrompts,
228
228
  modelFormatPreviousAnswers,
@@ -392,7 +392,7 @@ export default class PromptRunner {
392
392
  // Registry models — note about InferenceSpecification requirement
393
393
  if (combinedAnswers.modelSource === 'registry') {
394
394
  if (!combinedAnswers.artifactUri) {
395
- console.log(`\n āš ļø Model source is 'registry' but no artifact URI was resolved.`);
395
+ console.log('\n āš ļø Model source is \'registry\' but no artifact URI was resolved.');
396
396
  console.log(' The model package must have an InferenceSpecification with a valid');
397
397
  console.log(' ModelDataUrl or S3DataSource for the runtime resolver to work.');
398
398
  console.log(' If your model package was registered without an InferenceSpecification,');
@@ -421,7 +421,7 @@ export default class PromptRunner {
421
421
  // Apply auto-set model format for Triton backends with single format
422
422
  // Requirements: 3.3, 3.4, 3.5
423
423
  if (tritonAutoFormat) {
424
- combinedAnswers.modelFormat = tritonAutoFormat
424
+ combinedAnswers.modelFormat = tritonAutoFormat;
425
425
  }
426
426
 
427
427
  // Handle custom model name for transformers, diffusors, and Triton LLM backends
@@ -464,14 +464,14 @@ export default class PromptRunner {
464
464
 
465
465
  // Handle custom base image
466
466
  if (combinedAnswers.customBaseImage) {
467
- combinedAnswers.baseImage = combinedAnswers.customBaseImage
468
- combinedAnswers._baseImageSource = 'custom'
469
- delete combinedAnswers.customBaseImage
467
+ combinedAnswers.baseImage = combinedAnswers.customBaseImage;
468
+ combinedAnswers._baseImageSource = 'custom';
469
+ delete combinedAnswers.customBaseImage;
470
470
  }
471
471
 
472
472
  // Handle --base-image CLI override
473
473
  if (this.options['base-image']) {
474
- combinedAnswers.baseImage = this.options['base-image']
474
+ combinedAnswers.baseImage = this.options['base-image'];
475
475
  }
476
476
 
477
477
  // Map awsRoleArn to roleArn for templates
@@ -576,17 +576,17 @@ export default class PromptRunner {
576
576
  * @private
577
577
  */
578
578
  _getTritonAutoModelFormat(architecture, backend) {
579
- if (architecture !== 'triton') return null
579
+ if (architecture !== 'triton') return null;
580
580
 
581
- const meta = this._tritonBackends[backend]
582
- if (!meta || !meta.modelFormats) return null
581
+ const meta = this._tritonBackends[backend];
582
+ if (!meta || !meta.modelFormats) return null;
583
583
 
584
584
  // Only auto-set if there's exactly one format
585
585
  if (meta.modelFormats.length === 1) {
586
- return meta.modelFormats[0]
586
+ return meta.modelFormats[0];
587
587
  }
588
588
 
589
- return null
589
+ return null;
590
590
  }
591
591
 
592
592
  /**
@@ -725,52 +725,52 @@ export default class PromptRunner {
725
725
  * Requirements: 5.1, 5.2, 5.3, 5.4, 9.1, 9.2, 9.3
726
726
  * @private
727
727
  */
728
- async _queryMcpForBaseImage(frameworkAnswers, explicitConfig) {
728
+ async _queryMcpForBaseImage(frameworkAnswers, _explicitConfig) {
729
729
  // Skip if base image provided via CLI --base-image flag
730
- if (this.options['base-image']) return
730
+ if (this.options['base-image']) return;
731
731
 
732
- const cm = this.configManager
733
- if (!cm) return
732
+ const cm = this.configManager;
733
+ if (!cm) return;
734
734
 
735
- const mcpServers = cm.getMcpServerNames()
736
- if (!mcpServers.includes('base-image-picker')) return
735
+ const mcpServers = cm.getMcpServerNames();
736
+ if (!mcpServers.includes('base-image-picker')) return;
737
737
 
738
- const smart = this.options.smart === true
739
- const discover = this.options.discover === true
740
- const framework = frameworkAnswers.framework
741
- const modelServer = frameworkAnswers.modelServer
742
- const architecture = frameworkAnswers.architecture || frameworkAnswers.deploymentConfig?.split('-')[0]
743
- const isTransformer = framework === 'transformers'
744
- const isTriton = architecture === 'triton'
745
- const isDiffusors = architecture === 'diffusors'
738
+ const smart = this.options.smart === true;
739
+ const discover = this.options.discover === true;
740
+ const framework = frameworkAnswers.framework;
741
+ const modelServer = frameworkAnswers.modelServer;
742
+ const architecture = frameworkAnswers.architecture || frameworkAnswers.deploymentConfig?.split('-')[0];
743
+ const isTransformer = framework === 'transformers';
744
+ const isTriton = architecture === 'triton';
745
+ const isDiffusors = architecture === 'diffusors';
746
746
 
747
747
  // For non-transformer, non-triton, non-diffusors frameworks, prompt for optional search criteria
748
- let searchCriteria
748
+ let searchCriteria;
749
749
  if (!isTransformer && !isTriton && !isDiffusors) {
750
750
  const searchAnswer = await this._runPrompts(baseImageSearchPrompts.map(p => ({
751
751
  ...p,
752
752
  when: () => true // Always show for non-transformer since we already checked
753
- })))
754
- searchCriteria = searchAnswer.baseImageSearch
753
+ })));
754
+ searchCriteria = searchAnswer.baseImageSearch;
755
755
  }
756
756
 
757
- const modeLabel = [smart && '[smart]', discover && '[discover]'].filter(Boolean).join(' ')
758
- console.log(` šŸ” Querying base-image-picker${modeLabel ? ` ${modeLabel}` : ''}...`)
757
+ const modeLabel = [smart && '[smart]', discover && '[discover]'].filter(Boolean).join(' ');
758
+ console.log(` šŸ” Querying base-image-picker${modeLabel ? ` ${modeLabel}` : ''}...`);
759
759
 
760
- const context = { framework, modelServer, architecture }
760
+ const context = { framework, modelServer, architecture };
761
761
  if (searchCriteria && searchCriteria.trim()) {
762
- context.searchCriteria = searchCriteria.trim()
762
+ context.searchCriteria = searchCriteria.trim();
763
763
  }
764
764
 
765
- const result = await cm.queryMcpServer('base-image-picker', context)
765
+ const result = await cm.queryMcpServer('base-image-picker', context);
766
766
 
767
767
  if (result && result.metadata?.baseImage?.length > 0) {
768
- const entries = result.metadata.baseImage
769
- this._mcpBaseImageChoices = formatImageChoices(entries, isTransformer || isTriton || isDiffusors)
770
- const count = entries.length
771
- console.log(` āœ“ ${count} base image(s) available`)
768
+ const entries = result.metadata.baseImage;
769
+ this._mcpBaseImageChoices = formatImageChoices(entries, isTransformer || isTriton || isDiffusors);
770
+ const count = entries.length;
771
+ console.log(` āœ“ ${count} base image(s) available`);
772
772
  } else {
773
- console.log(' ↳ No MCP results, using default image')
773
+ console.log(' ↳ No MCP results, using default image');
774
774
  }
775
775
  }
776
776
 
@@ -782,47 +782,47 @@ export default class PromptRunner {
782
782
  * @private
783
783
  */
784
784
  _queryMcpForModels(architecture) {
785
- const cm = this.configManager
786
- if (!cm) return
785
+ const cm = this.configManager;
786
+ if (!cm) return;
787
787
 
788
- const mcpServers = cm.getMcpServerNames()
789
- if (!mcpServers.includes('model-picker')) return
788
+ const mcpServers = cm.getMcpServerNames();
789
+ if (!mcpServers.includes('model-picker')) return;
790
790
 
791
791
  try {
792
- const mcpConfigPath = path.join(GENERATOR_ROOT, 'config', 'mcp.json')
793
- if (!fs.existsSync(mcpConfigPath)) return
792
+ const mcpConfigPath = path.join(GENERATOR_ROOT, 'config', 'mcp.json');
793
+ if (!fs.existsSync(mcpConfigPath)) return;
794
794
 
795
- const mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'))
796
- const serverConfig = mcpConfig.mcpServers?.['model-picker']
797
- if (!serverConfig?.args?.length) return
795
+ const mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
796
+ const serverConfig = mcpConfig.mcpServers?.['model-picker'];
797
+ if (!serverConfig?.args?.length) return;
798
798
 
799
799
  // Resolve the server entry point directory from the args
800
- const serverEntryPoint = serverConfig.args[serverConfig.args.length - 1]
801
- const serverDir = path.dirname(serverEntryPoint)
800
+ const serverEntryPoint = serverConfig.args[serverConfig.args.length - 1];
801
+ const serverDir = path.dirname(serverEntryPoint);
802
802
 
803
803
  // Read manifest to find catalog path
804
- const manifestPath = path.join(serverDir, 'manifest.json')
805
- if (!fs.existsSync(manifestPath)) return
804
+ const manifestPath = path.join(serverDir, 'manifest.json');
805
+ if (!fs.existsSync(manifestPath)) return;
806
806
 
807
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
807
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
808
808
 
809
809
  // Select catalog based on architecture
810
810
  const catalogKey = architecture === 'diffusors'
811
811
  ? 'popular-diffusors'
812
- : 'popular-transformers'
813
- const catalogRelPath = manifest.catalogs?.[catalogKey]
814
- if (!catalogRelPath) return
812
+ : 'popular-transformers';
813
+ const catalogRelPath = manifest.catalogs?.[catalogKey];
814
+ if (!catalogRelPath) return;
815
815
 
816
- const catalogPath = path.resolve(serverDir, catalogRelPath)
817
- if (!fs.existsSync(catalogPath)) return
816
+ const catalogPath = path.resolve(serverDir, catalogRelPath);
817
+ if (!fs.existsSync(catalogPath)) return;
818
818
 
819
- const catalog = JSON.parse(fs.readFileSync(catalogPath, 'utf8'))
819
+ const catalog = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
820
820
 
821
821
  // Extract model IDs, filtering out glob patterns (entries with *)
822
- const modelIds = Object.keys(catalog).filter(id => !id.includes('*'))
822
+ const modelIds = Object.keys(catalog).filter(id => !id.includes('*'));
823
823
 
824
824
  if (modelIds.length > 0) {
825
- this._mcpModelChoices = modelIds
825
+ this._mcpModelChoices = modelIds;
826
826
  }
827
827
  } catch {
828
828
  // Silently fall back to hardcoded defaults
@@ -999,186 +999,186 @@ export default class PromptRunner {
999
999
  * @private
1000
1000
  */
1001
1001
  async _fetchAndDisplayModelInfo(modelId) {
1002
- console.log(`\n šŸ” Querying model-picker [discover]...`);
1003
-
1004
- const sources = [];
1005
- let chatTemplate = null;
1006
- let modelFamily = null;
1007
- let mcpUsed = false;
1008
-
1009
- // Try model-picker MCP server in discover mode (queries HuggingFace + merges with catalog)
1010
- const cm = this.configManager;
1011
- if (cm) {
1012
- const mcpServers = cm.getMcpServerNames();
1013
- if (mcpServers.includes('model-picker')) {
1014
- try {
1015
- const mcpConfigPath = path.join(GENERATOR_ROOT, 'config', 'mcp.json');
1016
- if (fs.existsSync(mcpConfigPath)) {
1017
- const mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
1018
- const serverConfig = mcpConfig.mcpServers?.['model-picker'];
1019
- if (serverConfig) {
1020
- const { McpClient } = await import('./mcp-client.js');
1021
- const client = new McpClient(serverConfig, { timeout: 15000 });
1022
-
1023
- // Override _buildContext to pass model_id and mode directly
1024
- client._getUnboundedParameterNames = () => [];
1025
- client._buildContext = () => ({});
1026
-
1027
- // Connect and call get_models directly
1028
- const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
1029
- const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js');
1030
-
1031
- const transport = new StdioClientTransport({
1032
- command: serverConfig.command,
1033
- args: serverConfig.args || [],
1034
- env: { ...process.env, ...(serverConfig.env || {}) },
1035
- stderr: 'pipe'
1036
- });
1037
-
1038
- const mcpClient = new Client(
1039
- { name: 'ml-container-creator', version: '1.0.0' },
1040
- { capabilities: {} }
1041
- );
1042
-
1043
- await mcpClient.connect(transport);
1044
-
1045
- const result = await mcpClient.callTool({
1046
- name: 'get_models',
1047
- arguments: { model_id: modelId, mode: 'discover' }
1048
- });
1049
-
1050
- await mcpClient.close();
1051
-
1052
- // Parse the response
1053
- const textBlock = result?.content?.find(b => b.type === 'text');
1054
- if (textBlock) {
1055
- const parsed = JSON.parse(textBlock.text);
1056
- if (parsed.values && Object.keys(parsed.values).length > 0) {
1057
- mcpUsed = true;
1058
- const vals = parsed.values;
1059
-
1060
- if (vals.chat_template) {
1061
- chatTemplate = vals.chat_template;
1062
- }
1063
- if (vals.family) {
1064
- modelFamily = vals.family;
1065
- }
1066
-
1067
- // Extract model source metadata for loading adapter
1068
- // Requirements: 2.1, 2.2, 2.3, 2.4
1069
- if (vals.provider) {
1070
- this._mcpModelSource = vals.provider;
1071
- }
1072
- if (vals.artifactUri) {
1073
- this._mcpArtifactUri = vals.artifactUri;
1074
- }
1075
-
1076
- // Determine sources based on what was returned
1077
- if (vals.tags || vals.pipeline_tag) {
1078
- sources.push('HuggingFace_Hub_API');
1079
- }
1080
- if (vals.validation_level || vals.framework_compatibility) {
1081
- sources.push('Model_Picker_Catalog');
1082
- }
1083
- if (sources.length === 0) {
1084
- sources.push('model-picker');
1085
- }
1086
- console.log(` āœ“ Resolved: ${modelId}`);
1087
- } else if (parsed.message) {
1088
- console.log(` ↳ ${parsed.message}`);
1002
+ console.log('\n šŸ” Querying model-picker [discover]...');
1003
+
1004
+ const sources = [];
1005
+ let chatTemplate = null;
1006
+ let modelFamily = null;
1007
+ let mcpUsed = false;
1008
+
1009
+ // Try model-picker MCP server in discover mode (queries HuggingFace + merges with catalog)
1010
+ const cm = this.configManager;
1011
+ if (cm) {
1012
+ const mcpServers = cm.getMcpServerNames();
1013
+ if (mcpServers.includes('model-picker')) {
1014
+ try {
1015
+ const mcpConfigPath = path.join(GENERATOR_ROOT, 'config', 'mcp.json');
1016
+ if (fs.existsSync(mcpConfigPath)) {
1017
+ const mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
1018
+ const serverConfig = mcpConfig.mcpServers?.['model-picker'];
1019
+ if (serverConfig) {
1020
+ const { McpClient } = await import('./mcp-client.js');
1021
+ const client = new McpClient(serverConfig, { timeout: 15000 });
1022
+
1023
+ // Override _buildContext to pass model_id and mode directly
1024
+ client._getUnboundedParameterNames = () => [];
1025
+ client._buildContext = () => ({});
1026
+
1027
+ // Connect and call get_models directly
1028
+ const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
1029
+ const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js');
1030
+
1031
+ const transport = new StdioClientTransport({
1032
+ command: serverConfig.command,
1033
+ args: serverConfig.args || [],
1034
+ env: { ...process.env, ...(serverConfig.env || {}) },
1035
+ stderr: 'pipe'
1036
+ });
1037
+
1038
+ const mcpClient = new Client(
1039
+ { name: 'ml-container-creator', version: '1.0.0' },
1040
+ { capabilities: {} }
1041
+ );
1042
+
1043
+ await mcpClient.connect(transport);
1044
+
1045
+ const result = await mcpClient.callTool({
1046
+ name: 'get_models',
1047
+ arguments: { model_id: modelId, mode: 'discover' }
1048
+ });
1049
+
1050
+ await mcpClient.close();
1051
+
1052
+ // Parse the response
1053
+ const textBlock = result?.content?.find(b => b.type === 'text');
1054
+ if (textBlock) {
1055
+ const parsed = JSON.parse(textBlock.text);
1056
+ if (parsed.values && Object.keys(parsed.values).length > 0) {
1057
+ mcpUsed = true;
1058
+ const vals = parsed.values;
1059
+
1060
+ if (vals.chat_template) {
1061
+ chatTemplate = vals.chat_template;
1062
+ }
1063
+ if (vals.family) {
1064
+ modelFamily = vals.family;
1065
+ }
1066
+
1067
+ // Extract model source metadata for loading adapter
1068
+ // Requirements: 2.1, 2.2, 2.3, 2.4
1069
+ if (vals.provider) {
1070
+ this._mcpModelSource = vals.provider;
1071
+ }
1072
+ if (vals.artifactUri) {
1073
+ this._mcpArtifactUri = vals.artifactUri;
1074
+ }
1075
+
1076
+ // Determine sources based on what was returned
1077
+ if (vals.tags || vals.pipeline_tag) {
1078
+ sources.push('HuggingFace_Hub_API');
1079
+ }
1080
+ if (vals.validation_level || vals.framework_compatibility) {
1081
+ sources.push('Model_Picker_Catalog');
1089
1082
  }
1083
+ if (sources.length === 0) {
1084
+ sources.push('model-picker');
1085
+ }
1086
+ console.log(` āœ“ Resolved: ${modelId}`);
1087
+ } else if (parsed.message) {
1088
+ console.log(` ↳ ${parsed.message}`);
1090
1089
  }
1091
1090
  }
1092
1091
  }
1093
- } catch (err) {
1094
- console.log(' ↳ model-picker unavailable, using fallback');
1095
1092
  }
1093
+ } catch (err) {
1094
+ console.log(' ↳ model-picker unavailable, using fallback');
1096
1095
  }
1097
1096
  }
1097
+ }
1098
1098
 
1099
- // Fallback to legacy path if MCP didn't resolve
1100
- if (!mcpUsed) {
1101
- const registryConfigManager = this.registryConfigManager;
1102
- if (registryConfigManager) {
1103
- // Only try HuggingFace API for bare model IDs (not prefixed URIs)
1104
- const isNonHfUri = modelId.startsWith('jumpstart://') ||
1099
+ // Fallback to legacy path if MCP didn't resolve
1100
+ if (!mcpUsed) {
1101
+ const registryConfigManager = this.registryConfigManager;
1102
+ if (registryConfigManager) {
1103
+ // Only try HuggingFace API for bare model IDs (not prefixed URIs)
1104
+ const isNonHfUri = modelId.startsWith('jumpstart://') ||
1105
1105
  modelId.startsWith('jumpstart-hub://') ||
1106
1106
  modelId.startsWith('s3://') ||
1107
1107
  modelId.startsWith('registry://');
1108
1108
 
1109
- if (!isNonHfUri) {
1110
- // Try HuggingFace API directly
1111
- try {
1112
- const hfData = await registryConfigManager._fetchHuggingFaceData(modelId);
1113
- if (hfData) {
1114
- sources.push('HuggingFace_Hub_API');
1115
- if (hfData.chatTemplate) {
1116
- chatTemplate = hfData.chatTemplate;
1117
- }
1118
- console.log(' āœ… Found on HuggingFace Hub');
1119
- } else {
1120
- console.log(' ā„¹ļø Not found on HuggingFace Hub (may be private or offline)');
1109
+ if (!isNonHfUri) {
1110
+ // Try HuggingFace API directly
1111
+ try {
1112
+ const hfData = await registryConfigManager._fetchHuggingFaceData(modelId);
1113
+ if (hfData) {
1114
+ sources.push('HuggingFace_Hub_API');
1115
+ if (hfData.chatTemplate) {
1116
+ chatTemplate = hfData.chatTemplate;
1121
1117
  }
1122
- } catch (error) {
1123
- console.log(' āš ļø HuggingFace API unavailable');
1118
+ console.log(' āœ… Found on HuggingFace Hub');
1119
+ } else {
1120
+ console.log(' ā„¹ļø Not found on HuggingFace Hub (may be private or offline)');
1124
1121
  }
1125
- } else {
1126
- // Non-HF URI (jumpstart://, s3://, etc.) — skip HF lookup silently
1127
- // The summary at the end of this function will report "No additional model information"
1122
+ } catch (error) {
1123
+ console.log(' āš ļø HuggingFace API unavailable');
1128
1124
  }
1125
+ } else {
1126
+ // Non-HF URI (jumpstart://, s3://, etc.) — skip HF lookup silently
1127
+ // The summary at the end of this function will report "No additional model information"
1128
+ }
1129
1129
 
1130
- // Check Model Registry for overrides
1131
- if (registryConfigManager.modelRegistry) {
1132
- let modelConfig = registryConfigManager.modelRegistry[modelId];
1133
-
1134
- if (!modelConfig) {
1135
- for (const [pattern, config] of Object.entries(registryConfigManager.modelRegistry)) {
1136
- if (pattern.includes('*')) {
1137
- const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
1138
- if (regex.test(modelId)) {
1139
- modelConfig = config;
1140
- console.log(` āœ… Matched pattern in Model_Registry: ${pattern}`);
1141
- break;
1142
- }
1130
+ // Check Model Registry for overrides
1131
+ if (registryConfigManager.modelRegistry) {
1132
+ let modelConfig = registryConfigManager.modelRegistry[modelId];
1133
+
1134
+ if (!modelConfig) {
1135
+ for (const [pattern, config] of Object.entries(registryConfigManager.modelRegistry)) {
1136
+ if (pattern.includes('*')) {
1137
+ const regex = new RegExp(`^${ pattern.replace(/\*/g, '.*') }$`);
1138
+ if (regex.test(modelId)) {
1139
+ modelConfig = config;
1140
+ console.log(` āœ… Matched pattern in Model_Registry: ${pattern}`);
1141
+ break;
1143
1142
  }
1144
1143
  }
1145
- } else {
1146
- console.log(' āœ… Found in Model_Registry');
1147
1144
  }
1145
+ } else {
1146
+ console.log(' āœ… Found in Model_Registry');
1147
+ }
1148
1148
 
1149
- if (modelConfig) {
1150
- sources.push('Model_Registry');
1151
- if (modelConfig.chatTemplate) {
1152
- chatTemplate = modelConfig.chatTemplate;
1153
- }
1154
- if (modelConfig.family) {
1155
- modelFamily = modelConfig.family;
1156
- }
1149
+ if (modelConfig) {
1150
+ sources.push('Model_Registry');
1151
+ if (modelConfig.chatTemplate) {
1152
+ chatTemplate = modelConfig.chatTemplate;
1153
+ }
1154
+ if (modelConfig.family) {
1155
+ modelFamily = modelConfig.family;
1157
1156
  }
1158
1157
  }
1159
1158
  }
1160
1159
  }
1160
+ }
1161
1161
 
1162
- // Display information
1163
- if (sources.length > 0) {
1164
- console.log('\nšŸ“‹ Model Information:');
1165
- console.log(` • Model ID: ${modelId}`);
1166
- if (modelFamily) {
1167
- console.log(` • Family: ${modelFamily}`);
1168
- }
1169
- if (chatTemplate) {
1170
- console.log(' • Chat Template: āœ… Available');
1171
- console.log(' (Will be injected into generated files)');
1172
- } else {
1173
- console.log(' • Chat Template: āŒ Not available');
1174
- console.log(' (Chat endpoints may require manual configuration)');
1175
- }
1176
- console.log(` • Sources: ${sources.join(', ')}`);
1162
+ // Display information
1163
+ if (sources.length > 0) {
1164
+ console.log('\nšŸ“‹ Model Information:');
1165
+ console.log(` • Model ID: ${modelId}`);
1166
+ if (modelFamily) {
1167
+ console.log(` • Family: ${modelFamily}`);
1168
+ }
1169
+ if (chatTemplate) {
1170
+ console.log(' • Chat Template: āœ… Available');
1171
+ console.log(' (Will be injected into generated files)');
1177
1172
  } else {
1178
- console.log(' ā„¹ļø No additional model information available');
1179
- console.log(' Proceeding with default configuration');
1173
+ console.log(' • Chat Template: āŒ Not available');
1174
+ console.log(' (Chat endpoints may require manual configuration)');
1180
1175
  }
1176
+ console.log(` • Sources: ${sources.join(', ')}`);
1177
+ } else {
1178
+ console.log(' ā„¹ļø No additional model information available');
1179
+ console.log(' Proceeding with default configuration');
1181
1180
  }
1181
+ }
1182
1182
 
1183
1183
 
1184
1184