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://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/dataset-evaluation/scripts/format_detector.py
"""Format detection for S3 JSONL files.
This module provides functionality to detect and validate JSONL file formats
stored in S3. It samples the first 1MB of a file to determine the format type
across 11 supported formats: Nova SFT, Nova DPO, Nova RLVR, GPT-OSS SFT,
GPT-OSS DPO, Open Weights SFT, Open Weights SFT Conv, Open Weights DPO,
Verl, Verl Legacy, and SageMaker Eval.
Usage:
result = detect_format("s3://my-bucket/data.jsonl")
if result.is_valid:
print(f"Format: {result.format_type}")
"""
import json
import logging
import os
from dataclasses import dataclass
from enum import Enum
from typing import Any, Callable, Optional
import boto3
os.environ.setdefault("AWS_SDK_UA_APP_ID", "AWSSkill-SageMaker")
logger = logging.getLogger(__name__)
# Type aliases for schema validators
MessageValidator = Callable[[list, int], "list[ValidationError]"]
RecordValidator = Callable[[dict, int], "list[ValidationError]"]
__all__ = [
"FormatType",
"ConfidenceLevel",
"ValidationError",
"FormatDetectionResult",
"detect_format",
]
class FormatType(Enum):
"""Supported JSONL format types."""
NOVA_SFT = "nova_sft"
NOVA_DPO = "nova_dpo"
NOVA_RLVR = "nova_rlvr"
GPT_OSS_SFT = "gpt_oss_sft"
GPT_OSS_DPO = "gpt_oss_dpo"
OPEN_WEIGHTS_SFT = "open_weights_sft"
OPEN_WEIGHTS_SFT_CONV = "open_weights_sft_conv"
OPEN_WEIGHTS_DPO = "open_weights_dpo"
VERL = "verl"
VERL_LEGACY = "verl_legacy"
SAGEMAKER_EVAL = "sagemaker_eval"
UNKNOWN = "unknown"
class ConfidenceLevel(Enum):
"""Confidence level for format detection results."""
HIGH = "high"
LOW = "low"
NONE = "none"
@dataclass
class ValidationError:
"""Represents a validation error found during format detection."""
line_number: int
error_type: str
message: str
@dataclass
class FormatDetectionResult:
"""Result of format detection operation."""
format_type: FormatType
is_valid: bool
lines_sampled: int
errors: list[ValidationError]
confidence: ConfidenceLevel
def _sample_local_file(file_path: str, sample_size: int) -> list[str]:
"""Sample lines from local JSONL file.
Args:
file_path: Path to local file
sample_size: Maximum bytes to read
Returns:
List of lines from file
Raises:
FileNotFoundError: If file doesn't exist
IOError: If file can't be read
"""
logger.debug("Sampling local file: %s", file_path)
with open(file_path, "rb") as f:
data = f.read(sample_size)
if not data:
return []
text = data.decode("utf-8", errors="replace")
last_newline_idx = text.rfind("\n")
if last_newline_idx == -1:
return []
complete_text = text[: last_newline_idx + 1]
lines = [line for line in complete_text.split("\n") if line]
return lines
def _sample_s3_file(s3_uri: str, sample_size_bytes: int, s3_client=None) -> list[str]:
"""Sample the first N bytes of an S3 file and return complete lines.
Reads the first sample_size_bytes from an S3 file using a Range request,
then truncates to the last complete newline to avoid partial lines.
Args:
s3_uri: S3 URI in format "s3://bucket/key"
sample_size_bytes: Number of bytes to sample (default 1MB)
s3_client: Optional boto3 S3 client to reuse
Returns:
List of complete JSONL lines (strings without trailing newlines)
Raises:
ValueError: If S3 URI is invalid (missing "s3://", bucket, or key)
botocore.exceptions.ClientError: If S3 access fails
"""
logger.debug("Sampling S3 file: %s (%d bytes)", s3_uri, sample_size_bytes)
# Parse S3 URI
if not s3_uri.startswith("s3://"):
raise ValueError(f"Invalid S3 URI: must start with 's3://' (got: {s3_uri})")
uri_without_prefix = s3_uri[5:] # Remove "s3://"
parts = uri_without_prefix.split("/", 1)
if len(parts) != 2 or not parts[0] or not parts[1]:
raise ValueError(f"Invalid S3 URI: must contain bucket and key (got: {s3_uri})")
bucket, key = parts
# Read first sample_size_bytes using Range header
client = s3_client or boto3.client("s3")
range_header = f"bytes=0-{sample_size_bytes - 1}"
response = client.get_object(Bucket=bucket, Key=key, Range=range_header)
data = response["Body"].read()
# Handle empty file
if not data:
return []
# Decode bytes to string
text = data.decode("utf-8", errors="replace")
# Find last complete newline to avoid truncated lines
last_newline_idx = text.rfind("\n")
if last_newline_idx == -1:
# No newlines found - return empty list if file is all one line
# (we can't be sure it's complete)
return []
# Keep only complete lines (up to and including last newline)
complete_text = text[: last_newline_idx + 1]
# Split on newlines and filter empty strings
lines = [line for line in complete_text.split("\n") if line]
return lines
def _classify_nova_format(record: dict) -> FormatType:
"""Classify Nova-specific format by checking last message structure.
Args:
record: Parsed JSON record with messages field
Returns:
FormatType.NOVA_DPO if last message has candidates field,
FormatType.NOVA_SFT if last message has standard content field,
FormatType.UNKNOWN otherwise
"""
messages = record.get("messages", [])
if not messages:
return FormatType.UNKNOWN
last_message = messages[-1]
if "candidates" in last_message:
return FormatType.NOVA_DPO
elif "content" in last_message and last_message["content"]:
return FormatType.NOVA_SFT
else:
return FormatType.UNKNOWN
def _classify_messages_format(record: dict) -> FormatType:
"""Distinguish Nova vs GPT-OSS/HF by inspecting content structure.
Nova has nested content arrays (list of dicts with 'text' field),
GPT-OSS/HF has flat content strings.
Args:
record: Parsed JSON record with messages field
Returns:
FormatType value for the detected format
"""
messages = record.get("messages")
# Critical type checking: messages must be a list
if not isinstance(messages, list):
return FormatType.UNKNOWN
if not messages:
return FormatType.UNKNOWN
first_message = messages[0]
# Check if content field exists
if "content" not in first_message:
return FormatType.UNKNOWN
content = first_message["content"]
# Nova: nested content arrays (list of dicts with 'text' field)
if isinstance(content, list):
return _classify_nova_format(record)
# GPT-OSS/HF: flat content strings
elif isinstance(content, str):
return FormatType.GPT_OSS_SFT
else:
return FormatType.UNKNOWN
def _classify_schema(samples: list[dict]) -> FormatType:
"""Top-level classifier that checks for all 11 supported formats.
Args:
samples: List of parsed JSON records
Returns:
FormatType value for the detected format
"""
if not samples:
return FormatType.UNKNOWN
first = samples[0]
fields = set(first.keys())
# SageMaker Evaluation: query + response
if "query" in fields and "response" in fields:
return FormatType.SAGEMAKER_EVAL
# Verl/RLVR: prompt + (reward_model or extra_info), no completion
if "prompt" in fields and ("reward_model" in fields or "extra_info" in fields):
if "completion" not in fields:
if isinstance(first["prompt"], list):
return FormatType.VERL
return FormatType.VERL_LEGACY
# Messages-based formats: Nova RLVR, Nova, GPT-OSS
if "messages" in fields:
if "reference_answer" in fields:
return FormatType.NOVA_RLVR
return _classify_messages_format(first)
# DPO: prompt/chosen/rejected
if {"prompt", "chosen", "rejected"}.issubset(fields):
if isinstance(first["prompt"], list):
return FormatType.GPT_OSS_DPO
return FormatType.OPEN_WEIGHTS_DPO
# SFT: prompt/completion
if {"prompt", "completion"}.issubset(fields):
if isinstance(first["prompt"], list):
return FormatType.OPEN_WEIGHTS_SFT_CONV
return FormatType.OPEN_WEIGHTS_SFT
return FormatType.UNKNOWN
def _validate_nova_messages(messages: list, line_num: int, is_dpo: bool) -> list[ValidationError]:
"""Validate Nova SFT/DPO message structure."""
errors = []
for msg_idx, msg in enumerate(messages):
if "role" not in msg:
errors.append(
ValidationError(
line_number=line_num,
error_type="missing_field",
message=f"Message {msg_idx} missing required field 'role'",
)
)
elif msg["role"] not in ["user", "assistant", "system"]:
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"Invalid role '{msg['role']}' in message {msg_idx}",
)
)
if "content" not in msg and "candidates" not in msg:
errors.append(
ValidationError(
line_number=line_num,
error_type="missing_field",
message=f"Message {msg_idx} missing 'content' or 'candidates'",
)
)
if "content" in msg and not isinstance(msg["content"], list):
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"Nova format content must be list, got {type(msg['content']).__name__}",
)
)
if is_dpo and "candidates" in msg:
for cand_idx, candidate in enumerate(msg["candidates"]):
if "preferenceLabel" not in candidate:
errors.append(
ValidationError(
line_number=line_num,
error_type="missing_field",
message=f"DPO message {msg_idx} candidate {cand_idx} missing 'preferenceLabel'",
)
)
elif candidate["preferenceLabel"] not in ["preferred", "non-preferred"]:
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"Invalid preferenceLabel '{candidate['preferenceLabel']}' in message {msg_idx} candidate {cand_idx}",
)
)
return errors
def _validate_gpt_messages(messages: list, line_num: int) -> list[ValidationError]:
"""Validate GPT-OSS SFT message structure."""
errors = []
for msg_idx, msg in enumerate(messages):
if "role" not in msg:
errors.append(
ValidationError(
line_number=line_num,
error_type="missing_field",
message=f"Message {msg_idx} missing required field 'role'",
)
)
elif msg["role"] not in ["user", "assistant", "system"]:
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"Invalid role '{msg['role']}' in message {msg_idx}",
)
)
if "content" not in msg:
errors.append(
ValidationError(
line_number=line_num,
error_type="missing_field",
message=f"Message {msg_idx} missing required field 'content'",
)
)
elif not isinstance(msg["content"], str):
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"GPT-OSS format content must be string, got {type(msg['content']).__name__}",
)
)
return errors
def _validate_rlvr_messages(messages: list, line_num: int) -> list[ValidationError]:
"""Validate Nova RLVR message structure."""
errors = []
for msg_idx, msg in enumerate(messages):
if "role" not in msg:
errors.append(
ValidationError(
line_number=line_num,
error_type="missing_field",
message=f"Message {msg_idx} missing required field 'role'",
)
)
elif msg["role"] not in ["user", "assistant", "system"]:
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"Invalid role '{msg['role']}' in message {msg_idx}",
)
)
if "content" not in msg:
errors.append(
ValidationError(
line_number=line_num,
error_type="missing_field",
message=f"Message {msg_idx} missing required field 'content'",
)
)
elif not isinstance(msg["content"], str):
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"Nova RLVR content must be string, got {type(msg['content']).__name__}",
)
)
return errors
def _validate_verl_prompt(record: dict, line_num: int) -> list[ValidationError]:
"""Validate Verl prompt structure (list of role/content dicts)."""
errors = []
if "prompt" not in record:
errors.append(
ValidationError(
line_number=line_num,
error_type="missing_field",
message="Missing required field 'prompt'",
)
)
elif not isinstance(record["prompt"], list):
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"Verl field 'prompt' must be list, got {type(record['prompt']).__name__}",
)
)
else:
for msg_idx, msg in enumerate(record["prompt"]):
if not isinstance(msg, dict) or "role" not in msg or "content" not in msg:
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"Prompt message {msg_idx} must have 'role' and 'content'",
)
)
if "reward_model" not in record and "extra_info" not in record:
errors.append(
ValidationError(
line_number=line_num,
error_type="missing_field",
message="Missing required field 'reward_model' or 'extra_info'",
)
)
return errors
def _validate_verl_legacy_prompt(record: dict, line_num: int) -> list[ValidationError]:
"""Validate Verl Legacy prompt structure (string) and extra_info."""
errors = []
if "prompt" not in record:
errors.append(
ValidationError(
line_number=line_num,
error_type="missing_field",
message="Missing required field 'prompt'",
)
)
elif not isinstance(record["prompt"], str):
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"Verl Legacy field 'prompt' must be string, got {type(record['prompt']).__name__}",
)
)
if "reward_model" not in record and "extra_info" not in record:
errors.append(
ValidationError(
line_number=line_num,
error_type="missing_field",
message="Missing required field 'reward_model' or 'extra_info'",
)
)
return errors
# Schema-driven format validation specs.
# Each entry defines required_fields (field->type mapping) and an optional
# message_validator or record_validator for complex per-record checks.
# - message_validator: called with (messages_list, line_num) -> list[ValidationError]
# Used for formats whose top-level required field is "messages" (list).
# - record_validator: called with (record, line_num) -> list[ValidationError]
# Used for formats needing whole-record access (verl, verl_legacy).
FORMAT_SCHEMAS: dict[FormatType, dict[str, Any]] = {
FormatType.NOVA_SFT: {
"required_fields": {"messages": list},
"message_validator": lambda msgs, ln: _validate_nova_messages(
msgs, ln, is_dpo=False
), # nosemgrep: python.lang.maintainability.return.return-not-in-function -- lambda inside dict literal, not a bare return
},
FormatType.NOVA_DPO: {
"required_fields": {"messages": list},
"message_validator": lambda msgs, ln: _validate_nova_messages(
msgs, ln, is_dpo=True
), # nosemgrep: python.lang.maintainability.return.return-not-in-function -- lambda inside dict literal, not a bare return
},
FormatType.NOVA_RLVR: {
"required_fields": {"messages": list, "reference_answer": dict},
"message_validator": _validate_rlvr_messages,
},
FormatType.GPT_OSS_SFT: {
"required_fields": {"messages": list},
"message_validator": _validate_gpt_messages,
},
FormatType.GPT_OSS_DPO: {
"required_fields": {"prompt": list, "chosen": list, "rejected": list},
"field_error_prefix": "GPT-OSS DPO",
},
FormatType.OPEN_WEIGHTS_SFT: {
"required_fields": {"prompt": str, "completion": str},
"field_error_prefix": "Open Weights SFT",
},
FormatType.OPEN_WEIGHTS_SFT_CONV: {
"required_fields": {"prompt": list, "completion": list},
"field_error_prefix": "Open Weights SFT Conv",
},
FormatType.OPEN_WEIGHTS_DPO: {
"required_fields": {"prompt": str, "chosen": str, "rejected": str},
"field_error_prefix": "Open Weights DPO",
},
FormatType.SAGEMAKER_EVAL: {
"required_fields": {"query": str, "response": str},
"field_error_prefix": "SageMaker Eval",
},
FormatType.VERL: {
"required_fields": {},
"record_validator": _validate_verl_prompt,
},
FormatType.VERL_LEGACY: {
"required_fields": {},
"record_validator": _validate_verl_legacy_prompt,
},
}
def _validate_samples(
samples: list[dict], expected_format: FormatType, line_numbers: list[int]
) -> tuple[bool, list[ValidationError]]:
"""Validate that all samples conform to the expected format schema.
Args:
samples: List of parsed JSON records
expected_format: Expected FormatType enum value
line_numbers: 1-based line numbers corresponding to each sample
Returns:
Tuple of (is_valid, errors) where errors is a list of ValidationError objects
"""
errors = []
schema = FORMAT_SCHEMAS.get(expected_format)
for record, line_num in zip(samples, line_numbers):
# Check schema consistency
detected_format = _classify_schema([record])
if detected_format != expected_format:
errors.append(
ValidationError(
line_number=line_num,
error_type="schema_mismatch",
message=f"Expected {expected_format.value} but found {detected_format.value}",
)
)
continue
if schema is None:
continue
# Record-level validator (verl, verl_legacy) handles everything
if "record_validator" in schema:
validator: RecordValidator = schema["record_validator"]
errors.extend(validator(record, line_num))
continue
# Check required fields exist with correct types
required = schema["required_fields"]
prefix: str = schema.get("field_error_prefix", "") or ""
skip_messages = False
for field, expected_type in required.items():
if field not in record:
errors.append(
ValidationError(
line_number=line_num,
error_type="missing_field",
message=f"Missing required field '{field}'",
)
)
if field == "messages":
skip_messages = True
elif not isinstance(record[field], expected_type):
actual = type(record[field]).__name__
if field == "messages":
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"Field 'messages' must be a list",
)
)
skip_messages = True
elif prefix:
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"{prefix} field '{field}' must be {expected_type.__name__}, got {actual}",
)
)
else:
errors.append(
ValidationError(
line_number=line_num,
error_type="invalid_structure",
message=f"Field '{field}' must be {expected_type.__name__}, got {actual}",
)
)
if skip_messages:
continue
# Message-level validator
if "message_validator" in schema:
msg_validator: MessageValidator = schema["message_validator"]
errors.extend(msg_validator(record["messages"], line_num))
logger.debug("Validation found %d error(s)", len(errors))
return (len(errors) == 0, errors)
def detect_format(
file_path: str, sample_size_bytes: int = 1_048_576, s3_client=None
) -> FormatDetectionResult:
"""Detect the format of a JSONL file in S3 or on local disk.
Samples the first sample_size_bytes of the file and analyzes the structure
to determine if it matches one of the 11 supported formats.
Args:
file_path: S3 URI (s3://bucket/key) or local file path
sample_size_bytes: Number of bytes to sample (default 1MB = 1,048,576 bytes)
s3_client: Optional boto3 S3 client to reuse (ignored for local files)
Returns:
FormatDetectionResult with format type, validation status, and any errors
"""
if file_path.startswith("s3://"):
lines = _sample_s3_file(file_path, sample_size_bytes, s3_client=s3_client)
else:
lines = _sample_local_file(file_path, sample_size_bytes)
# Parse JSON lines and collect parse errors
parsed_records = []
line_numbers = []
errors = []
for line_num, line in enumerate(lines, start=1):
try:
parsed_records.append(json.loads(line))
line_numbers.append(line_num)
except json.JSONDecodeError as e:
errors.append(
ValidationError(
line_number=line_num,
error_type="parse_error",
message=f"Invalid JSON: {str(e)}",
)
)
# If no successfully parsed records, return UNKNOWN with parse errors
if not parsed_records:
confidence = ConfidenceLevel.NONE if errors else ConfidenceLevel.HIGH
return FormatDetectionResult(
format_type=FormatType.UNKNOWN,
is_valid=len(errors) == 0,
lines_sampled=len(lines),
errors=errors,
confidence=confidence,
)
# Classify schema using first successfully parsed record
format_type = _classify_schema(parsed_records)
# Validate all parsed records against detected format
is_valid, validation_errors = _validate_samples(parsed_records, format_type, line_numbers)
errors.extend(validation_errors)
# Calculate confidence level
if len(errors) == 0:
confidence = ConfidenceLevel.HIGH
elif any(err.error_type == "parse_error" for err in errors):
confidence = ConfidenceLevel.NONE
else:
confidence = ConfidenceLevel.LOW
logger.debug(
"Detected format: %s (valid=%s, confidence=%s)",
format_type.value,
is_valid,
confidence.value,
)
return FormatDetectionResult(
format_type=format_type,
is_valid=len(errors) == 0,
lines_sampled=len(lines),
errors=errors,
confidence=confidence,
)
if __name__ == "__main__":
import argparse
import sys
parser = argparse.ArgumentParser(description="Detect and validate JSONL file formats")
parser.add_argument("file_path", help="S3 URI (s3://bucket/key) or local file path")
parser.add_argument(
"--sample-size", type=int, default=1_048_576, help="Bytes to sample (default: 1MB)"
)
parser.add_argument(
"--json", action="store_true", help="Output as JSON instead of human-readable"
)
args = parser.parse_args()
try:
result = detect_format(args.file_path, args.sample_size)
if args.json:
output = {
"format_type": result.format_type.value,
"is_valid": result.is_valid, # nosemgrep: python.lang.maintainability.is-function-without-parentheses -- dataclass field, not a method
"confidence": result.confidence.value,
"lines_sampled": result.lines_sampled,
"errors": [
{"line_number": e.line_number, "error_type": e.error_type, "message": e.message}
for e in result.errors
],
}
print(json.dumps(output, indent=2))
else:
print(f"Format: {result.format_type.value}")
print(
f"Valid: {'✓' if result.is_valid else '✗'}"
) # nosemgrep: python.lang.maintainability.is-function-without-parentheses -- dataclass field, not a method
print(f"Confidence: {result.confidence.name}")
print(f"Lines sampled: {result.lines_sampled}")
if result.errors:
print("Errors:")
for err in result.errors:
print(f" Line {err.line_number}: {err.message}")
sys.exit(
0 if result.is_valid else 1
) # nosemgrep: python.lang.maintainability.is-function-without-parentheses -- dataclass field, not a method
except (FileNotFoundError, IOError, ValueError) as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
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 or AWS.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download aws-ai-ml.zip