Registration flows through MLflow (the native Model Registry bridge)
Model and adapter registration go THROUGH MLflow: do/register and do/tune
call mlflow.register_model, and SageMaker's native bridge
(AutoModelRegistrationEnabled on the profile's MLflow App) auto-creates the
corresponding SageMaker Model Package Group + version. You no longer create the
group by hand, and there is no separate {project}-tune-models group — the tune
output and your do/register records land in ONE catalog per project.
Identity is kind-aware: a full_model (base model / full fine-tune) is its
own MLflow registered model → its own bridge MPG, carrying an inference spec
(deployable via do/add-ic / do/add-pod); a lora_adapter is linked to its
base via a native lineage edge with NO inference spec (hot-loaded via
do/adapter). The bridge group is {registered-model-name}-{hash}. Every
version carries an mlcc.artifact_kind tag so the additive-deploy verbs gate by
it (ADR-013): do/adapter refuses a full_model and points you at do/add-ic.
Enable + migration: the bridge needs UpdateModelPackage / CreateAction /
AddAssociation on the core role (BL164) — run
ml-container-creator bootstrap update --module core once (~15 min to
propagate). Pre-BL164 groups ({project}, {project}-tune-models) are left in
place and stay resolvable by ARN; registration is go-forward only.
Model Registration (MPG)¶
do/register records what you deploy as versioned Model Packages — base model, adapters, deployment context, and lineage — THROUGH the MLflow → SageMaker Model Registry bridge (see the note above). The resulting Model Package is the system of record; use it to audit what's running, govern versions, and feed the CI system with testable configurations.
The local deployment registry was removed
Earlier versions also wrote a machine-local registry.json deployment-history
file and shipped an mcc registry command. Both were removed — SageMaker MPG is
now the single registration record. do/register dataset and
do/register evaluator still use intentionally-local stores (there is no
SageMaker API equivalent); those are unaffected.
Quick Start¶
# Register a successful deployment
./do/register
# Register base model only (skip adapters)
./do/register --base-only
# Register a dataset from the last tune job
./do/register dataset --from-tune sft
# Register an evaluator
./do/register evaluator my-reward --type lambda --arn arn:aws:lambda:... --technique rlvr
# Register with notes
./do/register --notes "Upgraded to vLLM 0.8.5, 20% latency improvement"
# Register a partial success (e.g., deploy worked but tune failed)
./do/register --status partial --notes "Tune OOM on 32B model"
# Output as JSON (for scripting)
./do/register --json
# Register to CI table (DynamoDB)
./do/register --ci
What Gets Captured¶
Every registration records:
| Field | Source | Example |
|---|---|---|
| Project name | do/config |
qwen3-4b-vllm |
| Deployment config | do/config |
transformers-vllm |
| Architecture | Derived from config | transformers |
| Backend | Derived from config | vllm |
| Model name | do/config |
Qwen/Qwen3-4B |
| Instance type | do/config |
ml.g5.xlarge |
| Region | do/config |
us-east-1 |
| Deployment target | do/config |
realtime-inference |
| Base image | do/config |
vllm/vllm-openai:v0.8.5 |
| IC list | do/ic/*.conf |
All inference components + adapters |
| Parameters | Environment variables | Engine-specific env vars (secrets redacted) |
| Status | --status flag |
success, partial, failed |
| Notes | --notes flag |
Free-text |
| Generator version | npm global install | the installed @aws/ml-container-creator version |
Flags¶
| Flag | Description |
|---|---|
--status <value> |
One of: success, partial, failed (default: success) |
--notes "text" |
Free-text annotation |
--json |
Output deployment record as JSON to stdout |
--ci |
Write to CI DynamoDB table (implies --json) |
--ci-table <name> |
Override CI table name (default: mlcc-ci-table) |
--build-strategy <value> |
Record how the image was built (default: codebuild-submit) |
--project |
Include project-level metadata |
--base-only |
Register the base model only — skip adapter registration loop |
--exclude <name> |
Skip specific adapters (repeatable or comma-separated) |
Where registrations are stored¶
do/register creates versioned Model Packages in the project's SageMaker MPG ({project-name}). Query them with the AWS CLI or console, or via the helper:
With --ci, the registration is additionally written to the CI DynamoDB table (mlcc-ci-table by default) for the end-to-end test harness.
Subcommands¶
do/register supports three subcommands: model (default), dataset, and evaluator.
Model Registration (default)¶
When called without a subcommand (or with model), registers the deployed model as a versioned Model Package in SageMaker, then registers all adapters from do/adapters/*.conf.
ECR image optional
MPG registration works even if the container hasn't been pushed to ECR yet. When no valid ECR image URI is available (e.g. before do/build + do/push), the Model Package is created without an InferenceSpecification — metadata (instance type, deployment config, model name) is still captured in CustomerMetadataProperties.
# Register base model + all adapters
./do/register
# Register base model only
./do/register --base-only
# Register all adapters except a specific one
./do/register --exclude llama-factory
# Exclude multiple adapters
./do/register --exclude "llama-factory,experimental-v1"
Each adapter in do/adapters/*.conf is registered as a linked ModelPackage version with isAdapter=true and parentModelVersionArn pointing to the base model version.
Deploying from Registry¶
Use do/adapter add --from-registry to pull a previously registered adapter back into a project and deploy it as an inference component:
# Deploy using a specific version ARN from the project's registry
./do/adapter add my-sft --from-registry arn:aws:sagemaker:us-west-2:123456789012:model-package/my-project-c76813/24
# Interactive selection (queries the project's registry for adapter versions)
./do/adapter add --from-registry
Artifact-kind gate (ADR-013)
--from-registry resolves the version's mlcc.artifact_kind and REFUSES a
full_model — a full fine-tuned model cannot be hot-loaded as a LoRA adapter;
use do/add-ic (realtime) or do/add-pod (cluster) for those. An adapter
version (lora_adapter) is accepted. A legacy version with no recorded
artifact kind falls back to an adapter_config.json content check rather than
being hard-blocked.
With the MLflow bridge (BL164) there is ONE registry group per project
({registered-model-name}-{hash}) — the old split between a "deployment MPG"
and a {project}-tune-models MPG is gone. List adapter versions with:
What --from-registry does:
- Calls
get-versionwith the provided ARN to retrieve the version metadata (incl.mlcc.artifact_kind) - Runs the artifact-kind gate — refuses a
full_model, accepts alora_adapter - Reads
modelDataUrlfromCustomerMetadataProperties(the adapter weights S3 path) - Creates/updates
do/adapters/<name>.confwith the retrieved weights URI - Deploys the adapter as an inference component on the running endpoint
Prerequisites:
- The adapter must be registered in the project's registry (run
./do/registerfirst) - The endpoint must be deployed and InService
- The adapter weights must still exist at the registered S3 path
Workflow: Register once, deploy anywhere
# Project A: tune, stage, register
./do/tune --technique sft --dataset "hf://tatsu-lab/alpaca"
./do/adapter add tuned-sft --from-tune sft
./do/register
# Project B (or same project, fresh deployment): pull from registry
./do/adapter add my-sft --from-registry arn:aws:sagemaker:...:model-package/my-project/24
./do/test --adapter my-sft
./do/benchmark --adapter my-sft
This enables adapter portability across deployments, instance types, and even vLLM versions — as long as the base model architecture is compatible.
Dataset Registration¶
do/register dataset moves a dataset into the canonical MLCC datasets location
on the mlcc-core bucket and registers it so it can be reused across tuning and
optimization jobs. The canonical destination is:
When MLflow is configured, a dataset is registered as a first-class
mlflow.data Dataset in the project's experiment (browsable in the MLflow
Experiment Datasets tab), alongside the run-input lineage edge — MLflow is the
record of truth (BL167). The S3 sidecar beside the data at
s3://<CORE_BUCKET>/datasets/<name>/_dataset.json is written first as the durable
local record and the fallback when MLflow is unconfigured; it also remains the
version ledger (@v<N> / semver pinning resolves from it). It replaces the
legacy local ~/.ml-container-creator/datasets.json index (removed).
Record of truth + reconcile (no sync daemon)
Registration writes the local sidecar first (always), then MLflow. If the
MLflow App is briefly unreachable, do/register warns ("saved locally —
re-run to reconcile") and still succeeds — it never fails on App downtime. A
plain re-run of do/register converges MLflow to the local record (the
write is idempotent). A real error (bad input, permissions) fails loudly.
There is no background sync — you own reconciliation by re-running.
CORE_BUCKET is the mlcc-core bucket recorded in your active profile as
codebuildSourceS3Bucket (e.g. mlcc-core-<account>-<region>), provisioned by
ml-container-creator bootstrap.
Studio discoverability (native AI Registry)
After the sidecar is written, do/register dataset additively registers
the dataset in SageMaker's native AI Registry so it appears in Studio
Assets — pointing at the same canonical S3 URI (no copy). This is
best-effort: if it is skipped (unsupported region, old sagemaker SDK, or a
benchmark/evaluator technique) or fails, the sidecar still stands and the
dataset is fully usable. Studio visibility requires a Studio domain:
provision the sagemaker-domain bootstrap module so its DomainId is
captured into your profile and passed as the asset's domain tag. Datasets and
evaluators live in the AI Registry; models live in the Model Package
Group (never hub-mirrored). See
Dataset registry in the fine-tuning guide
for native versioning, technique→member mapping, and lineage details.
Provide exactly one input source:
# 1. Existing S3 dataset — copied to the canonical location, then registered.
# A single object is copied with `aws s3 cp`; a directory/prefix (trailing
# slash) is copied with `aws s3 sync`.
./do/register dataset alpaca-sft-1k \
--s3-uri s3://my-bucket/datasets/train.jsonl \
--technique sft \
--row-count 1000
# 2. HuggingFace dataset — staged to the canonical location via the stage-hf
# Processing Job, then registered. (do/tune no longer stages hf:// itself.)
./do/register dataset guanaco \
--hf-id timdettmers/openassistant-guanaco \
--technique sft \
--row-count 500 # forwarded to staging as --take
# Register from the last tune job (auto-derives name, URI, technique, row count)
./do/register dataset --from-tune sft
./do/register dataset --from-tune dpo
# Register from the last custom training job (do/train output)
./do/register dataset --from-train sft
# List registered datasets (from the S3 sidecars)
./do/register dataset --list
| Flag | Description |
|---|---|
<name> |
Dataset name (positional, or use --name). Also the canonical S3 key: datasets/<name>/ |
--s3-uri <s3://...> |
Existing S3 dataset. Copied to the canonical location (cp for an object, sync for a prefix). Mutually exclusive with --hf-id |
--hf-id <org/name> |
HuggingFace dataset ID. Staged to the canonical location via the stage-hf Processing Job. Mutually exclusive with --s3-uri |
--technique <tech> |
Technique: sft, dpo, rlvr, rlaif, mtrl, benchmark (default: benchmark). sft/dpo register under their own native member; rlvr/rlaif/mtrl under the RLVR member; benchmark is sidecar-only |
--row-count <n> |
Number of records. With --hf-id, passed to staging as --take |
--format <fmt> |
Format: jsonl, parquet, csv (default: jsonl) |
--column-schema <json> |
Column schema as JSON string |
--description <text> |
Human description shown on the native asset in Studio Assets |
--hf-split <split> |
(--hf-id only) Dataset split to stage (default: train) |
--column-map <map> |
(--hf-id only) Rename columns, e.g. prompt=question,completion=answer |
--attribution <text> |
Custom metadata: attribution (recorded under customMetadata) |
--lineage <text> |
Custom metadata: lineage |
--origination <text> |
Custom metadata: origination (e.g. hf://org/name@rev) |
--application <text> |
Custom metadata: application |
--force |
Register a new version even if the content hash is unchanged |
--list |
List registered datasets and exit. When MLflow is configured the read source is the shared MLflow catalog scoped to the active project; otherwise the S3 sidecars |
--all |
With --list: list datasets across every project under the profile, not just the active one |
--from-tune [technique] |
Auto-populate --s3-uri from the last tune job's persisted state |
One source only
--s3-uri and --hf-id are mutually exclusive. Exactly one must be
provided (or --from-tune, which resolves an s3:// URI). Both paths end
with the canonical s3://<CORE_BUCKET>/datasets/<name>/ URI being registered
and an S3 sidecar written beside it.
Idempotent versioning
Registering unchanged content (same content hash) without --force is a
no-op — no new sidecar version is created.
Shared MLflow catalog, grouped per project
When MLflow tracking is configured, the catalog is shared across the whole
bootstrap profile (one serverless MLflow store per profile). Runs are
grouped into a per-project experiment named after your project, so
do/register dataset --list and name resolution default to the active
project and do not collide across projects. Pass --all to list across every
project under the profile. Datasets registered before this grouping existed
live in the default experiment and remain visible (reads include both).
The do/tune flow reaches this same shared catalog automatically: the
profile's MLflow reference is resolved for you at runtime (no manual env var
to set). If your profile has no MLflow store configured, tuning proceeds
without MLflow tracking — it never falls back to a local tracking file.
Evaluator Registration¶
Register a reward function (RLVR) or preference model (RLAIF):
./do/register evaluator my-reward-fn \
--type lambda \
--arn arn:aws:lambda:us-west-2:123456789012:function:my-reward \
--technique rlvr \
--description "Custom reward function for code quality"
| Flag | Description |
|---|---|
<name> |
Evaluator name (positional, or use --name) |
--type <type> |
Type: lambda or model (required) |
--arn <arn> |
Lambda ARN or model S3 URI (required) |
--technique <tech> |
Technique: rlvr or rlaif (required) |
--description <text> |
Optional description |
Reward prompts also land in the MLflow Prompt Registry
A reward prompt (do/register prompt, RLAIF LLM-as-judge) is registered to
SageMaker's AI Registry (the Studio record + the reference do/tune resolves)
AND additively to the MLflow GenAI Prompt Registry — versioned,
browsable in the MLflow Prompts tab (BL167). The MLflow copy follows the same
local-first/reconcile-on-re-run hybrid. Reward functions (code/Lambda) have
no MLflow home and register only to the AI Registry.
Backward Compatibility
The older flag-based syntax (./do/register --dataset --dataset-name ... and ./do/register --evaluator --evaluator-name ...) still works but is deprecated in favor of subcommands.
To list registered model/adapter versions, query the MPG with the AWS CLI (aws sagemaker list-model-packages --model-package-group-name <project-name>) or the list-adapters helper shown above.
CI Table (DynamoDB)¶
With --ci, the record is written to a DynamoDB table (provisioned by the ci module — ml-container-creator bootstrap add-module ci). Each record gets a deterministic configId — a hash of deploymentConfig:modelName:instanceType:region:deploymentTarget:icCount:adapterCount.
If the configId already exists, the record is updated and testStatus is reset to untested — signaling the CI harness to re-validate.
# Register to CI table
./do/register --ci
# Use a custom table name
./do/register --ci --ci-table my-custom-table
CI Infrastructure Required
The CI table must exist before --ci works. Run ml-container-creator bootstrap with CI enabled to provision it. See Bootstrap for details.
Multi-IC and Adapter Tracking¶
For realtime-inference projects, the registry captures all inference components from do/ic/*.conf and all adapters from do/adapters/*.conf:
{
"icList": [
{"name": "default", "image": "qwen3-4b-latest", "gpuCount": 1, "copyCount": 1},
{"name": "tuned-sft", "isAdapter": true, "baseIcName": "default", "artifactUrl": "s3://..."}
]
}
In CI mode, only the first IC (alphabetically) is included to keep validation costs down.
Parameter Capture¶
The registry captures environment variables relevant to each architecture:
- Transformers/Diffusors — Engine-prefixed vars (
VLLM_*,SGLANG_*, etc.) +HF_MODEL_ID - HTTP — All non-system env vars from
do/config - Triton —
config.pbtxtcontent +TRITON_MODEL_REPOSITORY
Sensitive values (HF_TOKEN, AWS_SECRET_ACCESS_KEY, anything containing SECRET or TOKEN) are automatically redacted to ***REDACTED***.
Typical Workflow¶
# 1. Deploy
./do/build && ./do/push && ./do/deploy
# 2. Test
./do/test
# 3. Register (after confirming it works)
./do/register --notes "Initial deployment, inference validated"
# 4. Later: upgrade base image, re-deploy, re-register
./do/register --notes "vLLM 0.8.5 upgrade"
For CI pipelines, registration happens automatically as part of do/ci.