@aws/ml-container-creator 0.9.1 → 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 +2049 -0
- 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 +53 -68
- 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/jumpstart-public.json +101 -16
- package/servers/lib/catalogs/model-servers.json +201 -3
- package/servers/lib/catalogs/models.json +182 -26
- 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 -390
- package/src/lib/bootstrap-command-handler.js +710 -1148
- package/src/lib/bootstrap-config.js +36 -0
- package/src/lib/bootstrap-profile-manager.js +641 -0
- package/src/lib/bootstrap-provisioners.js +421 -0
- package/src/lib/ci-register-helpers.js +74 -0
- package/src/lib/config-loader.js +408 -0
- package/src/lib/config-manager.js +66 -1685
- package/src/lib/config-mcp-client.js +118 -0
- package/src/lib/config-validator.js +634 -0
- package/src/lib/cuda-resolver.js +149 -0
- package/src/lib/e2e-catalog-validator.js +251 -3
- package/src/lib/e2e-ci-recorder.js +103 -0
- package/src/lib/generated/cli-options.js +315 -311
- package/src/lib/generated/parameter-matrix.js +671 -0
- package/src/lib/generated/validation-rules.js +71 -71
- package/src/lib/marketplace-flow.js +276 -0
- package/src/lib/mcp-query-runner.js +768 -0
- package/src/lib/parameter-schema-validator.js +62 -18
- package/src/lib/path-prover-brain.js +607 -0
- package/src/lib/prompt-runner.js +41 -1504
- package/src/lib/prompts/feature-prompts.js +172 -0
- package/src/lib/prompts/index.js +48 -0
- package/src/lib/prompts/infrastructure-prompts.js +690 -0
- package/src/lib/prompts/model-prompts.js +552 -0
- package/src/lib/prompts/project-prompts.js +82 -0
- package/src/lib/prompts.js +2 -1446
- package/src/lib/registry-command-handler.js +135 -3
- package/src/lib/secrets-prompt-runner.js +251 -0
- package/src/lib/template-variable-resolver.js +422 -0
- 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
- package/config/parameter-schema.json +0 -88
|
@@ -16,17 +16,16 @@
|
|
|
16
16
|
* 9. Prompting (fallback)
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import fs from 'fs';
|
|
20
19
|
import path from 'path';
|
|
21
20
|
import { readFileSync } from 'node:fs';
|
|
22
21
|
import { resolve, dirname } from 'node:path';
|
|
23
22
|
import { fileURLToPath } from 'node:url';
|
|
24
|
-
import { McpClient } from './mcp-client.js';
|
|
25
23
|
import DeploymentConfigResolver from './deployment-config-resolver.js';
|
|
26
|
-
import BootstrapConfig from './bootstrap-config.js';
|
|
27
|
-
import { parseKeyValue } from './key-value-parser.js';
|
|
28
24
|
import ParameterSchemaValidator from './parameter-schema-validator.js';
|
|
29
|
-
import
|
|
25
|
+
import ConfigLoader from './config-loader.js';
|
|
26
|
+
import ConfigMcpClient from './config-mcp-client.js';
|
|
27
|
+
import ConfigValidator from './config-validator.js';
|
|
28
|
+
import { parameterMatrix } from './generated/parameter-matrix.js';
|
|
30
29
|
|
|
31
30
|
const __configMgrFilename = fileURLToPath(import.meta.url);
|
|
32
31
|
const __configMgrDir = dirname(__configMgrFilename);
|
|
@@ -86,6 +85,17 @@ export default class ConfigManager {
|
|
|
86
85
|
this.mcpSources = {};
|
|
87
86
|
this.mcpChoices = {};
|
|
88
87
|
this._sourceManifest = [];
|
|
88
|
+
this.GENERATOR_ROOT = GENERATOR_ROOT;
|
|
89
|
+
|
|
90
|
+
// Delegate modules
|
|
91
|
+
this._loader = new ConfigLoader(this);
|
|
92
|
+
this._mcpClient = new ConfigMcpClient(this);
|
|
93
|
+
this._validator = new ConfigValidator(this);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Delegate to config-loader for backward compatibility with tests */
|
|
97
|
+
_applyJsonConfig(config) {
|
|
98
|
+
return this._loader._applyJsonConfig(config);
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
/**
|
|
@@ -192,7 +202,16 @@ export default class ConfigManager {
|
|
|
192
202
|
// For http architecture, engine comes from the --engine CLI option or prompt
|
|
193
203
|
if (parts.architecture === 'http') {
|
|
194
204
|
if (!finalConfig.engine) {
|
|
195
|
-
|
|
205
|
+
// Infer engine from model format if possible
|
|
206
|
+
const formatToEngine = {
|
|
207
|
+
'pkl': 'sklearn',
|
|
208
|
+
'joblib': 'sklearn',
|
|
209
|
+
'json': 'xgboost',
|
|
210
|
+
'keras': 'tensorflow',
|
|
211
|
+
'h5': 'tensorflow',
|
|
212
|
+
'savedmodel': 'tensorflow'
|
|
213
|
+
};
|
|
214
|
+
finalConfig.engine = (finalConfig.modelFormat && formatToEngine[finalConfig.modelFormat]) || 'sklearn';
|
|
196
215
|
}
|
|
197
216
|
} else {
|
|
198
217
|
finalConfig.engine = parts.engine;
|
|
@@ -278,7 +297,14 @@ export default class ConfigManager {
|
|
|
278
297
|
|
|
279
298
|
if (projectNameFromArgument &&
|
|
280
299
|
!explicitDestination &&
|
|
281
|
-
finalConfig.destinationDir === '.') {
|
|
300
|
+
(!finalConfig.destinationDir || finalConfig.destinationDir === '.')) {
|
|
301
|
+
finalConfig.destinationDir = `./${finalConfig.projectName}`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Ensure destinationDir is never null — derive from projectName if not set.
|
|
305
|
+
// This covers interactive mode where destinationDir is non-promptable and no
|
|
306
|
+
// CLI positional argument was provided.
|
|
307
|
+
if (!finalConfig.destinationDir) {
|
|
282
308
|
finalConfig.destinationDir = `./${finalConfig.projectName}`;
|
|
283
309
|
}
|
|
284
310
|
|
|
@@ -438,671 +464,11 @@ export default class ConfigManager {
|
|
|
438
464
|
}
|
|
439
465
|
|
|
440
466
|
/**
|
|
441
|
-
* Gets the parameter matrix configuration
|
|
467
|
+
* Gets the parameter matrix configuration (generated from schema)
|
|
442
468
|
* @private
|
|
443
469
|
*/
|
|
444
470
|
_getParameterMatrix() {
|
|
445
|
-
return
|
|
446
|
-
deploymentConfig: {
|
|
447
|
-
cliOption: 'deployment-config',
|
|
448
|
-
envVar: null,
|
|
449
|
-
configFile: true,
|
|
450
|
-
packageJson: false,
|
|
451
|
-
mcp: false,
|
|
452
|
-
promptable: true,
|
|
453
|
-
required: true,
|
|
454
|
-
default: null,
|
|
455
|
-
valueSpace: 'bounded'
|
|
456
|
-
},
|
|
457
|
-
architecture: {
|
|
458
|
-
cliOption: null,
|
|
459
|
-
envVar: null,
|
|
460
|
-
configFile: false,
|
|
461
|
-
packageJson: false,
|
|
462
|
-
mcp: false,
|
|
463
|
-
promptable: false,
|
|
464
|
-
required: false,
|
|
465
|
-
default: null,
|
|
466
|
-
valueSpace: 'bounded'
|
|
467
|
-
},
|
|
468
|
-
backend: {
|
|
469
|
-
cliOption: null,
|
|
470
|
-
envVar: null,
|
|
471
|
-
configFile: false,
|
|
472
|
-
packageJson: false,
|
|
473
|
-
mcp: false,
|
|
474
|
-
promptable: false,
|
|
475
|
-
required: false,
|
|
476
|
-
default: null,
|
|
477
|
-
valueSpace: 'bounded'
|
|
478
|
-
},
|
|
479
|
-
engine: {
|
|
480
|
-
cliOption: 'engine',
|
|
481
|
-
envVar: null,
|
|
482
|
-
configFile: true,
|
|
483
|
-
packageJson: false,
|
|
484
|
-
mcp: false,
|
|
485
|
-
promptable: true,
|
|
486
|
-
required: false,
|
|
487
|
-
default: null,
|
|
488
|
-
valueSpace: 'bounded'
|
|
489
|
-
},
|
|
490
|
-
modelFormat: {
|
|
491
|
-
cliOption: 'model-format',
|
|
492
|
-
envVar: null,
|
|
493
|
-
configFile: true,
|
|
494
|
-
packageJson: false,
|
|
495
|
-
mcp: false,
|
|
496
|
-
promptable: true,
|
|
497
|
-
required: true,
|
|
498
|
-
default: null,
|
|
499
|
-
valueSpace: 'bounded'
|
|
500
|
-
},
|
|
501
|
-
modelName: {
|
|
502
|
-
cliOption: 'model-name',
|
|
503
|
-
envVar: null,
|
|
504
|
-
configFile: true,
|
|
505
|
-
packageJson: false,
|
|
506
|
-
mcp: false,
|
|
507
|
-
promptable: true,
|
|
508
|
-
required: false,
|
|
509
|
-
default: 'openai/gpt-oss-20b',
|
|
510
|
-
valueSpace: 'bounded'
|
|
511
|
-
},
|
|
512
|
-
includeSampleModel: {
|
|
513
|
-
cliOption: 'include-sample',
|
|
514
|
-
envVar: null,
|
|
515
|
-
configFile: true,
|
|
516
|
-
packageJson: false,
|
|
517
|
-
mcp: false,
|
|
518
|
-
promptable: true,
|
|
519
|
-
required: true,
|
|
520
|
-
default: false,
|
|
521
|
-
valueSpace: 'bounded'
|
|
522
|
-
},
|
|
523
|
-
includeTesting: {
|
|
524
|
-
cliOption: 'include-testing',
|
|
525
|
-
envVar: null,
|
|
526
|
-
configFile: true,
|
|
527
|
-
packageJson: false,
|
|
528
|
-
mcp: false,
|
|
529
|
-
promptable: false,
|
|
530
|
-
required: false,
|
|
531
|
-
default: true,
|
|
532
|
-
valueSpace: 'bounded'
|
|
533
|
-
},
|
|
534
|
-
instanceType: {
|
|
535
|
-
cliOption: 'instance-type',
|
|
536
|
-
envVar: 'ML_INSTANCE_TYPE',
|
|
537
|
-
configFile: true,
|
|
538
|
-
packageJson: false,
|
|
539
|
-
mcp: true,
|
|
540
|
-
promptable: true,
|
|
541
|
-
required: true,
|
|
542
|
-
default: null,
|
|
543
|
-
valueSpace: 'unbounded'
|
|
544
|
-
},
|
|
545
|
-
awsRegion: {
|
|
546
|
-
cliOption: 'region',
|
|
547
|
-
envVar: 'AWS_REGION',
|
|
548
|
-
ambientEnvVar: true, // AWS_REGION is commonly set in shells; treat as default, not explicit override
|
|
549
|
-
configFile: true,
|
|
550
|
-
packageJson: true,
|
|
551
|
-
mcp: true,
|
|
552
|
-
promptable: true,
|
|
553
|
-
required: false,
|
|
554
|
-
default: 'us-east-1',
|
|
555
|
-
valueSpace: 'unbounded'
|
|
556
|
-
},
|
|
557
|
-
awsRoleArn: {
|
|
558
|
-
cliOption: 'role-arn',
|
|
559
|
-
envVar: 'AWS_ROLE',
|
|
560
|
-
configFile: true,
|
|
561
|
-
packageJson: true,
|
|
562
|
-
mcp: true,
|
|
563
|
-
promptable: true,
|
|
564
|
-
required: false,
|
|
565
|
-
default: null,
|
|
566
|
-
valueSpace: 'unbounded'
|
|
567
|
-
},
|
|
568
|
-
configFile: {
|
|
569
|
-
cliOption: 'config',
|
|
570
|
-
envVar: 'ML_CONTAINER_CREATOR_CONFIG',
|
|
571
|
-
configFile: false,
|
|
572
|
-
packageJson: true,
|
|
573
|
-
mcp: false,
|
|
574
|
-
promptable: true,
|
|
575
|
-
required: false,
|
|
576
|
-
default: null,
|
|
577
|
-
valueSpace: 'bounded'
|
|
578
|
-
},
|
|
579
|
-
skipPrompts: {
|
|
580
|
-
cliOption: 'skip-prompts',
|
|
581
|
-
envVar: null,
|
|
582
|
-
configFile: false,
|
|
583
|
-
packageJson: false,
|
|
584
|
-
mcp: false,
|
|
585
|
-
promptable: false,
|
|
586
|
-
required: false,
|
|
587
|
-
default: false,
|
|
588
|
-
valueSpace: 'bounded'
|
|
589
|
-
},
|
|
590
|
-
projectName: {
|
|
591
|
-
cliOption: 'project-name',
|
|
592
|
-
envVar: null,
|
|
593
|
-
configFile: true,
|
|
594
|
-
packageJson: true,
|
|
595
|
-
mcp: false,
|
|
596
|
-
promptable: false,
|
|
597
|
-
required: true,
|
|
598
|
-
default: null,
|
|
599
|
-
valueSpace: 'bounded'
|
|
600
|
-
},
|
|
601
|
-
destinationDir: {
|
|
602
|
-
cliOption: 'project-dir',
|
|
603
|
-
envVar: null,
|
|
604
|
-
configFile: true,
|
|
605
|
-
packageJson: true,
|
|
606
|
-
mcp: false,
|
|
607
|
-
promptable: false,
|
|
608
|
-
required: true,
|
|
609
|
-
default: '.',
|
|
610
|
-
valueSpace: 'bounded'
|
|
611
|
-
},
|
|
612
|
-
buildTarget: {
|
|
613
|
-
cliOption: 'build-target',
|
|
614
|
-
envVar: 'ML_BUILD_TARGET',
|
|
615
|
-
configFile: true,
|
|
616
|
-
packageJson: false,
|
|
617
|
-
mcp: false,
|
|
618
|
-
promptable: true,
|
|
619
|
-
required: true,
|
|
620
|
-
default: 'codebuild',
|
|
621
|
-
valueSpace: 'bounded'
|
|
622
|
-
},
|
|
623
|
-
codebuildComputeType: {
|
|
624
|
-
cliOption: 'codebuild-compute-type',
|
|
625
|
-
envVar: 'ML_CODEBUILD_COMPUTE_TYPE',
|
|
626
|
-
configFile: true,
|
|
627
|
-
packageJson: false,
|
|
628
|
-
mcp: false,
|
|
629
|
-
promptable: true,
|
|
630
|
-
required: false,
|
|
631
|
-
default: 'BUILD_GENERAL1_MEDIUM',
|
|
632
|
-
valueSpace: 'bounded'
|
|
633
|
-
},
|
|
634
|
-
codebuildProjectName: {
|
|
635
|
-
cliOption: null,
|
|
636
|
-
envVar: null,
|
|
637
|
-
configFile: true,
|
|
638
|
-
packageJson: false,
|
|
639
|
-
mcp: false,
|
|
640
|
-
promptable: false,
|
|
641
|
-
required: false,
|
|
642
|
-
default: null,
|
|
643
|
-
valueSpace: 'bounded'
|
|
644
|
-
},
|
|
645
|
-
hfToken: {
|
|
646
|
-
cliOption: 'hf-token',
|
|
647
|
-
envVar: null,
|
|
648
|
-
configFile: true,
|
|
649
|
-
packageJson: false,
|
|
650
|
-
mcp: false,
|
|
651
|
-
promptable: true,
|
|
652
|
-
required: false,
|
|
653
|
-
default: null,
|
|
654
|
-
valueSpace: 'bounded'
|
|
655
|
-
},
|
|
656
|
-
hfTokenArn: {
|
|
657
|
-
cliOption: 'hf-token-arn',
|
|
658
|
-
envVar: null,
|
|
659
|
-
configFile: true,
|
|
660
|
-
packageJson: false,
|
|
661
|
-
mcp: false,
|
|
662
|
-
promptable: false,
|
|
663
|
-
required: false,
|
|
664
|
-
default: null,
|
|
665
|
-
valueSpace: 'bounded'
|
|
666
|
-
},
|
|
667
|
-
ngcTokenArn: {
|
|
668
|
-
cliOption: 'ngc-token-arn',
|
|
669
|
-
envVar: null,
|
|
670
|
-
configFile: true,
|
|
671
|
-
packageJson: false,
|
|
672
|
-
mcp: false,
|
|
673
|
-
promptable: false,
|
|
674
|
-
required: false,
|
|
675
|
-
default: null,
|
|
676
|
-
valueSpace: 'bounded'
|
|
677
|
-
},
|
|
678
|
-
deploymentTarget: {
|
|
679
|
-
cliOption: 'deployment-target',
|
|
680
|
-
envVar: 'ML_DEPLOYMENT_TARGET',
|
|
681
|
-
configFile: true,
|
|
682
|
-
packageJson: false,
|
|
683
|
-
mcp: false,
|
|
684
|
-
promptable: true,
|
|
685
|
-
required: true,
|
|
686
|
-
default: 'realtime-inference',
|
|
687
|
-
valueSpace: 'bounded'
|
|
688
|
-
},
|
|
689
|
-
hyperPodCluster: {
|
|
690
|
-
cliOption: 'hyperpod-cluster',
|
|
691
|
-
envVar: null,
|
|
692
|
-
configFile: true,
|
|
693
|
-
packageJson: false,
|
|
694
|
-
mcp: true,
|
|
695
|
-
promptable: true,
|
|
696
|
-
required: false,
|
|
697
|
-
default: null,
|
|
698
|
-
valueSpace: 'unbounded'
|
|
699
|
-
},
|
|
700
|
-
hyperPodNamespace: {
|
|
701
|
-
cliOption: 'hyperpod-namespace',
|
|
702
|
-
envVar: null,
|
|
703
|
-
configFile: true,
|
|
704
|
-
packageJson: false,
|
|
705
|
-
mcp: false,
|
|
706
|
-
promptable: true,
|
|
707
|
-
required: false,
|
|
708
|
-
default: 'default',
|
|
709
|
-
valueSpace: 'bounded'
|
|
710
|
-
},
|
|
711
|
-
hyperPodReplicas: {
|
|
712
|
-
cliOption: 'hyperpod-replicas',
|
|
713
|
-
envVar: null,
|
|
714
|
-
configFile: true,
|
|
715
|
-
packageJson: false,
|
|
716
|
-
mcp: false,
|
|
717
|
-
promptable: true,
|
|
718
|
-
required: false,
|
|
719
|
-
default: 1,
|
|
720
|
-
valueSpace: 'bounded'
|
|
721
|
-
},
|
|
722
|
-
fsxVolumeHandle: {
|
|
723
|
-
cliOption: 'fsx-volume-handle',
|
|
724
|
-
envVar: null,
|
|
725
|
-
configFile: true,
|
|
726
|
-
packageJson: false,
|
|
727
|
-
mcp: false,
|
|
728
|
-
promptable: true,
|
|
729
|
-
required: false,
|
|
730
|
-
default: null,
|
|
731
|
-
valueSpace: 'bounded'
|
|
732
|
-
},
|
|
733
|
-
baseImage: {
|
|
734
|
-
cliOption: 'base-image',
|
|
735
|
-
envVar: null,
|
|
736
|
-
configFile: true,
|
|
737
|
-
packageJson: false,
|
|
738
|
-
mcp: true,
|
|
739
|
-
promptable: true,
|
|
740
|
-
required: false,
|
|
741
|
-
default: null,
|
|
742
|
-
valueSpace: 'unbounded'
|
|
743
|
-
},
|
|
744
|
-
asyncS3OutputPath: {
|
|
745
|
-
cliOption: 'async-s3-output-path',
|
|
746
|
-
envVar: 'ML_ASYNC_S3_OUTPUT_PATH',
|
|
747
|
-
configFile: true,
|
|
748
|
-
packageJson: false,
|
|
749
|
-
mcp: true,
|
|
750
|
-
promptable: true,
|
|
751
|
-
required: false,
|
|
752
|
-
default: null,
|
|
753
|
-
valueSpace: 'unbounded'
|
|
754
|
-
},
|
|
755
|
-
asyncSnsSuccessTopic: {
|
|
756
|
-
cliOption: 'async-sns-success-topic',
|
|
757
|
-
envVar: null,
|
|
758
|
-
configFile: true,
|
|
759
|
-
packageJson: false,
|
|
760
|
-
mcp: true,
|
|
761
|
-
promptable: true,
|
|
762
|
-
required: false,
|
|
763
|
-
default: null,
|
|
764
|
-
valueSpace: 'unbounded'
|
|
765
|
-
},
|
|
766
|
-
asyncSnsErrorTopic: {
|
|
767
|
-
cliOption: 'async-sns-error-topic',
|
|
768
|
-
envVar: null,
|
|
769
|
-
configFile: true,
|
|
770
|
-
packageJson: false,
|
|
771
|
-
mcp: true,
|
|
772
|
-
promptable: true,
|
|
773
|
-
required: false,
|
|
774
|
-
default: null,
|
|
775
|
-
valueSpace: 'unbounded'
|
|
776
|
-
},
|
|
777
|
-
asyncMaxConcurrentInvocations: {
|
|
778
|
-
cliOption: 'async-max-concurrent',
|
|
779
|
-
envVar: null,
|
|
780
|
-
configFile: true,
|
|
781
|
-
packageJson: false,
|
|
782
|
-
mcp: false,
|
|
783
|
-
promptable: true,
|
|
784
|
-
required: false,
|
|
785
|
-
default: 1,
|
|
786
|
-
valueSpace: 'bounded'
|
|
787
|
-
},
|
|
788
|
-
batchInputPath: {
|
|
789
|
-
cliOption: 'batch-input-path',
|
|
790
|
-
envVar: 'ML_BATCH_INPUT_PATH',
|
|
791
|
-
configFile: true,
|
|
792
|
-
packageJson: false,
|
|
793
|
-
mcp: true,
|
|
794
|
-
promptable: true,
|
|
795
|
-
required: false,
|
|
796
|
-
default: null,
|
|
797
|
-
valueSpace: 'unbounded'
|
|
798
|
-
},
|
|
799
|
-
batchOutputPath: {
|
|
800
|
-
cliOption: 'batch-output-path',
|
|
801
|
-
envVar: 'ML_BATCH_OUTPUT_PATH',
|
|
802
|
-
configFile: true,
|
|
803
|
-
packageJson: false,
|
|
804
|
-
mcp: true,
|
|
805
|
-
promptable: true,
|
|
806
|
-
required: false,
|
|
807
|
-
default: null,
|
|
808
|
-
valueSpace: 'unbounded'
|
|
809
|
-
},
|
|
810
|
-
batchInstanceCount: {
|
|
811
|
-
cliOption: 'batch-instance-count',
|
|
812
|
-
envVar: null,
|
|
813
|
-
configFile: true,
|
|
814
|
-
packageJson: false,
|
|
815
|
-
mcp: false,
|
|
816
|
-
promptable: true,
|
|
817
|
-
required: false,
|
|
818
|
-
default: 1,
|
|
819
|
-
valueSpace: 'bounded'
|
|
820
|
-
},
|
|
821
|
-
batchSplitType: {
|
|
822
|
-
cliOption: 'batch-split-type',
|
|
823
|
-
envVar: null,
|
|
824
|
-
configFile: true,
|
|
825
|
-
packageJson: false,
|
|
826
|
-
mcp: false,
|
|
827
|
-
promptable: true,
|
|
828
|
-
required: false,
|
|
829
|
-
default: 'Line',
|
|
830
|
-
valueSpace: 'bounded'
|
|
831
|
-
},
|
|
832
|
-
batchStrategy: {
|
|
833
|
-
cliOption: 'batch-strategy',
|
|
834
|
-
envVar: null,
|
|
835
|
-
configFile: true,
|
|
836
|
-
packageJson: false,
|
|
837
|
-
mcp: false,
|
|
838
|
-
promptable: true,
|
|
839
|
-
required: false,
|
|
840
|
-
default: 'MultiRecord',
|
|
841
|
-
valueSpace: 'bounded'
|
|
842
|
-
},
|
|
843
|
-
batchJoinSource: {
|
|
844
|
-
cliOption: 'batch-join-source',
|
|
845
|
-
envVar: null,
|
|
846
|
-
configFile: true,
|
|
847
|
-
packageJson: false,
|
|
848
|
-
mcp: false,
|
|
849
|
-
promptable: true,
|
|
850
|
-
required: false,
|
|
851
|
-
default: 'None',
|
|
852
|
-
valueSpace: 'bounded'
|
|
853
|
-
},
|
|
854
|
-
batchMaxConcurrentTransforms: {
|
|
855
|
-
cliOption: 'batch-max-concurrent',
|
|
856
|
-
envVar: null,
|
|
857
|
-
configFile: true,
|
|
858
|
-
packageJson: false,
|
|
859
|
-
mcp: false,
|
|
860
|
-
promptable: true,
|
|
861
|
-
required: false,
|
|
862
|
-
default: 1,
|
|
863
|
-
valueSpace: 'bounded'
|
|
864
|
-
},
|
|
865
|
-
batchMaxPayloadInMB: {
|
|
866
|
-
cliOption: 'batch-max-payload',
|
|
867
|
-
envVar: null,
|
|
868
|
-
configFile: true,
|
|
869
|
-
packageJson: false,
|
|
870
|
-
mcp: false,
|
|
871
|
-
promptable: true,
|
|
872
|
-
required: false,
|
|
873
|
-
default: 6,
|
|
874
|
-
valueSpace: 'bounded'
|
|
875
|
-
},
|
|
876
|
-
endpointInitialInstanceCount: {
|
|
877
|
-
cliOption: 'endpoint-initial-instance-count',
|
|
878
|
-
envVar: null,
|
|
879
|
-
configFile: true,
|
|
880
|
-
packageJson: false,
|
|
881
|
-
mcp: false,
|
|
882
|
-
promptable: false,
|
|
883
|
-
required: false,
|
|
884
|
-
default: 1,
|
|
885
|
-
valueSpace: 'bounded',
|
|
886
|
-
schemaValidated: true
|
|
887
|
-
},
|
|
888
|
-
endpointDataCapturePercent: {
|
|
889
|
-
cliOption: 'endpoint-data-capture-percent',
|
|
890
|
-
envVar: null,
|
|
891
|
-
configFile: true,
|
|
892
|
-
packageJson: false,
|
|
893
|
-
mcp: false,
|
|
894
|
-
promptable: false,
|
|
895
|
-
required: false,
|
|
896
|
-
default: 0,
|
|
897
|
-
valueSpace: 'bounded',
|
|
898
|
-
schemaValidated: true
|
|
899
|
-
},
|
|
900
|
-
endpointVariantName: {
|
|
901
|
-
cliOption: 'endpoint-variant-name',
|
|
902
|
-
envVar: null,
|
|
903
|
-
configFile: true,
|
|
904
|
-
packageJson: false,
|
|
905
|
-
mcp: false,
|
|
906
|
-
promptable: false,
|
|
907
|
-
required: false,
|
|
908
|
-
default: 'AllTraffic',
|
|
909
|
-
valueSpace: 'bounded',
|
|
910
|
-
schemaValidated: true
|
|
911
|
-
},
|
|
912
|
-
endpointVolumeSize: {
|
|
913
|
-
cliOption: 'endpoint-volume-size',
|
|
914
|
-
envVar: null,
|
|
915
|
-
configFile: true,
|
|
916
|
-
packageJson: false,
|
|
917
|
-
mcp: false,
|
|
918
|
-
promptable: false,
|
|
919
|
-
required: false,
|
|
920
|
-
default: null,
|
|
921
|
-
valueSpace: 'bounded',
|
|
922
|
-
schemaValidated: true
|
|
923
|
-
},
|
|
924
|
-
icCpuCount: {
|
|
925
|
-
cliOption: 'ic-cpu-count',
|
|
926
|
-
envVar: null,
|
|
927
|
-
configFile: true,
|
|
928
|
-
packageJson: false,
|
|
929
|
-
mcp: false,
|
|
930
|
-
promptable: false,
|
|
931
|
-
required: false,
|
|
932
|
-
default: null,
|
|
933
|
-
valueSpace: 'bounded',
|
|
934
|
-
schemaValidated: true
|
|
935
|
-
},
|
|
936
|
-
icMemorySize: {
|
|
937
|
-
cliOption: 'ic-memory-size',
|
|
938
|
-
envVar: null,
|
|
939
|
-
configFile: true,
|
|
940
|
-
packageJson: false,
|
|
941
|
-
mcp: false,
|
|
942
|
-
promptable: false,
|
|
943
|
-
required: false,
|
|
944
|
-
default: null,
|
|
945
|
-
valueSpace: 'bounded',
|
|
946
|
-
schemaValidated: true
|
|
947
|
-
},
|
|
948
|
-
icGpuCount: {
|
|
949
|
-
cliOption: 'ic-gpu-count',
|
|
950
|
-
envVar: null,
|
|
951
|
-
configFile: true,
|
|
952
|
-
packageJson: false,
|
|
953
|
-
mcp: false,
|
|
954
|
-
promptable: false,
|
|
955
|
-
required: false,
|
|
956
|
-
default: null,
|
|
957
|
-
valueSpace: 'bounded',
|
|
958
|
-
schemaValidated: true
|
|
959
|
-
},
|
|
960
|
-
icCopyCount: {
|
|
961
|
-
cliOption: 'ic-copy-count',
|
|
962
|
-
envVar: null,
|
|
963
|
-
configFile: true,
|
|
964
|
-
packageJson: false,
|
|
965
|
-
mcp: false,
|
|
966
|
-
promptable: false,
|
|
967
|
-
required: false,
|
|
968
|
-
default: 1,
|
|
969
|
-
valueSpace: 'bounded',
|
|
970
|
-
schemaValidated: true
|
|
971
|
-
},
|
|
972
|
-
icModelWeight: {
|
|
973
|
-
cliOption: 'ic-model-weight',
|
|
974
|
-
envVar: null,
|
|
975
|
-
configFile: true,
|
|
976
|
-
packageJson: false,
|
|
977
|
-
mcp: false,
|
|
978
|
-
promptable: false,
|
|
979
|
-
required: false,
|
|
980
|
-
default: 1.0,
|
|
981
|
-
valueSpace: 'bounded',
|
|
982
|
-
schemaValidated: true
|
|
983
|
-
},
|
|
984
|
-
includeBenchmark: {
|
|
985
|
-
cliOption: 'include-benchmark',
|
|
986
|
-
envVar: 'ML_INCLUDE_BENCHMARK',
|
|
987
|
-
configFile: true,
|
|
988
|
-
packageJson: false,
|
|
989
|
-
mcp: false,
|
|
990
|
-
promptable: true,
|
|
991
|
-
required: false,
|
|
992
|
-
default: false,
|
|
993
|
-
valueSpace: 'bounded'
|
|
994
|
-
},
|
|
995
|
-
benchmarkConcurrency: {
|
|
996
|
-
cliOption: 'benchmark-concurrency',
|
|
997
|
-
envVar: null,
|
|
998
|
-
configFile: true,
|
|
999
|
-
packageJson: false,
|
|
1000
|
-
mcp: false,
|
|
1001
|
-
promptable: true,
|
|
1002
|
-
required: false,
|
|
1003
|
-
default: 10,
|
|
1004
|
-
valueSpace: 'bounded'
|
|
1005
|
-
},
|
|
1006
|
-
benchmarkInputTokensMean: {
|
|
1007
|
-
cliOption: 'benchmark-input-tokens',
|
|
1008
|
-
envVar: null,
|
|
1009
|
-
configFile: true,
|
|
1010
|
-
packageJson: false,
|
|
1011
|
-
mcp: false,
|
|
1012
|
-
promptable: true,
|
|
1013
|
-
required: false,
|
|
1014
|
-
default: 550,
|
|
1015
|
-
valueSpace: 'bounded'
|
|
1016
|
-
},
|
|
1017
|
-
benchmarkOutputTokensMean: {
|
|
1018
|
-
cliOption: 'benchmark-output-tokens',
|
|
1019
|
-
envVar: null,
|
|
1020
|
-
configFile: true,
|
|
1021
|
-
packageJson: false,
|
|
1022
|
-
mcp: false,
|
|
1023
|
-
promptable: true,
|
|
1024
|
-
required: false,
|
|
1025
|
-
default: 150,
|
|
1026
|
-
valueSpace: 'bounded'
|
|
1027
|
-
},
|
|
1028
|
-
benchmarkStreaming: {
|
|
1029
|
-
cliOption: 'benchmark-streaming',
|
|
1030
|
-
envVar: null,
|
|
1031
|
-
configFile: true,
|
|
1032
|
-
packageJson: false,
|
|
1033
|
-
mcp: false,
|
|
1034
|
-
promptable: true,
|
|
1035
|
-
required: false,
|
|
1036
|
-
default: true,
|
|
1037
|
-
valueSpace: 'bounded'
|
|
1038
|
-
},
|
|
1039
|
-
benchmarkRequestCount: {
|
|
1040
|
-
cliOption: 'benchmark-request-count',
|
|
1041
|
-
envVar: null,
|
|
1042
|
-
configFile: true,
|
|
1043
|
-
packageJson: false,
|
|
1044
|
-
mcp: false,
|
|
1045
|
-
promptable: true,
|
|
1046
|
-
required: false,
|
|
1047
|
-
default: null,
|
|
1048
|
-
valueSpace: 'bounded'
|
|
1049
|
-
},
|
|
1050
|
-
benchmarkS3OutputPath: {
|
|
1051
|
-
cliOption: 'benchmark-s3-output-path',
|
|
1052
|
-
envVar: 'ML_BENCHMARK_S3_OUTPUT_PATH',
|
|
1053
|
-
configFile: true,
|
|
1054
|
-
packageJson: false,
|
|
1055
|
-
mcp: false,
|
|
1056
|
-
promptable: true,
|
|
1057
|
-
required: false,
|
|
1058
|
-
default: null,
|
|
1059
|
-
valueSpace: 'bounded'
|
|
1060
|
-
},
|
|
1061
|
-
enableLora: {
|
|
1062
|
-
cliOption: 'enable-lora',
|
|
1063
|
-
envVar: null,
|
|
1064
|
-
configFile: true,
|
|
1065
|
-
packageJson: false,
|
|
1066
|
-
mcp: false,
|
|
1067
|
-
promptable: true,
|
|
1068
|
-
required: false,
|
|
1069
|
-
default: false,
|
|
1070
|
-
valueSpace: 'bounded'
|
|
1071
|
-
},
|
|
1072
|
-
maxLoras: {
|
|
1073
|
-
cliOption: 'max-loras',
|
|
1074
|
-
envVar: null,
|
|
1075
|
-
configFile: true,
|
|
1076
|
-
packageJson: false,
|
|
1077
|
-
mcp: false,
|
|
1078
|
-
promptable: true,
|
|
1079
|
-
required: false,
|
|
1080
|
-
default: 30,
|
|
1081
|
-
valueSpace: 'bounded'
|
|
1082
|
-
},
|
|
1083
|
-
maxLoraRank: {
|
|
1084
|
-
cliOption: 'max-lora-rank',
|
|
1085
|
-
envVar: null,
|
|
1086
|
-
configFile: true,
|
|
1087
|
-
packageJson: false,
|
|
1088
|
-
mcp: false,
|
|
1089
|
-
promptable: true,
|
|
1090
|
-
required: false,
|
|
1091
|
-
default: 64,
|
|
1092
|
-
valueSpace: 'bounded'
|
|
1093
|
-
},
|
|
1094
|
-
modelPackageArn: {
|
|
1095
|
-
cliOption: 'model-package-arn',
|
|
1096
|
-
envVar: null,
|
|
1097
|
-
configFile: true,
|
|
1098
|
-
packageJson: false,
|
|
1099
|
-
mcp: true,
|
|
1100
|
-
promptable: true,
|
|
1101
|
-
required: false,
|
|
1102
|
-
default: null,
|
|
1103
|
-
valueSpace: 'unbounded'
|
|
1104
|
-
}
|
|
1105
|
-
};
|
|
471
|
+
return parameterMatrix;
|
|
1106
472
|
}
|
|
1107
473
|
|
|
1108
474
|
/**
|
|
@@ -1199,30 +565,7 @@ export default class ConfigManager {
|
|
|
1199
565
|
* @private
|
|
1200
566
|
*/
|
|
1201
567
|
async _loadBootstrapConfig() {
|
|
1202
|
-
|
|
1203
|
-
const bootstrapConfig = new BootstrapConfig();
|
|
1204
|
-
const activeProfile = bootstrapConfig.getActiveProfile();
|
|
1205
|
-
if (!activeProfile) {
|
|
1206
|
-
return;
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
const profileConfig = activeProfile.config;
|
|
1210
|
-
const mapped = {};
|
|
1211
|
-
|
|
1212
|
-
if (profileConfig.roleArn) {
|
|
1213
|
-
mapped.awsRoleArn = profileConfig.roleArn;
|
|
1214
|
-
}
|
|
1215
|
-
if (profileConfig.awsRegion) {
|
|
1216
|
-
mapped.awsRegion = profileConfig.awsRegion;
|
|
1217
|
-
}
|
|
1218
|
-
if (profileConfig.awsProfile) {
|
|
1219
|
-
mapped.awsProfile = profileConfig.awsProfile;
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
this._mergeConfig(mapped);
|
|
1223
|
-
} catch (error) {
|
|
1224
|
-
// Ignore errors — config file may not exist or may be malformed
|
|
1225
|
-
}
|
|
568
|
+
return this._loader._loadBootstrapConfig();
|
|
1226
569
|
}
|
|
1227
570
|
|
|
1228
571
|
/**
|
|
@@ -1230,25 +573,7 @@ export default class ConfigManager {
|
|
|
1230
573
|
* @private
|
|
1231
574
|
*/
|
|
1232
575
|
async _loadPackageJsonConfig() {
|
|
1233
|
-
|
|
1234
|
-
const packageJsonPath = path.resolve(process.cwd(), 'package.json');
|
|
1235
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
1236
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
1237
|
-
const generatorConfig = packageJson['ml-container-creator'];
|
|
1238
|
-
if (generatorConfig) {
|
|
1239
|
-
// Filter config to only include parameters supported in package.json
|
|
1240
|
-
const filteredConfig = {};
|
|
1241
|
-
Object.entries(generatorConfig).forEach(([key, value]) => {
|
|
1242
|
-
if (this._isSourceSupported(key, 'packageJson')) {
|
|
1243
|
-
filteredConfig[key] = this._parseValue(key, value);
|
|
1244
|
-
}
|
|
1245
|
-
});
|
|
1246
|
-
this._mergeConfig(filteredConfig);
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
} catch (error) {
|
|
1250
|
-
// Ignore errors - this is optional
|
|
1251
|
-
}
|
|
576
|
+
return this._loader._loadPackageJsonConfig();
|
|
1252
577
|
}
|
|
1253
578
|
|
|
1254
579
|
/**
|
|
@@ -1256,215 +581,15 @@ export default class ConfigManager {
|
|
|
1256
581
|
* @private
|
|
1257
582
|
*/
|
|
1258
583
|
async _loadCustomConfigFile() {
|
|
1259
|
-
|
|
1260
|
-
const configPath = path.join(GENERATOR_ROOT, 'config', 'mcp.json');
|
|
1261
|
-
if (fs.existsSync(configPath)) {
|
|
1262
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
1263
|
-
this._mergeConfig(config);
|
|
1264
|
-
}
|
|
1265
|
-
} catch (error) {
|
|
1266
|
-
// Ignore errors - this is optional
|
|
1267
|
-
}
|
|
584
|
+
return this._loader._loadCustomConfigFile();
|
|
1268
585
|
}
|
|
1269
586
|
|
|
1270
587
|
/**
|
|
1271
588
|
* Load from CLI --config file or --config-json inline string.
|
|
1272
|
-
*
|
|
1273
|
-
* --config-json accepts either:
|
|
1274
|
-
* 1. An inline JSON string: --config-json='{"deploymentConfig":"transformers-vllm"}'
|
|
1275
|
-
* 2. A path to a JSON file: --config-json=config.json
|
|
1276
|
-
*
|
|
1277
|
-
* When both --config and --config-json are provided, --config-json wins
|
|
1278
|
-
* (it is applied second, so its values override --config values).
|
|
1279
|
-
*
|
|
1280
|
-
* Also checks the ML_CONTAINER_CREATOR_CONFIG environment variable as a
|
|
1281
|
-
* fallback for --config.
|
|
1282
|
-
*
|
|
1283
589
|
* @private
|
|
1284
590
|
*/
|
|
1285
591
|
async _loadCliConfigFile() {
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
// Check environment variable if CLI option not provided
|
|
1289
|
-
if (!configFile && process.env.ML_CONTAINER_CREATOR_CONFIG) {
|
|
1290
|
-
configFile = process.env.ML_CONTAINER_CREATOR_CONFIG;
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
if (configFile) {
|
|
1294
|
-
this._loadConfigFromFile(configFile);
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
// --config-json: inline JSON string or path to a JSON file
|
|
1298
|
-
const configJson = this.options['config-json'];
|
|
1299
|
-
if (configJson) {
|
|
1300
|
-
this._loadConfigFromJson(configJson);
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
/**
|
|
1305
|
-
* Load configuration from a JSON file path.
|
|
1306
|
-
* @param {string} configFile - Path to the JSON config file
|
|
1307
|
-
* @private
|
|
1308
|
-
*/
|
|
1309
|
-
_loadConfigFromFile(configFile) {
|
|
1310
|
-
try {
|
|
1311
|
-
const configPath = path.resolve(configFile);
|
|
1312
|
-
if (!fs.existsSync(configPath)) {
|
|
1313
|
-
throw new ConfigurationError(
|
|
1314
|
-
`Config file not found: ${configPath}`,
|
|
1315
|
-
'configFile',
|
|
1316
|
-
'cli'
|
|
1317
|
-
);
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
// Check if file is readable
|
|
1321
|
-
try {
|
|
1322
|
-
fs.accessSync(configPath, fs.constants.R_OK);
|
|
1323
|
-
} catch (accessError) {
|
|
1324
|
-
throw new ConfigurationError(
|
|
1325
|
-
`Config file is not readable: ${configPath}`,
|
|
1326
|
-
'configFile',
|
|
1327
|
-
'cli'
|
|
1328
|
-
);
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
1332
|
-
this._applyJsonConfig(config);
|
|
1333
|
-
} catch (error) {
|
|
1334
|
-
if (error instanceof ConfigurationError) {
|
|
1335
|
-
throw error;
|
|
1336
|
-
} else {
|
|
1337
|
-
throw new ConfigurationError(
|
|
1338
|
-
`Failed to load config file ${configFile}: ${error.message}`,
|
|
1339
|
-
'configFile',
|
|
1340
|
-
'cli'
|
|
1341
|
-
);
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
/**
|
|
1347
|
-
* Load configuration from an inline JSON string or a JSON file path.
|
|
1348
|
-
* Tries to parse as JSON first; if that fails and the value looks like
|
|
1349
|
-
* a file path, reads and parses the file instead.
|
|
1350
|
-
*
|
|
1351
|
-
* @param {string} configJson - Inline JSON string or path to a JSON file
|
|
1352
|
-
* @private
|
|
1353
|
-
*/
|
|
1354
|
-
_loadConfigFromJson(configJson) {
|
|
1355
|
-
let config;
|
|
1356
|
-
try {
|
|
1357
|
-
config = JSON.parse(configJson);
|
|
1358
|
-
} catch {
|
|
1359
|
-
// Not valid JSON — try as a file path
|
|
1360
|
-
try {
|
|
1361
|
-
const configPath = path.resolve(configJson);
|
|
1362
|
-
if (!fs.existsSync(configPath)) {
|
|
1363
|
-
throw new ConfigurationError(
|
|
1364
|
-
`--config-json value is not valid JSON and file not found: ${configJson}`,
|
|
1365
|
-
'configJson',
|
|
1366
|
-
'cli'
|
|
1367
|
-
);
|
|
1368
|
-
}
|
|
1369
|
-
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
1370
|
-
} catch (error) {
|
|
1371
|
-
if (error instanceof ConfigurationError) {
|
|
1372
|
-
throw error;
|
|
1373
|
-
}
|
|
1374
|
-
throw new ConfigurationError(
|
|
1375
|
-
`Failed to parse --config-json: ${error.message}`,
|
|
1376
|
-
'configJson',
|
|
1377
|
-
'cli'
|
|
1378
|
-
);
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
this._applyJsonConfig(config);
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
/**
|
|
1385
|
-
* Apply a parsed JSON config object, filtering to supported parameters.
|
|
1386
|
-
* Handles nested objects for endpoint, iC, and env var configuration.
|
|
1387
|
-
* @param {Object} config - Parsed JSON config object
|
|
1388
|
-
* @private
|
|
1389
|
-
*/
|
|
1390
|
-
_applyJsonConfig(config) {
|
|
1391
|
-
const filteredConfig = {};
|
|
1392
|
-
Object.entries(config).forEach(([key, value]) => {
|
|
1393
|
-
// Handle nested endpointConfig object
|
|
1394
|
-
if (key === 'endpointConfig' && typeof value === 'object' && value !== null) {
|
|
1395
|
-
const endpointMapping = {
|
|
1396
|
-
initialInstanceCount: 'endpointInitialInstanceCount',
|
|
1397
|
-
dataCapturePercent: 'endpointDataCapturePercent',
|
|
1398
|
-
variantName: 'endpointVariantName',
|
|
1399
|
-
volumeSize: 'endpointVolumeSize'
|
|
1400
|
-
};
|
|
1401
|
-
Object.entries(value).forEach(([nestedKey, nestedValue]) => {
|
|
1402
|
-
const flatKey = endpointMapping[nestedKey];
|
|
1403
|
-
if (flatKey && this._isSourceSupported(flatKey, 'configFile')) {
|
|
1404
|
-
filteredConfig[flatKey] = nestedValue;
|
|
1405
|
-
this._recordSource(flatKey, nestedValue, 'config-file');
|
|
1406
|
-
}
|
|
1407
|
-
});
|
|
1408
|
-
return;
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
// Handle nested icConfig object
|
|
1412
|
-
if (key === 'icConfig' && typeof value === 'object' && value !== null) {
|
|
1413
|
-
const icMapping = {
|
|
1414
|
-
cpuCount: 'icCpuCount',
|
|
1415
|
-
memorySize: 'icMemorySize',
|
|
1416
|
-
gpuCount: 'icGpuCount',
|
|
1417
|
-
copyCount: 'icCopyCount',
|
|
1418
|
-
modelWeight: 'icModelWeight'
|
|
1419
|
-
};
|
|
1420
|
-
Object.entries(value).forEach(([nestedKey, nestedValue]) => {
|
|
1421
|
-
const flatKey = icMapping[nestedKey];
|
|
1422
|
-
if (flatKey && this._isSourceSupported(flatKey, 'configFile')) {
|
|
1423
|
-
filteredConfig[flatKey] = nestedValue;
|
|
1424
|
-
this._recordSource(flatKey, nestedValue, 'config-file');
|
|
1425
|
-
}
|
|
1426
|
-
});
|
|
1427
|
-
return;
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
// Handle modelEnvVars object (merge with CLI, CLI takes precedence)
|
|
1431
|
-
if (key === 'modelEnvVars' && typeof value === 'object' && value !== null) {
|
|
1432
|
-
if (!this.config.modelEnvVars) {
|
|
1433
|
-
this.config.modelEnvVars = {};
|
|
1434
|
-
}
|
|
1435
|
-
// Only set keys not already provided by CLI (CLI has higher precedence)
|
|
1436
|
-
const cliModelEnvVars = (this.explicitConfig && this.explicitConfig.modelEnvVars) || {};
|
|
1437
|
-
Object.entries(value).forEach(([envKey, envValue]) => {
|
|
1438
|
-
if (!(envKey in cliModelEnvVars)) {
|
|
1439
|
-
this.config.modelEnvVars[envKey] = envValue;
|
|
1440
|
-
this._recordSource(`modelEnvVars.${envKey}`, envValue, 'config-file');
|
|
1441
|
-
}
|
|
1442
|
-
});
|
|
1443
|
-
return;
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
// Handle serverEnvVars object (merge with CLI, CLI takes precedence)
|
|
1447
|
-
if (key === 'serverEnvVars' && typeof value === 'object' && value !== null) {
|
|
1448
|
-
if (!this.config.serverEnvVars) {
|
|
1449
|
-
this.config.serverEnvVars = {};
|
|
1450
|
-
}
|
|
1451
|
-
// Only set keys not already provided by CLI (CLI has higher precedence)
|
|
1452
|
-
const cliServerEnvVars = (this.explicitConfig && this.explicitConfig.serverEnvVars) || {};
|
|
1453
|
-
Object.entries(value).forEach(([envKey, envValue]) => {
|
|
1454
|
-
if (!(envKey in cliServerEnvVars)) {
|
|
1455
|
-
this.config.serverEnvVars[envKey] = envValue;
|
|
1456
|
-
this._recordSource(`serverEnvVars.${envKey}`, envValue, 'config-file');
|
|
1457
|
-
}
|
|
1458
|
-
});
|
|
1459
|
-
return;
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
if (this._isSourceSupported(key, 'configFile')) {
|
|
1463
|
-
filteredConfig[key] = this._parseValue(key, value);
|
|
1464
|
-
this._recordSource(key, this._parseValue(key, value), 'config-file');
|
|
1465
|
-
}
|
|
1466
|
-
});
|
|
1467
|
-
this._mergeConfig(filteredConfig);
|
|
592
|
+
return this._loader._loadCliConfigFile();
|
|
1468
593
|
}
|
|
1469
594
|
|
|
1470
595
|
/**
|
|
@@ -1472,29 +597,7 @@ export default class ConfigManager {
|
|
|
1472
597
|
* @private
|
|
1473
598
|
*/
|
|
1474
599
|
async _loadEnvironmentVariables() {
|
|
1475
|
-
|
|
1476
|
-
const envMapping = {};
|
|
1477
|
-
Object.entries(this.parameterMatrix).forEach(([param, config]) => {
|
|
1478
|
-
if (config.envVar) {
|
|
1479
|
-
envMapping[config.envVar] = { param, ambient: config.ambientEnvVar === true };
|
|
1480
|
-
}
|
|
1481
|
-
});
|
|
1482
|
-
|
|
1483
|
-
Object.entries(envMapping).forEach(([envVar, { param: configKey, ambient }]) => {
|
|
1484
|
-
const value = process.env[envVar];
|
|
1485
|
-
if (value !== undefined && value !== '' && this._isSourceSupported(configKey, 'envVar')) {
|
|
1486
|
-
this.config[configKey] = this._parseValue(configKey, value);
|
|
1487
|
-
this._recordSource(configKey, this._parseValue(configKey, value), 'env-var');
|
|
1488
|
-
// Track as explicit configuration — unless the env var is ambient
|
|
1489
|
-
// (e.g. AWS_REGION is commonly set in shells as a default, not an override)
|
|
1490
|
-
if (!ambient) {
|
|
1491
|
-
if (!this.explicitConfig) {
|
|
1492
|
-
this.explicitConfig = {};
|
|
1493
|
-
}
|
|
1494
|
-
this.explicitConfig[configKey] = this._parseValue(configKey, value);
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
});
|
|
600
|
+
return this._loader._loadEnvironmentVariables();
|
|
1498
601
|
}
|
|
1499
602
|
|
|
1500
603
|
/**
|
|
@@ -1502,17 +605,7 @@ export default class ConfigManager {
|
|
|
1502
605
|
* @private
|
|
1503
606
|
*/
|
|
1504
607
|
async _loadCliArguments() {
|
|
1505
|
-
|
|
1506
|
-
if (this.args && this.args.length > 0) {
|
|
1507
|
-
this.config.projectName = this.args[0];
|
|
1508
|
-
// Track as explicit configuration
|
|
1509
|
-
if (!this.explicitConfig) {
|
|
1510
|
-
this.explicitConfig = {};
|
|
1511
|
-
}
|
|
1512
|
-
this.explicitConfig.projectName = this.args[0];
|
|
1513
|
-
// Track that project name came from positional argument (for subdirectory creation)
|
|
1514
|
-
this.projectNameFromArgument = true;
|
|
1515
|
-
}
|
|
608
|
+
return this._loader._loadCliArguments();
|
|
1516
609
|
}
|
|
1517
610
|
|
|
1518
611
|
/**
|
|
@@ -1520,95 +613,25 @@ export default class ConfigManager {
|
|
|
1520
613
|
* @private
|
|
1521
614
|
*/
|
|
1522
615
|
async _loadCliOptions() {
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
// Build CLI option mapping from parameter matrix
|
|
1526
|
-
Object.entries(this.parameterMatrix).forEach(([param, config]) => {
|
|
1527
|
-
if (config.cliOption && options[config.cliOption] !== undefined) {
|
|
1528
|
-
this.config[param] = this._parseValue(param, options[config.cliOption]);
|
|
1529
|
-
this._recordSource(param, this._parseValue(param, options[config.cliOption]), 'cli');
|
|
1530
|
-
// Track as explicit configuration
|
|
1531
|
-
if (!this.explicitConfig) {
|
|
1532
|
-
this.explicitConfig = {};
|
|
1533
|
-
}
|
|
1534
|
-
this.explicitConfig[param] = this._parseValue(param, options[config.cliOption]);
|
|
1535
|
-
}
|
|
1536
|
-
});
|
|
1537
|
-
|
|
1538
|
-
// Parse --model-env KEY=VALUE pairs
|
|
1539
|
-
this._parseEnvVarOptions('model-env', 'modelEnvVars');
|
|
1540
|
-
|
|
1541
|
-
// Parse --server-env KEY=VALUE pairs
|
|
1542
|
-
this._parseEnvVarOptions('server-env', 'serverEnvVars');
|
|
616
|
+
return this._loader._loadCliOptions();
|
|
1543
617
|
}
|
|
1544
618
|
|
|
1545
619
|
/**
|
|
1546
620
|
* Normalizes deprecated parameter values to their canonical equivalents.
|
|
1547
|
-
* Prints a deprecation warning when a deprecated value is encountered.
|
|
1548
621
|
* @private
|
|
1549
622
|
*/
|
|
1550
623
|
_normalizeDeprecatedValues() {
|
|
1551
|
-
|
|
1552
|
-
deploymentTarget: {
|
|
1553
|
-
'managed-inference': {
|
|
1554
|
-
canonical: 'realtime-inference',
|
|
1555
|
-
message: '--deployment-target=managed-inference is deprecated, use realtime-inference instead'
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
};
|
|
1559
|
-
|
|
1560
|
-
for (const [param, aliases] of Object.entries(DEPRECATED_VALUES)) {
|
|
1561
|
-
const currentValue = this.config[param];
|
|
1562
|
-
if (currentValue && aliases[currentValue]) {
|
|
1563
|
-
const { canonical, message } = aliases[currentValue];
|
|
1564
|
-
console.log(`\n⚠️ Deprecation: ${message}`);
|
|
1565
|
-
this.config[param] = canonical;
|
|
1566
|
-
// Also update explicit config if it was set there
|
|
1567
|
-
if (this.explicitConfig && this.explicitConfig[param] === currentValue) {
|
|
1568
|
-
this.explicitConfig[param] = canonical;
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
624
|
+
return this._loader._normalizeDeprecatedValues();
|
|
1572
625
|
}
|
|
1573
626
|
|
|
1574
627
|
/**
|
|
1575
628
|
* Parse --model-env or --server-env CLI options into env var collections.
|
|
1576
|
-
*
|
|
1577
|
-
*
|
|
1578
|
-
* @param {string} optionName - CLI option name (e.g., 'model-env')
|
|
1579
|
-
* @param {string} configKey - Config key to store results (e.g., 'modelEnvVars')
|
|
629
|
+
* @param {string} optionName - CLI option name
|
|
630
|
+
* @param {string} configKey - Config key to store results
|
|
1580
631
|
* @private
|
|
1581
632
|
*/
|
|
1582
633
|
_parseEnvVarOptions(optionName, configKey) {
|
|
1583
|
-
|
|
1584
|
-
if (rawValue === undefined || rawValue === null) {
|
|
1585
|
-
return;
|
|
1586
|
-
}
|
|
1587
|
-
|
|
1588
|
-
// Normalize to array (may receive a single string or an array)
|
|
1589
|
-
const values = Array.isArray(rawValue) ? rawValue : [rawValue];
|
|
1590
|
-
|
|
1591
|
-
// Initialize collection if not already present
|
|
1592
|
-
if (!this.config[configKey] || typeof this.config[configKey] !== 'object') {
|
|
1593
|
-
this.config[configKey] = {};
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
for (const entry of values) {
|
|
1597
|
-
if (typeof entry !== 'string' || entry.trim() === '') {
|
|
1598
|
-
continue;
|
|
1599
|
-
}
|
|
1600
|
-
const { key, value } = parseKeyValue(entry);
|
|
1601
|
-
this.config[configKey][key] = value;
|
|
1602
|
-
this._recordSource(`${configKey}.${key}`, value, 'cli');
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
// Track as explicit configuration
|
|
1606
|
-
if (Object.keys(this.config[configKey]).length > 0) {
|
|
1607
|
-
if (!this.explicitConfig) {
|
|
1608
|
-
this.explicitConfig = {};
|
|
1609
|
-
}
|
|
1610
|
-
this.explicitConfig[configKey] = { ...this.config[configKey] };
|
|
1611
|
-
}
|
|
634
|
+
return this._loader._parseEnvVarOptions(optionName, configKey);
|
|
1612
635
|
}
|
|
1613
636
|
|
|
1614
637
|
/**
|
|
@@ -1618,8 +641,7 @@ export default class ConfigManager {
|
|
|
1618
641
|
* @private
|
|
1619
642
|
*/
|
|
1620
643
|
async _queryMcpServers() {
|
|
1621
|
-
|
|
1622
|
-
// via queryMcpServer(). This method is kept for backward compatibility.
|
|
644
|
+
return this._mcpClient._queryMcpServers();
|
|
1623
645
|
}
|
|
1624
646
|
|
|
1625
647
|
/**
|
|
@@ -1630,70 +652,7 @@ export default class ConfigManager {
|
|
|
1630
652
|
* @returns {Promise<{ values: object, choices: object } | null>}
|
|
1631
653
|
*/
|
|
1632
654
|
async queryMcpServer(serverName, context = {}) {
|
|
1633
|
-
|
|
1634
|
-
try {
|
|
1635
|
-
const configPath = path.join(GENERATOR_ROOT, 'config', 'mcp.json');
|
|
1636
|
-
if (!fs.existsSync(configPath)) return null;
|
|
1637
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
1638
|
-
mcpServerConfigs = config.mcpServers;
|
|
1639
|
-
} catch {
|
|
1640
|
-
return null;
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
|
-
if (!mcpServerConfigs || !mcpServerConfigs[serverName]) return null;
|
|
1644
|
-
|
|
1645
|
-
const smart = this.options.smart === true;
|
|
1646
|
-
const discover = this.options.discover !== false;
|
|
1647
|
-
const serverConfig = mcpServerConfigs[serverName];
|
|
1648
|
-
|
|
1649
|
-
// Build a custom McpClient that passes context through
|
|
1650
|
-
const client = new McpClient(serverConfig, {
|
|
1651
|
-
timeout: 15000,
|
|
1652
|
-
parameterMatrix: this.parameterMatrix,
|
|
1653
|
-
smart,
|
|
1654
|
-
discover
|
|
1655
|
-
});
|
|
1656
|
-
|
|
1657
|
-
// Override the _buildContext to merge our search context
|
|
1658
|
-
const origBuildContext = client._buildContext.bind(client);
|
|
1659
|
-
client._buildContext = () => ({ ...origBuildContext(), ...context });
|
|
1660
|
-
|
|
1661
|
-
try {
|
|
1662
|
-
const result = await client.query();
|
|
1663
|
-
await client.close();
|
|
1664
|
-
|
|
1665
|
-
if (!result) {
|
|
1666
|
-
const diag = client.getDiagnosticMessage();
|
|
1667
|
-
if (diag) console.log(` ⚠️ ${serverName}: ${diag}`);
|
|
1668
|
-
return null;
|
|
1669
|
-
}
|
|
1670
|
-
|
|
1671
|
-
// Store values
|
|
1672
|
-
for (const [param, value] of Object.entries(result.values || {})) {
|
|
1673
|
-
const paramConfig = this.parameterMatrix[param];
|
|
1674
|
-
if (paramConfig && paramConfig.valueSpace === 'unbounded' && paramConfig.mcp === true) {
|
|
1675
|
-
this.mcpSources[param] = {
|
|
1676
|
-
server: serverName,
|
|
1677
|
-
value,
|
|
1678
|
-
timestamp: new Date().toISOString()
|
|
1679
|
-
};
|
|
1680
|
-
}
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
// Store choices
|
|
1684
|
-
for (const [param, choices] of Object.entries(result.choices || {})) {
|
|
1685
|
-
const paramConfig = this.parameterMatrix[param];
|
|
1686
|
-
if (paramConfig && paramConfig.valueSpace === 'unbounded' && paramConfig.mcp === true && Array.isArray(choices)) {
|
|
1687
|
-
this.mcpChoices[param] = choices;
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
|
|
1691
|
-
return result;
|
|
1692
|
-
} catch (err) {
|
|
1693
|
-
await client.close().catch(() => {});
|
|
1694
|
-
console.log(` ⚠️ ${serverName}: ${err.message}`);
|
|
1695
|
-
return null;
|
|
1696
|
-
}
|
|
655
|
+
return this._mcpClient.queryMcpServer(serverName, context);
|
|
1697
656
|
}
|
|
1698
657
|
|
|
1699
658
|
/**
|
|
@@ -1701,14 +660,7 @@ export default class ConfigManager {
|
|
|
1701
660
|
* @returns {string[]}
|
|
1702
661
|
*/
|
|
1703
662
|
getMcpServerNames() {
|
|
1704
|
-
|
|
1705
|
-
const configPath = path.join(GENERATOR_ROOT, 'config', 'mcp.json');
|
|
1706
|
-
if (!fs.existsSync(configPath)) return [];
|
|
1707
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
1708
|
-
return Object.keys(config.mcpServers || {});
|
|
1709
|
-
} catch {
|
|
1710
|
-
return [];
|
|
1711
|
-
}
|
|
663
|
+
return this._mcpClient.getMcpServerNames();
|
|
1712
664
|
}
|
|
1713
665
|
|
|
1714
666
|
/**
|
|
@@ -1766,377 +718,52 @@ export default class ConfigManager {
|
|
|
1766
718
|
}
|
|
1767
719
|
return true;
|
|
1768
720
|
});
|
|
721
|
+
|
|
722
|
+
// Also require key parameters that are needed for generation
|
|
723
|
+
const essentialParams = ['projectName', 'instanceType'];
|
|
724
|
+
const allRequired = [...new Set([...requiredForConfig, ...essentialParams])];
|
|
1769
725
|
|
|
1770
|
-
return
|
|
726
|
+
return allRequired.every(key =>
|
|
1771
727
|
this.config[key] !== undefined && this.config[key] !== null
|
|
1772
728
|
);
|
|
1773
729
|
}
|
|
1774
730
|
|
|
1775
731
|
/**
|
|
1776
732
|
* Validates the current configuration against the parameter matrix
|
|
1777
|
-
* Only reports errors for parameters that cannot be resolved through prompting or auto-generation
|
|
1778
733
|
* @returns {Array} Array of validation errors
|
|
1779
734
|
*/
|
|
1780
735
|
validateConfiguration() {
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
// Old-format deployment-config migration messages
|
|
1784
|
-
const oldFormatMigration = {
|
|
1785
|
-
'sklearn-flask': 'Use --deployment-config=http-flask --engine=sklearn instead',
|
|
1786
|
-
'sklearn-fastapi': 'Use --deployment-config=http-fastapi --engine=sklearn instead',
|
|
1787
|
-
'xgboost-flask': 'Use --deployment-config=http-flask --engine=xgboost instead',
|
|
1788
|
-
'xgboost-fastapi': 'Use --deployment-config=http-fastapi --engine=xgboost instead',
|
|
1789
|
-
'tensorflow-flask': 'Use --deployment-config=http-flask --engine=tensorflow instead',
|
|
1790
|
-
'tensorflow-fastapi': 'Use --deployment-config=http-fastapi --engine=tensorflow instead'
|
|
1791
|
-
};
|
|
1792
|
-
|
|
1793
|
-
// Validate deployment-config
|
|
1794
|
-
if (this.config.deploymentConfig) {
|
|
1795
|
-
const migrationMsg = oldFormatMigration[this.config.deploymentConfig];
|
|
1796
|
-
if (migrationMsg) {
|
|
1797
|
-
errors.push(`Unsupported deployment-config: ${this.config.deploymentConfig}. This value has been replaced. ${migrationMsg}`);
|
|
1798
|
-
} else if (!this.deploymentConfigResolver.isValid(this.config.deploymentConfig)) {
|
|
1799
|
-
const valid = this.deploymentConfigResolver.getAllConfigs().join(', ');
|
|
1800
|
-
errors.push(`Unsupported deployment-config: ${this.config.deploymentConfig}. Valid configs: ${valid}`);
|
|
1801
|
-
}
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
// Validate engine (only valid for http architecture)
|
|
1805
|
-
if (this.config.engine) {
|
|
1806
|
-
const validEngines = ['sklearn', 'xgboost', 'tensorflow'];
|
|
1807
|
-
if (!validEngines.includes(this.config.engine)) {
|
|
1808
|
-
errors.push(`Unsupported engine: ${this.config.engine}. Supported: ${validEngines.join(', ')}`);
|
|
1809
|
-
}
|
|
1810
|
-
}
|
|
1811
|
-
|
|
1812
|
-
// Validate model format based on architecture/engine
|
|
1813
|
-
if (this.config.modelFormat && this.config.deploymentConfig) {
|
|
1814
|
-
try {
|
|
1815
|
-
const parts = this.deploymentConfigResolver.decompose(this.config.deploymentConfig);
|
|
1816
|
-
if (parts.architecture === 'http') {
|
|
1817
|
-
const engine = this.config.engine || parts.engine;
|
|
1818
|
-
if (engine) {
|
|
1819
|
-
const supportedOptions = this._getSupportedOptions();
|
|
1820
|
-
const validFormats = supportedOptions.modelFormats[engine] || [];
|
|
1821
|
-
if (validFormats.length > 0 && !validFormats.includes(this.config.modelFormat)) {
|
|
1822
|
-
errors.push(`Unsupported model format '${this.config.modelFormat}' for engine '${engine}'. Supported: ${validFormats.join(', ')}`);
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
} catch {
|
|
1827
|
-
// deploymentConfig already flagged as invalid above
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1830
|
-
|
|
1831
|
-
// Validate mutual exclusion: plaintext token and ARN cannot both be set
|
|
1832
|
-
if (this.config.hfToken && this.config.hfTokenArn) {
|
|
1833
|
-
errors.push('Cannot specify both --hf-token and --hf-token-arn. Use one or the other.');
|
|
1834
|
-
}
|
|
1835
|
-
if (this.config.ngcTokenArn) {
|
|
1836
|
-
// Check ngcToken from CLI options (Commander converts --ngc-token to ngcToken)
|
|
1837
|
-
const ngcTokenFromCli = this.options['ngc-token'];
|
|
1838
|
-
if (ngcTokenFromCli) {
|
|
1839
|
-
errors.push('Cannot specify both --ngc-token and --ngc-token-arn. Use one or the other.');
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
// Validate AWS Role ARN format if provided
|
|
1844
|
-
if (this.config.awsRoleArn) {
|
|
1845
|
-
try {
|
|
1846
|
-
this._isValidArn(this.config.awsRoleArn);
|
|
1847
|
-
} catch (error) {
|
|
1848
|
-
if (error instanceof ValidationError) {
|
|
1849
|
-
errors.push(error.message);
|
|
1850
|
-
} else {
|
|
1851
|
-
errors.push(`Invalid AWS Role ARN format: ${this.config.awsRoleArn}. Expected format: arn:aws:iam::123456789012:role/RoleName`);
|
|
1852
|
-
}
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
|
|
1856
|
-
// Validate build target (renamed from deployTarget)
|
|
1857
|
-
const buildTarget = this.config.buildTarget || this.config.deployTarget;
|
|
1858
|
-
if (buildTarget && !this._getSupportedOptions().buildTargets.includes(buildTarget)) {
|
|
1859
|
-
errors.push(`Unsupported build target: ${buildTarget}. Supported targets: ${this._getSupportedOptions().buildTargets.join(', ')}`);
|
|
1860
|
-
}
|
|
1861
|
-
|
|
1862
|
-
// Validate CodeBuild compute type
|
|
1863
|
-
if (this.config.codebuildComputeType && !this._getSupportedOptions().codebuildComputeTypes.includes(this.config.codebuildComputeType)) {
|
|
1864
|
-
errors.push(`Unsupported CodeBuild compute type: ${this.config.codebuildComputeType}. Supported types: ${this._getSupportedOptions().codebuildComputeTypes.join(', ')}`);
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
|
-
// Validate CodeBuild project name format
|
|
1868
|
-
if (this.config.codebuildProjectName) {
|
|
1869
|
-
const projectNamePattern = /^[a-zA-Z0-9][a-zA-Z0-9\-_]{1,254}$/;
|
|
1870
|
-
if (!projectNamePattern.test(this.config.codebuildProjectName)) {
|
|
1871
|
-
errors.push(`Invalid CodeBuild project name: ${this.config.codebuildProjectName}. Project names must be 2-255 characters, start with a letter or number, and contain only letters, numbers, hyphens, and underscores.`);
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
// Validate model package ARN format if provided
|
|
1876
|
-
if (this.config.modelPackageArn) {
|
|
1877
|
-
const modelPackageArnPattern = /^arn:aws:sagemaker:[a-z0-9-]+:\d{12}:model-package\/[a-zA-Z0-9]([a-zA-Z0-9-])*\/\d+$/;
|
|
1878
|
-
if (!modelPackageArnPattern.test(this.config.modelPackageArn)) {
|
|
1879
|
-
errors.push('❌ Invalid model package ARN format. Expected: arn:aws:sagemaker:<region>:<account>:model-package/<name>/<version>');
|
|
1880
|
-
}
|
|
1881
|
-
}
|
|
1882
|
-
|
|
1883
|
-
// Only validate required parameters if we're skipping prompts
|
|
1884
|
-
// If prompts are available, missing parameters can be collected later
|
|
1885
|
-
if (this.skipPrompts) {
|
|
1886
|
-
Object.entries(this.parameterMatrix).forEach(([param, config]) => {
|
|
1887
|
-
if (config.required &&
|
|
1888
|
-
(this.config[param] === null || this.config[param] === undefined)) {
|
|
1889
|
-
|
|
1890
|
-
// Special case: modelFormat is not required for transformers/triton/diffusors
|
|
1891
|
-
if (param === 'modelFormat') {
|
|
1892
|
-
try {
|
|
1893
|
-
const parts = this.deploymentConfigResolver.decompose(this.config.deploymentConfig);
|
|
1894
|
-
if (parts.architecture === 'transformers' || parts.architecture === 'triton' || parts.architecture === 'diffusors') {
|
|
1895
|
-
return;
|
|
1896
|
-
}
|
|
1897
|
-
} catch {
|
|
1898
|
-
// If deploymentConfig is invalid, skip this check
|
|
1899
|
-
return;
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
|
-
// Only error for promptable required parameters that have no default and can't be auto-generated
|
|
1904
|
-
if (config.promptable && config.default === null && !this._canAutoGenerate(param)) {
|
|
1905
|
-
errors.push(`Required parameter '${param}' is missing and prompts are disabled`);
|
|
1906
|
-
}
|
|
1907
|
-
}
|
|
1908
|
-
});
|
|
1909
|
-
|
|
1910
|
-
// Validate that modelName is provided for diffusors architecture
|
|
1911
|
-
if (this.config.deploymentConfig) {
|
|
1912
|
-
try {
|
|
1913
|
-
const parts = this.deploymentConfigResolver.decompose(this.config.deploymentConfig);
|
|
1914
|
-
if (parts.architecture === 'diffusors') {
|
|
1915
|
-
const explicitModelName = this.explicitConfig && this.explicitConfig.modelName;
|
|
1916
|
-
if (!explicitModelName) {
|
|
1917
|
-
errors.push('Model name is required for diffusors architecture. Use --model-name to specify a HuggingFace diffusion model.');
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
} catch {
|
|
1921
|
-
// deploymentConfig already flagged as invalid above
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
}
|
|
1925
|
-
|
|
1926
|
-
// Validate schema-validated parameters (endpoint, iC)
|
|
1927
|
-
Object.entries(this.parameterMatrix).forEach(([param, config]) => {
|
|
1928
|
-
if (config.schemaValidated && this.config[param] !== null && this.config[param] !== undefined) {
|
|
1929
|
-
const result = this.schemaValidator.validate(param, this.config[param], this.config.deploymentTarget);
|
|
1930
|
-
if (!result.valid) {
|
|
1931
|
-
errors.push(result.error);
|
|
1932
|
-
}
|
|
1933
|
-
}
|
|
1934
|
-
});
|
|
1935
|
-
|
|
1936
|
-
return errors;
|
|
736
|
+
return this._validator.validateConfiguration();
|
|
1937
737
|
}
|
|
1938
738
|
|
|
1939
739
|
/**
|
|
1940
740
|
* Validates required parameters before file generation
|
|
1941
|
-
* This is called after all configuration sources have been processed and prompting is complete
|
|
1942
741
|
* @param {Object} finalConfig - The complete configuration object
|
|
1943
742
|
* @returns {Array} Array of validation errors for missing required parameters
|
|
1944
743
|
*/
|
|
1945
744
|
validateRequiredParameters(finalConfig) {
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
// First, validate individual parameter values
|
|
1949
|
-
Object.entries(finalConfig).forEach(([param, value]) => {
|
|
1950
|
-
if (value !== null && value !== undefined && value !== '') {
|
|
1951
|
-
try {
|
|
1952
|
-
this._validateParameterValue(param, value, finalConfig);
|
|
1953
|
-
} catch (error) {
|
|
1954
|
-
if (error instanceof ValidationError) {
|
|
1955
|
-
errors.push(error.message);
|
|
1956
|
-
} else {
|
|
1957
|
-
errors.push(`Invalid value for parameter '${param}': ${error.message}`);
|
|
1958
|
-
}
|
|
1959
|
-
}
|
|
1960
|
-
}
|
|
1961
|
-
});
|
|
1962
|
-
|
|
1963
|
-
// Then, validate required parameters are present
|
|
1964
|
-
Object.entries(this.parameterMatrix).forEach(([param, config]) => {
|
|
1965
|
-
if (config.required) {
|
|
1966
|
-
const value = finalConfig[param];
|
|
1967
|
-
const isEmpty = value === null || value === undefined || value === '';
|
|
1968
|
-
|
|
1969
|
-
// Special case: modelFormat is not required for transformers/triton/diffusors/marketplace
|
|
1970
|
-
if (param === 'modelFormat' && (finalConfig.architecture === 'transformers' || finalConfig.architecture === 'triton' || finalConfig.architecture === 'diffusors' || finalConfig.architecture === 'marketplace')) {
|
|
1971
|
-
return; // Skip validation
|
|
1972
|
-
}
|
|
1973
|
-
|
|
1974
|
-
// Special case: marketplace projects don't need container-related parameters
|
|
1975
|
-
if (finalConfig.architecture === 'marketplace' && (param === 'includeSampleModel' || param === 'buildTarget')) {
|
|
1976
|
-
return; // Skip validation — marketplace has no container to build
|
|
1977
|
-
}
|
|
1978
|
-
|
|
1979
|
-
// Special case: instanceType is not required for hyperpod-eks
|
|
1980
|
-
// when not provided (backward compatibility) — but it IS prompted now
|
|
1981
|
-
// so it should normally be present
|
|
1982
|
-
if (param === 'instanceType' && finalConfig.deploymentTarget === 'hyperpod-eks' && !finalConfig.instanceType) {
|
|
1983
|
-
return; // Skip validation only if truly missing for backward compat
|
|
1984
|
-
}
|
|
1985
|
-
|
|
1986
|
-
// Special case: instanceType is not required when attaching to an existing endpoint
|
|
1987
|
-
// The instance type is inherited from the existing endpoint configuration
|
|
1988
|
-
if (param === 'instanceType' && finalConfig.existingEndpointName) {
|
|
1989
|
-
return; // Skip validation — instance is inherited from existing endpoint
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
|
-
if (isEmpty) {
|
|
1993
|
-
if (config.promptable) {
|
|
1994
|
-
// Promptable required parameter is missing - this should not happen after prompting
|
|
1995
|
-
errors.push(`Required parameter '${param}' is missing. This parameter is required for ${finalConfig.architecture || 'the selected'} architecture.`);
|
|
1996
|
-
} else {
|
|
1997
|
-
// Non-promptable required parameter is missing - this is a configuration error
|
|
1998
|
-
errors.push(`Required non-promptable parameter '${param}' is missing. This parameter must be provided through CLI options, environment variables, or configuration files.`);
|
|
1999
|
-
}
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
});
|
|
2003
|
-
|
|
2004
|
-
// Finally, validate parameter combinations and dependencies
|
|
2005
|
-
const combinationErrors = this._validateParameterCombinations(finalConfig);
|
|
2006
|
-
errors.push(...combinationErrors);
|
|
2007
|
-
|
|
2008
|
-
return errors;
|
|
745
|
+
return this._validator.validateRequiredParameters(finalConfig);
|
|
2009
746
|
}
|
|
2010
747
|
|
|
2011
748
|
/**
|
|
2012
|
-
* Validates parameter combinations and dependencies
|
|
2013
|
-
* @param {Object} config - The configuration object to validate
|
|
2014
|
-
* @returns {Array} Array of validation errors for invalid combinations
|
|
2015
749
|
* @private
|
|
2016
750
|
*/
|
|
2017
751
|
_validateParameterCombinations(config) {
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
// Additional combination validations that aren't covered by individual parameter validation
|
|
2021
|
-
// For example, complex business rules that involve multiple parameters
|
|
2022
|
-
|
|
2023
|
-
// Validate that transformers architecture has sample model disabled
|
|
2024
|
-
if (config.architecture === 'transformers' && config.includeSampleModel === true) {
|
|
2025
|
-
errors.push(`Architecture '${config.architecture}' does not support sample models. The 'includeSampleModel' parameter will be automatically set to false.`);
|
|
2026
|
-
}
|
|
2027
|
-
// Validate that diffusors architecture has sample model disabled
|
|
2028
|
-
if (config.architecture === 'diffusors' && config.includeSampleModel === true) {
|
|
2029
|
-
errors.push(`Architecture '${config.architecture}' does not support sample models. The 'includeSampleModel' parameter will be automatically set to false.`);
|
|
2030
|
-
}
|
|
2031
|
-
// Validate that ineligible Triton backends have sample model disabled
|
|
2032
|
-
if (config.architecture === 'triton' && config.includeSampleModel === true) {
|
|
2033
|
-
const backendMeta = tritonBackends[config.backend];
|
|
2034
|
-
if (!backendMeta || !backendMeta.supportsSampleModel) {
|
|
2035
|
-
errors.push(`Triton backend '${config.backend}' does not support sample models. The 'includeSampleModel' parameter will be automatically set to false.`);
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
|
|
2039
|
-
return errors;
|
|
752
|
+
return this._validator._validateParameterCombinations(config);
|
|
2040
753
|
}
|
|
2041
754
|
|
|
2042
755
|
/**
|
|
2043
|
-
* Checks if a parameter can be auto-generated when missing
|
|
2044
|
-
* @param {string} param - Parameter name
|
|
2045
|
-
* @returns {boolean} True if parameter can be auto-generated
|
|
2046
756
|
* @private
|
|
2047
757
|
*/
|
|
2048
758
|
_canAutoGenerate(param) {
|
|
2049
|
-
|
|
2050
|
-
const autoGeneratable = [
|
|
2051
|
-
'modelFormat', // Can be inferred from engine
|
|
2052
|
-
'includeSampleModel', // Has default
|
|
2053
|
-
'includeTesting', // Has default
|
|
2054
|
-
'instanceType' // Has default
|
|
2055
|
-
];
|
|
2056
|
-
|
|
2057
|
-
return autoGeneratable.includes(param);
|
|
759
|
+
return this._validator._canAutoGenerate(param);
|
|
2058
760
|
}
|
|
2059
761
|
|
|
2060
762
|
/**
|
|
2061
|
-
* Fills auto-prompt defaults for parameters that have sensible defaults
|
|
2062
|
-
* or can be inferred from the current config. Promotes these into
|
|
2063
|
-
* explicitConfig so the wizard skips them.
|
|
2064
|
-
*
|
|
2065
|
-
* Only fills parameters that:
|
|
2066
|
-
* - Have a non-null default in the parameter matrix, OR
|
|
2067
|
-
* - Can be auto-generated (instanceType, modelFormat, etc.)
|
|
2068
|
-
*
|
|
2069
|
-
* Does NOT fill parameters that are truly ambiguous and need user input
|
|
2070
|
-
* (e.g., deploymentConfig when not provided).
|
|
2071
763
|
* @private
|
|
2072
764
|
*/
|
|
2073
765
|
_fillAutoPromptDefaults() {
|
|
2074
|
-
|
|
2075
|
-
this.explicitConfig = {};
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2078
|
-
// Derive architecture from deploymentConfig if available
|
|
2079
|
-
let architecture = this.config.architecture;
|
|
2080
|
-
if (!architecture && this.config.deploymentConfig) {
|
|
2081
|
-
try {
|
|
2082
|
-
const parts = this.deploymentConfigResolver.decompose(this.config.deploymentConfig);
|
|
2083
|
-
architecture = parts.architecture;
|
|
2084
|
-
this.config.architecture = parts.architecture;
|
|
2085
|
-
this.config.backend = parts.backend;
|
|
2086
|
-
this.config.engine = parts.engine;
|
|
2087
|
-
} catch {
|
|
2088
|
-
// Invalid deploymentConfig — will be caught by validation
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
|
-
|
|
2092
|
-
Object.entries(this.parameterMatrix).forEach(([param, config]) => {
|
|
2093
|
-
// Skip if already explicitly set
|
|
2094
|
-
if (this.explicitConfig[param] !== undefined && this.explicitConfig[param] !== null) {
|
|
2095
|
-
return;
|
|
2096
|
-
}
|
|
2097
|
-
|
|
2098
|
-
// For optional parameters: mark them as explicit (with null) so the wizard skips them.
|
|
2099
|
-
// The downstream template logic handles defaults for optional params.
|
|
2100
|
-
if (!config.required) {
|
|
2101
|
-
// Don't override if there's already a value in config
|
|
2102
|
-
if (this.config[param] !== undefined && this.config[param] !== null) {
|
|
2103
|
-
this.explicitConfig[param] = this.config[param];
|
|
2104
|
-
} else if (config.default !== null && config.default !== undefined) {
|
|
2105
|
-
this.config[param] = config.default;
|
|
2106
|
-
this.explicitConfig[param] = config.default;
|
|
2107
|
-
}
|
|
2108
|
-
return;
|
|
2109
|
-
}
|
|
2110
|
-
|
|
2111
|
-
// For required parameters: fill auto-generatable values
|
|
2112
|
-
if (this.config[param] === undefined || this.config[param] === null) {
|
|
2113
|
-
if (param === 'instanceType') {
|
|
2114
|
-
// If instance-sizer is configured and model is known, defer to sizer
|
|
2115
|
-
// The sizer query happens in PromptRunner after model is selected
|
|
2116
|
-
// For now, set a heuristic default that may be overridden by the sizer
|
|
2117
|
-
const arch = architecture || 'http';
|
|
2118
|
-
this.config[param] = arch === 'http' ? 'ml.m5.large' : 'ml.g5.xlarge';
|
|
2119
|
-
} else if (param === 'modelFormat') {
|
|
2120
|
-
if (architecture === 'transformers' || architecture === 'triton' || architecture === 'diffusors') {
|
|
2121
|
-
return; // Not needed for these architectures
|
|
2122
|
-
}
|
|
2123
|
-
const engine = this.config.engine || 'sklearn';
|
|
2124
|
-
const formatMap = { sklearn: 'pkl', xgboost: 'json', tensorflow: 'keras' };
|
|
2125
|
-
this.config[param] = formatMap[engine] || 'pkl';
|
|
2126
|
-
} else if (param === 'projectName') {
|
|
2127
|
-
this.config[param] = this._generateProjectName(architecture);
|
|
2128
|
-
} else {
|
|
2129
|
-
return; // Can't fill — leave for prompting
|
|
2130
|
-
}
|
|
2131
|
-
}
|
|
2132
|
-
|
|
2133
|
-
// Promote non-null values to explicitConfig so the wizard skips them
|
|
2134
|
-
if (this.config[param] !== undefined && this.config[param] !== null) {
|
|
2135
|
-
if (config.default !== null || this._canAutoGenerate(param)) {
|
|
2136
|
-
this.explicitConfig[param] = this.config[param];
|
|
2137
|
-
}
|
|
2138
|
-
}
|
|
2139
|
-
});
|
|
766
|
+
return this._validator._fillAutoPromptDefaults();
|
|
2140
767
|
}
|
|
2141
768
|
|
|
2142
769
|
/**
|
|
@@ -2144,49 +771,15 @@ export default class ConfigManager {
|
|
|
2144
771
|
* @returns {boolean}
|
|
2145
772
|
*/
|
|
2146
773
|
isAutoPrompt() {
|
|
2147
|
-
return this.
|
|
774
|
+
return this._validator.isAutoPrompt();
|
|
2148
775
|
}
|
|
2149
776
|
|
|
2150
777
|
/**
|
|
2151
|
-
* Gets the list of required parameters that are truly missing
|
|
2152
|
-
* auto-generated or defaulted. Used by auto-prompt mode to determine which
|
|
2153
|
-
* specific prompts to show.
|
|
2154
|
-
*
|
|
778
|
+
* Gets the list of required parameters that are truly missing.
|
|
2155
779
|
* @returns {string[]} Array of parameter names that need prompting
|
|
2156
780
|
*/
|
|
2157
781
|
getMissingRequiredParameters() {
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
Object.entries(this.parameterMatrix).forEach(([param, config]) => {
|
|
2161
|
-
if (!config.required || !config.promptable) return;
|
|
2162
|
-
|
|
2163
|
-
const value = this.config[param];
|
|
2164
|
-
const hasValue = value !== undefined && value !== null;
|
|
2165
|
-
|
|
2166
|
-
if (hasValue) return;
|
|
2167
|
-
|
|
2168
|
-
// Special case: modelFormat is not required for transformers/triton/diffusors
|
|
2169
|
-
if (param === 'modelFormat') {
|
|
2170
|
-
const architecture = this.config.architecture;
|
|
2171
|
-
if (architecture === 'transformers' || architecture === 'triton' || architecture === 'diffusors') {
|
|
2172
|
-
return;
|
|
2173
|
-
}
|
|
2174
|
-
// Can be inferred from engine
|
|
2175
|
-
if (this.config.engine || this.config.deploymentConfig) {
|
|
2176
|
-
return;
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
|
-
// Skip params that can be auto-generated
|
|
2181
|
-
if (this._canAutoGenerate(param)) return;
|
|
2182
|
-
|
|
2183
|
-
// Skip params that have a non-null default
|
|
2184
|
-
if (config.default !== null && config.default !== undefined) return;
|
|
2185
|
-
|
|
2186
|
-
missing.push(param);
|
|
2187
|
-
});
|
|
2188
|
-
|
|
2189
|
-
return missing;
|
|
782
|
+
return this._validator.getMissingRequiredParameters();
|
|
2190
783
|
}
|
|
2191
784
|
|
|
2192
785
|
/**
|
|
@@ -2254,243 +847,31 @@ export default class ConfigManager {
|
|
|
2254
847
|
}
|
|
2255
848
|
|
|
2256
849
|
/**
|
|
2257
|
-
* Validates a single parameter value
|
|
2258
|
-
* @param {string} parameter - Parameter name
|
|
2259
|
-
* @param {*} value - Parameter value
|
|
2260
|
-
* @param {Object} context - Additional context (e.g., other parameter values)
|
|
2261
|
-
* @throws {ValidationError} If parameter value is invalid
|
|
2262
850
|
* @private
|
|
2263
851
|
*/
|
|
2264
852
|
_validateParameterValue(parameter, value, context = {}) {
|
|
2265
|
-
|
|
2266
|
-
// Skip deprecated params — they have relaxed validation handled by the switch below
|
|
2267
|
-
const schemaRule = validationRules[parameter];
|
|
2268
|
-
if (schemaRule && value !== null && value !== undefined) {
|
|
2269
|
-
// Don't apply strict enum validation to internally-derived values
|
|
2270
|
-
// The switch statement below handles context-dependent validation
|
|
2271
|
-
const skipSchemaValidation = ['framework', 'modelServer', 'deploymentConfig'].includes(parameter);
|
|
2272
|
-
if (!skipSchemaValidation) {
|
|
2273
|
-
const error = schemaRule(value);
|
|
2274
|
-
if (error) {
|
|
2275
|
-
throw new ValidationError(error, parameter, value);
|
|
2276
|
-
}
|
|
2277
|
-
}
|
|
2278
|
-
}
|
|
2279
|
-
|
|
2280
|
-
// Second pass: context-dependent validations that require runtime state
|
|
2281
|
-
const supportedOptions = this._getSupportedOptions();
|
|
2282
|
-
|
|
2283
|
-
switch (parameter) {
|
|
2284
|
-
case 'deploymentConfig':
|
|
2285
|
-
if (value) {
|
|
2286
|
-
// Check for old-format configs with migration messages
|
|
2287
|
-
const oldFormatMigration = {
|
|
2288
|
-
'sklearn-flask': 'Use --deployment-config=http-flask --engine=sklearn instead',
|
|
2289
|
-
'sklearn-fastapi': 'Use --deployment-config=http-fastapi --engine=sklearn instead',
|
|
2290
|
-
'xgboost-flask': 'Use --deployment-config=http-flask --engine=xgboost instead',
|
|
2291
|
-
'xgboost-fastapi': 'Use --deployment-config=http-fastapi --engine=xgboost instead',
|
|
2292
|
-
'tensorflow-flask': 'Use --deployment-config=http-flask --engine=tensorflow instead',
|
|
2293
|
-
'tensorflow-fastapi': 'Use --deployment-config=http-fastapi --engine=tensorflow instead'
|
|
2294
|
-
};
|
|
2295
|
-
const migrationMsg = oldFormatMigration[value];
|
|
2296
|
-
if (migrationMsg) {
|
|
2297
|
-
throw new ValidationError(
|
|
2298
|
-
`Unsupported deployment-config: ${value}. This value has been replaced. ${migrationMsg}`,
|
|
2299
|
-
parameter,
|
|
2300
|
-
value
|
|
2301
|
-
);
|
|
2302
|
-
}
|
|
2303
|
-
if (!this.deploymentConfigResolver.isValid(value)) {
|
|
2304
|
-
const valid = this.deploymentConfigResolver.getAllConfigs().join(', ');
|
|
2305
|
-
throw new ValidationError(
|
|
2306
|
-
`Unsupported deployment-config: ${value}. Valid configs: ${valid}`,
|
|
2307
|
-
parameter,
|
|
2308
|
-
value
|
|
2309
|
-
);
|
|
2310
|
-
}
|
|
2311
|
-
}
|
|
2312
|
-
break;
|
|
2313
|
-
|
|
2314
|
-
case 'engine':
|
|
2315
|
-
if (value) {
|
|
2316
|
-
const validEngines = ['sklearn', 'xgboost', 'tensorflow'];
|
|
2317
|
-
if (!validEngines.includes(value)) {
|
|
2318
|
-
throw new ValidationError(
|
|
2319
|
-
`Unsupported engine: ${value}. Supported: ${validEngines.join(', ')}`,
|
|
2320
|
-
parameter,
|
|
2321
|
-
value
|
|
2322
|
-
);
|
|
2323
|
-
}
|
|
2324
|
-
}
|
|
2325
|
-
break;
|
|
2326
|
-
|
|
2327
|
-
case 'modelFormat':
|
|
2328
|
-
if (value && context.architecture === 'http' && context.engine) {
|
|
2329
|
-
const validFormats = supportedOptions.modelFormats[context.engine] || [];
|
|
2330
|
-
if (validFormats.length > 0 && !validFormats.includes(value)) {
|
|
2331
|
-
throw new ValidationError(
|
|
2332
|
-
`Model format '${value}' is not compatible with engine '${context.engine}'. Compatible formats: ${validFormats.join(', ')}`,
|
|
2333
|
-
parameter,
|
|
2334
|
-
value
|
|
2335
|
-
);
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
break;
|
|
2339
|
-
|
|
2340
|
-
case 'instanceType':
|
|
2341
|
-
if (value) {
|
|
2342
|
-
// Validate AWS SageMaker instance type format
|
|
2343
|
-
const instancePattern = /^ml\.[a-z0-9]+\.(nano|micro|small|medium|large|xlarge|[0-9]+xlarge)$/;
|
|
2344
|
-
if (!instancePattern.test(value)) {
|
|
2345
|
-
throw new ValidationError(
|
|
2346
|
-
`Invalid instance type format: ${value}. Expected format: ml.{family}.{size} (e.g., ml.m5.large, ml.g4dn.xlarge)`,
|
|
2347
|
-
parameter,
|
|
2348
|
-
value
|
|
2349
|
-
);
|
|
2350
|
-
}
|
|
2351
|
-
// Warn about CPU instances for transformers/triton (but don't block)
|
|
2352
|
-
if (context.architecture === 'transformers' || context.architecture === 'triton') {
|
|
2353
|
-
const cpuFamilies = ['t2', 't3', 't3a', 't4g', 'm4', 'm5', 'm5a', 'm5ad', 'm5d', 'm5dn', 'm5n', 'm5zn', 'm6a', 'm6g', 'm6gd', 'm6i', 'm6id', 'm6idn', 'm6in', 'c4', 'c5', 'c5a', 'c5ad', 'c5d', 'c5n', 'c6a', 'c6g', 'c6gd', 'c6gn', 'c6i', 'c6id', 'c6in', 'r4', 'r5', 'r5a', 'r5ad', 'r5b', 'r5d', 'r5dn', 'r5n', 'r6a', 'r6g', 'r6gd', 'r6i', 'r6id', 'r6idn', 'r6in'];
|
|
2354
|
-
const instanceFamily = value.split('.')[1];
|
|
2355
|
-
if (cpuFamilies.includes(instanceFamily)) {
|
|
2356
|
-
console.warn(`⚠️ Warning: Using CPU instance ${value} with ${context.architecture} architecture. GPU instances are recommended for better performance.`);
|
|
2357
|
-
}
|
|
2358
|
-
}
|
|
2359
|
-
}
|
|
2360
|
-
break;
|
|
2361
|
-
|
|
2362
|
-
case 'awsRegion':
|
|
2363
|
-
if (value && !supportedOptions.awsRegions.includes(value)) {
|
|
2364
|
-
throw new ValidationError(
|
|
2365
|
-
`Unsupported AWS region: ${value}. Supported regions: ${supportedOptions.awsRegions.join(', ')}`,
|
|
2366
|
-
parameter,
|
|
2367
|
-
value
|
|
2368
|
-
);
|
|
2369
|
-
}
|
|
2370
|
-
break;
|
|
2371
|
-
|
|
2372
|
-
case 'awsRoleArn':
|
|
2373
|
-
if (value) {
|
|
2374
|
-
this._isValidArn(value);
|
|
2375
|
-
}
|
|
2376
|
-
break;
|
|
2377
|
-
|
|
2378
|
-
case 'buildTarget':
|
|
2379
|
-
case 'deployTarget':
|
|
2380
|
-
if (value && !supportedOptions.buildTargets.includes(value)) {
|
|
2381
|
-
throw new ValidationError(
|
|
2382
|
-
`Unsupported build target: ${value}. Supported targets: ${supportedOptions.buildTargets.join(', ')}`,
|
|
2383
|
-
parameter,
|
|
2384
|
-
value
|
|
2385
|
-
);
|
|
2386
|
-
}
|
|
2387
|
-
break;
|
|
2388
|
-
|
|
2389
|
-
case 'codebuildComputeType':
|
|
2390
|
-
if (value && !supportedOptions.codebuildComputeTypes.includes(value)) {
|
|
2391
|
-
throw new ValidationError(
|
|
2392
|
-
`Unsupported CodeBuild compute type: ${value}. Supported types: ${supportedOptions.codebuildComputeTypes.join(', ')}`,
|
|
2393
|
-
parameter,
|
|
2394
|
-
value
|
|
2395
|
-
);
|
|
2396
|
-
}
|
|
2397
|
-
break;
|
|
2398
|
-
|
|
2399
|
-
case 'codebuildProjectName':
|
|
2400
|
-
if (value) {
|
|
2401
|
-
// AWS CodeBuild project names must follow specific naming rules
|
|
2402
|
-
const projectNamePattern = /^[a-zA-Z0-9][a-zA-Z0-9\-_]{1,254}$/;
|
|
2403
|
-
if (!projectNamePattern.test(value)) {
|
|
2404
|
-
throw new ValidationError(
|
|
2405
|
-
`Invalid CodeBuild project name: ${value}. Project names must be 2-255 characters, start with a letter or number, and contain only letters, numbers, hyphens, and underscores.`,
|
|
2406
|
-
parameter,
|
|
2407
|
-
value
|
|
2408
|
-
);
|
|
2409
|
-
}
|
|
2410
|
-
}
|
|
2411
|
-
break;
|
|
2412
|
-
|
|
2413
|
-
case 'modelPackageArn':
|
|
2414
|
-
if (value) {
|
|
2415
|
-
const modelPackageArnPattern = /^arn:aws:sagemaker:[a-z0-9-]+:\d{12}:model-package\/[a-zA-Z0-9]([a-zA-Z0-9-])*\/\d+$/;
|
|
2416
|
-
if (!modelPackageArnPattern.test(value)) {
|
|
2417
|
-
throw new ValidationError(
|
|
2418
|
-
'❌ Invalid model package ARN format. Expected: arn:aws:sagemaker:<region>:<account>:model-package/<name>/<version>',
|
|
2419
|
-
parameter,
|
|
2420
|
-
value
|
|
2421
|
-
);
|
|
2422
|
-
}
|
|
2423
|
-
}
|
|
2424
|
-
break;
|
|
2425
|
-
}
|
|
853
|
+
return this._validator._validateParameterValue(parameter, value, context);
|
|
2426
854
|
}
|
|
2427
855
|
|
|
2428
856
|
/**
|
|
2429
|
-
* Resolves HF_TOKEN references to actual token values
|
|
2430
|
-
* @param {string} tokenValue - The token value or "$HF_TOKEN" reference
|
|
2431
|
-
* @returns {string|null} Resolved token value
|
|
2432
857
|
* @private
|
|
2433
858
|
*/
|
|
2434
859
|
_resolveHfToken(tokenValue) {
|
|
2435
|
-
|
|
2436
|
-
return null;
|
|
2437
|
-
}
|
|
2438
|
-
|
|
2439
|
-
// Check if it's an environment variable reference
|
|
2440
|
-
if (tokenValue.trim() === '$HF_TOKEN') {
|
|
2441
|
-
const envToken = process.env.HF_TOKEN;
|
|
2442
|
-
if (!envToken) {
|
|
2443
|
-
console.warn('⚠️ Warning: $HF_TOKEN specified but HF_TOKEN environment variable is not set');
|
|
2444
|
-
console.warn(' The container will be built without authentication.');
|
|
2445
|
-
return null;
|
|
2446
|
-
}
|
|
2447
|
-
return envToken;
|
|
2448
|
-
}
|
|
2449
|
-
|
|
2450
|
-
// Direct token value
|
|
2451
|
-
return tokenValue;
|
|
860
|
+
return this._validator._resolveHfToken(tokenValue);
|
|
2452
861
|
}
|
|
2453
862
|
|
|
2454
863
|
/**
|
|
2455
|
-
* Validates AWS Role ARN format
|
|
2456
|
-
* @param {string} arn - The ARN to validate
|
|
2457
|
-
* @throws {ValidationError} If ARN format is invalid
|
|
2458
864
|
* @private
|
|
2459
865
|
*/
|
|
2460
866
|
_isValidArn(arn) {
|
|
2461
|
-
|
|
2462
|
-
if (!arnPattern.test(arn)) {
|
|
2463
|
-
throw new ValidationError(
|
|
2464
|
-
`Invalid AWS Role ARN format: ${arn}. Expected format: arn:aws:iam::123456789012:role/RoleName`,
|
|
2465
|
-
'awsRoleArn',
|
|
2466
|
-
arn
|
|
2467
|
-
);
|
|
2468
|
-
}
|
|
2469
|
-
return true;
|
|
867
|
+
return this._validator._isValidArn(arn);
|
|
2470
868
|
}
|
|
2471
869
|
|
|
2472
870
|
/**
|
|
2473
|
-
* Gets supported options for validation
|
|
2474
871
|
* @private
|
|
2475
872
|
*/
|
|
2476
873
|
_getSupportedOptions() {
|
|
2477
|
-
return
|
|
2478
|
-
deploymentConfigs: this.deploymentConfigResolver.getAllConfigs(),
|
|
2479
|
-
engines: ['sklearn', 'xgboost', 'tensorflow'],
|
|
2480
|
-
modelFormats: {
|
|
2481
|
-
'sklearn': ['pkl', 'joblib'],
|
|
2482
|
-
'xgboost': ['json', 'model', 'ubj'],
|
|
2483
|
-
'tensorflow': ['keras', 'h5', 'SavedModel']
|
|
2484
|
-
},
|
|
2485
|
-
buildTargets: ['codebuild'],
|
|
2486
|
-
codebuildComputeTypes: ['BUILD_GENERAL1_SMALL', 'BUILD_GENERAL1_MEDIUM', 'BUILD_GENERAL1_LARGE'],
|
|
2487
|
-
awsRegions: [
|
|
2488
|
-
'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2',
|
|
2489
|
-
'eu-west-1', 'eu-west-2', 'eu-central-1', 'eu-north-1',
|
|
2490
|
-
'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1',
|
|
2491
|
-
'ca-central-1', 'sa-east-1'
|
|
2492
|
-
]
|
|
2493
|
-
};
|
|
874
|
+
return this._validator._getSupportedOptions();
|
|
2494
875
|
}
|
|
2495
876
|
}
|
|
2496
877
|
|