Template System Documentation¶
This directory contains EJS templates that are processed and copied to generate complete ML container projects.
How Templates Work¶
Template Processing¶
All files in this directory are processed using EJS (Embedded JavaScript) templating:
<%= variable %> <%# Outputs escaped value %>
<%- variable %> <%# Outputs unescaped value %>
<% if (condition) { %>
Conditional content
<% } %>
Available Variables¶
All user answers from the prompting phase are available in templates:
| Variable | Type | Description | Example Values |
|---|---|---|---|
projectName |
string | Project name | my-ml-model |
destinationDir |
string | Output directory | ./my-ml-model-2024-12-02 |
framework |
string | ML framework | sklearn, xgboost, tensorflow, transformers |
modelFormat |
string | Model serialization format | pkl, joblib, json, keras, h5 |
modelServer |
string | Model serving framework | flask, fastapi, vllm, sglang |
includeSampleModel |
boolean | Include sample model | true, false |
includeTesting |
boolean | Include test suite | true, false |
testTypes |
string[] | Selected test types | ['local-model-cli', 'hosted-model-endpoint'] |
deployTarget |
string | Deployment target | sagemaker |
instanceType |
string | Instance configuration | cpu-optimized, gpu-enabled |
awsRegion |
string | AWS region | us-east-1 |
buildTimestamp |
string | Generation timestamp | 2024-12-02T15-30-45 |
Directory Structure¶
templates/
├── code/ # Model serving code
│ ├── flask/ # Flask-specific implementation
│ ├── model_handler.py # Model loading and inference (traditional ML)
│ ├── serve.py # Flask/FastAPI server (traditional ML)
│ ├── serve # vLLM/SGLang entrypoint (transformers)
│ └── start_server.py # Server startup script (traditional ML)
├── deploy/ # Deployment scripts
│ ├── build_and_push.sh # Build Docker image and push to ECR
│ ├── deploy.sh # Deploy to SageMaker endpoint
│ └── upload_to_s3.sh # Upload model to S3 (transformers only)
├── sample_model/ # Optional sample training code
│ ├── train_abalone.py # Sample model training
│ └── test_inference.py # Sample inference testing
├── test/ # Optional test suite
│ ├── test_endpoint.sh # Test hosted SageMaker endpoint
│ ├── test_local_image.sh # Test local Docker container
│ └── test_model_handler.py # Unit tests for model handler
├── Dockerfile # Container definition
├── nginx.conf # Nginx configuration (traditional ML only)
└── requirements.txt # Python dependencies
Conditional File Inclusion¶
Files are conditionally included based on user configuration:
Transformers Configuration¶
When framework === 'transformers':
- Excluded: Traditional ML serving files
- code/model_handler.py
- code/serve.py
- code/start_server.py
- nginx.conf
- requirements.txt (uses transformer-specific version)
- test/test_local_image.sh
- test/test_model_handler.py
- Included: Transformer-specific files
- code/serve (vLLM/SGLang entrypoint)
- deploy/upload_to_s3.sh
Traditional ML Configuration¶
When framework !== 'transformers':
- Excluded: Transformer-specific files
- code/serve
- deploy/upload_to_s3.sh
- Included: Traditional ML serving files
- All Flask/FastAPI serving code
- Nginx configuration
- Model handler
Optional Modules¶
- Sample Model: Excluded if
includeSampleModel === false - Test Suite: Excluded if
includeTesting === false - Flask Code: Excluded if
modelServer !== 'flask'
Template Examples¶
Using Variables in Shell Scripts¶
#!/bin/bash
# deploy/build_and_push.sh
PROJECT_NAME="<%= projectName %>"
REGION="<%= awsRegion %>"
echo "Building ${PROJECT_NAME} for region ${REGION}"
Conditional Content in Python¶
# code/model_handler.py
<% if (framework === 'sklearn') { %>
import joblib
model = joblib.load(model_path)
<% } else if (framework === 'xgboost') { %>
import xgboost as xgb
model = xgb.Booster()
model.load_model(model_path)
<% } %>
Using Arrays¶
# test/test_endpoint.sh
<% if (testTypes.includes('hosted-model-endpoint')) { %>
echo "Testing hosted endpoint..."
aws sagemaker-runtime invoke-endpoint \
--endpoint-name <%= projectName %>-endpoint \
--body file://test_data.json \
output.json
<% } %>
Adding New Templates¶
1. Create Template File¶
Add your template file in the appropriate directory:
2. Use EJS Syntax¶
# templates/code/my_new_file.py
"""
Generated for <%= projectName %>
Framework: <%= framework %>
"""
<% if (framework === 'sklearn') { %>
# sklearn-specific code
<% } %>
3. Add Conditional Exclusion (if needed)¶
In generators/app/index.js, add to ignorePatterns:
4. Update This Documentation¶
Document the new template and when it's included/excluded.
Best Practices¶
Template Design¶
- Keep templates simple - Complex logic belongs in the generator
- Use descriptive variable names - Make templates self-documenting
- Add comments - Explain why conditional logic exists
- Test all paths - Verify templates work for all configurations
Variable Usage¶
- Escape output by default - Use
<%= %>unless you need HTML - Validate in generator - Don't assume variables exist in templates
- Provide defaults - Use
<%= variable || 'default' %>
File Organization¶
- Group related files - Keep similar templates together
- Use subdirectories - Organize by feature or component
- Name clearly - File names should indicate purpose
Testing Templates¶
Manual Testing¶
# Link generator locally
npm link
# Run generator
yo ml-container-creator
# Test different configurations
# - sklearn + flask
# - xgboost + fastapi
# - transformers + vllm
# - With/without sample model
# - With/without tests
Automated Testing¶
See test/ directory for generator tests that verify template generation.
Troubleshooting¶
Template Not Copied¶
- Check if file matches an ignore pattern
- Verify file is in templates directory
- Check for EJS syntax errors
Variables Not Replaced¶
- Ensure variable exists in
this.answers - Check EJS syntax:
<%= variable %>not{{ variable }} - Verify template is processed with
copyTplnotcopy
Conditional Logic Not Working¶
- Test condition in generator first
- Use
console.log()to debug values - Check for typos in variable names