AWS AI ML
AWSOfficialPublished by AWS
- Skill
- Tool integration
- Cloud and DevOps
- Apache-2.0
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured framework for managing the Amazon SageMaker model lifecycle. It covers planning, dataset preparation, fine-tuning, model evaluation, and endpoint deployment. The skill assists users in navigating SageMaker-specific workflows, including selecting base models from catalogs, configuring training and processing jobs, and diagnosing endpoint health. It supports various fine-tuning techniques and deployment pathways, ensuring users follow validated procedures for their machine learning projects on AWS.
What it does
- Guides users through the end-to-end lifecycle of SageMaker model customization.
- Assists in selecting and evaluating base models from SageMaker JumpStart and public hubs.
- Provides workflows for validating and transforming training and evaluation datasets.
- Supports configuration of fine-tuning techniques like SFT, DPO, RLVR, and RLAIF.
- Facilitates the deployment of models to SageMaker endpoints or Bedrock.
- Diagnoses endpoint issues including latency, errors, and container logs.
When to use it
- When planning a new model customization or deployment project on SageMaker.
- When you need to validate or transform datasets for training or evaluation.
- When selecting a base model or choosing a fine-tuning technique.
- When you need to configure training, processing, or evaluation jobs.
- When deploying a model to a SageMaker endpoint or troubleshooting an existing one.
- When setting up or managing a SageMaker Managed MLflow environment.
Advantages
- Provides structured, step-by-step workflows for complex SageMaker tasks.
- Ensures dataset formatting meets specific SageMaker requirements.
- Offers clear routing to relevant documentation based on user intent.
- Helps resolve configuration issues for IAM, S3, and SDK settings.
- Simplifies the diagnosis of endpoint performance and failure logs.
Usage examples
- “I need to fine-tune a model on SageMaker. Where do I start?”
- The agent will initiate the planning workflow, prompting you to define your use case and requirements before guiding you through model selection and dataset preparation.
- “My SageMaker endpoint is returning errors. Can you help?”
- The agent will route you to the endpoint diagnostics workflow to collect logs, check instance status, and analyze latency or failure metrics.
- “How do I convert my CSV training data for SageMaker?”
- The agent will guide you through the dataset transformation workflow, recommending the appropriate tools to convert your data into the required JSONL format.
- “Which base model should I use for a text summarization task?”
- The agent will trigger the model selection workflow to help you evaluate available models and resolve the correct Hub model ID for your specific requirements.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/aws/aws-ai-ml/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip aws-ai-ml.zip -d .agents/skills
Documentation by AWS
AWS AI/ML Model Customization
Domain expertise for fine-tuning and deploying models on Amazon SageMaker. Covers the full model customization lifecycle from planning through production deployment.
Routing
Match the user's intent to the appropriate reference folder and load only that content.
| User intent | Reference | When to use |
|---|---|---|
| Plan a model customization project, discover scope of work, resume or modify a plan | references/planning/ | User's request relates to model customization or deployment (fine-tuning, training, building, customizing, reviewing data, deploying or standing up a model — including selecting or deploying an off-the-shelf or base model with no training — or getting advice on approach). Always co-activate with other intents to discover full scope. Load this reference FIRST when the request matches multiple rows in this table — read its plan templates before routing to a single-action reference. |
| Define the business problem, success criteria, or use case spec | references/use-case-specification/ | User says "define my use case", "capture requirements", "what should I decide up front", or as default first step in any plan. Skip only if user explicitly declines. |
| Select or change a base model | references/model-selection/ | User asks which model to use, mentions a model name or family, or wants to evaluate what's available. Always activate model-selection even for known model names because the exact Hub model ID must be resolved. Recommended: route to use-case-specification first to capture requirements — this produces better filtering results. Routing to use-case-specification first is not required if user provides a specific model name/ID or declines. If intent is ambiguous (fine-tune vs deploy as-is), model-selection MUST confirm which path before proceeding. Base model filtering for deployment MUST go through select-for-deployment.md and its scripts for any final recommendation. |
| Choose a fine-tuning technique (SFT, DPO, RLVR, RLAIF) | references/finetuning-technique/ | User has decided to fine-tune and needs to choose a technique, or technique needs validation against the selected model's recipes. Requires a base model to be selected first. |
| Validate dataset quality and format | references/dataset-evaluation/ | User says "is my dataset okay", "check my training data", "I have my own data", or before starting any fine-tuning job. |
| Transform or convert a dataset between formats | references/dataset-transformation/ | User says "transform", "convert", "reformat", or dataset schema needs to change. Always use this rather than writing inline transformation code. |
| Generate fine-tuning code and start training | references/finetuning/ | User says "start training", "fine-tune my model", "I'm ready to train", or plan reaches the finetuning step. Supports SFT, DPO, RLVR, RLAIF trainers. |
| Train a model with a SageMaker Training job | references/training-jobs/ | Use when the goal is to fit or update model parameters and produce model artifacts or checkpoints. Covers recipes, JumpStart traditional ML, custom training scripts or containers, distributed training, and training-job diagnosis. |
| Process data with a SageMaker Processing job | references/processing-jobs/ | Use when the goal is preprocessing, feature engineering, evaluation, transformation, or ETL without model training as the primary operation. Covers scripts, framework processors, Spark, custom containers, Athena, Redshift, and processing-job diagnosis. |
| Evaluate or benchmark a trained model | references/model-evaluation/ | User says "evaluate my model", "run a benchmark", "test model performance", "compare models". Supports LLM-as-Judge and Custom Scorer. |
| Deploy, benchmark, or optimize a model on an endpoint or Bedrock | references/model-deployment/ | User says "deploy my model", "create an endpoint", or "make it available" (plain deploy) — or, for the inference-optimization sub-workflows on SageMaker Real-Time Endpoints only, "benchmark my endpoint" / "compare benchmark runs" (benchmarking), or states a performance/cost/latency/throughput goal for a new deployment such as "find the cheapest instance" (recommendations). Handles Nova vs OSS deployment pathways. |
| Set up IAM roles, S3 buckets, SDK configuration | references/sdk-getting-started/ | User says "set up", "getting started", "check my environment", "configure SDK", or as first step in any plan involving SageMaker training/evaluation/deployment. |
| Manage project directory and artifacts | references/directory-management/ | Starting a new project, resuming existing one, or when PLAN.md needs to be associated with a project directory. |
| Set up, update, or delete a SageMaker Managed MLflow app | references/manage-mlflow/ | User says "set up MLflow", "create MLflow app", "update my MLflow app", "delete my MLflow app", "I need an MLflow server", asks "what is SageMaker MLflow", or a workflow needs an MLflow backend and none is connected. |
| Diagnose a failing or unhealthy SageMaker endpoint | references/endpoint-diagnostics/ | User reports endpoint errors, latency, inference failures, or a deployment that failed. "What's the status of my endpoint?", "Is my endpoint erroring?", "My endpoint failed — why?", "How many instances are running behind my endpoint?", "Is the latency my model or SageMaker?", "Show me the container logs for my endpoint." NOT for training-job issues, endpoint deletion, scaling changes, or new deployments. |
Rules
Service boundary. This skill covers SageMaker, including deployment of a customized model to Bedrock. Bedrock-native work (invoking Bedrock's managed foundation models, Bedrock Agents, Knowledge Bases, Guardrails, AgentCore) is supported by AWS but has no validated workflow in this skill: let the user know and proceed with best-effort guidance using general AWS knowledge. If the user asks for a model on AWS without naming a service, compare SageMaker and Bedrock or ask one service-choice question. Do not select a service by default. The two services have different model catalogs, deployment paths, and pricing, and a default selection can send the user into an incorrect workflow.
Progressive disclosure. Load only the reference folder relevant to the current user intent. Do not load all references at once.
Best-effort help. If the user's request falls outside this skill's references, do not dead-end the conversation. Help them using general AWS knowledge and documentation, and inform the user that the guidance is not covered by this skill's validated workflows.
Usage attribution. Before running any AWS CLI command or packaged script, set
export AWS_SDK_UA_APP_ID=AWSSkill-SageMaker.
Files
- ATTRIBUTION.mdAdded by AutoGineer883 B
- LICENSELicence file added by AutoGineer9.9 KiB
- NOTICELicence file added by AutoGineer67 B
- SKILL.md8.1 KiB
- references/dataset-evaluation/overview.md5.4 KiB
- references/dataset-evaluation/references/custom-scorer-evaluation-dataset-formats.md3.6 KiB
- references/dataset-evaluation/references/strategy_data_requirements.md4.8 KiB
- references/dataset-evaluation/scripts/format_detector.pyScript26.7 KiB
- references/dataset-transformation/code_templates/transformation.pyScript1.5 KiB
- references/dataset-transformation/overview.md13.3 KiB
- references/dataset-transformation/references/code_output_guide.md2.8 KiB
- references/dataset-transformation/references/dataset_transformation_code.md5.2 KiB
- references/dataset-transformation/references/sagemaker_dataset_formats.md2.9 KiB
- references/dataset-transformation/scripts/transformation_tools.pyScript4.8 KiB
- references/directory-management/overview.md1.9 KiB
- references/endpoint-diagnostics/overview.md3.5 KiB
- references/endpoint-diagnostics/references/minimum_iam_policy.md1.2 KiB
- references/endpoint-diagnostics/scripts/collect_diagnostics.pyScript12.8 KiB
- references/finetuning-technique/overview.md2.1 KiB
- references/finetuning-technique/references/finetune_technique_selection_guide.md2.3 KiB
- references/finetuning-technique/scripts/get_recipes.pyScript817 B
- references/finetuning/code_templates/dpo.pyScript6.4 KiB
- references/finetuning/code_templates/rlaif_builtin.pyScript5.8 KiB
- references/finetuning/code_templates/rlaif_custom_prompt.pyScript6.3 KiB
- references/finetuning/code_templates/rlvr.pyScript6.5 KiB
- references/finetuning/code_templates/sft.pyScript6.1 KiB
- references/finetuning/overview.md11.4 KiB
- references/finetuning/references/code_output_guide.md3.2 KiB
- references/finetuning/references/continuous_customization.md8.4 KiB
- references/finetuning/references/eula_links.md7.3 KiB
- references/finetuning/references/rlaif_guide.md3.8 KiB
- references/finetuning/references/rlvr_reward_function.md10.0 KiB
- references/finetuning/scripts/mlflow_reference.pyScript841 B
- references/finetuning/templates/nova_rlvr_reward_function_source_template.pyScript12.8 KiB
- references/finetuning/templates/rlvr_reward_function_source_template.pyScript9.1 KiB
- references/manage-mlflow/overview.md4.8 KiB
- references/manage-mlflow/references/app-creation-workflow.md3.2 KiB
- references/manage-mlflow/references/app-deletion-workflow.md2.3 KiB
- references/manage-mlflow/references/app-discovery-workflow.md1.5 KiB
- references/manage-mlflow/references/app-update-workflow.md1.7 KiB
- references/manage-mlflow/references/sm-mlflow-guide.md1.3 KiB
- references/model-deployment/code_templates/deploy-jumpstart-sagemaker.pyScript7.5 KiB
- references/model-deployment/code_templates/deploy-nova-bedrock.pyScript2.0 KiB
- references/model-deployment/code_templates/deploy-nova-sagemaker.pyScript1.9 KiB
- references/model-deployment/code_templates/deploy-oss-bedrock.pyScript4.0 KiB
- references/model-deployment/code_templates/deploy-oss-sagemaker.pyScript2.6 KiB
- references/model-deployment/code_templates/optimize-benchmark.pyScript4.6 KiB
- references/model-deployment/code_templates/optimize-huggingface-to-s3.pyScript2.0 KiB
- references/model-deployment/code_templates/optimize-recommendation.pyScript6.4 KiB
- references/model-deployment/overview.md16.6 KiB
- references/model-deployment/references/code_output_guide.md3.2 KiB
- references/model-deployment/references/deploy-jumpstart-sagemaker.md14.3 KiB
- references/model-deployment/references/deploy-nova-bedrock.md4.4 KiB
- references/model-deployment/references/deploy-nova-sagemaker.md5.8 KiB
- references/model-deployment/references/deploy-oss-bedrock.md5.2 KiB
- references/model-deployment/references/deploy-oss-sagemaker.md12.9 KiB
- references/model-deployment/references/model-licenses.md11.9 KiB
- references/model-deployment/references/optimization/benchmark-workflow.md12.7 KiB
- references/model-deployment/references/optimization/benchmarking-guidance.md8.9 KiB
- references/model-deployment/references/optimization/huggingface-to-s3.md3.1 KiB
- references/model-deployment/references/optimization/interpreting-results.md8.5 KiB
- references/model-deployment/references/optimization/recommendation-deploy.md1.5 KiB
- references/model-deployment/references/optimization/recommendation-options.md3.1 KiB
- references/model-deployment/references/optimization/recommendation-workflow.md10.3 KiB
- references/model-evaluation/code_templates/custom_scorer_evaluator.pyScript2.5 KiB
- references/model-evaluation/code_templates/llmaaj_evaluator.pyScript2.8 KiB
- references/model-evaluation/overview.md5.2 KiB
- references/model-evaluation/references/code_output_guide.md3.3 KiB
- references/model-evaluation/references/create-reward-function.md3.0 KiB
- references/model-evaluation/references/custom-lambda-scorer.md4.4 KiB
- references/model-evaluation/references/custom-scorer-evaluation.md10.5 KiB
- references/model-evaluation/references/evaluation-type-guide.md10.1 KiB
- references/model-evaluation/references/llmaaj-builtin-evaluation.md5.0 KiB
- references/model-evaluation/references/llmaaj-custom-evaluation.md2.5 KiB
- references/model-evaluation/references/llmaaj-evaluation.md14.6 KiB
- references/model-evaluation/references/supported-judge-models.md2.9 KiB
- references/model-evaluation/scripts/nova_reward_function_source_template.pyScript13.1 KiB
- references/model-evaluation/scripts/reward_function_source_template.pyScript9.2 KiB
- references/model-evaluation/scripts/validate_custom_metrics.pyScript3.5 KiB
- references/model-selection/overview.md10.3 KiB
- references/model-selection/references/benchmarks/agentic-index.md2.8 KiB
- references/model-selection/references/benchmarks/coding-index.md2.8 KiB
- references/model-selection/references/benchmarks/gpqa.md2.8 KiB
- references/model-selection/references/benchmarks/hle.md2.8 KiB
- references/model-selection/references/benchmarks/ifbench.md2.8 KiB
- references/model-selection/references/benchmarks/intelligence-index.md2.8 KiB
- references/model-selection/references/benchmarks/mmmu-pro.md3.0 KiB
- references/model-selection/references/benchmarks/tau2.md2.8 KiB
- references/model-selection/references/model-licenses.md7.3 KiB
- references/model-selection/references/model-selection.md8.2 KiB
- references/model-selection/references/select-for-deployment.md19.8 KiB
- references/model-selection/scripts/filter_deployable_models.pyScript10.1 KiB
- references/model-selection/scripts/get_deployable_models.pyScript21.7 KiB
- references/model-selection/scripts/get_model_names.pyScript1.4 KiB
- references/model-selection/scripts/search_deployable_models.pyScript34.9 KiB
- references/planning/overview.md10.3 KiB
- references/planning/references/deploy-base-model-plan.md699 B
- references/planning/references/evaluate-first-plan.md1.9 KiB
- references/planning/references/input-output-contracts.md3.1 KiB
- references/planning/references/model-customization-plan.md1.5 KiB
- references/planning/references/region-availability.md1.2 KiB
- references/planning/references/skill-routing-constraints.md2.7 KiB
- references/processing-jobs/code_templates/dataset_definition_processing.pyScript4.1 KiB
- references/processing-jobs/code_templates/framework_processing.pyScript3.0 KiB
- references/processing-jobs/code_templates/script_processing.pyScript2.9 KiB
- references/processing-jobs/code_templates/spark_processing.pyScript3.7 KiB
- references/processing-jobs/overview.md6.7 KiB
- references/processing-jobs/references/configuration.md4.6 KiB
- references/processing-jobs/references/troubleshooting.md2.4 KiB
- references/sdk-getting-started/overview.md643 B
- references/sdk-getting-started/references/execution-role-setup.md2.4 KiB
- references/sdk-getting-started/references/sagemaker-python-sdk-setup.md2.1 KiB
- references/training-jobs/code_templates/custom_training.pyScript6.2 KiB
- references/training-jobs/code_templates/jumpstart_training.pyScript5.6 KiB
- references/training-jobs/code_templates/recipe_training.pyScript5.7 KiB
- references/training-jobs/overview.md9.1 KiB
- references/training-jobs/references/configuration.md4.0 KiB
- references/training-jobs/references/troubleshooting.md4.4 KiB
- references/use-case-specification/overview.md2.0 KiB
- references/use-case-specification/references/spec-for-deployment.md4.0 KiB
- references/use-case-specification/references/spec-for-finetuning.md2.1 KiB
references/model-selection/scripts/get_deployable_models.py
"""
Retrieves all deployable models from a SageMaker Hub and outputs structured metadata as JSON.
Usage:
python get_deployable_models.py <hub-name> [--list-values] [--create-snapshot]
[--snapshot-file <path>] [region]
Arguments:
hub-name Name of the SageMaker Hub to query (e.g., SageMakerPublicHub)
region Optional AWS region override (defaults to boto3 session default)
snapshot-file Optional short-lived selection-session cache for public-Hub Search
Output:
JSON array of model objects with fields: name, data_types, input_modalities,
output_modalities, size, license, languages, context_window, model_type,
framework, provider, tasks, bedrock_eligible.
"""
import json
import os
import sys
import tempfile
from collections.abc import Mapping
from datetime import datetime, timezone
import boto3
import botocore.exceptions
os.environ.setdefault("AWS_SDK_UA_APP_ID", "AWSSkill-SageMaker")
SNAPSHOT_SCHEMA_VERSION = 1
USAGE = (
"Usage: python get_deployable_models.py <hub-name> [--list-values] "
"[--create-snapshot] [--snapshot-file <path>] [region]"
)
# Canonical, non-overlapping size buckets. The hub's raw @model-size values are
# inconsistent — a mix of ranges (e.g. "1b-10b", "10b-70b") and exact parameter
# counts (e.g. "2b", "7b", "30b", "550b"), plus "<1b" and "unknown". Because the
# size filter is an EXACT match, that inconsistency makes deterministic
# soft->hard mapping impossible (e.g. "small" -> "1b-10b" would silently miss a
# model tagged "7b"). We normalize every raw value into one of these buckets so
# the filter and the soft-constraint mapping only ever deal with a clean set.
SIZE_BUCKETS = ("<=10b", "11b-70b", "71b-100b", ">100b", "unknown")
class CatalogSnapshotError(ValueError):
"""A selection-session catalog snapshot is missing or malformed."""
def normalize_size_bucket(raw):
"""Map a raw @model-size value to a canonical, non-overlapping bucket.
Assignment rule by parameter count N (in billions):
N <= 10 -> "<=10b"
10 < N <= 70 -> "11b-70b"
70 < N <= 100 -> "71b-100b"
N > 100 -> ">100b"
unparseable / missing -> "unknown"
Handles both exact values ("7b", "30b", "550b") and the hub's own range
labels ("1b-10b", "10b-70b", "70b-100b", ">100b", "<1b"). Range labels are
classified by their UPPER bound (the largest models the range can contain),
except ">100b" which is unbounded above and maps to ">100b". This keeps the
mapping total and deterministic.
Args:
raw: The raw size string from the @model-size keyword, or None.
Returns:
One of SIZE_BUCKETS.
"""
if not raw:
return "unknown"
value = raw.strip().lower()
if value == "unknown":
return "unknown"
def bucket_for(n):
if n <= 10:
return "<=10b"
if n <= 70:
return "11b-70b"
if n <= 100:
return "71b-100b"
return ">100b"
# ">100b" (or any ">Nb") is unbounded above -> largest bucket.
if value.startswith(">"):
return ">100b"
# "<1b" (or any "<Nb") -> use the upper bound N (e.g. <1b has upper bound 1).
if value.startswith("<"):
num = _parse_billions(value[1:])
return bucket_for(num) if num is not None else "unknown"
# Range label like "1b-10b" / "10b-70b" / "70b-100b": classify by upper bound.
if "-" in value:
upper = _parse_billions(value.split("-", 1)[1])
return bucket_for(upper) if upper is not None else "unknown"
# Exact value like "7b", "30b", "550b".
num = _parse_billions(value)
return bucket_for(num) if num is not None else "unknown"
def _parse_billions(token):
"""Parse a size token like '7b', '550b', '10' into a number of billions.
Returns a float, or None if it cannot be parsed.
"""
if not token:
return None
t = token.strip().lower().rstrip("b").strip()
try:
return float(t)
except ValueError:
return None
# Document fields read when the matching keyword is absent. The Hub writes at
# most 50 keywords per model and a model with many languages can run out of
# slots before these are written, while the document still carries them. Only
# Search returns HubContentDocument; ListHubContents does not, so the snapshot
# is unaffected. normalized field -> (document field, keyword prefix)
_DOCUMENT_FALLBACK_FIELDS = {
"size": ("ModelSize", "@model-size:"),
"context_window": ("ContextWindow", "@context-window:"),
}
def _document_scalar(model, field):
"""Return one lower-cased string value of a top-level document field, or None.
Keywords are the lower-cased document value, so lower-casing here keeps the
normalized shape identical whichever source supplied it.
"""
document = model.get("HubContentDocument")
if not isinstance(document, str) or not document:
return None
try:
parsed = json.loads(document)
except ValueError:
return None
if not isinstance(parsed, dict):
return None
value = parsed.get(field)
if isinstance(value, list):
value = next((item for item in value if isinstance(item, str) and item), None)
if isinstance(value, str) and value:
return value.lower()
return None
def normalize_hub_content(model):
"""Convert a List or Search HubContent model to the skill's normalized shape.
Args:
model: A HubContent model mapping returned by ListHubContents or Search.
Returns:
A model dict with extracted and normalized metadata fields.
"""
keywords = model.get("HubContentSearchKeywords", [])
entry = {
"name": model.get("HubContentName"),
}
for kw in keywords:
if kw.startswith("@data-type:"):
entry.setdefault("data_types", []).append(kw.split(":", 1)[1])
elif kw.startswith("@input-modality:"):
entry.setdefault("input_modalities", []).append(kw.split(":", 1)[1])
elif kw.startswith("@output-modality:"):
entry.setdefault("output_modalities", []).append(kw.split(":", 1)[1])
elif kw.startswith("@model-size:"):
# Preserve the raw hub value for display, and store the normalized
# canonical bucket used by the filter and soft-constraint mapping.
raw_size = kw.split(":", 1)[1]
entry["size_raw"] = raw_size
entry["size"] = normalize_size_bucket(raw_size)
elif kw.startswith("@license:"):
entry["license"] = kw.split(":", 1)[1]
elif kw.startswith("@language:"):
entry.setdefault("languages", []).append(kw.split(":", 1)[1])
elif kw.startswith("@context-window:"):
entry["context_window"] = kw.split(":", 1)[1]
elif kw.startswith("@model-type:"):
entry["model_type"] = kw.split(":", 1)[1]
elif kw.startswith("@framework:"):
# Framework is stored separately from provider for display and
# future structured filtering.
entry["framework"] = kw.split(":", 1)[1]
elif kw.startswith("@provider:"):
entry["provider"] = kw.split(":", 1)[1]
elif kw.startswith("@task:"):
entry.setdefault("tasks", []).append(kw.split(":", 1)[1])
entry["bedrock_eligible"] = "@capability:bedrock_console" in keywords
entry["customization_eligible"] = "@capability:customization" in keywords
# A keyword, when present, is authoritative; the document fills in only
# what the keyword cap dropped.
for normalized_field, (document_field, prefix) in _DOCUMENT_FALLBACK_FIELDS.items():
if any(kw.startswith(prefix) for kw in keywords):
continue
value = _document_scalar(model, document_field)
if value is None:
continue
if normalized_field == "size":
entry["size_raw"] = value
entry["size"] = normalize_size_bucket(value)
else:
entry[normalized_field] = value
# ListHubContents exposes this model-level catalog date, while Search only
# exposes version-level CreationTime. Cache the List field so a version
# import cannot silently reorder the customer-visible first 20 models.
original_creation_time = model.get("OriginalCreationTime")
if original_creation_time:
entry["original_creation_time"] = (
original_creation_time.isoformat()
if hasattr(original_creation_time, "isoformat")
else str(original_creation_time)
)
return entry
def get_deployable_models(hub_name, region_name=None, sm_client=None):
"""Query a SageMaker Hub and return structured model metadata.
Args:
hub_name: Name of the SageMaker Hub.
region_name: Optional AWS region override.
sm_client: Optional existing SageMaker client. The CLI supplies one so
snapshot metadata uses the exact region that served the List call.
Returns:
List of model dicts with extracted metadata fields.
"""
sm_client = sm_client or boto3.client("sagemaker", region_name=region_name)
# Retrieve all models with pagination
all_contents = []
next_token = None
while True:
params = {
"HubName": hub_name,
"HubContentType": "Model",
"MaxResults": 100,
}
if next_token:
params["NextToken"] = next_token
response = sm_client.list_hub_contents(**params)
all_contents.extend(response.get("HubContentSummaries", []))
next_token = response.get("NextToken")
if not next_token:
break
return [normalize_hub_content(model) for model in all_contents]
def list_available_values(models):
"""Extract all unique values for each metadata field across all models.
Returns a dict of field_name -> sorted list of unique values.
"""
values: dict[str, set[str]] = {
"tasks": set(),
"data_types": set(),
"input_modalities": set(),
"output_modalities": set(),
"sizes": set(),
"licenses": set(),
"languages": set(),
"context_windows": set(),
"model_types": set(),
"providers": set(),
"frameworks": set(),
}
for model in models:
for task in model.get("tasks", []):
values["tasks"].add(task)
for dt in model.get("data_types", []):
values["data_types"].add(dt)
for im in model.get("input_modalities", []):
values["input_modalities"].add(im)
for om in model.get("output_modalities", []):
values["output_modalities"].add(om)
if "size" in model:
values["sizes"].add(model["size"])
if "license" in model:
values["licenses"].add(model["license"])
for lang in model.get("languages", []):
values["languages"].add(lang)
if "context_window" in model:
values["context_windows"].add(model["context_window"])
if "model_type" in model:
values["model_types"].add(model["model_type"])
if "provider" in model:
values["providers"].add(model["provider"])
if "framework" in model:
values["frameworks"].add(model["framework"])
return {k: sorted(v) for k, v in values.items()}
def build_catalog_snapshot(hub_name, region_name, models, fetched_at=None):
"""Build the short-lived catalog cache used across the confirmation pause.
``catalog_models`` contains compact, normalized ListHubContents summaries.
Search query construction needs their exact values, including inconsistent
license spellings such as ``Apache 2.0`` and ``Apache-2.0``. The same complete
summaries support local fallback. HubContentDocument, markdown, and
credentials never enter this normalized shape.
"""
if not isinstance(hub_name, str) or not hub_name:
raise CatalogSnapshotError("Snapshot hub_name must be a non-empty string")
if not isinstance(region_name, str) or not region_name:
raise CatalogSnapshotError(
"A configured AWS region is required to create a catalog snapshot"
)
if not isinstance(models, list) or not all(
isinstance(model, Mapping) for model in models
):
raise CatalogSnapshotError("Snapshot catalog_models must be a list of objects")
timestamp = fetched_at or datetime.now(timezone.utc)
if hasattr(timestamp, "isoformat"):
timestamp = timestamp.isoformat()
if not isinstance(timestamp, str) or not timestamp:
raise CatalogSnapshotError("Snapshot fetched_at must be a non-empty timestamp")
original_creation_times = {
model["name"]: model["original_creation_time"]
for model in models
if isinstance(model.get("name"), str)
and isinstance(model.get("original_creation_time"), str)
}
raw_sizes_by_bucket = {
bucket: sorted(
{
model["size_raw"]
for model in models
if model.get("size") == bucket
and isinstance(model.get("size_raw"), str)
},
key=str.lower,
)
for bucket in SIZE_BUCKETS
}
return {
"schema_version": SNAPSHOT_SCHEMA_VERSION,
"hub_name": hub_name,
"region": region_name,
"fetched_at": timestamp,
"model_count": len(models),
"available_values": list_available_values(models),
"raw_sizes_by_bucket": raw_sizes_by_bucket,
"original_creation_times": original_creation_times,
"catalog_models": models,
}
def validate_catalog_snapshot(snapshot, expected_hub=None, expected_region=None):
"""Validate a snapshot before using it for Search or local fallback."""
if not isinstance(snapshot, Mapping):
raise CatalogSnapshotError("Catalog snapshot must be a JSON object")
if snapshot.get("schema_version") != SNAPSHOT_SCHEMA_VERSION:
raise CatalogSnapshotError(
f"Unsupported catalog snapshot schema: {snapshot.get('schema_version')!r}"
)
hub_name = snapshot.get("hub_name")
region = snapshot.get("region")
fetched_at = snapshot.get("fetched_at")
models = snapshot.get("catalog_models")
model_count = snapshot.get("model_count")
if not isinstance(hub_name, str) or not hub_name:
raise CatalogSnapshotError("Catalog snapshot hub_name must be a non-empty string")
if expected_hub is not None and hub_name != expected_hub:
raise CatalogSnapshotError(
f"Catalog snapshot is for Hub {hub_name!r}, not {expected_hub!r}"
)
if not isinstance(region, str) or not region:
raise CatalogSnapshotError("Catalog snapshot region must be a non-empty string")
if expected_region is not None and region != expected_region:
raise CatalogSnapshotError(
f"Catalog snapshot is for region {region!r}, not {expected_region!r}"
)
if not isinstance(fetched_at, str) or not fetched_at:
raise CatalogSnapshotError("Catalog snapshot fetched_at must be a timestamp string")
if not isinstance(models, list) or not all(
isinstance(model, Mapping) for model in models
):
raise CatalogSnapshotError("Catalog snapshot catalog_models must be a list of objects")
if not isinstance(model_count, int) or isinstance(model_count, bool):
raise CatalogSnapshotError("Catalog snapshot model_count must be an integer")
if model_count != len(models):
raise CatalogSnapshotError(
"Catalog snapshot model_count does not match catalog_models length"
)
names = [model.get("name") for model in models]
if any(not isinstance(name, str) or not name for name in names):
raise CatalogSnapshotError("Every cached model must have a non-empty name")
if len(names) != len(set(names)):
raise CatalogSnapshotError("Catalog snapshot contains duplicate model names")
for field in (
"available_values",
"raw_sizes_by_bucket",
"original_creation_times",
):
if not isinstance(snapshot.get(field), Mapping):
raise CatalogSnapshotError(f"Catalog snapshot {field} must be an object")
return snapshot
def write_catalog_snapshot(path, snapshot):
"""Write a validated per-session snapshot with private file permissions."""
validate_catalog_snapshot(snapshot)
descriptor = None
try:
descriptor = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
# os.open's mode applies only to new files. Restrict an existing
# caller-owned path before writing any snapshot content as well.
os.fchmod(descriptor, 0o600)
with os.fdopen(descriptor, "w", encoding="utf-8") as snapshot_file:
descriptor = None
json.dump(snapshot, snapshot_file)
except OSError as error:
raise CatalogSnapshotError(f"Could not write catalog snapshot: {error}") from error
finally:
if descriptor is not None:
os.close(descriptor)
def create_catalog_snapshot_file(snapshot):
"""Create a unique snapshot path and remove it if writing fails."""
validate_catalog_snapshot(snapshot)
descriptor, path = tempfile.mkstemp(prefix="sherpa-model-catalog.")
os.close(descriptor)
try:
write_catalog_snapshot(path, snapshot)
except Exception:
# This function owns the path, so failed creation must not leave an
# unknown cache file behind. Do not apply this cleanup to caller-owned
# paths accepted by write_catalog_snapshot().
try:
os.unlink(path)
except FileNotFoundError:
pass
raise
return path
def load_catalog_snapshot(path, expected_hub=None, expected_region=None):
"""Read and validate a selection-session cache from disk."""
try:
with open(path, encoding="utf-8") as snapshot_file:
snapshot = json.load(snapshot_file)
except FileNotFoundError as error:
raise CatalogSnapshotError(f"Catalog snapshot not found: {path}") from error
except json.JSONDecodeError as error:
raise CatalogSnapshotError(f"Catalog snapshot is not valid JSON: {error}") from error
except OSError as error:
raise CatalogSnapshotError(f"Could not read catalog snapshot: {error}") from error
return validate_catalog_snapshot(snapshot, expected_hub, expected_region)
def _parse_cli_args(argv):
"""Parse the legacy positional region plus snapshot options."""
args = list(argv)
list_values_mode = False
create_snapshot = False
snapshot_path = None
if "--list-values" in args:
list_values_mode = True
args.remove("--list-values")
if "--create-snapshot" in args:
create_snapshot = True
args.remove("--create-snapshot")
if "--snapshot-file" in args:
option_index = args.index("--snapshot-file")
if option_index + 1 >= len(args):
raise CatalogSnapshotError("--snapshot-file requires a path")
snapshot_path = args[option_index + 1]
del args[option_index : option_index + 2]
if create_snapshot and snapshot_path:
raise CatalogSnapshotError(
"Use either --create-snapshot or --snapshot-file, not both"
)
if create_snapshot and not list_values_mode:
raise CatalogSnapshotError("--create-snapshot requires --list-values")
if not args or len(args) > 2 or any(arg.startswith("-") for arg in args):
raise CatalogSnapshotError(USAGE)
return (
args[0],
args[1] if len(args) == 2 else None,
list_values_mode,
create_snapshot,
snapshot_path,
)
def main(argv=None):
"""Run the catalog CLI, optionally creating a selection-session cache."""
try:
(
hub_name,
region_name,
list_values_mode,
create_snapshot,
snapshot_path,
) = _parse_cli_args(sys.argv[1:] if argv is None else argv)
except CatalogSnapshotError as error:
print(str(error), file=sys.stderr)
return 1
try:
sm_client = boto3.client("sagemaker", region_name=region_name)
results = get_deployable_models(
hub_name,
region_name,
sm_client=sm_client,
)
snapshot = None
if create_snapshot or snapshot_path:
resolved_region = region_name or sm_client.meta.region_name
snapshot = build_catalog_snapshot(hub_name, resolved_region, results)
if create_snapshot:
snapshot_path = create_catalog_snapshot_file(snapshot)
else:
write_catalog_snapshot(snapshot_path, snapshot)
except botocore.exceptions.ClientError as e:
error_code = e.response["Error"]["Code"]
error_msg = e.response["Error"]["Message"]
print(f"Error: AWS API call failed ({error_code}): {error_msg}", file=sys.stderr)
return 1
except botocore.exceptions.NoCredentialsError:
print(
"Error: No AWS credentials found. Configure credentials via 'aws configure' or environment variables.",
file=sys.stderr,
)
return 1
except botocore.exceptions.EndpointConnectionError as e:
print(f"Error: Could not connect to SageMaker endpoint: {e}", file=sys.stderr)
return 1
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
return 1
if create_snapshot:
assert snapshot is not None
print(
json.dumps(
{
"snapshot_file": snapshot_path,
"available_values": snapshot["available_values"],
}
)
)
elif list_values_mode:
print(
json.dumps(
snapshot["available_values"]
if snapshot is not None
else list_available_values(results)
)
)
else:
print(json.dumps(results))
return 0
if __name__ == "__main__":
raise SystemExit(main())
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python
Security
What our scanner noticed· Low risk
- Instructions that use the network
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- AWS
- Publisher
- AWS (Official)
- Source
- aws/agent-toolkit-for-aws · skills/core-skills/aws-ai-ml · commit 0a218f8
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- NOTICE (added by AutoGineer from NOTICE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Also distributed in
- aws/agent-toolkit-for-aws
AutoGineer is not affiliated with or endorsed by AWS.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download aws-ai-ml.zip