diff --git a/.gitignore b/.gitignore index b7a858e341..ad6e488dbd 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,4 @@ env/ **/_repack_script_launcher.sh tests/data/**/_repack_model.py tests/data/experiment/sagemaker-dev-1.0.tar.gz -src/sagemaker/serve/tmp_workspace +src/sagemaker/serve/tmp_workspace \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 935209f836..6475a3ad04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## v2.208.0 (2024-02-15) + +### Features + + * Telemetry metrics + * TGI 1.4.0 + * Support collection type and target store for feature store ingestion. + +### Bug Fixes and Other Changes + + * bump jinja2 to 3.1.3 in doc/requirments.txt + * chore(deps): bump jinja2 from 3.0.3 to 3.1.3 in /requirements/extras + * Fix dependabot alert in transformers package + * Bump Apache Airflow version to 2.8.0 + * skip failing mxnet tests + +### Documentation Changes + + * change order of pipelines topics + * Explain the ClarifyCheckStep and QualityCheckStep parameters + * fix the ClarifyCheckStep documentation to mention PDP + ## v2.207.1 (2024-02-06) ### Bug Fixes and Other Changes diff --git a/VERSION b/VERSION index 807ccf7aeb..f22a158e5a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.207.2.dev0 +2.208.1.dev0 diff --git a/doc/requirements.txt b/doc/requirements.txt index 640a85ca08..3d5618ce32 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -2,5 +2,5 @@ sphinx==5.1.1 sphinx-rtd-theme==0.5.0 docutils==0.15.2 packaging==20.9 -jinja2<3.1 +jinja2==3.1.3 schema==0.7.5 diff --git a/doc/workflows/index.rst b/doc/workflows/index.rst index bde061aaaf..f47452e645 100644 --- a/doc/workflows/index.rst +++ b/doc/workflows/index.rst @@ -7,7 +7,7 @@ The SageMaker Python SDK supports managed training and inference for a variety o .. toctree:: :maxdepth: 2 + pipelines/index airflow/index step_functions/index - pipelines/index lineage/index diff --git a/src/sagemaker/image_uri_config/autogluon.json b/src/sagemaker/image_uri_config/autogluon.json index aecc3584df..57ce47f94c 100644 --- a/src/sagemaker/image_uri_config/autogluon.json +++ b/src/sagemaker/image_uri_config/autogluon.json @@ -10,7 +10,8 @@ "0.5": "0.5.2", "0.6": "0.6.2", "0.7": "0.7.0", - "0.8": "0.8.2" + "0.8": "0.8.2", + "1.0": "1.0.0" }, "versions": { "0.3.1": { @@ -438,6 +439,47 @@ "py_versions": [ "py39" ] + }, + "1.0.0": { + "registries": { + "af-south-1": "626614931356", + "il-central-1": "780543022126", + "ap-east-1": "871362719292", + "ap-northeast-1": "763104351884", + "ap-northeast-2": "763104351884", + "ap-northeast-3": "364406365360", + "ap-south-1": "763104351884", + "ap-southeast-1": "763104351884", + "ap-southeast-2": "763104351884", + "ap-southeast-3": "907027046896", + "ap-southeast-4": "457447274322", + "ca-central-1": "763104351884", + "eu-central-1": "763104351884", + "eu-north-1": "763104351884", + "eu-west-1": "763104351884", + "eu-west-2": "763104351884", + "eu-west-3": "763104351884", + "eu-south-1": "692866216735", + "me-south-1": "217643126080", + "sa-east-1": "763104351884", + "us-east-1": "763104351884", + "us-east-2": "763104351884", + "us-gov-east-1": "446045086412", + "us-gov-west-1": "442386744353", + "us-iso-east-1": "886529160074", + "us-isob-east-1": "094389454867", + "us-west-1": "763104351884", + "us-west-2": "763104351884", + "ca-west-1": "204538143572" + }, + "repository": "autogluon-training", + "processors": [ + "cpu", + "gpu" + ], + "py_versions": [ + "py310" + ] } } }, @@ -448,7 +490,8 @@ "0.5": "0.5.2", "0.6": "0.6.2", "0.7": "0.7.0", - "0.8": "0.8.2" + "0.8": "0.8.2", + "1.0": "1.0.0" }, "versions": { "0.3.1": { @@ -901,6 +944,49 @@ "py_versions": [ "py39" ] + }, + "1.0.0": { + "registries": { + "af-south-1": "626614931356", + "il-central-1": "780543022126", + "ap-east-1": "871362719292", + "ap-northeast-1": "763104351884", + "ap-northeast-2": "763104351884", + "ap-northeast-3": "364406365360", + "ap-south-1": "763104351884", + "ap-southeast-1": "763104351884", + "ap-southeast-2": "763104351884", + "ap-southeast-3": "907027046896", + "ap-southeast-4": "457447274322", + "ca-central-1": "763104351884", + "cn-north-1": "727897471807", + "cn-northwest-1": "727897471807", + "eu-central-1": "763104351884", + "eu-north-1": "763104351884", + "eu-west-1": "763104351884", + "eu-west-2": "763104351884", + "eu-west-3": "763104351884", + "eu-south-1": "692866216735", + "me-south-1": "217643126080", + "sa-east-1": "763104351884", + "us-east-1": "763104351884", + "us-east-2": "763104351884", + "us-gov-east-1": "446045086412", + "us-gov-west-1": "442386744353", + "us-iso-east-1": "886529160074", + "us-isob-east-1": "094389454867", + "us-west-1": "763104351884", + "us-west-2": "763104351884", + "ca-west-1": "204538143572" + }, + "repository": "autogluon-inference", + "processors": [ + "cpu", + "gpu" + ], + "py_versions": [ + "py310" + ] } } } diff --git a/src/sagemaker/image_uri_config/huggingface-llm.json b/src/sagemaker/image_uri_config/huggingface-llm.json index 637e831c50..0e90a478b3 100644 --- a/src/sagemaker/image_uri_config/huggingface-llm.json +++ b/src/sagemaker/image_uri_config/huggingface-llm.json @@ -10,7 +10,8 @@ "1.0": "1.0.3", "1.1": "1.1.0", "1.2": "1.2.0", - "1.3": "1.3.3" + "1.3": "1.3.3", + "1.4": "1.4.0" }, "versions": { "0.6.0": { @@ -388,6 +389,53 @@ "container_version": { "gpu": "cu121-ubuntu20.04" } + }, + "1.4.0": { + "py_versions": [ + "py310" + ], + "registries": { + "af-south-1": "626614931356", + "il-central-1": "780543022126", + "ap-east-1": "871362719292", + "ap-northeast-1": "763104351884", + "ap-northeast-2": "763104351884", + "ap-northeast-3": "364406365360", + "ap-south-1": "763104351884", + "ap-south-2": "772153158452", + "ap-southeast-1": "763104351884", + "ap-southeast-2": "763104351884", + "ap-southeast-3": "907027046896", + "ap-southeast-4": "457447274322", + "ca-central-1": "763104351884", + "cn-north-1": "727897471807", + "cn-northwest-1": "727897471807", + "eu-central-1": "763104351884", + "eu-central-2": "380420809688", + "eu-north-1": "763104351884", + "eu-west-1": "763104351884", + "eu-west-2": "763104351884", + "eu-west-3": "763104351884", + "eu-south-1": "692866216735", + "eu-south-2": "503227376785", + "me-south-1": "217643126080", + "me-central-1": "914824155844", + "sa-east-1": "763104351884", + "us-east-1": "763104351884", + "us-east-2": "763104351884", + "us-gov-east-1": "446045086412", + "us-gov-west-1": "442386744353", + "us-iso-east-1": "886529160074", + "us-isob-east-1": "094389454867", + "us-west-1": "763104351884", + "us-west-2": "763104351884", + "ca-west-1": "204538143572" + }, + "tag_prefix": "2.1.1-tgi1.4.0", + "repository": "huggingface-pytorch-tgi-inference", + "container_version": { + "gpu": "cu121-ubuntu20.04" + } } } } diff --git a/src/sagemaker/serve/utils/telemetry_logger.py b/src/sagemaker/serve/utils/telemetry_logger.py index cb57a9f0a7..84044c4d96 100644 --- a/src/sagemaker/serve/utils/telemetry_logger.py +++ b/src/sagemaker/serve/utils/telemetry_logger.py @@ -13,12 +13,15 @@ """Placeholder docstring""" from __future__ import absolute_import import logging +from time import perf_counter + import requests -from sagemaker import Session +from sagemaker import Session, exceptions from sagemaker.serve.mode.function_pointers import Mode from sagemaker.serve.utils.exceptions import ModelBuilderException from sagemaker.serve.utils.types import ModelServer +from sagemaker.user_agent import SDK_VERSION logger = logging.getLogger(__name__) @@ -63,13 +66,21 @@ def wrapper(self, *args, **kwargs): f"{func_name}" f"&x-modelServer={MODEL_SERVER_TO_CODE[str(self.model_server)]}" f"&x-imageTag={image_uri_tail}" + f"&x-sdkVersion={SDK_VERSION}" ) if self.model_server == ModelServer.DJL_SERVING or self.model_server == ModelServer.TGI: extra += f"&x-modelName={self.model}" + if self.sagemaker_session and self.sagemaker_session.endpoint_arn: + extra += f"&x-endpointArn={self.sagemaker_session.endpoint_arn}" + + start_timer = perf_counter() try: response = func(self, *args, **kwargs) + stop_timer = perf_counter() + elapsed = stop_timer - start_timer + extra += f"&x-latency={round(elapsed, 2)}" if not self.serve_settings.telemetry_opt_out: _send_telemetry( "1", @@ -79,7 +90,15 @@ def wrapper(self, *args, **kwargs): None, extra, ) - except ModelBuilderException as e: + except ( + ModelBuilderException, + exceptions.CapacityError, + exceptions.UnexpectedStatusException, + exceptions.AsyncInferenceError, + ) as e: + stop_timer = perf_counter() + elapsed = stop_timer - start_timer + extra += f"&x-latency={round(elapsed, 2)}" if not self.serve_settings.telemetry_opt_out: _send_telemetry( "0", diff --git a/src/sagemaker/session.py b/src/sagemaker/session.py index ff5a82a902..14b2e904e8 100644 --- a/src/sagemaker/session.py +++ b/src/sagemaker/session.py @@ -243,6 +243,7 @@ def __init__( # sagemaker_config is validated and initialized inside :func:`_initialize`, # so if default_bucket is None and the sagemaker_config has a default S3 bucket configured, # _default_bucket_name_override will be set again inside :func:`_initialize`. + self.endpoint_arn = None self._default_bucket = None self._default_bucket_name_override = default_bucket # this may also be set again inside :func:`_initialize` if it is None @@ -4284,9 +4285,12 @@ def create_endpoint(self, endpoint_name, config_name, tags=None, wait=True, live tags, "{}.{}.{}".format(SAGEMAKER, ENDPOINT, TAGS) ) - self.sagemaker_client.create_endpoint( + res = self.sagemaker_client.create_endpoint( EndpointName=endpoint_name, EndpointConfigName=config_name, Tags=tags ) + if res: + self.endpoint_arn = res["EndpointArn"] + if wait: self.wait_for_endpoint(endpoint_name, live_logging=live_logging) return endpoint_name @@ -4344,9 +4348,11 @@ def update_endpoint(self, endpoint_name, endpoint_config_name, wait=True): "existing endpoint name".format(endpoint_name) ) - self.sagemaker_client.update_endpoint( + res = self.sagemaker_client.update_endpoint( EndpointName=endpoint_name, EndpointConfigName=endpoint_config_name ) + if res: + self.endpoint_arn = res["EndpointArn"] if wait: self.wait_for_endpoint(endpoint_name) diff --git a/src/sagemaker/workflow/clarify_check_step.py b/src/sagemaker/workflow/clarify_check_step.py index cefb6b95b4..c53e7e9666 100644 --- a/src/sagemaker/workflow/clarify_check_step.py +++ b/src/sagemaker/workflow/clarify_check_step.py @@ -132,8 +132,10 @@ class ModelExplainabilityCheckConfig(ClarifyCheckConfig): Attributes: model_config (ModelConfig): Config of the model and its endpoint to be created. - explainability_config (SHAPConfig): Config of the specific explainability method. - Currently, only SHAP is supported. + explainability_config (SHAPConfig or PDPConfig): Config of the explainability method. + Supports SHAP or PDP. + For `PDPConfig`, `features` must be specified. + `top_k_features` based on SHAP is currently not supported. model_scores (str or int or ModelPredictedLabelConfig): Index or JMESPath expression to locate the predicted scores in the model output (default: None). This is not required if the model output is a single score. Alternatively, @@ -166,6 +168,10 @@ def __init__( ): """Constructs a ClarifyCheckStep. + To understand the `skip_check`, `fail_on_violation`, `register_new_baseline` + and `supplied_baseline_constraints` parameters, check the following documentation: + https://docs.aws.amazon.com/sagemaker/latest/dg/pipelines-quality-clarify-baseline-lifecycle.html + Args: name (str): The name of the ClarifyCheckStep step. clarify_check_config (ClarifyCheckConfig): A ClarifyCheckConfig instance. diff --git a/src/sagemaker/workflow/quality_check_step.py b/src/sagemaker/workflow/quality_check_step.py index 8257ed3844..c99ce587ac 100644 --- a/src/sagemaker/workflow/quality_check_step.py +++ b/src/sagemaker/workflow/quality_check_step.py @@ -135,6 +135,11 @@ def __init__( ): """Constructs a QualityCheckStep. + To understand the `skip_check`, `fail_on_violation`, `register_new_baseline`, + `supplied_baseline_constraints` and `supplied_baseline_constraints` parameters, + check the following documentation: + https://docs.aws.amazon.com/sagemaker/latest/dg/pipelines-quality-clarify-baseline-lifecycle.html + Args: name (str): The name of the QualityCheckStep step. quality_check_config (QualityCheckConfig): A QualityCheckConfig instance. diff --git a/tests/unit/sagemaker/image_uris/test_huggingface_llm.py b/tests/unit/sagemaker/image_uris/test_huggingface_llm.py index 9a441bbcf9..910d3517c2 100644 --- a/tests/unit/sagemaker/image_uris/test_huggingface_llm.py +++ b/tests/unit/sagemaker/image_uris/test_huggingface_llm.py @@ -28,6 +28,7 @@ "1.2.0": "2.1.1-tgi1.2.0-gpu-py310-cu121-ubuntu20.04", "1.3.1": "2.1.1-tgi1.3.1-gpu-py310-cu121-ubuntu20.04", "1.3.3": "2.1.1-tgi1.3.3-gpu-py310-cu121-ubuntu20.04", + "1.4.0": "2.1.1-tgi1.4.0-gpu-py310-cu121-ubuntu20.04", }, "inf2": { "0.0.16": "1.13.1-optimum0.0.16-neuronx-py310-ubuntu22.04", diff --git a/tests/unit/sagemaker/serve/utils/test_telemetry_logger.py b/tests/unit/sagemaker/serve/utils/test_telemetry_logger.py index 7c4b014e8a..73d24c9505 100644 --- a/tests/unit/sagemaker/serve/utils/test_telemetry_logger.py +++ b/tests/unit/sagemaker/serve/utils/test_telemetry_logger.py @@ -20,6 +20,7 @@ _construct_url, ) from sagemaker.serve.utils.exceptions import ModelBuilderException, LocalModelOutOfMemoryException +from sagemaker.user_agent import SDK_VERSION MOCK_SESSION = Mock() MOCK_FUNC_NAME = "Mock.deploy" @@ -32,6 +33,7 @@ ) MOCK_HUGGINGFACE_ID = "meta-llama/Llama-2-7b-hf" MOCK_EXCEPTION = LocalModelOutOfMemoryException("mock raise ex") +MOCK_ENDPOINT_ARN = "arn:aws:sagemaker:us-west-2:123456789012:endpoint/test" class ModelBuilderMock: @@ -72,15 +74,22 @@ def test_capture_telemetry_decorator_djl_success(self, mock_send_telemetry): mock_model_builder.model = MOCK_HUGGINGFACE_ID mock_model_builder.mode = Mode.LOCAL_CONTAINER mock_model_builder.model_server = ModelServer.DJL_SERVING + mock_model_builder.sagemaker_session.endpoint_arn = MOCK_ENDPOINT_ARN mock_model_builder.mock_deploy() + args = mock_send_telemetry.call_args.args + latency = str(args[5]).split("latency=")[1] expected_extra_str = ( f"{MOCK_FUNC_NAME}" "&x-modelServer=4" "&x-imageTag=djl-inference:0.25.0-deepspeed0.11.0-cu118" + f"&x-sdkVersion={SDK_VERSION}" f"&x-modelName={MOCK_HUGGINGFACE_ID}" + f"&x-endpointArn={MOCK_ENDPOINT_ARN}" + f"&x-latency={latency}" ) + mock_send_telemetry.assert_called_once_with( "1", 2, MOCK_SESSION, None, None, expected_extra_str ) @@ -93,15 +102,22 @@ def test_capture_telemetry_decorator_tgi_success(self, mock_send_telemetry): mock_model_builder.model = MOCK_HUGGINGFACE_ID mock_model_builder.mode = Mode.LOCAL_CONTAINER mock_model_builder.model_server = ModelServer.TGI + mock_model_builder.sagemaker_session.endpoint_arn = MOCK_ENDPOINT_ARN mock_model_builder.mock_deploy() + args = mock_send_telemetry.call_args.args + latency = str(args[5]).split("latency=")[1] expected_extra_str = ( f"{MOCK_FUNC_NAME}" "&x-modelServer=6" "&x-imageTag=huggingface-pytorch-inference:2.0.0-transformers4.28.1-cpu-py310-ubuntu20.04" + f"&x-sdkVersion={SDK_VERSION}" f"&x-modelName={MOCK_HUGGINGFACE_ID}" + f"&x-endpointArn={MOCK_ENDPOINT_ARN}" + f"&x-latency={latency}" ) + mock_send_telemetry.assert_called_once_with( "1", 2, MOCK_SESSION, None, None, expected_extra_str ) @@ -126,6 +142,7 @@ def test_capture_telemetry_decorator_handle_exception_success(self, mock_send_te mock_model_builder.model = MOCK_HUGGINGFACE_ID mock_model_builder.mode = Mode.LOCAL_CONTAINER mock_model_builder.model_server = ModelServer.DJL_SERVING + mock_model_builder.sagemaker_session.endpoint_arn = MOCK_ENDPOINT_ARN mock_exception = Mock() mock_exception_obj = MOCK_EXCEPTION @@ -134,12 +151,18 @@ def test_capture_telemetry_decorator_handle_exception_success(self, mock_send_te with self.assertRaises(ModelBuilderException) as _: mock_model_builder.mock_deploy(mock_exception) + args = mock_send_telemetry.call_args.args + latency = str(args[5]).split("latency=")[1] expected_extra_str = ( f"{MOCK_FUNC_NAME}" "&x-modelServer=4" "&x-imageTag=djl-inference:0.25.0-deepspeed0.11.0-cu118" + f"&x-sdkVersion={SDK_VERSION}" f"&x-modelName={MOCK_HUGGINGFACE_ID}" + f"&x-endpointArn={MOCK_ENDPOINT_ARN}" + f"&x-latency={latency}" ) + mock_send_telemetry.assert_called_once_with( "0", 2,