From 223af400074accaac9e30ed758301e5891368471 Mon Sep 17 00:00:00 2001 From: Aditi Sharma <165942273+Aditi2424@users.noreply.github.com> Date: Fri, 18 Jul 2025 12:24:31 -0700 Subject: [PATCH 01/10] Update telemetry status to be Integer for parity (#130) Co-authored-by: adishaa --- .../hyperpod/common/telemetry/telemetry_logging.py | 4 ++-- .../unit_tests/common/telemetry/test_telemetry_logging.py | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sagemaker/hyperpod/common/telemetry/telemetry_logging.py b/src/sagemaker/hyperpod/common/telemetry/telemetry_logging.py index 79eb2d29..e4891fb2 100644 --- a/src/sagemaker/hyperpod/common/telemetry/telemetry_logging.py +++ b/src/sagemaker/hyperpod/common/telemetry/telemetry_logging.py @@ -160,7 +160,7 @@ def wrapper(*args, **kwargs): duration = round(perf_counter() - start, 2) extra += f"&x-latency={duration}" _send_telemetry_request( - Status.SUCCESS, + STATUS_TO_CODE[str(Status.SUCCESS)], [FEATURE_TO_CODE[str(feature)]], None, None, @@ -172,7 +172,7 @@ def wrapper(*args, **kwargs): duration = round(perf_counter() - start, 2) extra += f"&x-latency={duration}" _send_telemetry_request( - Status.FAILURE, + STATUS_TO_CODE[str(Status.FAILURE)], [FEATURE_TO_CODE[str(feature)]], None, str(e), diff --git a/test/unit_tests/common/telemetry/test_telemetry_logging.py b/test/unit_tests/common/telemetry/test_telemetry_logging.py index 12939bdc..a54e36c5 100644 --- a/test/unit_tests/common/telemetry/test_telemetry_logging.py +++ b/test/unit_tests/common/telemetry/test_telemetry_logging.py @@ -17,6 +17,8 @@ import requests import logging +from src.sagemaker.hyperpod.common.telemetry.telemetry_logging import STATUS_TO_CODE + # Test data MOCK_CONTEXTS = { "eks_arn": "arn:aws:eks:us-west-2:123456789012:cluster/my-cluster", @@ -163,7 +165,7 @@ def sample_function(): args = mock_telemetry.call_args[0] # Check status - assert args[0] == Status.SUCCESS + assert args[0] == STATUS_TO_CODE[str(Status.SUCCESS)] # Check feature code assert args[1] == [FEATURE_TO_CODE[str(Feature.HYPERPOD)]] @@ -198,11 +200,11 @@ def sample_function(succeed: bool): # Check success call success_call = mock_telemetry.call_args_list[0] - assert success_call[0][0] == Status.SUCCESS + assert success_call[0][0] == STATUS_TO_CODE[str(Status.SUCCESS)] # Check failure call failure_call = mock_telemetry.call_args_list[1] - assert failure_call[0][0] == Status.FAILURE + assert failure_call[0][0] == STATUS_TO_CODE[str(Status.FAILURE)] # Test _requests_helper From cf772969569f68467ff4d8cb8f24af2a7edecd5b Mon Sep 17 00:00:00 2001 From: maheshxb Date: Fri, 18 Jul 2025 12:31:54 -0700 Subject: [PATCH 02/10] Release new version for Health Monitoring Agent (1.0.643.0_1.0.192.0) with minor improvements and bug fixes (#137) --- .../health-monitoring-agent/values.yaml | 2 +- helm_chart/readme.md | 26 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/helm_chart/HyperPodHelmChart/charts/health-monitoring-agent/values.yaml b/helm_chart/HyperPodHelmChart/charts/health-monitoring-agent/values.yaml index 56287fd0..08bf4b9d 100644 --- a/helm_chart/HyperPodHelmChart/charts/health-monitoring-agent/values.yaml +++ b/helm_chart/HyperPodHelmChart/charts/health-monitoring-agent/values.yaml @@ -1,2 +1,2 @@ namespace: "aws-hyperpod" -hmaimage: "905418368575.dkr.ecr.us-west-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.552.0_1.0.161.0" +hmaimage: "905418368575.dkr.ecr.us-west-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0" \ No newline at end of file diff --git a/helm_chart/readme.md b/helm_chart/readme.md index b6a47b48..44ec7b24 100644 --- a/helm_chart/readme.md +++ b/helm_chart/readme.md @@ -171,19 +171,19 @@ helm upgrade dependencies helm_chart/HyperPodHelmChart --namespace kube-system - Training job auto resume is expected to work with Kubeflow training operator release v1.7.0, v1.8.0, v1.8.1 https://github.com/kubeflow/training-operator/releases - If you intend to use the Health Monitoring Agent container image from another region, please see below list to find relevant region's URI. ``` - IAD 767398015722.dkr.ecr.us-east-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 - PDX 905418368575.dkr.ecr.us-west-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 - CMH 851725546812.dkr.ecr.us-east-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 - SFO 011528288828.dkr.ecr.us-west-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 - FRA 211125453373.dkr.ecr.eu-central-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 - ARN 654654141839.dkr.ecr.eu-north-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 - DUB 533267293120.dkr.ecr.eu-west-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 - LHR 011528288831.dkr.ecr.eu-west-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 - NRT 533267052152.dkr.ecr.ap-northeast-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 - BOM 011528288864.dkr.ecr.ap-south-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 - SIN 905418428165.dkr.ecr.ap-southeast-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 - SYD 851725636348.dkr.ecr.ap-southeast-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 - GRU 025066253954.dkr.ecr.sa-east-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.448.0_1.0.115.0 + IAD 767398015722.dkr.ecr.us-east-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + PDX 905418368575.dkr.ecr.us-west-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + CMH 851725546812.dkr.ecr.us-east-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + SFO 011528288828.dkr.ecr.us-west-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + FRA 211125453373.dkr.ecr.eu-central-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + ARN 654654141839.dkr.ecr.eu-north-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + DUB 533267293120.dkr.ecr.eu-west-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + LHR 011528288831.dkr.ecr.eu-west-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + NRT 533267052152.dkr.ecr.ap-northeast-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + BOM 011528288864.dkr.ecr.ap-south-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + SIN 905418428165.dkr.ecr.ap-southeast-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + SYD 851725636348.dkr.ecr.ap-southeast-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + GRU 025066253954.dkr.ecr.sa-east-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 ``` ## 7. Troubleshooting From 0342f60245c0fdfe422afd4ba4e9c40c8c32a36e Mon Sep 17 00:00:00 2001 From: jiayelamazon Date: Fri, 18 Jul 2025 14:28:16 -0700 Subject: [PATCH 03/10] Release new version for Health Monitoring Agent (1.0.674.0_1.0.199.0) with minor improvements and bug fixes. (#139) --- .../health-monitoring-agent/values.yaml | 2 +- helm_chart/readme.md | 26 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/helm_chart/HyperPodHelmChart/charts/health-monitoring-agent/values.yaml b/helm_chart/HyperPodHelmChart/charts/health-monitoring-agent/values.yaml index 08bf4b9d..6622f1cf 100644 --- a/helm_chart/HyperPodHelmChart/charts/health-monitoring-agent/values.yaml +++ b/helm_chart/HyperPodHelmChart/charts/health-monitoring-agent/values.yaml @@ -1,2 +1,2 @@ namespace: "aws-hyperpod" -hmaimage: "905418368575.dkr.ecr.us-west-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0" \ No newline at end of file +hmaimage: "905418368575.dkr.ecr.us-west-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0" \ No newline at end of file diff --git a/helm_chart/readme.md b/helm_chart/readme.md index 44ec7b24..2b6fe6e5 100644 --- a/helm_chart/readme.md +++ b/helm_chart/readme.md @@ -171,19 +171,19 @@ helm upgrade dependencies helm_chart/HyperPodHelmChart --namespace kube-system - Training job auto resume is expected to work with Kubeflow training operator release v1.7.0, v1.8.0, v1.8.1 https://github.com/kubeflow/training-operator/releases - If you intend to use the Health Monitoring Agent container image from another region, please see below list to find relevant region's URI. ``` - IAD 767398015722.dkr.ecr.us-east-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 - PDX 905418368575.dkr.ecr.us-west-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 - CMH 851725546812.dkr.ecr.us-east-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 - SFO 011528288828.dkr.ecr.us-west-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 - FRA 211125453373.dkr.ecr.eu-central-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 - ARN 654654141839.dkr.ecr.eu-north-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 - DUB 533267293120.dkr.ecr.eu-west-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 - LHR 011528288831.dkr.ecr.eu-west-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 - NRT 533267052152.dkr.ecr.ap-northeast-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 - BOM 011528288864.dkr.ecr.ap-south-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 - SIN 905418428165.dkr.ecr.ap-southeast-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 - SYD 851725636348.dkr.ecr.ap-southeast-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 - GRU 025066253954.dkr.ecr.sa-east-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.643.0_1.0.192.0 + IAD 767398015722.dkr.ecr.us-east-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 + PDX 905418368575.dkr.ecr.us-west-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 + CMH 851725546812.dkr.ecr.us-east-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 + SFO 011528288828.dkr.ecr.us-west-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 + FRA 211125453373.dkr.ecr.eu-central-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 + ARN 654654141839.dkr.ecr.eu-north-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 + DUB 533267293120.dkr.ecr.eu-west-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 + LHR 011528288831.dkr.ecr.eu-west-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 + NRT 533267052152.dkr.ecr.ap-northeast-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 + BOM 011528288864.dkr.ecr.ap-south-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 + SIN 905418428165.dkr.ecr.ap-southeast-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 + SYD 851725636348.dkr.ecr.ap-southeast-2.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 + GRU 025066253954.dkr.ecr.sa-east-1.amazonaws.com/hyperpod-health-monitoring-agent:1.0.674.0_1.0.199.0 ``` ## 7. Troubleshooting From 8bbdf5604f3337fdcc711507234c1c540cc73819 Mon Sep 17 00:00:00 2001 From: adishaa Date: Sun, 20 Jul 2025 23:51:28 -0700 Subject: [PATCH 04/10] documentation working setup --- .readthedocs.yaml | 20 +++++ doc/Makefile | 20 +++++ doc/conf.py | 82 +++++++++++++++++++- doc/getting_started.ipynb | 75 ++++++++++++++++++ doc/index.md | 58 ++++++++++++++ doc/index.rst | 16 ---- doc/requirements.txt | 8 ++ src/sagemaker/hyperpod/cli/training_utils.py | 2 + 8 files changed, 262 insertions(+), 19 deletions(-) create mode 100644 .readthedocs.yaml create mode 100644 doc/Makefile create mode 100644 doc/getting_started.ipynb create mode 100644 doc/index.md delete mode 100644 doc/index.rst create mode 100644 doc/requirements.txt diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..ff22c87e --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,20 @@ +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.9" + +python: + install: + - method: pip + path: . + - requirements: doc/requirements.txt + +sphinx: + configuration: doc/conf.py + fail_on_warning: false + +formats: + - pdf + - epub diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 00000000..d33a0a7e --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = -W +SPHINXBUILD = python3 -msphinx +SPHINXPROJ = sagemaker +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/doc/conf.py b/doc/conf.py index 68bf9c75..01fa77ca 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -3,7 +3,9 @@ import datetime import os import shutil - +import sys +import re +from pathlib import Path def run_apidoc(app): """Generate doc stubs using sphinx-apidoc.""" @@ -41,8 +43,33 @@ def setup(app): app.connect("builder-inited", run_apidoc) +# Get version from setup.py +def get_version(): + try: + # Find the project root directory (where setup.py is located) + project_root = Path(__file__).parent.parent + setup_py_path = project_root / "setup.py" + + # Read setup.py content + with open(setup_py_path, "r") as f: + setup_py_content = f.read() + + # Extract version using regex + version_match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', setup_py_content) + if version_match: + return version_match.group(1) + else: + print("Warning: Could not find version in setup.py") + return "unknown" + except Exception as e: + print(f"Warning: Could not extract version from setup.py: {e}") + return "unknown" + + # Sphinx configuration below. project = "SageMaker HyperPod CLI" +version = get_version() +release = version # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {"python": ("http://docs.python.org/", None)} @@ -53,16 +80,65 @@ def setup(app): "sphinx.ext.napoleon", "sphinx.ext.todo", "sphinx.ext.viewcode", + "nbsphinx", + # Use either myst_parser or myst_nb, not both + # "myst_parser", + "myst_nb", + "sphinx_design", ] -source_suffix = ".rst" +# Mock modules that might not be available during documentation build +autodoc_mock_imports = [ + 'sagemaker.hyperpod.training.config.hyperpod_pytorch_job_config', + 'hyperpod_pytorch_job_template.registry' +] + +source_suffix = { + '.rst': 'restructuredtext', + '.ipynb': 'myst-nb', + '.md': 'myst-nb', +} master_doc = "index" autoclass_content = "class" autodoc_member_order = "bysource" default_role = "py:obj" -html_theme = "haiku" +html_theme = "sphinx_book_theme" +html_theme_options = { + "repository_url": "https://github.com/aws/sagemaker-hyperpod-cli", + "use_repository_button": True, + "use_issues_button": True, + "use_edit_page_button": True, + "path_to_docs": "doc", + "show_navbar_depth": 2, +} htmlhelp_basename = "{}doc".format(project) napoleon_use_rtype = False + +# nbsphinx configuration +nbsphinx_allow_errors = True +nbsphinx_kernel_name = 'python3' + +# MyST-NB configuration +myst_enable_extensions = [ + "amsmath", + "colon_fence", + "deflist", + "dollarmath", + "html_image", + "html_admonition", + # "linkify", # Commented out until linkify-it-py is installed + "replacements", + "smartquotes", + "substitution", + "tasklist", +] +myst_heading_anchors = 3 +nb_execution_mode = "off" + +# Make version available to MyST templates +myst_substitutions = { + "version": version, +} diff --git a/doc/getting_started.ipynb b/doc/getting_started.ipynb new file mode 100644 index 00000000..88e71ab6 --- /dev/null +++ b/doc/getting_started.ipynb @@ -0,0 +1,75 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(getting_started)=\n", + "# Getting Started with SageMaker HyperPod CLI\n", + "\n", + "This notebook provides a quick introduction to using the SageMaker HyperPod CLI." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Installation\n", + "\n", + "You can install the SageMaker HyperPod CLI using pip:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pip install sagemaker-hyperpod-cli" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basic Usage\n", + "\n", + "Here's a simple example of how to use the SageMaker HyperPod CLI:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Import the necessary modules\n", + "import sagemaker_hyperpod_cli\n", + "\n", + "# Example code here\n", + "print(\"Hello from SageMaker HyperPod CLI!\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 00000000..3cba6bd2 --- /dev/null +++ b/doc/index.md @@ -0,0 +1,58 @@ +(hpcli_docs_mainpage)= + +# SageMaker HyperPod CLI and SDK Documentation + +**Version**: {{ version }} + +```{toctree} +:hidden: +:maxdepth: 1 + +Getting Started +API reference <_apidoc/modules> +``` + +SageMaker HyperPod CLI and SDK provide a seamless way to manage distributed training and inference workloads on EKS-hosted SageMaker HyperPod clusters—without needing Kubernetes expertise. Use the powerful CLI to launch and monitor training jobs and endpoints, or leverage the Python SDK to do the same programmatically with minimal code, including support for JumpStart models, custom endpoints, and built-in monitoring. + +::::{container} +::::{grid} +:gutter: 3 + +:::{grid-item-card} Installation +:link: getting_started +:link-type: ref + +Get the CLI/ SDK setup +::: + +:::{grid-item-card} Quickstart +:link: getting_started +:link-type: ref + +Beginner's guide to using CLI/ SDK +::: + +:::{grid-item-card} Training +:link: getting_started +:link-type: ref + +Detailed guide on creating Pytorch training jobs +::: + +:::{grid-item-card} Inference +:link: getting_started +:link-type: ref + +Detailed guide on creating, invoking and monitoring endpoints +::: + +:::{grid-item-card} Contributor's Guide +:link: getting_started +:link-type: ref + +Improve SageMaker Hyperpod CLI and SDK +::: + +:::: +:::: + diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index 0f5525de..00000000 --- a/doc/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -HyperpodCLI -======================= - -Please replace this text with a short description of your package. - -.. toctree:: - - _apidoc/modules - - -Indices and tables -__________________ - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 00000000..2dc058d9 --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1,8 @@ +sphinx>=4.0.0,<8.0.0 +nbsphinx>=0.8.8 +myst-nb>=0.17.1 +ipykernel>=6.0.0 +jupyter>=1.0.0 +sphinx-book-theme>=1.0.0 +linkify-it-py>=2.0.0 +sphinx-design>=0.5.0 \ No newline at end of file diff --git a/src/sagemaker/hyperpod/cli/training_utils.py b/src/sagemaker/hyperpod/cli/training_utils.py index eeecb022..ac000b28 100644 --- a/src/sagemaker/hyperpod/cli/training_utils.py +++ b/src/sagemaker/hyperpod/cli/training_utils.py @@ -32,9 +32,11 @@ def generate_click_command( 1) Injects click.options from the JSON Schema under `schema_pkg` 2) At runtime, pops `version`, builds the flat model from `registry`, calls .to_domain() 3) Finally invokes your handler as `func(version, domain_config)` + - `version_key`: if given, hard-codes the version (no --version flag injected) - `schema_pkg`: the importable package root to read schema.json from - `registry`: a dict mapping version → flat‐model class, e.g. hyperpod_pytorch_job_template.registry.SCHEMA_REGISTRY + """ if registry is None: raise ValueError("You must pass a registry mapping version→Model") From 1d475e9368f062b3e9471a722cbb5376e7c1f127 Mon Sep 17 00:00:00 2001 From: adishaa Date: Tue, 22 Jul 2025 09:40:03 -0700 Subject: [PATCH 05/10] training inference documentation changes --- doc/_static/image.png | Bin 0 -> 2566 bytes doc/_static/image_dark.png | Bin 0 -> 37824 bytes doc/_static/image_light.svg | 1 + doc/conf.py | 8 +- doc/getting_started.ipynb | 75 -------------- doc/getting_started.md | 120 ++++++++++++++++++++++ doc/index.md | 11 +- doc/inference.md | 198 ++++++++++++++++++++++++++++++++++++ doc/installation.ipynb | 158 ++++++++++++++++++++++++++++ doc/requirements.txt | 4 +- doc/training.md | 172 +++++++++++++++++++++++++++++++ 11 files changed, 664 insertions(+), 83 deletions(-) create mode 100644 doc/_static/image.png create mode 100644 doc/_static/image_dark.png create mode 100644 doc/_static/image_light.svg delete mode 100644 doc/getting_started.ipynb create mode 100644 doc/getting_started.md create mode 100644 doc/inference.md create mode 100644 doc/installation.ipynb create mode 100644 doc/training.md diff --git a/doc/_static/image.png b/doc/_static/image.png new file mode 100644 index 0000000000000000000000000000000000000000..c90c4cd252ca97857991071607edc089caeab6ad GIT binary patch literal 2566 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 literal 0 HcmV?d00001 diff --git a/doc/_static/image_dark.png b/doc/_static/image_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..ebcadd9407e18ad25593b77849567b90a2b85391 GIT binary patch literal 37824 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 literal 0 HcmV?d00001 diff --git a/doc/_static/image_light.svg b/doc/_static/image_light.svg new file mode 100644 index 00000000..2aed204d --- /dev/null +++ b/doc/_static/image_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/doc/conf.py b/doc/conf.py index 01fa77ca..7c86f7c5 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -81,10 +81,10 @@ def get_version(): "sphinx.ext.todo", "sphinx.ext.viewcode", "nbsphinx", - # Use either myst_parser or myst_nb, not both - # "myst_parser", "myst_nb", "sphinx_design", + "sphinx_tabs.tabs", + "sphinx_copybutton" ] # Mock modules that might not be available during documentation build @@ -106,6 +106,10 @@ def get_version(): html_theme = "sphinx_book_theme" html_theme_options = { + "logo": { + "image_light": "_static/image.png", + "image_dark": "_static/image.png", + }, "repository_url": "https://github.com/aws/sagemaker-hyperpod-cli", "use_repository_button": True, "use_issues_button": True, diff --git a/doc/getting_started.ipynb b/doc/getting_started.ipynb deleted file mode 100644 index 88e71ab6..00000000 --- a/doc/getting_started.ipynb +++ /dev/null @@ -1,75 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "(getting_started)=\n", - "# Getting Started with SageMaker HyperPod CLI\n", - "\n", - "This notebook provides a quick introduction to using the SageMaker HyperPod CLI." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Installation\n", - "\n", - "You can install the SageMaker HyperPod CLI using pip:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pip install sagemaker-hyperpod-cli" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Basic Usage\n", - "\n", - "Here's a simple example of how to use the SageMaker HyperPod CLI:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Import the necessary modules\n", - "import sagemaker_hyperpod_cli\n", - "\n", - "# Example code here\n", - "print(\"Hello from SageMaker HyperPod CLI!\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/doc/getting_started.md b/doc/getting_started.md new file mode 100644 index 00000000..e3e8d6c8 --- /dev/null +++ b/doc/getting_started.md @@ -0,0 +1,120 @@ +(getting_started)= + +# Getting Started + +This guide will help you get started with the SageMaker HyperPod CLI and SDK to perform basic operations. + +## Cluster Management + +### List Available Clusters + +List all available SageMaker HyperPod clusters in your account: + +**CLI** +```bash +hyp list-cluster [--region ] [--namespace ] [--output ] +``` + +**SDK** +```python +from sagemaker.hyperpod.hyperpod_manager import HyperPodManager + +clusters = HyperPodManager.list_clusters(region='us-east-2') +print(clusters) +``` + +**Parameters:** +- `region` (string) - Optional. The AWS region where the SageMaker HyperPod and EKS clusters are located. If not specified, uses the region from your current AWS account credentials. +- `namespace` (string) - Optional. The namespace to check quota with. Only SageMaker managed namespaces are supported. +- `output` (enum) - Optional. The output format: `table` or `json` (default). + +### Connect to a Cluster + +Configure your local kubectl environment to interact with a specific SageMaker HyperPod cluster and namespace: + +**CLI** +```bash +hyp set-cluster-context --cluster-name [--namespace ] +``` + +**SDK** +```python +from sagemaker.hyperpod.hyperpod_manager import HyperPodManager + +HyperPodManager.set_context('', region='us-east-2') +``` + +**Parameters:** +- `cluster-name` (string) - Required. The SageMaker HyperPod cluster name to configure with. +- `namespace` (string) - Optional. The namespace to connect to. If not specified, the CLI will automatically discover accessible namespaces. + +### Get Current Cluster Context + +View information about the currently configured cluster context: + +**CLI** +```bash +hyp get-cluster-context +``` + +**SDK** +```python +from sagemaker.hyperpod.hyperpod_manager import HyperPodManager + +# Get current context information +context = HyperPodManager.get_context() +print(context) +``` + +## Job Management + +### List Pods for a Training Job + +View all pods associated with a specific training job: + +**CLI** +```bash +hyp list-pods hyp-pytorch-job --job-name +``` + +**SDK** +```python +# List all pods created for this job +pytorch_job.list_pods() +``` + +**Parameters:** +- `job-name` (string) - Required. The name of the job to list pods for. + +### Access Pod Logs + +View logs for a specific pod within a training job: + +**CLI** +```bash +hyp get-logs hyp-pytorch-job --pod-name --job-name +``` + +**SDK** +```python +# Check the logs from pod0 +pytorch_job.get_logs_from_pod("demo-pod-0") +``` + +**Parameters:** +- `job-name` (string) - Required. The name of the job to get logs for. +- `pod-name` (string) - Required. The name of the pod to get logs from. + +## Next Steps + +After setting up your environment and connecting to a cluster, you can: + +- Create and manage PyTorch training jobs +- Deploy and manage inference endpoints +- Monitor cluster resources and job performance + +For more detailed information on specific commands, use the `--help` flag: + +```bash +hyp --help +``` \ No newline at end of file diff --git a/doc/index.md b/doc/index.md index 3cba6bd2..2dcc7cbf 100644 --- a/doc/index.md +++ b/doc/index.md @@ -2,13 +2,14 @@ # SageMaker HyperPod CLI and SDK Documentation -**Version**: {{ version }} - ```{toctree} :hidden: :maxdepth: 1 +Installation Getting Started +Training +Inference API reference <_apidoc/modules> ``` @@ -19,7 +20,7 @@ SageMaker HyperPod CLI and SDK provide a seamless way to manage distributed trai :gutter: 3 :::{grid-item-card} Installation -:link: getting_started +:link: installation :link-type: ref Get the CLI/ SDK setup @@ -33,14 +34,14 @@ Beginner's guide to using CLI/ SDK ::: :::{grid-item-card} Training -:link: getting_started +:link: training :link-type: ref Detailed guide on creating Pytorch training jobs ::: :::{grid-item-card} Inference -:link: getting_started +:link: inference :link-type: ref Detailed guide on creating, invoking and monitoring endpoints diff --git a/doc/inference.md b/doc/inference.md new file mode 100644 index 00000000..073a3ad4 --- /dev/null +++ b/doc/inference.md @@ -0,0 +1,198 @@ +(inference)= + +# Inference with SageMaker HyperPod + +SageMaker HyperPod provides powerful capabilities for deploying and managing inference endpoints on EKS-hosted clusters. This guide covers how to create, invoke, and manage inference endpoints using both the HyperPod CLI and SDK. + +## Overview + +SageMaker HyperPod inference endpoints allow you to: + +- Deploy pre-trained JumpStart models +- Deploy custom models with your own inference code +- Configure resource requirements for inference +- Manage endpoint lifecycle +- Invoke endpoints for real-time predictions +- Monitor endpoint performance + +## Creating Inference Endpoints + +You can create inference endpoints using either JumpStart models or custom models: + +### JumpStart Model Endpoints + +**CLI** +```bash +hyp create hyp-jumpstart-endpoint \ + --version 1.0 \ + --model-id jumpstart-model-id \ + --instance-type ml.g5.8xlarge \ + --endpoint-name endpoint-jumpstart \ + --tls-output-s3-uri s3://sample-bucket +``` + +**SDK** +```python +from sagemaker.hyperpod.inference import HyperPodJumpstartEndpoint + +# Create a JumpStart endpoint +endpoint = HyperPodJumpstartEndpoint( + endpoint_name="endpoint-jumpstart", + model_id="jumpstart-model-id", + instance_type="ml.g5.8xlarge", + tls_output_s3_uri="s3://sample-bucket" +) + +# Deploy the endpoint +endpoint.create() +``` + +### Custom Model Endpoints + +**CLI** +```bash +hyp create hyp-custom-endpoint \ + --version 1.0 \ + --endpoint-name endpoint-custom \ + --model-uri s3://my-bucket/model-artifacts \ + --image 123456789012.dkr.ecr.us-west-2.amazonaws.com/my-inference-image:latest \ + --instance-type ml.g5.8xlarge \ + --tls-output-s3-uri s3://sample-bucket +``` + +**SDK** +```python +from sagemaker.hyperpod.inference import HyperPodCustomEndpoint + +# Create a custom endpoint +endpoint = HyperPodCustomEndpoint( + endpoint_name="endpoint-custom", + model_uri="s3://my-bucket/model-artifacts", + image="123456789012.dkr.ecr.us-west-2.amazonaws.com/my-inference-image:latest", + instance_type="ml.g5.8xlarge", + tls_output_s3_uri="s3://sample-bucket" +) + +# Deploy the endpoint +endpoint.create() +``` + +## Key Parameters + +When creating an inference endpoint, you'll need to specify: + +- **endpoint-name**: Unique identifier for your endpoint +- **model-id** (JumpStart): ID of the pre-trained JumpStart model +- **model-uri** (Custom): S3 location of your model artifacts +- **image** (Custom): Docker image containing your inference code +- **instance-type**: The EC2 instance type to use +- **tls-output-s3-uri**: S3 location to store TLS certificates + +## Managing Inference Endpoints + +### List Endpoints + +**CLI** +```bash +# List JumpStart endpoints +hyp list hyp-jumpstart-endpoint + +# List custom endpoints +hyp list hyp-custom-endpoint +``` + +**SDK** +```python +from sagemaker.hyperpod.inference import HyperPodJumpstartEndpoint, HyperPodCustomEndpoint + +# List JumpStart endpoints +jumpstart_endpoints = HyperPodJumpstartEndpoint.list() +print(jumpstart_endpoints) + +# List custom endpoints +custom_endpoints = HyperPodCustomEndpoint.list() +print(custom_endpoints) +``` + +### Describe an Endpoint + +**CLI** +```bash +# Describe JumpStart endpoint +hyp describe hyp-jumpstart-endpoint --endpoint-name + +# Describe custom endpoint +hyp describe hyp-custom-endpoint --endpoint-name +``` + +**SDK** +```python +from sagemaker.hyperpod.inference import HyperPodJumpstartEndpoint, HyperPodCustomEndpoint + +# Get JumpStart endpoint details +jumpstart_endpoint = HyperPodJumpstartEndpoint.load(endpoint_name="endpoint-jumpstart") +jumpstart_details = jumpstart_endpoint.describe() +print(jumpstart_details) + +# Get custom endpoint details +custom_endpoint = HyperPodCustomEndpoint.load(endpoint_name="endpoint-custom") +custom_details = custom_endpoint.describe() +print(custom_details) +``` + +### Invoke an Endpoint + +**CLI** +```bash +# Invoke custom endpoint +hyp invoke hyp-custom-endpoint \ + --endpoint-name \ + --content-type "application/json" \ + --payload '{"inputs": "What is machine learning?"}' +``` + +**SDK** +```python +from sagemaker.hyperpod.inference import HyperPodCustomEndpoint + +# Load the endpoint +endpoint = HyperPodCustomEndpoint.load(endpoint_name="endpoint-custom") + +# Invoke the endpoint +response = endpoint.invoke( + payload={"inputs": "What is machine learning?"}, + content_type="application/json" +) +print(response) +``` + +### Delete an Endpoint + +**CLI** +```bash +# Delete JumpStart endpoint +hyp delete hyp-jumpstart-endpoint --endpoint-name + +# Delete custom endpoint +hyp delete hyp-custom-endpoint --endpoint-name +``` + +**SDK** +```python +from sagemaker.hyperpod.inference import HyperPodJumpstartEndpoint, HyperPodCustomEndpoint + +# Delete JumpStart endpoint +jumpstart_endpoint = HyperPodJumpstartEndpoint.load(endpoint_name="endpoint-jumpstart") +jumpstart_endpoint.delete() + +# Delete custom endpoint +custom_endpoint = HyperPodCustomEndpoint.load(endpoint_name="endpoint-custom") +custom_endpoint.delete() +``` + +## Inference Example Notebooks + +For detailed examples of inference with HyperPod, see: +- [CLI Inference FSX Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/CLI/inference-fsx-model-e2e-cli.ipynb) +- [CLI Inference Jumpstart Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/CLI/inference-jumpstart-e2e-cli.ipynb) +- [CLI Inference S3 Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/CLI/inference-s3-model-e2e-cli.ipynb) diff --git a/doc/installation.ipynb b/doc/installation.ipynb new file mode 100644 index 00000000..8c70fdfe --- /dev/null +++ b/doc/installation.ipynb @@ -0,0 +1,158 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(installation)=\n", + "\n", + "# Installation\n", + "\n", + "This notebook provides installation instructions for the SageMaker HyperPod CLI and SDK." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## System Requirements\n", + "\n", + "### Supported Platforms\n", + "- Linux\n", + "- macOS\n", + "\n", + "**Note**: Windows is not supported at this time.\n", + "\n", + "### Supported ML Frameworks\n", + "- PyTorch (version ≥ 1.10)\n", + "\n", + "### Supported Python Versions\n", + "- 3.9\n", + "- 3.10\n", + "- 3.11" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "### For Training\n", + "SageMaker HyperPod CLI currently supports `PyTorchJob` training workloads.\n", + "To run these jobs, install the **SageMaker Training Operator**.\n", + "\n", + "[Install the SageMaker Training Operator](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-eks-operator-install.html)\n", + "\n", + "### For Inference\n", + "The CLI supports creating inference endpoints using JumpStart models or custom configurations.\n", + "To enable this, install the **SageMaker Inference Operator**.\n", + "\n", + "[Install the SageMaker Inference Operator](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-hyperpod-model-deployment-setup.html)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Installation Options" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Option 1: Install from PyPI\n", + "\n", + "You can install the SageMaker HyperPod CLI and SDK directly using `pip`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Install from PyPI\n", + "!pip install sagemaker-hyperpod" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To verify that the installation was successful, run:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Verify CLI installation\n", + "!hyp --help" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Option 2: Install from Source\n", + "\n", + "Clone the GitHub repository and install the CLI from source:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Clone the repository\n", + "!git clone https://github.com/aws/sagemaker-hyperpod-cli.git" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Change to the repository directory\n", + "%cd sagemaker-hyperpod-cli" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Install using pip\n", + "!pip install ." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/doc/requirements.txt b/doc/requirements.txt index 2dc058d9..c1bffd2f 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -5,4 +5,6 @@ ipykernel>=6.0.0 jupyter>=1.0.0 sphinx-book-theme>=1.0.0 linkify-it-py>=2.0.0 -sphinx-design>=0.5.0 \ No newline at end of file +sphinx-design>=0.5.0 +sphinx-tabs>=3.4.1 +sphinx-copybutton \ No newline at end of file diff --git a/doc/training.md b/doc/training.md new file mode 100644 index 00000000..6cdc0332 --- /dev/null +++ b/doc/training.md @@ -0,0 +1,172 @@ +(training)= + +# Training with SageMaker HyperPod + +SageMaker HyperPod provides powerful capabilities for running distributed training workloads on EKS-hosted clusters. This guide covers how to create and manage training jobs using both the HyperPod CLI and SDK. + +## Overview + +SageMaker HyperPod training jobs allow you to: + +- Run distributed PyTorch training workloads +- Specify custom Docker images with your training code +- Configure resource requirements (instance types, GPUs) +- Set up node selection with label selectors +- Manage job scheduling and priorities +- Mount volumes and persistent volume claims +- Store model artifacts in S3 + +## Creating Training Jobs + +You can create training jobs using either the CLI or SDK approach: + +**CLI** +```bash +hyp create hyp-pytorch-job \ + --version 1.0 \ + --job-name test-pytorch-job \ + --image pytorch/pytorch:latest \ + --command '["python", "train.py"]' \ + --args '["--epochs", "10", "--batch-size", "32"]' \ + --environment '{"PYTORCH_CUDA_ALLOC_CONF": "max_split_size_mb:32"}' \ + --pull-policy "IfNotPresent" \ + --instance-type ml.p4d.24xlarge \ + --tasks-per-node 8 \ + --label-selector '{"accelerator": "nvidia", "network": "efa"}' \ + --deep-health-check-passed-nodes-only true \ + --scheduler-type "kueue" \ + --queue-name "training-queue" \ + --priority "high" \ + --max-retry 3 \ + --volumes '["data-vol", "model-vol", "checkpoint-vol"]' \ + --persistent-volume-claims '["shared-data-pvc", "model-registry-pvc"]' \ + --output-s3-uri s3://my-bucket/model-artifacts +``` + +**SDK** +```python +from sagemaker.hyperpod import HyperPodPytorchJob +from sagemaker.hyperpod.job import ReplicaSpec, Template, Spec, Container, Resources, RunPolicy, Metadata + +# Define job specifications +nproc_per_node = "1" # Number of processes per node +replica_specs = [ + ReplicaSpec( + name = "pod", # Replica name + template = Template( + spec = Spec( + containers = [ + Container( + # Container name + name="container-name", + + # Training image + image="123456789012.dkr.ecr.us-west-2.amazonaws.com/my-training-image:latest", + + # Always pull image + image_pull_policy="Always", + resources=Resources( + # No GPUs requested + requests={"nvidia.com/gpu": "0"}, + # No GPU limit + limits={"nvidia.com/gpu": "0"}, + ), + # Command to run + command=["python", "train.py"], + # Script arguments + args=["--epochs", "10", "--batch-size", "32"], + ) + ] + ) + ), + ) +] + +# Create the PyTorch job +pytorch_job = HyperPodPytorchJob( + job_name="my-pytorch-job", + replica_specs=replica_specs, + run_policy=RunPolicy( + clean_pod_policy="Running" # Keep pods after completion + ) +) + +# Submit the job +pytorch_job.create() +``` + +## Key Parameters + +When creating a training job, you'll need to specify: + +- **job-name**: Unique identifier for your training job +- **image**: Docker image containing your training environment +- **command**: Command to run inside the container +- **args**: Arguments to pass to the command +- **instance-type**: The EC2 instance type to use +- **tasks-per-node**: Number of processes to run per node +- **output-s3-uri**: S3 location to store model artifacts + +## Managing Training Jobs + +### List Training Jobs + +**CLI** +```bash +hyp list hyp-pytorch-job +``` + +**SDK** +```python +from sagemaker.hyperpod import HyperPodManager + +# List all PyTorch jobs +jobs = HyperPodManager.list_jobs(job_type="hyp-pytorch-job") +print(jobs) +``` + +### Describe a Training Job + +**CLI** +```bash +hyp describe hyp-pytorch-job --job-name +``` + +**SDK** +```python +from sagemaker.hyperpod import HyperPodPytorchJob + +# Get an existing job +job = HyperPodPytorchJob.load(job_name="my-pytorch-job") + +# Get job details +job_details = job.describe() +print(job_details) +``` + +### Delete a Training Job + +**CLI** +```bash +hyp delete hyp-pytorch-job --job-name +``` + +**SDK** +```python +from sagemaker.hyperpod import HyperPodPytorchJob + +# Get an existing job +job = HyperPodPytorchJob.load(job_name="my-pytorch-job") + +# Delete the job +job.delete() +``` + +## Training Example Notebooks + +For detailed examples of training with HyperPod, see: + +- [CLI Training Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/training/CLI/training-e2e-cli.ipynb) +- [SDK Training Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/training/SDK/training_sdk_example.ipynb) + +These examples demonstrate end-to-end workflows for creating and managing training jobs using both the CLI and SDK approaches. From 0eb80de8f4d6fb64c87a75165dfa1007a2a339c3 Mon Sep 17 00:00:00 2001 From: adishaa Date: Tue, 22 Jul 2025 12:26:50 -0700 Subject: [PATCH 06/10] Add more inference examples --- doc/index.md | 7 ------- doc/inference.md | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/doc/index.md b/doc/index.md index 2dcc7cbf..2f20a6a9 100644 --- a/doc/index.md +++ b/doc/index.md @@ -47,13 +47,6 @@ Detailed guide on creating Pytorch training jobs Detailed guide on creating, invoking and monitoring endpoints ::: -:::{grid-item-card} Contributor's Guide -:link: getting_started -:link-type: ref - -Improve SageMaker Hyperpod CLI and SDK -::: - :::: :::: diff --git a/doc/inference.md b/doc/inference.md index 073a3ad4..a107f853 100644 --- a/doc/inference.md +++ b/doc/inference.md @@ -196,3 +196,6 @@ For detailed examples of inference with HyperPod, see: - [CLI Inference FSX Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/CLI/inference-fsx-model-e2e-cli.ipynb) - [CLI Inference Jumpstart Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/CLI/inference-jumpstart-e2e-cli.ipynb) - [CLI Inference S3 Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/CLI/inference-s3-model-e2e-cli.ipynb) +- [SDK Inference FSX Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/SDK/inference-fsx-model-e2e.ipynb) +- [SDK Inference Jumpstart Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/SDK/inference-jumpstart-e2e.ipynb) +- [SDK Inference S3 Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/SDK/inference-s3-model-e2e.ipynb) From c56bcacf85f4d8c61eb83f4dcfd17525f18e6549 Mon Sep 17 00:00:00 2001 From: adishaa Date: Wed, 23 Jul 2025 09:51:30 -0700 Subject: [PATCH 07/10] UI changes for documentation --- .gitignore | 1 + doc/_static/custom.css | 53 +++++++ doc/conf.py | 7 +- doc/examples.md | 21 +++ doc/getting_started.ipynb | 75 --------- doc/index.md | 12 +- doc/installation.ipynb | 158 ------------------- doc/installation.md | 70 ++++++++ src/sagemaker/hyperpod/cli/training_utils.py | 1 - 9 files changed, 161 insertions(+), 237 deletions(-) create mode 100644 doc/_static/custom.css create mode 100644 doc/examples.md delete mode 100644 doc/getting_started.ipynb delete mode 100644 doc/installation.ipynb create mode 100644 doc/installation.md diff --git a/.gitignore b/.gitignore index f72c7e06..8a264a78 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ __pycache__/ /.mypy_cache /doc/_apidoc/ +doc/_build/ /build /sagemaker-hyperpod/build diff --git a/doc/_static/custom.css b/doc/_static/custom.css new file mode 100644 index 00000000..53dea4ed --- /dev/null +++ b/doc/_static/custom.css @@ -0,0 +1,53 @@ +/* Custom styles for SageMaker HyperPod documentation */ + +/* Adjust logo size and alignment */ +.navbar-brand img { + max-height: 40px; + width: auto; + margin-right: 10px; + vertical-align: middle; +} + +.navbar-brand .title { + font-weight: 800; + color: #111827; +} + +/* Ensure logo container doesn't force wrapping */ +.navbar-brand-box { + width: auto; + flex-shrink: 0; +} + +/* Header styling */ +header { + background-color: white; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); /* shadow-sm */ + position: sticky; + top: 0; + z-index: 50; +} + +/* Preserving your existing typography */ +h1 { + font-size: 1.875rem; /* text-3xl */ + font-weight: 700; /* font-bold */ + color: #111827; /* text-gray-900 */ +} + +h2 { + font-size: 1.5rem; /* text-2xl */ + font-weight: 700; /* font-bold */ + color: #111827; +} + +h3 { + font-size: 1.25rem; + font-weight: 500; + color: #111827; +} + +p { + font-size: 0.875rem; /* Tailwind's text-sm */ + color: #4b5563; /* Tailwind's gray-600 */ +} diff --git a/doc/conf.py b/doc/conf.py index 7c86f7c5..d197d9aa 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -107,6 +107,7 @@ def get_version(): html_theme = "sphinx_book_theme" html_theme_options = { "logo": { + "text": "SageMaker HyperPod
CLI and SDK", "image_light": "_static/image.png", "image_dark": "_static/image.png", }, @@ -116,9 +117,13 @@ def get_version(): "use_edit_page_button": True, "path_to_docs": "doc", "show_navbar_depth": 2, + "use_fullscreen_button": False, + "use_download_button": False, + "home_page_in_toc": True } htmlhelp_basename = "{}doc".format(project) - +html_static_path = ["_static"] +html_css_files = ["custom.css"] napoleon_use_rtype = False # nbsphinx configuration diff --git a/doc/examples.md b/doc/examples.md new file mode 100644 index 00000000..56ff5632 --- /dev/null +++ b/doc/examples.md @@ -0,0 +1,21 @@ +(examples)= + +# Example Notebooks + +## Training Example Notebooks + +For detailed examples of training with HyperPod, see: + +- [CLI Training Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/training/CLI/training-e2e-cli.ipynb) +- [SDK Training Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/training/SDK/training_sdk_example.ipynb) + +## Inference Example Notebooks + +For detailed examples of inference with HyperPod, see: + +- [CLI Inference FSX Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/CLI/inference-fsx-model-e2e-cli.ipynb) +- [CLI Inference Jumpstart Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/CLI/inference-jumpstart-e2e-cli.ipynb) +- [CLI Inference S3 Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/CLI/inference-s3-model-e2e-cli.ipynb) +- [SDK Inference FSX Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/SDK/inference-fsx-model-e2e.ipynb) +- [SDK Inference Jumpstart Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/SDK/inference-jumpstart-e2e.ipynb) +- [SDK Inference S3 Model Example](https://github.com/aws/sagemaker-hyperpod-cli/blob/main/examples/inference/SDK/inference-s3-model-e2e.ipynb) \ No newline at end of file diff --git a/doc/getting_started.ipynb b/doc/getting_started.ipynb deleted file mode 100644 index 88e71ab6..00000000 --- a/doc/getting_started.ipynb +++ /dev/null @@ -1,75 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "(getting_started)=\n", - "# Getting Started with SageMaker HyperPod CLI\n", - "\n", - "This notebook provides a quick introduction to using the SageMaker HyperPod CLI." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Installation\n", - "\n", - "You can install the SageMaker HyperPod CLI using pip:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pip install sagemaker-hyperpod-cli" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Basic Usage\n", - "\n", - "Here's a simple example of how to use the SageMaker HyperPod CLI:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Import the necessary modules\n", - "import sagemaker_hyperpod_cli\n", - "\n", - "# Example code here\n", - "print(\"Hello from SageMaker HyperPod CLI!\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/doc/index.md b/doc/index.md index 2f20a6a9..1e507b88 100644 --- a/doc/index.md +++ b/doc/index.md @@ -1,6 +1,6 @@ (hpcli_docs_mainpage)= -# SageMaker HyperPod CLI and SDK Documentation +# Overview ```{toctree} :hidden: @@ -10,13 +10,14 @@ Installation Getting Started Training Inference +Example Notebooks API reference <_apidoc/modules> ``` SageMaker HyperPod CLI and SDK provide a seamless way to manage distributed training and inference workloads on EKS-hosted SageMaker HyperPod clusters—without needing Kubernetes expertise. Use the powerful CLI to launch and monitor training jobs and endpoints, or leverage the Python SDK to do the same programmatically with minimal code, including support for JumpStart models, custom endpoints, and built-in monitoring. ::::{container} -::::{grid} +::::{grid} 1 2 4 4 :gutter: 3 :::{grid-item-card} Installation @@ -47,6 +48,13 @@ Detailed guide on creating Pytorch training jobs Detailed guide on creating, invoking and monitoring endpoints ::: +:::{grid-item-card} Example Notebooks +:link: examples +:link-type: ref + +Notebooks that demonstrate end-to-end workflows +::: + :::: :::: diff --git a/doc/installation.ipynb b/doc/installation.ipynb deleted file mode 100644 index 8c70fdfe..00000000 --- a/doc/installation.ipynb +++ /dev/null @@ -1,158 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "(installation)=\n", - "\n", - "# Installation\n", - "\n", - "This notebook provides installation instructions for the SageMaker HyperPod CLI and SDK." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## System Requirements\n", - "\n", - "### Supported Platforms\n", - "- Linux\n", - "- macOS\n", - "\n", - "**Note**: Windows is not supported at this time.\n", - "\n", - "### Supported ML Frameworks\n", - "- PyTorch (version ≥ 1.10)\n", - "\n", - "### Supported Python Versions\n", - "- 3.9\n", - "- 3.10\n", - "- 3.11" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Prerequisites\n", - "\n", - "### For Training\n", - "SageMaker HyperPod CLI currently supports `PyTorchJob` training workloads.\n", - "To run these jobs, install the **SageMaker Training Operator**.\n", - "\n", - "[Install the SageMaker Training Operator](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-eks-operator-install.html)\n", - "\n", - "### For Inference\n", - "The CLI supports creating inference endpoints using JumpStart models or custom configurations.\n", - "To enable this, install the **SageMaker Inference Operator**.\n", - "\n", - "[Install the SageMaker Inference Operator](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-hyperpod-model-deployment-setup.html)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Installation Options" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Option 1: Install from PyPI\n", - "\n", - "You can install the SageMaker HyperPod CLI and SDK directly using `pip`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Install from PyPI\n", - "!pip install sagemaker-hyperpod" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To verify that the installation was successful, run:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Verify CLI installation\n", - "!hyp --help" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Option 2: Install from Source\n", - "\n", - "Clone the GitHub repository and install the CLI from source:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Clone the repository\n", - "!git clone https://github.com/aws/sagemaker-hyperpod-cli.git" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Change to the repository directory\n", - "%cd sagemaker-hyperpod-cli" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Install using pip\n", - "!pip install ." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/doc/installation.md b/doc/installation.md new file mode 100644 index 00000000..6ff9419d --- /dev/null +++ b/doc/installation.md @@ -0,0 +1,70 @@ +(installation)= + +# Installation + +This guide provides installation instructions for the SageMaker HyperPod CLI and SDK. + +## System Requirements + +### Supported Platforms +- Linux +- macOS + +```{note} + Windows is not supported at this time. +``` + +### Supported ML Frameworks +- PyTorch (version ≥ 1.10) + +### Supported Python Versions +- 3.9 +- 3.10 +- 3.11 + +## Prerequisites + +### For Training +SageMaker HyperPod CLI currently supports `PyTorchJob` training workloads. +To run these jobs, install the **SageMaker Training Operator**. + +[Install the SageMaker Training Operator](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-eks-operator-install.html) + +### For Inference +The CLI supports creating inference endpoints using JumpStart models or custom configurations. +To enable this, install the **SageMaker Inference Operator**. + +[Install the SageMaker Inference Operator](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-hyperpod-model-deployment-setup.html) + +## Installation Options + +### Option 1: Install from PyPI + +You can install the SageMaker HyperPod CLI and SDK directly using `pip`: + +```bash +# Install from PyPI +pip install sagemaker-hyperpod +``` + +To verify that the installation was successful, run: + +```bash +# Verify CLI installation +hyp --help +``` + +### Option 2: Install from Source + +Clone the GitHub repository and install the CLI from source: + +```bash +# Clone the repository +git clone https://github.com/aws/sagemaker-hyperpod-cli.git + +# Change to the repository directory +cd sagemaker-hyperpod-cli + +# Install using pip +pip install . +``` diff --git a/src/sagemaker/hyperpod/cli/training_utils.py b/src/sagemaker/hyperpod/cli/training_utils.py index ac000b28..eec1868d 100644 --- a/src/sagemaker/hyperpod/cli/training_utils.py +++ b/src/sagemaker/hyperpod/cli/training_utils.py @@ -3,7 +3,6 @@ import click from typing import Callable, Optional, Mapping, Type - def load_schema_for_version( version: str, base_package: str, From f6936a75501cbe69fcd3a454bb80f37faebd49d4 Mon Sep 17 00:00:00 2001 From: adishaa Date: Wed, 23 Jul 2025 12:28:15 -0700 Subject: [PATCH 08/10] Change to tabbed view for CLI and SDK --- doc/index.md | 2 ++ doc/inference.md | 48 ++++++++++++++++++++++++++++++++++++------------ doc/training.md | 36 ++++++++++++++++++++++++------------ 3 files changed, 62 insertions(+), 24 deletions(-) diff --git a/doc/index.md b/doc/index.md index 1e507b88..d39c7541 100644 --- a/doc/index.md +++ b/doc/index.md @@ -16,6 +16,8 @@ API reference <_apidoc/modules> SageMaker HyperPod CLI and SDK provide a seamless way to manage distributed training and inference workloads on EKS-hosted SageMaker HyperPod clusters—without needing Kubernetes expertise. Use the powerful CLI to launch and monitor training jobs and endpoints, or leverage the Python SDK to do the same programmatically with minimal code, including support for JumpStart models, custom endpoints, and built-in monitoring. +## Start Here + ::::{container} ::::{grid} 1 2 4 4 :gutter: 3 diff --git a/doc/inference.md b/doc/inference.md index 67237385..3edb2a7d 100644 --- a/doc/inference.md +++ b/doc/inference.md @@ -21,7 +21,8 @@ You can create inference endpoints using either JumpStart models or custom model ### JumpStart Model Endpoints -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash hyp create hyp-jumpstart-endpoint \ --version 1.0 \ @@ -30,8 +31,9 @@ hyp create hyp-jumpstart-endpoint \ --endpoint-name endpoint-jumpstart \ --tls-output-s3-uri s3://sample-bucket ``` +```` -**SDK** +````{tab-item} SDK ```python from sagemaker.hyperpod.inference import HyperPodJumpstartEndpoint @@ -46,10 +48,13 @@ endpoint = HyperPodJumpstartEndpoint( # Deploy the endpoint endpoint.create() ``` +```` +````` ### Custom Model Endpoints -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash hyp create hyp-custom-endpoint \ --version 1.0 \ @@ -59,8 +64,9 @@ hyp create hyp-custom-endpoint \ --instance-type ml.g5.8xlarge \ --tls-output-s3-uri s3://sample-bucket ``` +```` -**SDK** +````{tab-item} SDK ```python from sagemaker.hyperpod.inference import HyperPodCustomEndpoint @@ -76,6 +82,8 @@ endpoint = HyperPodCustomEndpoint( # Deploy the endpoint endpoint.create() ``` +```` +````` ## Key Parameters @@ -92,7 +100,8 @@ When creating an inference endpoint, you'll need to specify: ### List Endpoints -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash # List JumpStart endpoints hyp list hyp-jumpstart-endpoint @@ -100,8 +109,9 @@ hyp list hyp-jumpstart-endpoint # List custom endpoints hyp list hyp-custom-endpoint ``` +```` -**SDK** +````{tab-item} SDK ```python from sagemaker.hyperpod.inference import HyperPodJumpstartEndpoint, HyperPodCustomEndpoint @@ -113,10 +123,13 @@ print(jumpstart_endpoints) custom_endpoints = HyperPodCustomEndpoint.list() print(custom_endpoints) ``` +```` +````` ### Describe an Endpoint -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash # Describe JumpStart endpoint hyp describe hyp-jumpstart-endpoint --endpoint-name @@ -124,8 +137,9 @@ hyp describe hyp-jumpstart-endpoint --endpoint-name # Describe custom endpoint hyp describe hyp-custom-endpoint --endpoint-name ``` +```` -**SDK** +````{tab-item} SDK ```python from sagemaker.hyperpod.inference import HyperPodJumpstartEndpoint, HyperPodCustomEndpoint @@ -139,10 +153,13 @@ custom_endpoint = HyperPodCustomEndpoint.load(endpoint_name="endpoint-custom") custom_details = custom_endpoint.describe() print(custom_details) ``` +```` +````` ### Invoke an Endpoint -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash # Invoke custom endpoint hyp invoke hyp-custom-endpoint \ @@ -150,8 +167,9 @@ hyp invoke hyp-custom-endpoint \ --content-type "application/json" \ --payload '{"inputs": "What is machine learning?"}' ``` +```` -**SDK** +````{tab-item} SDK ```python from sagemaker.hyperpod.inference import HyperPodCustomEndpoint @@ -165,10 +183,13 @@ response = endpoint.invoke( ) print(response) ``` +```` +````` ### Delete an Endpoint -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash # Delete JumpStart endpoint hyp delete hyp-jumpstart-endpoint --endpoint-name @@ -176,8 +197,9 @@ hyp delete hyp-jumpstart-endpoint --endpoint-name # Delete custom endpoint hyp delete hyp-custom-endpoint --endpoint-name ``` +```` -**SDK** +````{tab-item} SDK ```python from sagemaker.hyperpod.inference import HyperPodJumpstartEndpoint, HyperPodCustomEndpoint @@ -189,6 +211,8 @@ jumpstart_endpoint.delete() custom_endpoint = HyperPodCustomEndpoint.load(endpoint_name="endpoint-custom") custom_endpoint.delete() ``` +```` +````` ## Inference Example Notebooks diff --git a/doc/training.md b/doc/training.md index 6cdc0332..76bb4c79 100644 --- a/doc/training.md +++ b/doc/training.md @@ -20,7 +20,8 @@ SageMaker HyperPod training jobs allow you to: You can create training jobs using either the CLI or SDK approach: -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash hyp create hyp-pytorch-job \ --version 1.0 \ @@ -42,8 +43,8 @@ hyp create hyp-pytorch-job \ --persistent-volume-claims '["shared-data-pvc", "model-registry-pvc"]' \ --output-s3-uri s3://my-bucket/model-artifacts ``` - -**SDK** +```` +````{tab-item} SDK ```python from sagemaker.hyperpod import HyperPodPytorchJob from sagemaker.hyperpod.job import ReplicaSpec, Template, Spec, Container, Resources, RunPolicy, Metadata @@ -94,6 +95,8 @@ pytorch_job = HyperPodPytorchJob( # Submit the job pytorch_job.create() ``` +```` +````` ## Key Parameters @@ -111,12 +114,13 @@ When creating a training job, you'll need to specify: ### List Training Jobs -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash hyp list hyp-pytorch-job ``` - -**SDK** +```` +````{tab-item} SDK ```python from sagemaker.hyperpod import HyperPodManager @@ -124,15 +128,18 @@ from sagemaker.hyperpod import HyperPodManager jobs = HyperPodManager.list_jobs(job_type="hyp-pytorch-job") print(jobs) ``` +```` +````` ### Describe a Training Job -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash hyp describe hyp-pytorch-job --job-name ``` - -**SDK** +```` +````{tab-item} SDK ```python from sagemaker.hyperpod import HyperPodPytorchJob @@ -143,15 +150,18 @@ job = HyperPodPytorchJob.load(job_name="my-pytorch-job") job_details = job.describe() print(job_details) ``` +```` +````` ### Delete a Training Job -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash hyp delete hyp-pytorch-job --job-name ``` - -**SDK** +```` +````{tab-item} SDK ```python from sagemaker.hyperpod import HyperPodPytorchJob @@ -161,6 +171,8 @@ job = HyperPodPytorchJob.load(job_name="my-pytorch-job") # Delete the job job.delete() ``` +```` +````` ## Training Example Notebooks From 843296f827344223b36719b2a3676b0be7ae9f18 Mon Sep 17 00:00:00 2001 From: adishaa Date: Wed, 23 Jul 2025 12:39:34 -0700 Subject: [PATCH 09/10] Change to tabbed view getting started page --- doc/getting_started.md | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/doc/getting_started.md b/doc/getting_started.md index e3e8d6c8..1ba925ce 100644 --- a/doc/getting_started.md +++ b/doc/getting_started.md @@ -10,18 +10,22 @@ This guide will help you get started with the SageMaker HyperPod CLI and SDK to List all available SageMaker HyperPod clusters in your account: -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash hyp list-cluster [--region ] [--namespace ] [--output ] ``` +```` -**SDK** +````{tab-item} SDK ```python from sagemaker.hyperpod.hyperpod_manager import HyperPodManager clusters = HyperPodManager.list_clusters(region='us-east-2') print(clusters) ``` +```` +````` **Parameters:** - `region` (string) - Optional. The AWS region where the SageMaker HyperPod and EKS clusters are located. If not specified, uses the region from your current AWS account credentials. @@ -32,17 +36,21 @@ print(clusters) Configure your local kubectl environment to interact with a specific SageMaker HyperPod cluster and namespace: -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash hyp set-cluster-context --cluster-name [--namespace ] ``` +```` -**SDK** +````{tab-item} SDK ```python from sagemaker.hyperpod.hyperpod_manager import HyperPodManager HyperPodManager.set_context('', region='us-east-2') ``` +```` +````` **Parameters:** - `cluster-name` (string) - Required. The SageMaker HyperPod cluster name to configure with. @@ -52,12 +60,14 @@ HyperPodManager.set_context('', region='us-east-2') View information about the currently configured cluster context: -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash hyp get-cluster-context ``` +```` -**SDK** +````{tab-item} SDK ```python from sagemaker.hyperpod.hyperpod_manager import HyperPodManager @@ -65,6 +75,8 @@ from sagemaker.hyperpod.hyperpod_manager import HyperPodManager context = HyperPodManager.get_context() print(context) ``` +```` +````` ## Job Management @@ -72,16 +84,20 @@ print(context) View all pods associated with a specific training job: -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash hyp list-pods hyp-pytorch-job --job-name ``` +```` -**SDK** +````{tab-item} SDK ```python # List all pods created for this job pytorch_job.list_pods() ``` +```` +````` **Parameters:** - `job-name` (string) - Required. The name of the job to list pods for. @@ -90,16 +106,20 @@ pytorch_job.list_pods() View logs for a specific pod within a training job: -**CLI** +`````{tab-set} +````{tab-item} CLI ```bash hyp get-logs hyp-pytorch-job --pod-name --job-name ``` +```` -**SDK** +````{tab-item} SDK ```python # Check the logs from pod0 pytorch_job.get_logs_from_pod("demo-pod-0") ``` +```` +````` **Parameters:** - `job-name` (string) - Required. The name of the job to get logs for. From 532de64ea5250478574dc0060a0261026acf6ac9 Mon Sep 17 00:00:00 2001 From: adishaa Date: Wed, 23 Jul 2025 12:41:23 -0700 Subject: [PATCH 10/10] clean up custom css --- doc/_static/custom.css | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/doc/_static/custom.css b/doc/_static/custom.css index 53dea4ed..82a2499b 100644 --- a/doc/_static/custom.css +++ b/doc/_static/custom.css @@ -22,22 +22,21 @@ /* Header styling */ header { background-color: white; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); /* shadow-sm */ + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); position: sticky; top: 0; z-index: 50; } -/* Preserving your existing typography */ h1 { - font-size: 1.875rem; /* text-3xl */ - font-weight: 700; /* font-bold */ - color: #111827; /* text-gray-900 */ + font-size: 1.875rem; + font-weight: 700; + color: #111827; } h2 { - font-size: 1.5rem; /* text-2xl */ - font-weight: 700; /* font-bold */ + font-size: 1.5rem; + font-weight: 700; color: #111827; } @@ -48,6 +47,6 @@ h3 { } p { - font-size: 0.875rem; /* Tailwind's text-sm */ - color: #4b5563; /* Tailwind's gray-600 */ + font-size: 0.875rem; + color: #4b5563; }