@aws/ml-container-creator 0.2.1 → 0.2.2
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/bin/cli.js +88 -86
- package/config/bootstrap-stack.json +211 -0
- package/config/parameter-schema.json +88 -0
- package/infra/ci-harness/bin/ci-harness.ts +26 -0
- package/infra/ci-harness/buildspec.yml +352 -0
- package/infra/ci-harness/cdk.json +27 -0
- package/infra/ci-harness/lambda/scanner/index.ts +199 -0
- package/infra/ci-harness/lib/ci-harness-stack.ts +609 -0
- package/infra/ci-harness/package-lock.json +3979 -0
- package/infra/ci-harness/package.json +32 -0
- package/infra/ci-harness/tsconfig.json +38 -0
- package/package.json +13 -3
- package/src/app.js +318 -318
- package/src/copy-tpl.js +19 -19
- package/src/lib/asset-manager.js +74 -74
- package/src/lib/aws-profile-parser.js +45 -45
- package/src/lib/bootstrap-command-handler.js +560 -547
- package/src/lib/bootstrap-config.js +45 -45
- package/src/lib/ci-register-helpers.js +19 -19
- package/src/lib/ci-report-helpers.js +37 -37
- package/src/lib/ci-stage-helpers.js +49 -49
- package/src/lib/comment-generator.js +4 -4
- package/src/lib/config-manager.js +105 -105
- package/src/lib/deployment-config-resolver.js +10 -10
- package/src/lib/deployment-registry.js +153 -153
- package/src/lib/engine-prefix-resolver.js +8 -8
- package/src/lib/key-value-parser.js +6 -6
- package/src/lib/manifest-cli.js +108 -108
- package/src/lib/prompt-runner.js +224 -224
- package/src/lib/prompts.js +121 -121
- package/src/lib/registry-command-handler.js +174 -174
- package/src/lib/registry-loader.js +52 -52
- package/src/lib/sensitive-redactor.js +9 -9
- package/src/lib/template-engine.js +1 -1
- package/src/lib/template-manager.js +62 -62
- package/src/prompt-adapter.js +18 -18
package/src/app.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import fs from 'fs'
|
|
5
|
-
import path from 'path'
|
|
6
|
-
import { fileURLToPath } from 'url'
|
|
7
|
-
import { spawn } from 'child_process'
|
|
8
|
-
|
|
9
|
-
import { copyTpl } from './copy-tpl.js'
|
|
10
|
-
import { runPrompts } from './prompt-adapter.js'
|
|
11
|
-
import ConfigManager from './lib/config-manager.js'
|
|
12
|
-
import PromptRunner from './lib/prompt-runner.js'
|
|
13
|
-
import TemplateManager from './lib/template-manager.js'
|
|
14
|
-
import DeploymentConfigResolver from './lib/deployment-config-resolver.js'
|
|
15
|
-
import CommentGenerator from './lib/comment-generator.js'
|
|
16
|
-
import ConfigurationManager from './lib/configuration-manager.js'
|
|
17
|
-
import RegistryLoader from './lib/registry-loader.js'
|
|
18
|
-
import { resolvePrefixedEnvVars } from './lib/engine-prefix-resolver.js'
|
|
19
|
-
import ejs from 'ejs'
|
|
20
|
-
|
|
21
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
22
|
-
const __dirname = path.dirname(__filename)
|
|
23
|
-
const GENERATOR_ROOT = path.resolve(__dirname, '..')
|
|
24
|
-
const TEMPLATE_DIR = path.join(GENERATOR_ROOT, 'templates')
|
|
25
|
-
const LIB_DIR = path.join(GENERATOR_ROOT, 'src', 'lib')
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { spawn } from 'child_process';
|
|
8
|
+
|
|
9
|
+
import { copyTpl } from './copy-tpl.js';
|
|
10
|
+
import { runPrompts } from './prompt-adapter.js';
|
|
11
|
+
import ConfigManager from './lib/config-manager.js';
|
|
12
|
+
import PromptRunner from './lib/prompt-runner.js';
|
|
13
|
+
import TemplateManager from './lib/template-manager.js';
|
|
14
|
+
import DeploymentConfigResolver from './lib/deployment-config-resolver.js';
|
|
15
|
+
import CommentGenerator from './lib/comment-generator.js';
|
|
16
|
+
import ConfigurationManager from './lib/configuration-manager.js';
|
|
17
|
+
import RegistryLoader from './lib/registry-loader.js';
|
|
18
|
+
import { resolvePrefixedEnvVars } from './lib/engine-prefix-resolver.js';
|
|
19
|
+
import ejs from 'ejs';
|
|
20
|
+
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = path.dirname(__filename);
|
|
23
|
+
const GENERATOR_ROOT = path.resolve(__dirname, '..');
|
|
24
|
+
const TEMPLATE_DIR = path.join(GENERATOR_ROOT, 'templates');
|
|
25
|
+
const LIB_DIR = path.join(GENERATOR_ROOT, 'src', 'lib');
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Main application entry point.
|
|
@@ -37,41 +37,41 @@ export async function run(projectName, options) {
|
|
|
37
37
|
// --- Phase: Initializing ---
|
|
38
38
|
// Convert commander's camelCase options to kebab-case for ConfigManager compatibility
|
|
39
39
|
// (ConfigManager expects kebab-case format for option keys)
|
|
40
|
-
const kebabOptions = _toKebabCaseOptions(options)
|
|
40
|
+
const kebabOptions = _toKebabCaseOptions(options);
|
|
41
41
|
|
|
42
42
|
// Build a lightweight adapter that satisfies ConfigManager's generator interface
|
|
43
|
-
const generatorAdapter = _createGeneratorAdapter(projectName, kebabOptions)
|
|
44
|
-
const args = projectName ? [projectName] : []
|
|
43
|
+
const generatorAdapter = _createGeneratorAdapter(projectName, kebabOptions);
|
|
44
|
+
const args = projectName ? [projectName] : [];
|
|
45
45
|
|
|
46
|
-
const configManager = new ConfigManager({ options: kebabOptions, args })
|
|
46
|
+
const configManager = new ConfigManager({ options: kebabOptions, args });
|
|
47
47
|
|
|
48
|
-
let baseConfig
|
|
48
|
+
let baseConfig;
|
|
49
49
|
try {
|
|
50
|
-
baseConfig = await configManager.loadConfiguration()
|
|
50
|
+
baseConfig = await configManager.loadConfiguration();
|
|
51
51
|
} catch (error) {
|
|
52
|
-
console.log(`⚠️ ${error.message}`)
|
|
53
|
-
return
|
|
52
|
+
console.log(`⚠️ ${error.message}`);
|
|
53
|
+
return;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
const errors = configManager.validateConfiguration()
|
|
56
|
+
const errors = configManager.validateConfiguration();
|
|
57
57
|
if (errors.length > 0) {
|
|
58
|
-
console.log(`⚠️ ${errors[0]}`)
|
|
59
|
-
return
|
|
58
|
+
console.log(`⚠️ ${errors[0]}`);
|
|
59
|
+
return;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
// Initialize registry system
|
|
63
|
-
let registryConfigManager = null
|
|
64
|
-
let tritonBackends = {}
|
|
63
|
+
let registryConfigManager = null;
|
|
64
|
+
let tritonBackends = {};
|
|
65
65
|
try {
|
|
66
|
-
const validateEnvVars = kebabOptions['validate-env-vars'] !== false
|
|
67
|
-
const validateWithDocker = kebabOptions['validate-with-docker'] === true
|
|
68
|
-
const offline = kebabOptions['offline'] === true
|
|
66
|
+
const validateEnvVars = kebabOptions['validate-env-vars'] !== false;
|
|
67
|
+
const validateWithDocker = kebabOptions['validate-with-docker'] === true;
|
|
68
|
+
const offline = kebabOptions['offline'] === true;
|
|
69
69
|
|
|
70
|
-
let effectiveValidateWithDocker = validateWithDocker
|
|
70
|
+
let effectiveValidateWithDocker = validateWithDocker;
|
|
71
71
|
if (validateWithDocker && !validateEnvVars) {
|
|
72
|
-
console.log('\n⚠️ Warning: --validate-with-docker requires --validate-env-vars to be enabled')
|
|
73
|
-
console.log(' Docker validation will be disabled')
|
|
74
|
-
effectiveValidateWithDocker = false
|
|
72
|
+
console.log('\n⚠️ Warning: --validate-with-docker requires --validate-env-vars to be enabled');
|
|
73
|
+
console.log(' Docker validation will be disabled');
|
|
74
|
+
effectiveValidateWithDocker = false;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
registryConfigManager = new ConfigurationManager({
|
|
@@ -79,82 +79,82 @@ export async function run(projectName, options) {
|
|
|
79
79
|
validateWithDocker: effectiveValidateWithDocker,
|
|
80
80
|
offline,
|
|
81
81
|
hfTimeout: 5000
|
|
82
|
-
})
|
|
82
|
+
});
|
|
83
83
|
|
|
84
|
-
await registryConfigManager.loadRegistries()
|
|
84
|
+
await registryConfigManager.loadRegistries();
|
|
85
85
|
|
|
86
|
-
const registryLoader = new RegistryLoader()
|
|
87
|
-
tritonBackends = await registryLoader.loadTritonBackends()
|
|
86
|
+
const registryLoader = new RegistryLoader();
|
|
87
|
+
tritonBackends = await registryLoader.loadTritonBackends();
|
|
88
88
|
|
|
89
|
-
console.log('\n📚 Registry System Initialized')
|
|
90
|
-
console.log(' • Framework Registry: Loaded')
|
|
91
|
-
console.log(' • Model Registry: Loaded')
|
|
92
|
-
console.log(' • Instance Accelerator Mapping: Loaded')
|
|
89
|
+
console.log('\n📚 Registry System Initialized');
|
|
90
|
+
console.log(' • Framework Registry: Loaded');
|
|
91
|
+
console.log(' • Model Registry: Loaded');
|
|
92
|
+
console.log(' • Instance Accelerator Mapping: Loaded');
|
|
93
93
|
|
|
94
94
|
if (validateEnvVars) {
|
|
95
|
-
console.log(' • Environment Variable Validation: Enabled')
|
|
95
|
+
console.log(' • Environment Variable Validation: Enabled');
|
|
96
96
|
if (effectiveValidateWithDocker) {
|
|
97
|
-
console.log(' • Docker Introspection Validation: Enabled (experimental)')
|
|
97
|
+
console.log(' • Docker Introspection Validation: Enabled (experimental)');
|
|
98
98
|
}
|
|
99
99
|
} else {
|
|
100
|
-
console.log(' • Environment Variable Validation: Disabled')
|
|
100
|
+
console.log(' • Environment Variable Validation: Disabled');
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
if (offline) {
|
|
104
|
-
console.log(' • HuggingFace API: Offline mode')
|
|
104
|
+
console.log(' • HuggingFace API: Offline mode');
|
|
105
105
|
}
|
|
106
106
|
} catch (error) {
|
|
107
|
-
console.log('\n⚠️ Registry system initialization failed, using defaults')
|
|
108
|
-
console.log(` Error: ${error.message}`)
|
|
109
|
-
registryConfigManager = null
|
|
110
|
-
tritonBackends = {}
|
|
107
|
+
console.log('\n⚠️ Registry system initialization failed, using defaults');
|
|
108
|
+
console.log(` Error: ${error.message}`);
|
|
109
|
+
registryConfigManager = null;
|
|
110
|
+
tritonBackends = {};
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// Attach registry info to the adapter so PromptRunner can access it
|
|
114
|
-
generatorAdapter.registryConfigManager = registryConfigManager
|
|
115
|
-
generatorAdapter.configManager = configManager
|
|
116
|
-
generatorAdapter.baseConfig = baseConfig
|
|
114
|
+
generatorAdapter.registryConfigManager = registryConfigManager;
|
|
115
|
+
generatorAdapter.configManager = configManager;
|
|
116
|
+
generatorAdapter.baseConfig = baseConfig;
|
|
117
117
|
|
|
118
118
|
// --- Phase: Prompting ---
|
|
119
|
-
let answers
|
|
119
|
+
let answers;
|
|
120
120
|
if (configManager.shouldSkipPrompts()) {
|
|
121
|
-
console.log('\n🚀 Skipping prompts - using configuration from other sources')
|
|
122
|
-
answers = configManager.getFinalConfiguration()
|
|
121
|
+
console.log('\n🚀 Skipping prompts - using configuration from other sources');
|
|
122
|
+
answers = configManager.getFinalConfiguration();
|
|
123
123
|
|
|
124
124
|
// Infer modelSource from model name prefix if not set
|
|
125
|
-
const modelName = answers.modelName
|
|
125
|
+
const modelName = answers.modelName;
|
|
126
126
|
if (!answers.modelSource && modelName) {
|
|
127
127
|
if (modelName.startsWith('s3://')) {
|
|
128
|
-
answers.modelSource = 's3'
|
|
128
|
+
answers.modelSource = 's3';
|
|
129
129
|
if (!answers.artifactUri) {
|
|
130
|
-
answers.artifactUri = modelName
|
|
130
|
+
answers.artifactUri = modelName;
|
|
131
131
|
}
|
|
132
132
|
} else if (modelName.startsWith('jumpstart://')) {
|
|
133
|
-
answers.modelSource = 'jumpstart'
|
|
133
|
+
answers.modelSource = 'jumpstart';
|
|
134
134
|
} else if (modelName.startsWith('jumpstart-hub://')) {
|
|
135
|
-
answers.modelSource = 'jumpstart-hub'
|
|
135
|
+
answers.modelSource = 'jumpstart-hub';
|
|
136
136
|
} else if (modelName.startsWith('registry://')) {
|
|
137
|
-
answers.modelSource = 'registry'
|
|
137
|
+
answers.modelSource = 'registry';
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
// Warn about unsupported model sources
|
|
142
142
|
if (answers.modelSource === 'jumpstart-hub') {
|
|
143
|
-
console.log('\n ⚠️ JumpStart Private Hub models are not yet fully supported.')
|
|
144
|
-
console.log(' The generated project will not be able to download model artifacts at runtime.')
|
|
145
|
-
console.log(' This feature is tracked for a future release.')
|
|
146
|
-
console.log(' Falling back to HuggingFace source.\n')
|
|
147
|
-
answers.modelSource = 'huggingface'
|
|
148
|
-
delete answers.artifactUri
|
|
143
|
+
console.log('\n ⚠️ JumpStart Private Hub models are not yet fully supported.');
|
|
144
|
+
console.log(' The generated project will not be able to download model artifacts at runtime.');
|
|
145
|
+
console.log(' This feature is tracked for a future release.');
|
|
146
|
+
console.log(' Falling back to HuggingFace source.\n');
|
|
147
|
+
answers.modelSource = 'huggingface';
|
|
148
|
+
delete answers.artifactUri;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
// Note about registry model requirements
|
|
152
152
|
if (answers.modelSource === 'registry') {
|
|
153
|
-
console.log('\n ℹ️ Registry model: the container will resolve the artifact URI at startup')
|
|
154
|
-
console.log(' via DescribeModelPackage. Ensure the model package has a valid')
|
|
155
|
-
console.log(' InferenceSpecification with ModelDataUrl or S3DataSource.')
|
|
156
|
-
console.log(' If your model package lacks an InferenceSpecification, use the S3 path')
|
|
157
|
-
console.log(' directly instead: --model-name="s3://bucket/path/model.tar.gz"\n')
|
|
153
|
+
console.log('\n ℹ️ Registry model: the container will resolve the artifact URI at startup');
|
|
154
|
+
console.log(' via DescribeModelPackage. Ensure the model package has a valid');
|
|
155
|
+
console.log(' InferenceSpecification with ModelDataUrl or S3DataSource.');
|
|
156
|
+
console.log(' If your model package lacks an InferenceSpecification, use the S3 path');
|
|
157
|
+
console.log(' directly instead: --model-name="s3://bucket/path/model.tar.gz"\n');
|
|
158
158
|
}
|
|
159
159
|
} else {
|
|
160
160
|
const promptRunner = new PromptRunner({
|
|
@@ -162,25 +162,25 @@ export async function run(projectName, options) {
|
|
|
162
162
|
options: kebabOptions,
|
|
163
163
|
registryConfigManager,
|
|
164
164
|
baseConfig
|
|
165
|
-
})
|
|
166
|
-
const promptAnswers = await promptRunner.run()
|
|
167
|
-
answers = configManager.getFinalConfiguration(promptAnswers)
|
|
165
|
+
});
|
|
166
|
+
const promptAnswers = await promptRunner.run();
|
|
167
|
+
answers = configManager.getFinalConfiguration(promptAnswers);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
// Ensure template variables have defaults and enrich with registry data
|
|
171
|
-
await _ensureTemplateVariables(answers, registryConfigManager)
|
|
171
|
+
await _ensureTemplateVariables(answers, registryConfigManager);
|
|
172
172
|
|
|
173
173
|
// --- Phase: Writing ---
|
|
174
|
-
const destDir = path.resolve(answers.destinationDir)
|
|
175
|
-
fs.mkdirSync(destDir, { recursive: true })
|
|
174
|
+
const destDir = path.resolve(answers.destinationDir);
|
|
175
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
176
176
|
|
|
177
|
-
await writeProject(TEMPLATE_DIR, destDir, answers, registryConfigManager, tritonBackends, configManager)
|
|
177
|
+
await writeProject(TEMPLATE_DIR, destDir, answers, registryConfigManager, tritonBackends, configManager);
|
|
178
178
|
|
|
179
179
|
// --- Phase: End ---
|
|
180
|
-
await postGenerate(destDir, answers, tritonBackends)
|
|
180
|
+
await postGenerate(destDir, answers, tritonBackends);
|
|
181
181
|
|
|
182
|
-
console.log('\n✅ Project generated successfully!')
|
|
183
|
-
console.log(` 📁 ${destDir}`)
|
|
182
|
+
console.log('\n✅ Project generated successfully!');
|
|
183
|
+
console.log(` 📁 ${destDir}`);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
/**
|
|
@@ -196,46 +196,46 @@ export async function run(projectName, options) {
|
|
|
196
196
|
export async function writeProject(templateDir, destDir, answers, registryConfigManager = null, tritonBackends = {}, configManager = null) {
|
|
197
197
|
// Validate required parameters via ConfigManager
|
|
198
198
|
if (configManager) {
|
|
199
|
-
const requiredParamErrors = configManager.validateRequiredParameters(answers)
|
|
199
|
+
const requiredParamErrors = configManager.validateRequiredParameters(answers);
|
|
200
200
|
if (requiredParamErrors.length > 0) {
|
|
201
|
-
console.log('\n❌ Required Parameter Validation Failed:')
|
|
201
|
+
console.log('\n❌ Required Parameter Validation Failed:');
|
|
202
202
|
requiredParamErrors.forEach(error => {
|
|
203
|
-
console.log(` • ${error}`)
|
|
204
|
-
})
|
|
205
|
-
console.log('\nPlease provide the missing required parameters and try again.')
|
|
206
|
-
throw new Error('Required parameters are missing. Cannot proceed with file generation.')
|
|
203
|
+
console.log(` • ${error}`);
|
|
204
|
+
});
|
|
205
|
+
console.log('\nPlease provide the missing required parameters and try again.');
|
|
206
|
+
throw new Error('Required parameters are missing. Cannot proceed with file generation.');
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
// Validate environment variables if registry system is available
|
|
211
211
|
if (registryConfigManager && (answers.frameworkVersion || answers.architecture === 'triton')) {
|
|
212
|
-
await _validateEnvironmentVariables(answers, registryConfigManager)
|
|
212
|
+
await _validateEnvironmentVariables(answers, registryConfigManager);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
// Validate template configuration
|
|
216
|
-
const templateManager = new TemplateManager(answers)
|
|
217
|
-
templateManager.validate()
|
|
216
|
+
const templateManager = new TemplateManager(answers);
|
|
217
|
+
templateManager.validate();
|
|
218
218
|
|
|
219
219
|
// Generate comments for templates
|
|
220
|
-
const commentGenerator = new CommentGenerator()
|
|
221
|
-
const comments = commentGenerator.generateDockerfileComments(answers)
|
|
220
|
+
const commentGenerator = new CommentGenerator();
|
|
221
|
+
const comments = commentGenerator.generateDockerfileComments(answers);
|
|
222
222
|
|
|
223
223
|
// Prepare ordered environment variables
|
|
224
|
-
const orderedEnvVars = _getOrderedEnvVars(answers.envVars || {})
|
|
224
|
+
const orderedEnvVars = _getOrderedEnvVars(answers.envVars || {});
|
|
225
225
|
|
|
226
226
|
// Append model env vars and prefixed server env vars
|
|
227
|
-
const modelEnvVars = answers.modelEnvVars || {}
|
|
228
|
-
const serverEnvVars = answers.serverEnvVars || {}
|
|
229
|
-
const engine = answers.modelServer || answers.backend || ''
|
|
227
|
+
const modelEnvVars = answers.modelEnvVars || {};
|
|
228
|
+
const serverEnvVars = answers.serverEnvVars || {};
|
|
229
|
+
const engine = answers.modelServer || answers.backend || '';
|
|
230
230
|
|
|
231
231
|
Object.entries(modelEnvVars).forEach(([key, value]) => {
|
|
232
|
-
orderedEnvVars.push({ key, value })
|
|
233
|
-
})
|
|
232
|
+
orderedEnvVars.push({ key, value });
|
|
233
|
+
});
|
|
234
234
|
|
|
235
|
-
const prefixedServerEnvVars = resolvePrefixedEnvVars(engine, serverEnvVars)
|
|
235
|
+
const prefixedServerEnvVars = resolvePrefixedEnvVars(engine, serverEnvVars);
|
|
236
236
|
Object.entries(prefixedServerEnvVars).forEach(([key, value]) => {
|
|
237
|
-
orderedEnvVars.push({ key, value })
|
|
238
|
-
})
|
|
237
|
+
orderedEnvVars.push({ key, value });
|
|
238
|
+
});
|
|
239
239
|
|
|
240
240
|
// Prepare template variables
|
|
241
241
|
const templateVars = {
|
|
@@ -243,130 +243,130 @@ export async function writeProject(templateDir, destDir, answers, registryConfig
|
|
|
243
243
|
comments,
|
|
244
244
|
orderedEnvVars,
|
|
245
245
|
serverEnvVars: prefixedServerEnvVars
|
|
246
|
-
}
|
|
246
|
+
};
|
|
247
247
|
|
|
248
248
|
// Build ignore patterns
|
|
249
|
-
const ignorePatterns = []
|
|
249
|
+
const ignorePatterns = [];
|
|
250
250
|
|
|
251
251
|
if (answers.deploymentTarget !== 'hyperpod-eks') {
|
|
252
|
-
ignorePatterns.push('**/hyperpod/**')
|
|
252
|
+
ignorePatterns.push('**/hyperpod/**');
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
// Resolve architecture
|
|
256
|
-
const resolver = new DeploymentConfigResolver()
|
|
257
|
-
let architecture = answers.architecture
|
|
256
|
+
const resolver = new DeploymentConfigResolver();
|
|
257
|
+
let architecture = answers.architecture;
|
|
258
258
|
|
|
259
259
|
if (!architecture && answers.deploymentConfig) {
|
|
260
260
|
try {
|
|
261
|
-
const parts = resolver.decompose(answers.deploymentConfig)
|
|
262
|
-
architecture = parts.architecture
|
|
261
|
+
const parts = resolver.decompose(answers.deploymentConfig);
|
|
262
|
+
architecture = parts.architecture;
|
|
263
263
|
} catch (e) {
|
|
264
|
-
architecture = answers.framework === 'transformers' ? 'transformers' : 'http'
|
|
264
|
+
architecture = answers.framework === 'transformers' ? 'transformers' : 'http';
|
|
265
265
|
}
|
|
266
266
|
} else if (!architecture) {
|
|
267
|
-
architecture = answers.framework === 'transformers' ? 'transformers' : 'http'
|
|
267
|
+
architecture = answers.framework === 'transformers' ? 'transformers' : 'http';
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
// Exclude sample_model when not needed
|
|
271
271
|
if (!answers.includeSampleModel || architecture === 'transformers' || architecture === 'diffusors') {
|
|
272
|
-
ignorePatterns.push('**/sample_model/**')
|
|
272
|
+
ignorePatterns.push('**/sample_model/**');
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
// Always exclude triton and diffusors source directories
|
|
276
|
-
ignorePatterns.push('**/triton/**')
|
|
277
|
-
ignorePatterns.push('**/diffusors/**')
|
|
276
|
+
ignorePatterns.push('**/triton/**');
|
|
277
|
+
ignorePatterns.push('**/diffusors/**');
|
|
278
278
|
|
|
279
279
|
// For triton and diffusors, exclude the default Dockerfile
|
|
280
280
|
if (architecture === 'triton' || architecture === 'diffusors') {
|
|
281
|
-
ignorePatterns.push('**/Dockerfile')
|
|
281
|
+
ignorePatterns.push('**/Dockerfile');
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
// Copy all templates with EJS rendering
|
|
285
|
-
copyTpl(templateDir, destDir, templateVars, ignorePatterns)
|
|
285
|
+
copyTpl(templateDir, destDir, templateVars, ignorePatterns);
|
|
286
286
|
|
|
287
287
|
// Architecture-specific file routing (delete files that don't belong)
|
|
288
288
|
switch (architecture) {
|
|
289
289
|
case 'http':
|
|
290
|
-
_unlinkIfExists(path.join(destDir, 'code/chat_template.jinja'))
|
|
291
|
-
_unlinkIfExists(path.join(destDir, 'code/serve'))
|
|
292
|
-
_unlinkIfExists(path.join(destDir, 'code/serving.properties'))
|
|
293
|
-
_unlinkIfExists(path.join(destDir, 'code/start_server.sh'))
|
|
290
|
+
_unlinkIfExists(path.join(destDir, 'code/chat_template.jinja'));
|
|
291
|
+
_unlinkIfExists(path.join(destDir, 'code/serve'));
|
|
292
|
+
_unlinkIfExists(path.join(destDir, 'code/serving.properties'));
|
|
293
|
+
_unlinkIfExists(path.join(destDir, 'code/start_server.sh'));
|
|
294
294
|
|
|
295
295
|
if (answers.modelServer !== 'flask' && answers.backend !== 'flask') {
|
|
296
|
-
_unlinkIfExists(path.join(destDir, 'code/flask/wsgi.py'))
|
|
297
|
-
_unlinkIfExists(path.join(destDir, 'code/flask/gunicorn_config.py'))
|
|
296
|
+
_unlinkIfExists(path.join(destDir, 'code/flask/wsgi.py'));
|
|
297
|
+
_unlinkIfExists(path.join(destDir, 'code/flask/gunicorn_config.py'));
|
|
298
298
|
}
|
|
299
|
-
break
|
|
299
|
+
break;
|
|
300
300
|
|
|
301
301
|
case 'transformers':
|
|
302
|
-
_unlinkIfExists(path.join(destDir, 'code/model_handler.py'))
|
|
303
|
-
_unlinkIfExists(path.join(destDir, 'code/serve.py'))
|
|
304
|
-
_unlinkIfExists(path.join(destDir, 'code/start_server.py'))
|
|
305
|
-
_unlinkIfExists(path.join(destDir, 'nginx-predictors.conf'))
|
|
306
|
-
_unlinkIfExists(path.join(destDir, 'code/flask/wsgi.py'))
|
|
307
|
-
_unlinkIfExists(path.join(destDir, 'code/flask/gunicorn_config.py'))
|
|
308
|
-
break
|
|
302
|
+
_unlinkIfExists(path.join(destDir, 'code/model_handler.py'));
|
|
303
|
+
_unlinkIfExists(path.join(destDir, 'code/serve.py'));
|
|
304
|
+
_unlinkIfExists(path.join(destDir, 'code/start_server.py'));
|
|
305
|
+
_unlinkIfExists(path.join(destDir, 'nginx-predictors.conf'));
|
|
306
|
+
_unlinkIfExists(path.join(destDir, 'code/flask/wsgi.py'));
|
|
307
|
+
_unlinkIfExists(path.join(destDir, 'code/flask/gunicorn_config.py'));
|
|
308
|
+
break;
|
|
309
309
|
|
|
310
310
|
case 'triton':
|
|
311
|
-
_unlinkIfExists(path.join(destDir, 'code/serve.py'))
|
|
312
|
-
_unlinkIfExists(path.join(destDir, 'code/model_handler.py'))
|
|
313
|
-
_unlinkIfExists(path.join(destDir, 'code/start_server.py'))
|
|
314
|
-
_unlinkIfExists(path.join(destDir, 'nginx-predictors.conf'))
|
|
315
|
-
_unlinkIfExists(path.join(destDir, 'code/flask/wsgi.py'))
|
|
316
|
-
_unlinkIfExists(path.join(destDir, 'code/flask/gunicorn_config.py'))
|
|
317
|
-
_unlinkIfExists(path.join(destDir, 'code/chat_template.jinja'))
|
|
318
|
-
_unlinkIfExists(path.join(destDir, 'code/serve'))
|
|
319
|
-
_unlinkIfExists(path.join(destDir, 'code/serving.properties'))
|
|
320
|
-
_unlinkIfExists(path.join(destDir, 'code/start_server.sh'))
|
|
311
|
+
_unlinkIfExists(path.join(destDir, 'code/serve.py'));
|
|
312
|
+
_unlinkIfExists(path.join(destDir, 'code/model_handler.py'));
|
|
313
|
+
_unlinkIfExists(path.join(destDir, 'code/start_server.py'));
|
|
314
|
+
_unlinkIfExists(path.join(destDir, 'nginx-predictors.conf'));
|
|
315
|
+
_unlinkIfExists(path.join(destDir, 'code/flask/wsgi.py'));
|
|
316
|
+
_unlinkIfExists(path.join(destDir, 'code/flask/gunicorn_config.py'));
|
|
317
|
+
_unlinkIfExists(path.join(destDir, 'code/chat_template.jinja'));
|
|
318
|
+
_unlinkIfExists(path.join(destDir, 'code/serve'));
|
|
319
|
+
_unlinkIfExists(path.join(destDir, 'code/serving.properties'));
|
|
320
|
+
_unlinkIfExists(path.join(destDir, 'code/start_server.sh'));
|
|
321
321
|
|
|
322
322
|
// Generate Triton-specific files
|
|
323
|
-
_generateTritonFiles(templateDir, destDir, templateVars, answers, tritonBackends)
|
|
324
|
-
break
|
|
323
|
+
_generateTritonFiles(templateDir, destDir, templateVars, answers, tritonBackends);
|
|
324
|
+
break;
|
|
325
325
|
|
|
326
326
|
case 'diffusors':
|
|
327
|
-
_unlinkIfExists(path.join(destDir, 'code/model_handler.py'))
|
|
328
|
-
_unlinkIfExists(path.join(destDir, 'code/serve.py'))
|
|
329
|
-
_unlinkIfExists(path.join(destDir, 'code/start_server.py'))
|
|
330
|
-
_unlinkIfExists(path.join(destDir, 'nginx-predictors.conf'))
|
|
331
|
-
_unlinkIfExists(path.join(destDir, 'code/flask/wsgi.py'))
|
|
332
|
-
_unlinkIfExists(path.join(destDir, 'code/flask/gunicorn_config.py'))
|
|
333
|
-
_unlinkIfExists(path.join(destDir, 'code/chat_template.jinja'))
|
|
334
|
-
_unlinkIfExists(path.join(destDir, 'code/serving.properties'))
|
|
327
|
+
_unlinkIfExists(path.join(destDir, 'code/model_handler.py'));
|
|
328
|
+
_unlinkIfExists(path.join(destDir, 'code/serve.py'));
|
|
329
|
+
_unlinkIfExists(path.join(destDir, 'code/start_server.py'));
|
|
330
|
+
_unlinkIfExists(path.join(destDir, 'nginx-predictors.conf'));
|
|
331
|
+
_unlinkIfExists(path.join(destDir, 'code/flask/wsgi.py'));
|
|
332
|
+
_unlinkIfExists(path.join(destDir, 'code/flask/gunicorn_config.py'));
|
|
333
|
+
_unlinkIfExists(path.join(destDir, 'code/chat_template.jinja'));
|
|
334
|
+
_unlinkIfExists(path.join(destDir, 'code/serving.properties'));
|
|
335
335
|
|
|
336
336
|
// Copy diffusors-specific templates
|
|
337
|
-
_renderTemplate(path.join(templateDir, 'diffusors/Dockerfile'), path.join(destDir, 'Dockerfile'), templateVars)
|
|
338
|
-
_renderTemplate(path.join(templateDir, 'diffusors/serve'), path.join(destDir, 'code/serve'), templateVars)
|
|
339
|
-
_renderTemplate(path.join(templateDir, 'diffusors/start_server.sh'), path.join(destDir, 'code/start_server.sh'), templateVars)
|
|
340
|
-
_copyFile(path.join(templateDir, 'diffusors/patch_image_api.py'), path.join(destDir, 'code/patch_image_api.py'))
|
|
341
|
-
break
|
|
337
|
+
_renderTemplate(path.join(templateDir, 'diffusors/Dockerfile'), path.join(destDir, 'Dockerfile'), templateVars);
|
|
338
|
+
_renderTemplate(path.join(templateDir, 'diffusors/serve'), path.join(destDir, 'code/serve'), templateVars);
|
|
339
|
+
_renderTemplate(path.join(templateDir, 'diffusors/start_server.sh'), path.join(destDir, 'code/start_server.sh'), templateVars);
|
|
340
|
+
_copyFile(path.join(templateDir, 'diffusors/patch_image_api.py'), path.join(destDir, 'code/patch_image_api.py'));
|
|
341
|
+
break;
|
|
342
342
|
|
|
343
343
|
default:
|
|
344
344
|
// Fallback to HTTP behavior
|
|
345
|
-
_unlinkIfExists(path.join(destDir, 'code/chat_template.jinja'))
|
|
346
|
-
_unlinkIfExists(path.join(destDir, 'code/serve'))
|
|
347
|
-
_unlinkIfExists(path.join(destDir, 'code/serving.properties'))
|
|
348
|
-
_unlinkIfExists(path.join(destDir, 'code/start_server.sh'))
|
|
345
|
+
_unlinkIfExists(path.join(destDir, 'code/chat_template.jinja'));
|
|
346
|
+
_unlinkIfExists(path.join(destDir, 'code/serve'));
|
|
347
|
+
_unlinkIfExists(path.join(destDir, 'code/serving.properties'));
|
|
348
|
+
_unlinkIfExists(path.join(destDir, 'code/start_server.sh'));
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
// nginx-tensorrt.conf: only needed for TensorRT-LLM
|
|
352
352
|
if (answers.modelServer !== 'tensorrt-llm' && answers.backend !== 'tensorrt-llm') {
|
|
353
|
-
_unlinkIfExists(path.join(destDir, 'nginx-tensorrt.conf'))
|
|
353
|
+
_unlinkIfExists(path.join(destDir, 'nginx-tensorrt.conf'));
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
// nginx-diffusors.conf: only needed for diffusors architecture
|
|
357
357
|
if (answers.architecture !== 'diffusors') {
|
|
358
|
-
_unlinkIfExists(path.join(destDir, 'nginx-diffusors.conf'))
|
|
358
|
+
_unlinkIfExists(path.join(destDir, 'nginx-diffusors.conf'));
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
// Copy PROJECT_README.md as README.md (overwriting the template README)
|
|
362
|
-
_renderTemplate(path.join(templateDir, 'PROJECT_README.md'), path.join(destDir, 'README.md'), templateVars)
|
|
362
|
+
_renderTemplate(path.join(templateDir, 'PROJECT_README.md'), path.join(destDir, 'README.md'), templateVars);
|
|
363
363
|
|
|
364
364
|
// Copy do/lib/ Node.js modules (plain copy, no EJS)
|
|
365
|
-
const doLibDir = path.join(destDir, 'do', 'lib')
|
|
366
|
-
fs.mkdirSync(doLibDir, { recursive: true })
|
|
367
|
-
_copyFile(path.join(LIB_DIR, 'manifest-cli.js'), path.join(doLibDir, 'manifest-cli.js'))
|
|
368
|
-
_copyFile(path.join(LIB_DIR, 'asset-manager.js'), path.join(doLibDir, 'asset-manager.js'))
|
|
369
|
-
_copyFile(path.join(LIB_DIR, 'bootstrap-config.js'), path.join(doLibDir, 'bootstrap-config.js'))
|
|
365
|
+
const doLibDir = path.join(destDir, 'do', 'lib');
|
|
366
|
+
fs.mkdirSync(doLibDir, { recursive: true });
|
|
367
|
+
_copyFile(path.join(LIB_DIR, 'manifest-cli.js'), path.join(doLibDir, 'manifest-cli.js'));
|
|
368
|
+
_copyFile(path.join(LIB_DIR, 'asset-manager.js'), path.join(doLibDir, 'asset-manager.js'));
|
|
369
|
+
_copyFile(path.join(LIB_DIR, 'bootstrap-config.js'), path.join(doLibDir, 'bootstrap-config.js'));
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
/**
|
|
@@ -379,15 +379,15 @@ export async function writeProject(templateDir, destDir, answers, registryConfig
|
|
|
379
379
|
*/
|
|
380
380
|
export async function postGenerate(destDir, answers, tritonBackends = {}) {
|
|
381
381
|
// Set executable permissions on shell scripts
|
|
382
|
-
_setExecutablePermissions(destDir)
|
|
382
|
+
_setExecutablePermissions(destDir);
|
|
383
383
|
|
|
384
384
|
// Run sample model training if requested
|
|
385
|
-
const architecture = answers.architecture
|
|
385
|
+
const architecture = answers.architecture;
|
|
386
386
|
const skipSampleTraining = architecture === 'transformers' ||
|
|
387
|
-
(architecture === 'triton' && !tritonBackends[answers.backend]?.supportsSampleModel)
|
|
387
|
+
(architecture === 'triton' && !tritonBackends[answers.backend]?.supportsSampleModel);
|
|
388
388
|
|
|
389
389
|
if (answers.includeSampleModel && !skipSampleTraining) {
|
|
390
|
-
await _runSampleModelTraining(destDir)
|
|
390
|
+
await _runSampleModelTraining(destDir);
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
393
|
|
|
@@ -402,13 +402,13 @@ export async function postGenerate(destDir, answers, tritonBackends = {}) {
|
|
|
402
402
|
* @returns {object} Options with kebab-case keys
|
|
403
403
|
*/
|
|
404
404
|
function _toKebabCaseOptions(options) {
|
|
405
|
-
const kebabOptions = {}
|
|
405
|
+
const kebabOptions = {};
|
|
406
406
|
for (const [key, value] of Object.entries(options)) {
|
|
407
407
|
// Convert camelCase to kebab-case
|
|
408
|
-
const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase()
|
|
409
|
-
kebabOptions[kebabKey] = value
|
|
408
|
+
const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
409
|
+
kebabOptions[kebabKey] = value;
|
|
410
410
|
}
|
|
411
|
-
return kebabOptions
|
|
411
|
+
return kebabOptions;
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
/**
|
|
@@ -420,31 +420,31 @@ function _toKebabCaseOptions(options) {
|
|
|
420
420
|
* @returns {object} Generator-like adapter
|
|
421
421
|
*/
|
|
422
422
|
function _createGeneratorAdapter(projectName, options) {
|
|
423
|
-
const args = projectName ? [projectName] : []
|
|
424
|
-
let _destinationPath = process.cwd()
|
|
423
|
+
const args = projectName ? [projectName] : [];
|
|
424
|
+
let _destinationPath = process.cwd();
|
|
425
425
|
|
|
426
426
|
const adapter = {
|
|
427
427
|
options,
|
|
428
428
|
args,
|
|
429
429
|
destinationPath(...segments) {
|
|
430
|
-
if (segments.length === 0) return _destinationPath
|
|
431
|
-
return path.join(_destinationPath, ...segments)
|
|
430
|
+
if (segments.length === 0) return _destinationPath;
|
|
431
|
+
return path.join(_destinationPath, ...segments);
|
|
432
432
|
},
|
|
433
433
|
destinationRoot(newRoot) {
|
|
434
434
|
if (newRoot !== undefined) {
|
|
435
|
-
_destinationPath = path.resolve(newRoot)
|
|
435
|
+
_destinationPath = path.resolve(newRoot);
|
|
436
436
|
}
|
|
437
|
-
return _destinationPath
|
|
437
|
+
return _destinationPath;
|
|
438
438
|
},
|
|
439
439
|
registryConfigManager: null,
|
|
440
440
|
configManager: null,
|
|
441
441
|
baseConfig: {},
|
|
442
442
|
async prompt(prompts) {
|
|
443
|
-
return runPrompts(prompts)
|
|
443
|
+
return runPrompts(prompts);
|
|
444
444
|
}
|
|
445
|
-
}
|
|
445
|
+
};
|
|
446
446
|
|
|
447
|
-
return adapter
|
|
447
|
+
return adapter;
|
|
448
448
|
}
|
|
449
449
|
|
|
450
450
|
/**
|
|
@@ -491,62 +491,62 @@ async function _ensureTemplateVariables(answers, registryConfigManager = null) {
|
|
|
491
491
|
modelSource: 'huggingface',
|
|
492
492
|
artifactUri: '',
|
|
493
493
|
modelLoadStrategy: 'runtime'
|
|
494
|
-
}
|
|
494
|
+
};
|
|
495
495
|
|
|
496
496
|
Object.entries(defaults).forEach(([key, value]) => {
|
|
497
497
|
if (answers[key] === undefined) {
|
|
498
|
-
answers[key] = value
|
|
498
|
+
answers[key] = value;
|
|
499
499
|
}
|
|
500
|
-
})
|
|
500
|
+
});
|
|
501
501
|
|
|
502
502
|
// Backward compatibility: populate framework and modelServer from architecture/backend
|
|
503
503
|
if (!answers.framework && answers.architecture) {
|
|
504
|
-
answers.framework = answers.architecture
|
|
504
|
+
answers.framework = answers.architecture;
|
|
505
505
|
}
|
|
506
506
|
if (!answers.modelServer && answers.backend) {
|
|
507
|
-
answers.modelServer = answers.backend
|
|
507
|
+
answers.modelServer = answers.backend;
|
|
508
508
|
}
|
|
509
509
|
|
|
510
510
|
// Always include testing with all available test types
|
|
511
|
-
answers.includeTesting = true
|
|
511
|
+
answers.includeTesting = true;
|
|
512
512
|
if (!answers.testTypes || answers.testTypes.length === 0) {
|
|
513
513
|
if (answers.architecture === 'transformers' || answers.framework === 'transformers') {
|
|
514
|
-
answers.testTypes = ['hosted-model-endpoint']
|
|
514
|
+
answers.testTypes = ['hosted-model-endpoint'];
|
|
515
515
|
} else {
|
|
516
|
-
answers.testTypes = ['local-model-cli', 'local-model-server', 'hosted-model-endpoint']
|
|
516
|
+
answers.testTypes = ['local-model-cli', 'local-model-server', 'hosted-model-endpoint'];
|
|
517
517
|
}
|
|
518
518
|
}
|
|
519
519
|
|
|
520
520
|
// Merge catalog env vars into answers.envVars with correct precedence
|
|
521
|
-
await _mergeEnvVarsWithPrecedence(answers, registryConfigManager)
|
|
521
|
+
await _mergeEnvVarsWithPrecedence(answers, registryConfigManager);
|
|
522
522
|
|
|
523
523
|
// For Triton architecture, set default base image fallback
|
|
524
524
|
if (answers.architecture === 'triton' && !answers.baseImage) {
|
|
525
525
|
// Try to look up base image from framework registry using deployment-config key
|
|
526
|
-
const tritonRegistryKey = answers.deploymentConfig
|
|
526
|
+
const tritonRegistryKey = answers.deploymentConfig;
|
|
527
527
|
if (tritonRegistryKey && registryConfigManager?.frameworkRegistry) {
|
|
528
|
-
const tritonFrameworkConfig = registryConfigManager.frameworkRegistry[tritonRegistryKey]
|
|
528
|
+
const tritonFrameworkConfig = registryConfigManager.frameworkRegistry[tritonRegistryKey];
|
|
529
529
|
if (tritonFrameworkConfig) {
|
|
530
530
|
const versions = Object.keys(tritonFrameworkConfig).sort((a, b) =>
|
|
531
531
|
b.localeCompare(a, undefined, { numeric: true })
|
|
532
|
-
)
|
|
532
|
+
);
|
|
533
533
|
if (versions.length > 0) {
|
|
534
|
-
const latestConfig = tritonFrameworkConfig[versions[0]]
|
|
534
|
+
const latestConfig = tritonFrameworkConfig[versions[0]];
|
|
535
535
|
if (latestConfig.baseImage) {
|
|
536
|
-
answers.baseImage = latestConfig.baseImage
|
|
536
|
+
answers.baseImage = latestConfig.baseImage;
|
|
537
537
|
}
|
|
538
538
|
if (latestConfig.inferenceAmiVersion && !answers.inferenceAmiVersion) {
|
|
539
|
-
answers.inferenceAmiVersion = latestConfig.inferenceAmiVersion
|
|
539
|
+
answers.inferenceAmiVersion = latestConfig.inferenceAmiVersion;
|
|
540
540
|
}
|
|
541
541
|
if (latestConfig.accelerator) {
|
|
542
|
-
answers.accelerator = latestConfig.accelerator
|
|
542
|
+
answers.accelerator = latestConfig.accelerator;
|
|
543
543
|
}
|
|
544
544
|
}
|
|
545
545
|
}
|
|
546
546
|
}
|
|
547
547
|
// Final fallback: hardcoded default Triton base image
|
|
548
548
|
if (!answers.baseImage) {
|
|
549
|
-
answers.baseImage = 'nvcr.io/nvidia/tritonserver:24.08-py3'
|
|
549
|
+
answers.baseImage = 'nvcr.io/nvidia/tritonserver:24.08-py3';
|
|
550
550
|
}
|
|
551
551
|
}
|
|
552
552
|
|
|
@@ -554,34 +554,34 @@ async function _ensureTemplateVariables(answers, registryConfigManager = null) {
|
|
|
554
554
|
if (answers.framework === 'transformers' && answers.modelName && registryConfigManager) {
|
|
555
555
|
try {
|
|
556
556
|
// Fetch HuggingFace data for model-specific info
|
|
557
|
-
const hfData = await registryConfigManager._fetchHuggingFaceData(answers.modelName)
|
|
557
|
+
const hfData = await registryConfigManager._fetchHuggingFaceData(answers.modelName);
|
|
558
558
|
|
|
559
559
|
// Merge chatTemplate if available and not already set
|
|
560
560
|
if (hfData && hfData.chatTemplate && !answers.chatTemplate) {
|
|
561
|
-
answers.chatTemplate = hfData.chatTemplate
|
|
562
|
-
answers.chatTemplateSource = 'HuggingFace_Hub_API'
|
|
561
|
+
answers.chatTemplate = hfData.chatTemplate;
|
|
562
|
+
answers.chatTemplateSource = 'HuggingFace_Hub_API';
|
|
563
563
|
}
|
|
564
564
|
|
|
565
565
|
// Check Model Registry for chatTemplate overrides
|
|
566
566
|
if (registryConfigManager.modelRegistry) {
|
|
567
|
-
const modelConfig = _findModelConfig(answers.modelName, registryConfigManager)
|
|
567
|
+
const modelConfig = _findModelConfig(answers.modelName, registryConfigManager);
|
|
568
568
|
|
|
569
569
|
if (modelConfig && modelConfig.chatTemplate) {
|
|
570
|
-
answers.chatTemplate = modelConfig.chatTemplate
|
|
571
|
-
answers.chatTemplateSource = 'Model_Registry'
|
|
570
|
+
answers.chatTemplate = modelConfig.chatTemplate;
|
|
571
|
+
answers.chatTemplateSource = 'Model_Registry';
|
|
572
572
|
}
|
|
573
573
|
}
|
|
574
574
|
|
|
575
575
|
// Set framework-level metadata (non-envVar fields)
|
|
576
576
|
if (answers.frameworkVersion && registryConfigManager.frameworkRegistry) {
|
|
577
|
-
const frameworkConfig = registryConfigManager.frameworkRegistry[answers.framework]?.[answers.frameworkVersion]
|
|
577
|
+
const frameworkConfig = registryConfigManager.frameworkRegistry[answers.framework]?.[answers.frameworkVersion];
|
|
578
578
|
|
|
579
579
|
if (frameworkConfig) {
|
|
580
580
|
if (frameworkConfig.inferenceAmiVersion && !answers.inferenceAmiVersion) {
|
|
581
|
-
answers.inferenceAmiVersion = frameworkConfig.inferenceAmiVersion
|
|
581
|
+
answers.inferenceAmiVersion = frameworkConfig.inferenceAmiVersion;
|
|
582
582
|
}
|
|
583
583
|
if (frameworkConfig.accelerator) {
|
|
584
|
-
answers.accelerator = frameworkConfig.accelerator
|
|
584
|
+
answers.accelerator = frameworkConfig.accelerator;
|
|
585
585
|
}
|
|
586
586
|
}
|
|
587
587
|
}
|
|
@@ -598,7 +598,7 @@ async function _ensureTemplateVariables(answers, registryConfigManager = null) {
|
|
|
598
598
|
* @returns {Array<{key: string, value: string}>} Ordered array
|
|
599
599
|
*/
|
|
600
600
|
function _getOrderedEnvVars(envVars) {
|
|
601
|
-
const entries = Object.entries(envVars)
|
|
601
|
+
const entries = Object.entries(envVars);
|
|
602
602
|
|
|
603
603
|
const priorities = {
|
|
604
604
|
'LD_LIBRARY_PATH': 1,
|
|
@@ -617,26 +617,26 @@ function _getOrderedEnvVars(envVars) {
|
|
|
617
617
|
'WORKER': 4,
|
|
618
618
|
'THREAD': 4,
|
|
619
619
|
'default': 5
|
|
620
|
-
}
|
|
620
|
+
};
|
|
621
621
|
|
|
622
622
|
function getPriority(key) {
|
|
623
|
-
if (priorities[key]) return priorities[key]
|
|
623
|
+
if (priorities[key]) return priorities[key];
|
|
624
624
|
for (const [pattern, priority] of Object.entries(priorities)) {
|
|
625
625
|
if (pattern !== 'default' && key.includes(pattern)) {
|
|
626
|
-
return priority
|
|
626
|
+
return priority;
|
|
627
627
|
}
|
|
628
628
|
}
|
|
629
|
-
return priorities.default
|
|
629
|
+
return priorities.default;
|
|
630
630
|
}
|
|
631
631
|
|
|
632
632
|
const sorted = entries.sort(([keyA], [keyB]) => {
|
|
633
|
-
const priorityA = getPriority(keyA)
|
|
634
|
-
const priorityB = getPriority(keyB)
|
|
635
|
-
if (priorityA !== priorityB) return priorityA - priorityB
|
|
636
|
-
return keyA.localeCompare(keyB)
|
|
637
|
-
})
|
|
633
|
+
const priorityA = getPriority(keyA);
|
|
634
|
+
const priorityB = getPriority(keyB);
|
|
635
|
+
if (priorityA !== priorityB) return priorityA - priorityB;
|
|
636
|
+
return keyA.localeCompare(keyB);
|
|
637
|
+
});
|
|
638
638
|
|
|
639
|
-
return sorted.map(([key, value]) => ({ key, value }))
|
|
639
|
+
return sorted.map(([key, value]) => ({ key, value }));
|
|
640
640
|
}
|
|
641
641
|
|
|
642
642
|
/**
|
|
@@ -649,60 +649,60 @@ function _getOrderedEnvVars(envVars) {
|
|
|
649
649
|
async function _validateEnvironmentVariables(answers, registryConfigManager) {
|
|
650
650
|
// Get framework configuration
|
|
651
651
|
// For Triton configs, look up using deploymentConfig key (e.g. 'triton-fil')
|
|
652
|
-
let frameworkConfig
|
|
652
|
+
let frameworkConfig;
|
|
653
653
|
if (answers.architecture === 'triton' && answers.deploymentConfig) {
|
|
654
|
-
const tritonEntry = registryConfigManager.frameworkRegistry?.[answers.deploymentConfig]
|
|
654
|
+
const tritonEntry = registryConfigManager.frameworkRegistry?.[answers.deploymentConfig];
|
|
655
655
|
if (tritonEntry) {
|
|
656
|
-
const versions = Object.keys(tritonEntry)
|
|
656
|
+
const versions = Object.keys(tritonEntry);
|
|
657
657
|
if (versions.length > 0) {
|
|
658
|
-
frameworkConfig = tritonEntry[versions[0]]
|
|
658
|
+
frameworkConfig = tritonEntry[versions[0]];
|
|
659
659
|
}
|
|
660
660
|
}
|
|
661
661
|
}
|
|
662
662
|
if (!frameworkConfig) {
|
|
663
|
-
frameworkConfig = registryConfigManager.frameworkRegistry?.[answers.framework]?.[answers.frameworkVersion]
|
|
663
|
+
frameworkConfig = registryConfigManager.frameworkRegistry?.[answers.framework]?.[answers.frameworkVersion];
|
|
664
664
|
}
|
|
665
665
|
|
|
666
666
|
if (!frameworkConfig || !frameworkConfig.envVars) {
|
|
667
|
-
return // No env vars to validate
|
|
667
|
+
return; // No env vars to validate
|
|
668
668
|
}
|
|
669
669
|
|
|
670
|
-
console.log('\n🔍 Validating environment variables...')
|
|
670
|
+
console.log('\n🔍 Validating environment variables...');
|
|
671
671
|
|
|
672
672
|
// Validate environment variables
|
|
673
673
|
const validationResult = registryConfigManager.validateEnvironmentVariables(
|
|
674
674
|
frameworkConfig.envVars,
|
|
675
675
|
frameworkConfig
|
|
676
|
-
)
|
|
676
|
+
);
|
|
677
677
|
|
|
678
678
|
// Display validation results
|
|
679
679
|
if (validationResult.errors && validationResult.errors.length > 0) {
|
|
680
|
-
console.log('\n❌ Environment Variable Validation Errors:')
|
|
680
|
+
console.log('\n❌ Environment Variable Validation Errors:');
|
|
681
681
|
validationResult.errors.forEach(error => {
|
|
682
|
-
console.log(` • ${error.key}: ${error.message}`)
|
|
683
|
-
})
|
|
682
|
+
console.log(` • ${error.key}: ${error.message}`);
|
|
683
|
+
});
|
|
684
684
|
}
|
|
685
685
|
|
|
686
686
|
if (validationResult.warnings && validationResult.warnings.length > 0) {
|
|
687
|
-
console.log('\n⚠️ Environment Variable Validation Warnings:')
|
|
687
|
+
console.log('\n⚠️ Environment Variable Validation Warnings:');
|
|
688
688
|
validationResult.warnings.forEach(warning => {
|
|
689
|
-
console.log(` • ${warning.key ? `${warning.key}: ` : ''}${warning.message}`)
|
|
690
|
-
})
|
|
689
|
+
console.log(` • ${warning.key ? `${warning.key}: ` : ''}${warning.message}`);
|
|
690
|
+
});
|
|
691
691
|
}
|
|
692
692
|
|
|
693
693
|
if (validationResult.strategiesUsed && validationResult.strategiesUsed.length > 0) {
|
|
694
|
-
console.log(`\n✅ Validation methods used: ${validationResult.strategiesUsed.join(', ')}`)
|
|
694
|
+
console.log(`\n✅ Validation methods used: ${validationResult.strategiesUsed.join(', ')}`);
|
|
695
695
|
}
|
|
696
696
|
|
|
697
697
|
if (!validationResult.errors || validationResult.errors.length === 0) {
|
|
698
698
|
if (!validationResult.warnings || validationResult.warnings.length === 0) {
|
|
699
|
-
console.log(' ✅ All environment variables validated successfully')
|
|
699
|
+
console.log(' ✅ All environment variables validated successfully');
|
|
700
700
|
}
|
|
701
701
|
}
|
|
702
702
|
|
|
703
703
|
// In non-interactive mode (skip-prompts), throw on errors
|
|
704
704
|
if (validationResult.errors && validationResult.errors.length > 0) {
|
|
705
|
-
throw new Error('Environment variable validation failed. Please fix the errors and try again.')
|
|
705
|
+
throw new Error('Environment variable validation failed. Please fix the errors and try again.');
|
|
706
706
|
}
|
|
707
707
|
}
|
|
708
708
|
|
|
@@ -719,60 +719,60 @@ async function _validateEnvironmentVariables(answers, registryConfigManager) {
|
|
|
719
719
|
* @param {object|null} registryConfigManager - Registry configuration manager
|
|
720
720
|
*/
|
|
721
721
|
async function _mergeEnvVarsWithPrecedence(answers, registryConfigManager) {
|
|
722
|
-
if (!registryConfigManager) return
|
|
722
|
+
if (!registryConfigManager) return;
|
|
723
723
|
|
|
724
724
|
// Capture CLI-provided env vars before merging (highest precedence)
|
|
725
|
-
const cliEnvVars = { ...answers.envVars }
|
|
725
|
+
const cliEnvVars = { ...answers.envVars };
|
|
726
726
|
|
|
727
727
|
// Resolve the framework config for the selected framework + version
|
|
728
|
-
const frameworkName = answers.framework || answers.deploymentConfig
|
|
729
|
-
const frameworkVersion = answers.frameworkVersion
|
|
730
|
-
let frameworkConfig = null
|
|
728
|
+
const frameworkName = answers.framework || answers.deploymentConfig;
|
|
729
|
+
const frameworkVersion = answers.frameworkVersion;
|
|
730
|
+
let frameworkConfig = null;
|
|
731
731
|
|
|
732
732
|
if (frameworkName && registryConfigManager.frameworkRegistry) {
|
|
733
|
-
const frameworkVersions = registryConfigManager.frameworkRegistry[frameworkName]
|
|
733
|
+
const frameworkVersions = registryConfigManager.frameworkRegistry[frameworkName];
|
|
734
734
|
if (frameworkVersions) {
|
|
735
735
|
if (frameworkVersion && frameworkVersions[frameworkVersion]) {
|
|
736
|
-
frameworkConfig = frameworkVersions[frameworkVersion]
|
|
736
|
+
frameworkConfig = frameworkVersions[frameworkVersion];
|
|
737
737
|
} else {
|
|
738
738
|
// Fall back to latest version for Triton and other non-versioned lookups
|
|
739
739
|
const versions = Object.keys(frameworkVersions).sort((a, b) =>
|
|
740
740
|
b.localeCompare(a, undefined, { numeric: true })
|
|
741
|
-
)
|
|
741
|
+
);
|
|
742
742
|
if (versions.length > 0) {
|
|
743
|
-
frameworkConfig = frameworkVersions[versions[0]]
|
|
743
|
+
frameworkConfig = frameworkVersions[versions[0]];
|
|
744
744
|
}
|
|
745
745
|
}
|
|
746
746
|
}
|
|
747
747
|
}
|
|
748
748
|
|
|
749
749
|
// Resolve the model config (exact match or pattern match)
|
|
750
|
-
let modelConfig = null
|
|
750
|
+
let modelConfig = null;
|
|
751
751
|
if (answers.modelName && registryConfigManager.modelRegistry) {
|
|
752
|
-
modelConfig = _findModelConfig(answers.modelName, registryConfigManager)
|
|
752
|
+
modelConfig = _findModelConfig(answers.modelName, registryConfigManager);
|
|
753
753
|
}
|
|
754
754
|
|
|
755
755
|
// Layer 1: catalog defaults (Image_Entry defaults.envVars)
|
|
756
|
-
const catalogDefaults = frameworkConfig?.envVars || {}
|
|
756
|
+
const catalogDefaults = frameworkConfig?.envVars || {};
|
|
757
757
|
|
|
758
758
|
// Layer 2: framework profile envVars
|
|
759
|
-
let frameworkProfileEnvVars = {}
|
|
759
|
+
let frameworkProfileEnvVars = {};
|
|
760
760
|
if (answers.frameworkProfile && frameworkConfig?.profiles) {
|
|
761
|
-
const profile = frameworkConfig.profiles[answers.frameworkProfile]
|
|
761
|
+
const profile = frameworkConfig.profiles[answers.frameworkProfile];
|
|
762
762
|
if (profile?.envVars) {
|
|
763
|
-
frameworkProfileEnvVars = profile.envVars
|
|
763
|
+
frameworkProfileEnvVars = profile.envVars;
|
|
764
764
|
}
|
|
765
765
|
}
|
|
766
766
|
|
|
767
767
|
// Layer 3: model entry envVars
|
|
768
|
-
const modelEntryEnvVars = modelConfig?.envVars || {}
|
|
768
|
+
const modelEntryEnvVars = modelConfig?.envVars || {};
|
|
769
769
|
|
|
770
770
|
// Layer 4: model profile envVars
|
|
771
|
-
let modelProfileEnvVars = {}
|
|
771
|
+
let modelProfileEnvVars = {};
|
|
772
772
|
if (answers.modelProfile && modelConfig?.profiles) {
|
|
773
|
-
const profile = modelConfig.profiles[answers.modelProfile]
|
|
773
|
+
const profile = modelConfig.profiles[answers.modelProfile];
|
|
774
774
|
if (profile?.envVars) {
|
|
775
|
-
modelProfileEnvVars = profile.envVars
|
|
775
|
+
modelProfileEnvVars = profile.envVars;
|
|
776
776
|
}
|
|
777
777
|
}
|
|
778
778
|
|
|
@@ -785,7 +785,7 @@ async function _mergeEnvVarsWithPrecedence(answers, registryConfigManager) {
|
|
|
785
785
|
...modelEntryEnvVars,
|
|
786
786
|
...modelProfileEnvVars,
|
|
787
787
|
...cliEnvVars
|
|
788
|
-
}
|
|
788
|
+
};
|
|
789
789
|
}
|
|
790
790
|
|
|
791
791
|
/**
|
|
@@ -796,23 +796,23 @@ async function _mergeEnvVarsWithPrecedence(answers, registryConfigManager) {
|
|
|
796
796
|
* @returns {object|null} Model configuration or null
|
|
797
797
|
*/
|
|
798
798
|
function _findModelConfig(modelName, registryConfigManager) {
|
|
799
|
-
if (!registryConfigManager?.modelRegistry) return null
|
|
799
|
+
if (!registryConfigManager?.modelRegistry) return null;
|
|
800
800
|
|
|
801
801
|
// Exact match first
|
|
802
|
-
const exact = registryConfigManager.modelRegistry[modelName]
|
|
803
|
-
if (exact) return exact
|
|
802
|
+
const exact = registryConfigManager.modelRegistry[modelName];
|
|
803
|
+
if (exact) return exact;
|
|
804
804
|
|
|
805
805
|
// Pattern matching with glob-style wildcards
|
|
806
806
|
for (const [pattern, config] of Object.entries(registryConfigManager.modelRegistry)) {
|
|
807
807
|
if (pattern.includes('*')) {
|
|
808
|
-
const regex = new RegExp(`^${pattern.replace(/\*/g, '.*')}$`)
|
|
808
|
+
const regex = new RegExp(`^${pattern.replace(/\*/g, '.*')}$`);
|
|
809
809
|
if (regex.test(modelName)) {
|
|
810
|
-
return config
|
|
810
|
+
return config;
|
|
811
811
|
}
|
|
812
812
|
}
|
|
813
813
|
}
|
|
814
814
|
|
|
815
|
-
return null
|
|
815
|
+
return null;
|
|
816
816
|
}
|
|
817
817
|
|
|
818
818
|
/**
|
|
@@ -824,33 +824,33 @@ function _findModelConfig(modelName, registryConfigManager) {
|
|
|
824
824
|
* @param {object} answers - Configuration answers
|
|
825
825
|
* @param {object} tritonBackends - Triton backends catalog
|
|
826
826
|
*/
|
|
827
|
-
function _generateTritonFiles(templateDir, destDir, templateVars, answers,
|
|
828
|
-
const modelName = answers.modelName || 'model'
|
|
829
|
-
const backend = answers.backend
|
|
827
|
+
function _generateTritonFiles(templateDir, destDir, templateVars, answers, _tritonBackends) {
|
|
828
|
+
const modelName = answers.modelName || 'model';
|
|
829
|
+
const backend = answers.backend;
|
|
830
830
|
|
|
831
831
|
// Copy Triton Dockerfile
|
|
832
832
|
_renderTemplate(
|
|
833
833
|
path.join(templateDir, 'triton/Dockerfile'),
|
|
834
834
|
path.join(destDir, 'Dockerfile'),
|
|
835
835
|
templateVars
|
|
836
|
-
)
|
|
836
|
+
);
|
|
837
837
|
|
|
838
838
|
// Create model repository directory structure
|
|
839
|
-
const modelRepoPath = path.join(destDir, `model_repository/${modelName}`)
|
|
840
|
-
fs.mkdirSync(path.join(modelRepoPath, '1'), { recursive: true })
|
|
839
|
+
const modelRepoPath = path.join(destDir, `model_repository/${modelName}`);
|
|
840
|
+
fs.mkdirSync(path.join(modelRepoPath, '1'), { recursive: true });
|
|
841
841
|
|
|
842
842
|
// Copy config.pbtxt
|
|
843
843
|
_renderTemplate(
|
|
844
844
|
path.join(templateDir, 'triton/config.pbtxt'),
|
|
845
845
|
path.join(modelRepoPath, 'config.pbtxt'),
|
|
846
846
|
templateVars
|
|
847
|
-
)
|
|
847
|
+
);
|
|
848
848
|
|
|
849
849
|
// Create version 1 directory with .gitkeep
|
|
850
850
|
fs.writeFileSync(
|
|
851
851
|
path.join(modelRepoPath, '1/.gitkeep'),
|
|
852
852
|
'# Placeholder for model artifacts\n'
|
|
853
|
-
)
|
|
853
|
+
);
|
|
854
854
|
|
|
855
855
|
// For triton-python backend: copy model.py and requirements.txt
|
|
856
856
|
if (backend === 'python') {
|
|
@@ -858,12 +858,12 @@ function _generateTritonFiles(templateDir, destDir, templateVars, answers, trito
|
|
|
858
858
|
path.join(templateDir, 'triton/model.py'),
|
|
859
859
|
path.join(modelRepoPath, '1/model.py'),
|
|
860
860
|
templateVars
|
|
861
|
-
)
|
|
861
|
+
);
|
|
862
862
|
_renderTemplate(
|
|
863
863
|
path.join(templateDir, 'triton/requirements.txt'),
|
|
864
864
|
path.join(destDir, 'triton/requirements.txt'),
|
|
865
865
|
templateVars
|
|
866
|
-
)
|
|
866
|
+
);
|
|
867
867
|
}
|
|
868
868
|
}
|
|
869
869
|
|
|
@@ -875,10 +875,10 @@ function _generateTritonFiles(templateDir, destDir, templateVars, answers, trito
|
|
|
875
875
|
* @param {object} vars - Template variables
|
|
876
876
|
*/
|
|
877
877
|
function _renderTemplate(src, dest, vars) {
|
|
878
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true })
|
|
879
|
-
const content = fs.readFileSync(src, 'utf8')
|
|
880
|
-
const rendered = ejs.render(content, vars, { filename: src })
|
|
881
|
-
fs.writeFileSync(dest, rendered)
|
|
878
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
879
|
+
const content = fs.readFileSync(src, 'utf8');
|
|
880
|
+
const rendered = ejs.render(content, vars, { filename: src });
|
|
881
|
+
fs.writeFileSync(dest, rendered);
|
|
882
882
|
}
|
|
883
883
|
|
|
884
884
|
/**
|
|
@@ -888,8 +888,8 @@ function _renderTemplate(src, dest, vars) {
|
|
|
888
888
|
* @param {string} dest - Destination file path
|
|
889
889
|
*/
|
|
890
890
|
function _copyFile(src, dest) {
|
|
891
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true })
|
|
892
|
-
fs.copyFileSync(src, dest)
|
|
891
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
892
|
+
fs.copyFileSync(src, dest);
|
|
893
893
|
}
|
|
894
894
|
|
|
895
895
|
/**
|
|
@@ -900,7 +900,7 @@ function _copyFile(src, dest) {
|
|
|
900
900
|
function _unlinkIfExists(filePath) {
|
|
901
901
|
try {
|
|
902
902
|
if (fs.existsSync(filePath)) {
|
|
903
|
-
fs.unlinkSync(filePath)
|
|
903
|
+
fs.unlinkSync(filePath);
|
|
904
904
|
}
|
|
905
905
|
} catch (e) {
|
|
906
906
|
// Silently continue
|
|
@@ -926,20 +926,20 @@ function _setExecutablePermissions(destDir) {
|
|
|
926
926
|
'do/register',
|
|
927
927
|
'do/ci',
|
|
928
928
|
'do/manifest'
|
|
929
|
-
]
|
|
929
|
+
];
|
|
930
930
|
|
|
931
931
|
shellScripts.forEach(script => {
|
|
932
|
-
const scriptPath = path.join(destDir, script)
|
|
932
|
+
const scriptPath = path.join(destDir, script);
|
|
933
933
|
try {
|
|
934
934
|
if (fs.existsSync(scriptPath)) {
|
|
935
|
-
const stats = fs.statSync(scriptPath)
|
|
936
|
-
const newMode = stats.mode | 0o755
|
|
937
|
-
fs.chmodSync(scriptPath, newMode)
|
|
935
|
+
const stats = fs.statSync(scriptPath);
|
|
936
|
+
const newMode = stats.mode | 0o755;
|
|
937
|
+
fs.chmodSync(scriptPath, newMode);
|
|
938
938
|
}
|
|
939
939
|
} catch (error) {
|
|
940
940
|
// Silently continue if chmod fails (e.g., on Windows)
|
|
941
941
|
}
|
|
942
|
-
})
|
|
942
|
+
});
|
|
943
943
|
}
|
|
944
944
|
|
|
945
945
|
/**
|
|
@@ -949,33 +949,33 @@ function _setExecutablePermissions(destDir) {
|
|
|
949
949
|
* @param {string} destDir - Path to the generated project directory
|
|
950
950
|
*/
|
|
951
951
|
async function _runSampleModelTraining(destDir) {
|
|
952
|
-
const trainingScriptName = 'train_abalone.py'
|
|
953
|
-
const trainingScript = path.join(destDir, `sample_model/${trainingScriptName}`)
|
|
954
|
-
const sampleModelDir = path.join(destDir, 'sample_model')
|
|
955
|
-
const requirementsFile = path.join(destDir, 'requirements.txt')
|
|
952
|
+
const trainingScriptName = 'train_abalone.py';
|
|
953
|
+
const trainingScript = path.join(destDir, `sample_model/${trainingScriptName}`);
|
|
954
|
+
const sampleModelDir = path.join(destDir, 'sample_model');
|
|
955
|
+
const requirementsFile = path.join(destDir, 'requirements.txt');
|
|
956
956
|
|
|
957
|
-
console.log('\n🤖 Training sample model...')
|
|
958
|
-
console.log('This will generate the model file needed for Docker build.')
|
|
957
|
+
console.log('\n🤖 Training sample model...');
|
|
958
|
+
console.log('This will generate the model file needed for Docker build.');
|
|
959
959
|
|
|
960
960
|
try {
|
|
961
961
|
if (!fs.existsSync(trainingScript)) {
|
|
962
|
-
console.log('⚠️ Training script not found, skipping model training')
|
|
963
|
-
return
|
|
962
|
+
console.log('⚠️ Training script not found, skipping model training');
|
|
963
|
+
return;
|
|
964
964
|
}
|
|
965
965
|
|
|
966
966
|
// Install dependencies
|
|
967
967
|
if (fs.existsSync(requirementsFile)) {
|
|
968
|
-
console.log('📦 Installing dependencies from requirements.txt...')
|
|
969
|
-
await _spawnAsync('pip', ['install', '-q', '-r', requirementsFile], { cwd: destDir })
|
|
968
|
+
console.log('📦 Installing dependencies from requirements.txt...');
|
|
969
|
+
await _spawnAsync('pip', ['install', '-q', '-r', requirementsFile], { cwd: destDir });
|
|
970
970
|
}
|
|
971
971
|
|
|
972
972
|
// Run training script
|
|
973
|
-
await _spawnAsync('python', [trainingScriptName], { cwd: sampleModelDir })
|
|
974
|
-
console.log('✅ Sample model training completed successfully!')
|
|
975
|
-
console.log(`📁 Model file saved in: ${sampleModelDir}`)
|
|
973
|
+
await _spawnAsync('python', [trainingScriptName], { cwd: sampleModelDir });
|
|
974
|
+
console.log('✅ Sample model training completed successfully!');
|
|
975
|
+
console.log(`📁 Model file saved in: ${sampleModelDir}`);
|
|
976
976
|
} catch (error) {
|
|
977
|
-
console.log('⚠️ Error during sample model training:', error.message)
|
|
978
|
-
console.log(`Please run manually: python sample_model/${trainingScriptName}`)
|
|
977
|
+
console.log('⚠️ Error during sample model training:', error.message);
|
|
978
|
+
console.log(`Please run manually: python sample_model/${trainingScriptName}`);
|
|
979
979
|
}
|
|
980
980
|
}
|
|
981
981
|
|
|
@@ -990,18 +990,18 @@ async function _runSampleModelTraining(destDir) {
|
|
|
990
990
|
*/
|
|
991
991
|
function _spawnAsync(command, args, opts = {}) {
|
|
992
992
|
return new Promise((resolve, reject) => {
|
|
993
|
-
const proc = spawn(command, args, { ...opts, stdio: 'inherit' })
|
|
993
|
+
const proc = spawn(command, args, { ...opts, stdio: 'inherit' });
|
|
994
994
|
|
|
995
995
|
proc.on('close', (code) => {
|
|
996
996
|
if (code === 0) {
|
|
997
|
-
resolve()
|
|
997
|
+
resolve();
|
|
998
998
|
} else {
|
|
999
|
-
reject(new Error(`${command} exited with code ${code}`))
|
|
999
|
+
reject(new Error(`${command} exited with code ${code}`));
|
|
1000
1000
|
}
|
|
1001
|
-
})
|
|
1001
|
+
});
|
|
1002
1002
|
|
|
1003
1003
|
proc.on('error', (error) => {
|
|
1004
|
-
reject(error)
|
|
1005
|
-
})
|
|
1006
|
-
})
|
|
1004
|
+
reject(error);
|
|
1005
|
+
});
|
|
1006
|
+
});
|
|
1007
1007
|
}
|