sagemaker-core 1.0.62__py3-none-any.whl → 2.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sagemaker/__init__.py +2 -0
- sagemaker/core/__init__.py +16 -0
- sagemaker/core/_studio.py +116 -0
- sagemaker/core/_version.py +11 -0
- sagemaker/core/accept_types.py +131 -0
- sagemaker/core/analytics.py +744 -0
- sagemaker/core/apiutils/__init__.py +13 -0
- sagemaker/core/apiutils/_base_types.py +228 -0
- sagemaker/core/apiutils/_boto_functions.py +130 -0
- sagemaker/core/apiutils/_utils.py +34 -0
- sagemaker/core/base_deserializers.py +35 -0
- sagemaker/core/base_serializers.py +35 -0
- sagemaker/core/clarify/__init__.py +2898 -0
- sagemaker/core/collection.py +467 -0
- sagemaker/core/common_utils.py +2399 -0
- sagemaker/core/compute_resource_requirements/__init__.py +18 -0
- sagemaker/core/compute_resource_requirements/resource_requirements.py +94 -0
- sagemaker/core/config/__init__.py +181 -0
- sagemaker/core/config/config.py +238 -0
- sagemaker/core/config/config_manager.py +595 -0
- sagemaker/core/config/config_schema.py +1220 -0
- sagemaker/core/config/config_utils.py +297 -0
- {sagemaker_core/main → sagemaker/core}/config_schema.py +408 -3
- sagemaker/core/constants.py +73 -0
- sagemaker/core/content_types.py +137 -0
- sagemaker/core/debugger/__init__.py +39 -0
- sagemaker/core/debugger/debugger.py +945 -0
- sagemaker/core/debugger/framework_profile.py +292 -0
- sagemaker/core/debugger/metrics_config.py +468 -0
- sagemaker/core/debugger/profiler.py +42 -0
- sagemaker/core/debugger/profiler_config.py +190 -0
- sagemaker/core/debugger/profiler_constants.py +40 -0
- sagemaker/core/debugger/utils.py +148 -0
- sagemaker/core/deprecations.py +254 -0
- sagemaker/core/deserializers/__init__.py +10 -0
- sagemaker/core/deserializers/base.py +424 -0
- sagemaker/core/deserializers/implementations.py +157 -0
- sagemaker/core/drift_check_baselines.py +106 -0
- sagemaker/core/enums.py +51 -0
- sagemaker/core/environment_variables.py +101 -0
- sagemaker/core/exceptions.py +108 -0
- sagemaker/core/experiments/__init__.py +53 -0
- sagemaker/core/experiments/_api_types.py +251 -0
- sagemaker/core/experiments/_environment.py +124 -0
- sagemaker/core/experiments/_helper.py +294 -0
- sagemaker/core/experiments/_metrics.py +333 -0
- sagemaker/core/experiments/_run_context.py +58 -0
- sagemaker/core/experiments/_utils.py +216 -0
- sagemaker/core/experiments/experiment.py +247 -0
- sagemaker/core/experiments/run.py +970 -0
- sagemaker/core/experiments/trial.py +296 -0
- sagemaker/core/experiments/trial_component.py +387 -0
- sagemaker/core/explainer/__init__.py +24 -0
- sagemaker/core/explainer/clarify_explainer_config.py +298 -0
- sagemaker/core/explainer/explainer_config.py +44 -0
- sagemaker/core/fw_utils.py +1220 -0
- sagemaker/core/git_utils.py +415 -0
- sagemaker/core/helper/pipeline_variable.py +82 -0
- sagemaker/core/helper/session_helper.py +2977 -0
- sagemaker/core/hyperparameters.py +172 -0
- sagemaker/core/image_retriever/__init__.py +3 -0
- sagemaker/core/image_retriever/image_retriever.py +640 -0
- sagemaker/core/image_retriever/image_retriever_utils.py +509 -0
- sagemaker/core/image_retriever/test.py +7 -0
- sagemaker/core/image_uri_config/autogluon.json +1335 -0
- sagemaker/core/image_uri_config/blazingtext.json +50 -0
- sagemaker/core/image_uri_config/chainer.json +104 -0
- sagemaker/core/image_uri_config/clarify.json +39 -0
- sagemaker/core/image_uri_config/coach-mxnet.json +70 -0
- sagemaker/core/image_uri_config/coach-tensorflow.json +186 -0
- sagemaker/core/image_uri_config/data-wrangler.json +91 -0
- sagemaker/core/image_uri_config/debugger.json +34 -0
- sagemaker/core/image_uri_config/detailed-profiler.json +18 -0
- sagemaker/core/image_uri_config/djl-deepspeed.json +385 -0
- sagemaker/core/image_uri_config/djl-fastertransformer.json +167 -0
- sagemaker/core/image_uri_config/djl-lmi.json +136 -0
- sagemaker/core/image_uri_config/djl-neuronx.json +258 -0
- sagemaker/core/image_uri_config/djl-tensorrtllm.json +262 -0
- sagemaker/core/image_uri_config/factorization-machines.json +50 -0
- sagemaker/core/image_uri_config/forecasting-deepar.json +50 -0
- sagemaker/core/image_uri_config/huggingface-llm-neuronx.json +770 -0
- sagemaker/core/image_uri_config/huggingface-llm.json +1267 -0
- sagemaker/core/image_uri_config/huggingface-neuron.json +52 -0
- sagemaker/core/image_uri_config/huggingface-neuronx.json +686 -0
- sagemaker/core/image_uri_config/huggingface-tei-cpu.json +298 -0
- sagemaker/core/image_uri_config/huggingface-tei.json +298 -0
- sagemaker/core/image_uri_config/huggingface-training-compiler.json +195 -0
- sagemaker/core/image_uri_config/huggingface-vllm-neuronx.json +38 -0
- sagemaker/core/image_uri_config/huggingface.json +2287 -0
- sagemaker/core/image_uri_config/hyperpod-recipes-neuron.json +52 -0
- sagemaker/core/image_uri_config/image-classification-neo.json +43 -0
- sagemaker/core/image_uri_config/image-classification.json +50 -0
- sagemaker/core/image_uri_config/inferentia-mxnet.json +88 -0
- sagemaker/core/image_uri_config/inferentia-pytorch.json +127 -0
- sagemaker/core/image_uri_config/inferentia-tensorflow.json +88 -0
- sagemaker/core/image_uri_config/instance_gpu_info.json +782 -0
- sagemaker/core/image_uri_config/ipinsights.json +50 -0
- sagemaker/core/image_uri_config/kmeans.json +50 -0
- sagemaker/core/image_uri_config/knn.json +50 -0
- sagemaker/core/image_uri_config/lda.json +26 -0
- sagemaker/core/image_uri_config/linear-learner.json +50 -0
- sagemaker/core/image_uri_config/model-monitor.json +42 -0
- sagemaker/core/image_uri_config/mxnet.json +1154 -0
- sagemaker/core/image_uri_config/neo-mxnet.json +64 -0
- sagemaker/core/image_uri_config/neo-pytorch.json +341 -0
- sagemaker/core/image_uri_config/neo-tensorflow.json +109 -0
- sagemaker/core/image_uri_config/ntm.json +50 -0
- sagemaker/core/image_uri_config/object-detection.json +50 -0
- sagemaker/core/image_uri_config/object2vec.json +50 -0
- sagemaker/core/image_uri_config/pca.json +50 -0
- sagemaker/core/image_uri_config/pytorch-neuron.json +43 -0
- sagemaker/core/image_uri_config/pytorch-smp.json +218 -0
- sagemaker/core/image_uri_config/pytorch-training-compiler.json +80 -0
- sagemaker/core/image_uri_config/pytorch.json +3101 -0
- sagemaker/core/image_uri_config/randomcutforest.json +50 -0
- sagemaker/core/image_uri_config/ray-pytorch.json +46 -0
- sagemaker/core/image_uri_config/ray-tensorflow.json +194 -0
- sagemaker/core/image_uri_config/sagemaker-base-python.json +46 -0
- sagemaker/core/image_uri_config/sagemaker-distribution.json +37 -0
- sagemaker/core/image_uri_config/sagemaker-geospatial.json +13 -0
- sagemaker/core/image_uri_config/sagemaker-tritonserver.json +252 -0
- sagemaker/core/image_uri_config/semantic-segmentation.json +50 -0
- sagemaker/core/image_uri_config/seq2seq.json +50 -0
- sagemaker/core/image_uri_config/sklearn.json +494 -0
- sagemaker/core/image_uri_config/spark.json +280 -0
- sagemaker/core/image_uri_config/sparkml-serving.json +97 -0
- sagemaker/core/image_uri_config/stabilityai.json +53 -0
- sagemaker/core/image_uri_config/tensorflow.json +5086 -0
- sagemaker/core/image_uri_config/vw.json +25 -0
- sagemaker/core/image_uri_config/xgboost-neo.json +43 -0
- sagemaker/core/image_uri_config/xgboost.json +972 -0
- sagemaker/core/image_uris.py +816 -0
- sagemaker/core/inference_config.py +144 -0
- sagemaker/core/inference_recommender/__init__.py +18 -0
- sagemaker/core/inference_recommender/inference_recommender_mixin.py +622 -0
- sagemaker/core/inputs.py +366 -0
- sagemaker/core/instance_group.py +61 -0
- sagemaker/core/instance_types.py +164 -0
- sagemaker/core/instance_types_gpu_info.py +43 -0
- sagemaker/core/interactive_apps/__init__.py +41 -0
- sagemaker/core/interactive_apps/base_interactive_app.py +204 -0
- sagemaker/core/interactive_apps/detail_profiler_app.py +139 -0
- sagemaker/core/interactive_apps/tensorboard.py +149 -0
- sagemaker/core/iterators.py +197 -0
- sagemaker/core/job.py +380 -0
- sagemaker/core/jumpstart/__init__.py +156 -0
- sagemaker/core/jumpstart/accessors.py +390 -0
- sagemaker/core/jumpstart/artifacts/__init__.py +69 -0
- sagemaker/core/jumpstart/artifacts/environment_variables.py +252 -0
- sagemaker/core/jumpstart/artifacts/hyperparameters.py +120 -0
- sagemaker/core/jumpstart/artifacts/image_uris.py +139 -0
- sagemaker/core/jumpstart/artifacts/incremental_training.py +87 -0
- sagemaker/core/jumpstart/artifacts/instance_types.py +223 -0
- sagemaker/core/jumpstart/artifacts/kwargs.py +289 -0
- sagemaker/core/jumpstart/artifacts/metric_definitions.py +117 -0
- sagemaker/core/jumpstart/artifacts/model_packages.py +202 -0
- sagemaker/core/jumpstart/artifacts/model_uris.py +252 -0
- sagemaker/core/jumpstart/artifacts/payloads.py +96 -0
- sagemaker/core/jumpstart/artifacts/predictors.py +540 -0
- sagemaker/core/jumpstart/artifacts/resource_names.py +86 -0
- sagemaker/core/jumpstart/artifacts/resource_requirements.py +162 -0
- sagemaker/core/jumpstart/artifacts/script_uris.py +172 -0
- sagemaker/core/jumpstart/cache.py +663 -0
- sagemaker/core/jumpstart/configs.py +50 -0
- sagemaker/core/jumpstart/constants.py +198 -0
- sagemaker/core/jumpstart/deserializers.py +81 -0
- sagemaker/core/jumpstart/document.py +76 -0
- sagemaker/core/jumpstart/enums.py +168 -0
- sagemaker/core/jumpstart/exceptions.py +236 -0
- sagemaker/core/jumpstart/factory/utils.py +833 -0
- sagemaker/core/jumpstart/filters.py +597 -0
- sagemaker/core/jumpstart/hub/constants.py +16 -0
- sagemaker/core/jumpstart/hub/hub.py +291 -0
- sagemaker/core/jumpstart/hub/interfaces.py +936 -0
- sagemaker/core/jumpstart/hub/parser_utils.py +70 -0
- sagemaker/core/jumpstart/hub/parsers.py +288 -0
- sagemaker/core/jumpstart/hub/types.py +35 -0
- sagemaker/core/jumpstart/hub/utils.py +260 -0
- sagemaker/core/jumpstart/models.py +501 -0
- sagemaker/core/jumpstart/notebook_utils.py +575 -0
- sagemaker/core/jumpstart/parameters.py +20 -0
- sagemaker/core/jumpstart/payload_utils.py +239 -0
- sagemaker/core/jumpstart/region_config.json +171 -0
- sagemaker/core/jumpstart/search.py +171 -0
- sagemaker/core/jumpstart/serializers.py +81 -0
- sagemaker/core/jumpstart/session_utils.py +234 -0
- sagemaker/core/jumpstart/types.py +3044 -0
- sagemaker/core/jumpstart/utils.py +1731 -0
- sagemaker/core/jumpstart/validators.py +257 -0
- sagemaker/core/lambda_helper.py +312 -0
- sagemaker/core/lineage/__init__.py +42 -0
- sagemaker/core/lineage/_api_types.py +239 -0
- sagemaker/core/lineage/_utils.py +49 -0
- sagemaker/core/lineage/action.py +345 -0
- sagemaker/core/lineage/artifact.py +646 -0
- sagemaker/core/lineage/association.py +190 -0
- sagemaker/core/lineage/context.py +505 -0
- sagemaker/core/lineage/lineage_trial_component.py +191 -0
- sagemaker/core/lineage/query.py +732 -0
- sagemaker/core/lineage/visualizer.py +346 -0
- sagemaker/core/local/__init__.py +18 -0
- sagemaker/core/local/data.py +423 -0
- sagemaker/core/local/entities.py +678 -0
- sagemaker/core/local/exceptions.py +17 -0
- sagemaker/core/local/image.py +1243 -0
- sagemaker/core/local/local_session.py +739 -0
- sagemaker/core/local/utils.py +246 -0
- sagemaker/core/logs.py +181 -0
- sagemaker/core/metadata_properties.py +56 -0
- sagemaker/core/metric_definitions.py +91 -0
- sagemaker/core/mlflow/__init__.py +38 -0
- sagemaker/core/mlflow/forward_sagemaker_metrics.py +44 -0
- sagemaker/core/model_card/__init__.py +26 -0
- sagemaker/core/model_life_cycle.py +51 -0
- sagemaker/core/model_metrics.py +160 -0
- sagemaker/core/model_monitor/__init__.py +66 -0
- sagemaker/core/model_monitor/clarify_model_monitoring.py +1497 -0
- sagemaker/core/model_monitor/cron_expression_generator.py +82 -0
- sagemaker/core/model_monitor/data_capture_config.py +115 -0
- sagemaker/core/model_monitor/data_quality_monitoring_config.py +66 -0
- sagemaker/core/model_monitor/dataset_format.py +102 -0
- sagemaker/core/model_monitor/model_monitoring.py +4266 -0
- sagemaker/core/model_monitor/monitoring_alert.py +76 -0
- sagemaker/core/model_monitor/monitoring_files.py +506 -0
- sagemaker/core/model_monitor/utils.py +793 -0
- sagemaker/core/model_registry.py +480 -0
- sagemaker/core/model_uris.py +97 -0
- sagemaker/core/modules/__init__.py +19 -0
- sagemaker/core/modules/configs.py +239 -0
- sagemaker/core/modules/constants.py +37 -0
- sagemaker/core/modules/distributed.py +182 -0
- sagemaker/core/modules/local_core/local_container.py +605 -0
- sagemaker/core/modules/templates.py +83 -0
- sagemaker/core/modules/train/__init__.py +14 -0
- sagemaker/core/modules/train/container_drivers/__init__.py +14 -0
- sagemaker/core/modules/train/container_drivers/common/__init__.py +14 -0
- sagemaker/core/modules/train/container_drivers/common/utils.py +205 -0
- sagemaker/core/modules/train/container_drivers/distributed_drivers/__init__.py +14 -0
- sagemaker/core/modules/train/container_drivers/distributed_drivers/basic_script_driver.py +81 -0
- sagemaker/core/modules/train/container_drivers/distributed_drivers/mpi_driver.py +123 -0
- sagemaker/core/modules/train/container_drivers/distributed_drivers/mpi_utils.py +302 -0
- sagemaker/core/modules/train/container_drivers/distributed_drivers/torchrun_driver.py +129 -0
- sagemaker/core/modules/train/container_drivers/scripts/__init__.py +14 -0
- sagemaker/core/modules/train/container_drivers/scripts/environment.py +305 -0
- sagemaker/core/modules/train/sm_recipes/__init__.py +0 -0
- sagemaker/core/modules/train/sm_recipes/utils.py +330 -0
- sagemaker/core/modules/types.py +19 -0
- sagemaker/core/modules/utils.py +194 -0
- sagemaker/core/network.py +185 -0
- sagemaker/core/parameter.py +173 -0
- sagemaker/core/payloads.py +185 -0
- sagemaker/core/processing.py +1599 -0
- sagemaker/core/remote_function/__init__.py +19 -0
- sagemaker/core/remote_function/checkpoint_location.py +47 -0
- sagemaker/core/remote_function/client.py +1310 -0
- sagemaker/core/remote_function/core/__init__.py +0 -0
- sagemaker/core/remote_function/core/_custom_dispatch_table.py +72 -0
- sagemaker/core/remote_function/core/pipeline_variables.py +347 -0
- sagemaker/core/remote_function/core/serialization.py +410 -0
- sagemaker/core/remote_function/core/stored_function.py +223 -0
- sagemaker/core/remote_function/custom_file_filter.py +128 -0
- sagemaker/core/remote_function/errors.py +102 -0
- sagemaker/core/remote_function/invoke_function.py +167 -0
- sagemaker/core/remote_function/job.py +2121 -0
- sagemaker/core/remote_function/logging_config.py +38 -0
- sagemaker/core/remote_function/runtime_environment/__init__.py +14 -0
- sagemaker/core/remote_function/runtime_environment/bootstrap_runtime_environment.py +605 -0
- sagemaker/core/remote_function/runtime_environment/mpi_utils_remote.py +252 -0
- sagemaker/core/remote_function/runtime_environment/runtime_environment_manager.py +554 -0
- sagemaker/core/remote_function/runtime_environment/spark_app.py +18 -0
- sagemaker/core/remote_function/spark_config.py +149 -0
- sagemaker/core/resource_requirements.py +168 -0
- {sagemaker_core/main → sagemaker/core}/resources.py +19098 -10895
- sagemaker/core/s3/__init__.py +41 -0
- sagemaker/core/s3/client.py +367 -0
- sagemaker/core/s3/utils.py +175 -0
- sagemaker/core/script_uris.py +93 -0
- sagemaker/core/serializers/__init__.py +11 -0
- sagemaker/core/serializers/base.py +510 -0
- sagemaker/core/serializers/implementations.py +159 -0
- sagemaker/core/serializers/utils.py +223 -0
- sagemaker/core/serverless_inference_config.py +63 -0
- sagemaker/core/session_settings.py +55 -0
- sagemaker/core/shapes/__init__.py +3 -0
- sagemaker/core/shapes/model_card_shapes.py +159 -0
- {sagemaker_core/main → sagemaker/core/shapes}/shapes.py +5810 -1806
- sagemaker/core/spark/__init__.py +16 -0
- sagemaker/core/spark/defaults.py +16 -0
- sagemaker/core/spark/processing.py +1380 -0
- sagemaker/core/telemetry/__init__.py +23 -0
- sagemaker/core/telemetry/constants.py +82 -0
- sagemaker/core/telemetry/telemetry_logging.py +285 -0
- sagemaker/core/tools/__init__.py +1 -0
- {sagemaker_core → sagemaker/core}/tools/codegen.py +4 -4
- {sagemaker_core → sagemaker/core}/tools/constants.py +23 -15
- {sagemaker_core → sagemaker/core}/tools/data_extractor.py +1 -1
- {sagemaker_core → sagemaker/core}/tools/method.py +1 -1
- sagemaker/core/tools/model_card/generate_model_card_from_schema.py +562 -0
- {sagemaker_core → sagemaker/core}/tools/resources_codegen.py +165 -98
- {sagemaker_core → sagemaker/core}/tools/resources_extractor.py +5 -13
- {sagemaker_core → sagemaker/core}/tools/shapes_codegen.py +16 -17
- {sagemaker_core → sagemaker/core}/tools/shapes_extractor.py +29 -67
- {sagemaker_core → sagemaker/core}/tools/templates.py +39 -17
- sagemaker/core/training/__init__.py +14 -0
- sagemaker/core/training/configs.py +345 -0
- sagemaker/core/training/constants.py +37 -0
- sagemaker/core/training/utils.py +77 -0
- sagemaker/core/training_compiler/__init__.py +16 -0
- sagemaker/core/training_compiler/config.py +197 -0
- sagemaker/core/training_compiler_config.py +197 -0
- sagemaker/core/transformer.py +793 -0
- sagemaker/core/user_agent.py +76 -0
- sagemaker/core/utilities/__init__.py +24 -0
- sagemaker/core/utilities/cache.py +169 -0
- sagemaker/core/utilities/search_expression.py +133 -0
- sagemaker/core/utils/__init__.py +48 -0
- sagemaker/core/utils/code_injection/__init__.py +0 -0
- {sagemaker_core/main → sagemaker/core/utils}/code_injection/codec.py +2 -2
- {sagemaker_core/main → sagemaker/core/utils}/code_injection/shape_dag.py +5979 -176
- {sagemaker_core/main → sagemaker/core/utils}/exceptions.py +8 -8
- sagemaker_core/main/default_configs_helper.py → sagemaker/core/utils/intelligent_defaults_helper.py +5 -6
- {sagemaker_core/main → sagemaker/core/utils}/logs.py +1 -2
- {sagemaker_core/main → sagemaker/core/utils}/utils.py +27 -22
- sagemaker/core/workflow/__init__.py +152 -0
- sagemaker/core/workflow/conditions.py +313 -0
- sagemaker/core/workflow/entities.py +58 -0
- sagemaker/core/workflow/execution_variables.py +89 -0
- sagemaker/core/workflow/functions.py +193 -0
- sagemaker/core/workflow/parameters.py +222 -0
- sagemaker/core/workflow/pipeline_context.py +394 -0
- sagemaker/core/workflow/pipeline_definition_config.py +31 -0
- sagemaker/core/workflow/properties.py +285 -0
- sagemaker/core/workflow/step_outputs.py +65 -0
- sagemaker/core/workflow/utilities.py +514 -0
- sagemaker/lineage/__init__.py +33 -0
- sagemaker/lineage/action.py +28 -0
- sagemaker/lineage/artifact.py +28 -0
- sagemaker/lineage/context.py +28 -0
- sagemaker/lineage/lineage_trial_component.py +28 -0
- {sagemaker_core-1.0.62.dist-info → sagemaker_core-2.3.1.dist-info}/METADATA +28 -9
- sagemaker_core-2.3.1.dist-info/RECORD +351 -0
- sagemaker_core-2.3.1.dist-info/top_level.txt +1 -0
- sagemaker_core/_version.py +0 -3
- sagemaker_core/helper/session_helper.py +0 -769
- sagemaker_core/resources/__init__.py +0 -1
- sagemaker_core/shapes/__init__.py +0 -1
- sagemaker_core/tools/__init__.py +0 -1
- sagemaker_core-1.0.62.dist-info/RECORD +0 -35
- sagemaker_core-1.0.62.dist-info/top_level.txt +0 -1
- {sagemaker_core → sagemaker/core/helper}/__init__.py +0 -0
- {sagemaker_core/helper → sagemaker/core/jumpstart/factory}/__init__.py +0 -0
- {sagemaker_core/main → sagemaker/core/jumpstart/hub}/__init__.py +0 -0
- {sagemaker_core/main/code_injection → sagemaker/core/modules/local_core}/__init__.py +0 -0
- {sagemaker_core/main → sagemaker/core/utils}/code_injection/base.py +0 -0
- {sagemaker_core/main → sagemaker/core/utils}/code_injection/constants.py +0 -0
- {sagemaker_core/main → sagemaker/core/utils}/user_agent.py +0 -0
- {sagemaker_core-1.0.62.dist-info → sagemaker_core-2.3.1.dist-info}/WHEEL +0 -0
- {sagemaker_core-1.0.62.dist-info → sagemaker_core-2.3.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
|
5
|
+
# the License is located at
|
|
6
|
+
#
|
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
|
8
|
+
#
|
|
9
|
+
# or in the "license" file accompanying this file. This file is
|
|
10
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
11
|
+
# ANY KIND, either express or implied. See the License for the specific
|
|
12
|
+
# language governing permissions and limitations under the License.
|
|
13
|
+
"""A base class for starting/accessing apps hosted on Amazon SageMaker Studio"""
|
|
14
|
+
|
|
15
|
+
from __future__ import absolute_import
|
|
16
|
+
|
|
17
|
+
import abc
|
|
18
|
+
import base64
|
|
19
|
+
import json
|
|
20
|
+
import logging
|
|
21
|
+
import os
|
|
22
|
+
import re
|
|
23
|
+
import webbrowser
|
|
24
|
+
|
|
25
|
+
from typing import Optional
|
|
26
|
+
import boto3
|
|
27
|
+
from sagemaker.core.helper.session_helper import Session, NOTEBOOK_METADATA_FILE
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BaseInteractiveApp(abc.ABC):
|
|
33
|
+
"""BaseInteractiveApp is a base class for creating/accessing apps hosted on SageMaker."""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
region: Optional[str] = None,
|
|
38
|
+
):
|
|
39
|
+
"""Initialize a BaseInteractiveApp object.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
region (str): Optional. The AWS Region, e.g. us-east-1. If not specified,
|
|
43
|
+
one is created using the default AWS configuration chain.
|
|
44
|
+
Default: ``None``
|
|
45
|
+
"""
|
|
46
|
+
if isinstance(region, str):
|
|
47
|
+
self.region = region
|
|
48
|
+
else:
|
|
49
|
+
try:
|
|
50
|
+
self.region = Session().boto_region_name
|
|
51
|
+
except ValueError:
|
|
52
|
+
raise ValueError(
|
|
53
|
+
"Failed to get the Region information from the default config. Please either "
|
|
54
|
+
"pass your Region manually as an input argument or set up the local AWS"
|
|
55
|
+
" configuration."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
self._sagemaker_client = boto3.client("sagemaker", region_name=self.region)
|
|
59
|
+
# Used to store domain and user profile info retrieved from Studio environment.
|
|
60
|
+
self._domain_id = None
|
|
61
|
+
self._user_profile_name = None
|
|
62
|
+
self._in_studio_env = False
|
|
63
|
+
self._get_domain_and_user()
|
|
64
|
+
|
|
65
|
+
def __str__(self):
|
|
66
|
+
"""Return str(self)."""
|
|
67
|
+
return f"{type(self).__name__}(region={self.region})"
|
|
68
|
+
|
|
69
|
+
def __repr__(self):
|
|
70
|
+
"""Return repr(self)."""
|
|
71
|
+
return self.__str__()
|
|
72
|
+
|
|
73
|
+
def _get_domain_and_user(self):
|
|
74
|
+
"""Get domain id and user profile from Studio environment.
|
|
75
|
+
|
|
76
|
+
To verify Studio environment, we check if NOTEBOOK_METADATA_FILE exists
|
|
77
|
+
and domain id and user profile name are present in the file.
|
|
78
|
+
"""
|
|
79
|
+
if not os.path.isfile(NOTEBOOK_METADATA_FILE):
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
with open(NOTEBOOK_METADATA_FILE, "rb") as metadata_file:
|
|
84
|
+
metadata = json.loads(metadata_file.read())
|
|
85
|
+
except OSError as err:
|
|
86
|
+
logger.warning("Could not load metadata due to unexpected error. %s", err)
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
if "DomainId" in metadata and "UserProfileName" in metadata:
|
|
90
|
+
self._in_studio_env = True
|
|
91
|
+
self._domain_id = metadata.get("DomainId")
|
|
92
|
+
self._user_profile_name = metadata.get("UserProfileName")
|
|
93
|
+
|
|
94
|
+
def _get_presigned_url(
|
|
95
|
+
self,
|
|
96
|
+
create_presigned_url_kwargs: dict,
|
|
97
|
+
redirect: Optional[str] = None,
|
|
98
|
+
state: Optional[str] = None,
|
|
99
|
+
):
|
|
100
|
+
"""Generate a presigned URL to access a user's domain / user profile.
|
|
101
|
+
|
|
102
|
+
Optional state and redirect parameters can be used to to have presigned URL automatically
|
|
103
|
+
redirect to a specific app and provide modifying data.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
create_presigned_url_kwargs (dict): Required. This dictionary should include the
|
|
107
|
+
parameters that will be used when calling create_presigned_domain_url via the boto3
|
|
108
|
+
client. At a minimum, this should include the "DomainId" and "UserProfileName"
|
|
109
|
+
parameters as defined by create_presigned_domain_url's documentation.
|
|
110
|
+
Default: ``None``
|
|
111
|
+
redirect (str): Optional. This value will be appended to the resulting presigned URL
|
|
112
|
+
in the format "&redirect=<redirect parameter>". This is used to automatically
|
|
113
|
+
redirect the user into a specific Studio app.
|
|
114
|
+
Default: ``None``
|
|
115
|
+
state (str): Optional. This value will be appended to the resulting presigned URL
|
|
116
|
+
in the format "&state=<state parameter base64 encoded>". This is used to
|
|
117
|
+
automatically apply a state to the given app. Should be used in conjuction with
|
|
118
|
+
the redirect parameter.
|
|
119
|
+
Default: ``None``
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
str: A presigned URL.
|
|
123
|
+
"""
|
|
124
|
+
response = self._sagemaker_client.create_presigned_domain_url(**create_presigned_url_kwargs)
|
|
125
|
+
if response["ResponseMetadata"]["HTTPStatusCode"] == 200:
|
|
126
|
+
url = response["AuthorizedUrl"]
|
|
127
|
+
else:
|
|
128
|
+
raise ValueError(
|
|
129
|
+
"An invalid status code was returned when creating a presigned URL."
|
|
130
|
+
f" See response for more: {response}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if redirect:
|
|
134
|
+
url += f"&redirect={redirect}"
|
|
135
|
+
|
|
136
|
+
if state:
|
|
137
|
+
url += f"&state={base64.b64encode(bytes(state, 'utf-8')).decode('utf-8')}"
|
|
138
|
+
|
|
139
|
+
logger.warning(
|
|
140
|
+
"A presigned domain URL was generated. This is sensitive and should not be shared with"
|
|
141
|
+
" others."
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
return url
|
|
145
|
+
|
|
146
|
+
def _open_url_in_web_browser(self, url: str):
|
|
147
|
+
"""Open a URL in the default web browser.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
url (str): The URL to open.
|
|
151
|
+
"""
|
|
152
|
+
webbrowser.open(url)
|
|
153
|
+
|
|
154
|
+
def _validate_job_name(self, job_name: str):
|
|
155
|
+
"""Validate training job name format.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
job_name (str): The job name to validate.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
bool: Whether the supplied job name is valid.
|
|
162
|
+
"""
|
|
163
|
+
job_name_regex = "^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62}"
|
|
164
|
+
if not re.fullmatch(job_name_regex, job_name):
|
|
165
|
+
raise ValueError(
|
|
166
|
+
f"Invalid job name. Job name must match regular expression {job_name_regex}"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def _validate_domain_id(self, domain_id: str):
|
|
170
|
+
"""Validate domain id format.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
domain_id (str): Required. The domain ID to validate.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
bool: Whether the supplied domain ID is valid.
|
|
177
|
+
"""
|
|
178
|
+
if domain_id is None or len(domain_id) > 63:
|
|
179
|
+
return False
|
|
180
|
+
return True
|
|
181
|
+
|
|
182
|
+
def _validate_user_profile_name(self, user_profile_name: str):
|
|
183
|
+
"""Validate user profile name format.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
user_profile_name (str): Required. The user profile name to validate.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
bool: Whether the supplied user profile name is valid.
|
|
190
|
+
"""
|
|
191
|
+
user_profile_name_regex = "^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62}"
|
|
192
|
+
if user_profile_name is None or not re.fullmatch(
|
|
193
|
+
user_profile_name_regex, user_profile_name
|
|
194
|
+
):
|
|
195
|
+
return False
|
|
196
|
+
return True
|
|
197
|
+
|
|
198
|
+
@abc.abstractmethod
|
|
199
|
+
def get_app_url(self):
|
|
200
|
+
"""Abstract method to generate a URL to help access the application in Studio.
|
|
201
|
+
|
|
202
|
+
Classes that inherit from BaseInteractiveApp should implement and override with what
|
|
203
|
+
parameters are needed for its specific use case.
|
|
204
|
+
"""
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
|
5
|
+
# the License is located at
|
|
6
|
+
#
|
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
|
8
|
+
#
|
|
9
|
+
# or in the "license" file accompanying this file. This file is
|
|
10
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
11
|
+
# ANY KIND, either express or implied. See the License for the specific
|
|
12
|
+
# language governing permissions and limitations under the License.
|
|
13
|
+
"""Methods for starting up and accessing DetailProfiler apps
|
|
14
|
+
|
|
15
|
+
This module contains methods for starting up and accessing
|
|
16
|
+
DetailProfiler apps hosted on SageMaker
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import absolute_import
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import logging
|
|
22
|
+
import os
|
|
23
|
+
import re
|
|
24
|
+
|
|
25
|
+
from typing import Optional
|
|
26
|
+
from sagemaker.core.helper.session_helper import Session, NOTEBOOK_METADATA_FILE
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class DetailProfilerApp(object):
|
|
32
|
+
"""Class for creating/accessing a DetailProfiler app hosted on SageMaker."""
|
|
33
|
+
|
|
34
|
+
def __init__(self, region: Optional[str] = None):
|
|
35
|
+
"""Initialize a DetailProfilerApp object.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
region (str): The name of the region e.g. us-east-1. If not specified,
|
|
39
|
+
one is created using the default AWS configuration chain.
|
|
40
|
+
"""
|
|
41
|
+
if region:
|
|
42
|
+
self.region = region
|
|
43
|
+
else:
|
|
44
|
+
try:
|
|
45
|
+
self.region = Session().boto_region_name
|
|
46
|
+
except ValueError:
|
|
47
|
+
raise ValueError(
|
|
48
|
+
"Failed to get region from default config. Please eihter pass region "
|
|
49
|
+
"as an input argument or setup the local AWS config."
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
self._domain_id = None
|
|
53
|
+
self._user_profile_name = None
|
|
54
|
+
self._valid_domain_and_user = False
|
|
55
|
+
self._get_domain_and_user()
|
|
56
|
+
|
|
57
|
+
def __str__(self):
|
|
58
|
+
"""Return str(self)."""
|
|
59
|
+
return f"DetailProfilerApp(region={self.region})"
|
|
60
|
+
|
|
61
|
+
def __repr__(self):
|
|
62
|
+
"""Return repr(self)."""
|
|
63
|
+
return self.__str__()
|
|
64
|
+
|
|
65
|
+
def get_app_url(self, training_job_name: Optional[str] = None):
|
|
66
|
+
"""Get an unsigned URL for DetailProfiler app hosted on SageMaker.
|
|
67
|
+
|
|
68
|
+
For users that are already in SM Studio notebook instance, the method tries to
|
|
69
|
+
get domain id and user profile from the Studio environment. If succeeded, the
|
|
70
|
+
generated URL will direct to SM DetailProfiler app. Otherwise it will direct to
|
|
71
|
+
DetailProfiler landing page on SageMaker console. For non Studio users, the
|
|
72
|
+
URL will direct to the DetailProfiler landing page on SageMaker console.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
training_job_name (str): Optional. The name of the training job to pre-load in
|
|
76
|
+
DetailProfiler.
|
|
77
|
+
If not provided, no job will be automatically loaded when the URL is opened.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
str: An unsigned URL for DetailProfiler hosted on SageMaker.
|
|
81
|
+
"""
|
|
82
|
+
if self._valid_domain_and_user:
|
|
83
|
+
url = f"https://{self._domain_id}.studio.{self.region}.sagemaker.aws/profiler/default"
|
|
84
|
+
if training_job_name is not None:
|
|
85
|
+
self._validate_job_name(training_job_name)
|
|
86
|
+
url += f"/#!/welcome?profile={training_job_name}"
|
|
87
|
+
else:
|
|
88
|
+
dp = "profiler-landing"
|
|
89
|
+
url = "https://{region}.console.aws.amazon.com/sagemaker/home?region={region}#/{dp}".format(
|
|
90
|
+
region=self.region, dp=dp
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return url
|
|
94
|
+
|
|
95
|
+
def _get_domain_and_user(self):
|
|
96
|
+
"""Validate studio domain id and user profile
|
|
97
|
+
|
|
98
|
+
Get and validate studio domain id and user profile
|
|
99
|
+
from NOTEBOOK_METADATA_FILE in studio environment.
|
|
100
|
+
|
|
101
|
+
Set _valid_domain_and_user to True if validation succeeded.
|
|
102
|
+
"""
|
|
103
|
+
if not os.path.isfile(NOTEBOOK_METADATA_FILE):
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
with open(NOTEBOOK_METADATA_FILE, "rb") as f:
|
|
107
|
+
metadata = json.loads(f.read())
|
|
108
|
+
self._domain_id = metadata.get("DomainId")
|
|
109
|
+
self._user_profile_name = metadata.get("UserProfileName")
|
|
110
|
+
if self._validate_domain_id() is True and self._validate_user_profile_name() is True:
|
|
111
|
+
self._valid_domain_and_user = True
|
|
112
|
+
else:
|
|
113
|
+
logger.warning(
|
|
114
|
+
"NOTEBOOK_METADATA_FILE detected but failed to get"
|
|
115
|
+
"valid domain and user from it."
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def _validate_job_name(self, job_name: str):
|
|
119
|
+
"""Validate training job name format."""
|
|
120
|
+
job_name_regex = "^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62}"
|
|
121
|
+
if not re.fullmatch(job_name_regex, job_name):
|
|
122
|
+
raise ValueError(
|
|
123
|
+
f"Invalid job name. " f"Job name must match regular expression {job_name_regex}"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def _validate_domain_id(self):
|
|
127
|
+
"""Validate domain id format."""
|
|
128
|
+
if self._domain_id is None or len(self._domain_id) > 63:
|
|
129
|
+
return False
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
def _validate_user_profile_name(self):
|
|
133
|
+
"""Validate user profile name format."""
|
|
134
|
+
user_profile_name_regex = "^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62}"
|
|
135
|
+
if self._user_profile_name is None or not re.fullmatch(
|
|
136
|
+
user_profile_name_regex, self._user_profile_name
|
|
137
|
+
):
|
|
138
|
+
return False
|
|
139
|
+
return True
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
|
5
|
+
# the License is located at
|
|
6
|
+
#
|
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
|
8
|
+
#
|
|
9
|
+
# or in the "license" file accompanying this file. This file is
|
|
10
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
11
|
+
# ANY KIND, either express or implied. See the License for the specific
|
|
12
|
+
# language governing permissions and limitations under the License.
|
|
13
|
+
"""Methods for TensorBoard apps hosted on SageMaker.
|
|
14
|
+
|
|
15
|
+
This module contains methods for starting up and accessing
|
|
16
|
+
TensorBoard apps hosted on SageMaker
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import absolute_import
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
|
|
22
|
+
from typing import Optional
|
|
23
|
+
|
|
24
|
+
from sagemaker.core.interactive_apps.base_interactive_app import BaseInteractiveApp
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TensorBoardApp(BaseInteractiveApp):
|
|
30
|
+
"""TensorBoardApp is a class for creating/accessing a TensorBoard app hosted on Studio."""
|
|
31
|
+
|
|
32
|
+
def get_app_url(
|
|
33
|
+
self,
|
|
34
|
+
training_job_name: Optional[str] = None,
|
|
35
|
+
open_in_default_web_browser: Optional[bool] = True,
|
|
36
|
+
create_presigned_domain_url: Optional[bool] = False,
|
|
37
|
+
domain_id: Optional[str] = None,
|
|
38
|
+
user_profile_name: Optional[str] = None,
|
|
39
|
+
optional_create_presigned_url_kwargs: Optional[dict] = None,
|
|
40
|
+
):
|
|
41
|
+
"""Generate a URL to help access the TensorBoard application hosted in Studio.
|
|
42
|
+
|
|
43
|
+
For users that are already in SageMaker Studio, this method tries to get the
|
|
44
|
+
domain id and the user profile from the Studio environment. If successful, the generated
|
|
45
|
+
URL will direct to the TensorBoard application in SageMaker. Otherwise, it will direct
|
|
46
|
+
to the TensorBoard landing page in the SageMaker console. If a user outside of SageMaker
|
|
47
|
+
Studio passes in a valid domain ID and user profile name, the generated URL will be
|
|
48
|
+
presigned - authenticating the user and redirecting to the TensorBoard app once used.
|
|
49
|
+
Otherwise, the URL will direct to the TensorBoard landing page in the SageMaker console.
|
|
50
|
+
By default, the generated URL will attempt to open in the environment's default web
|
|
51
|
+
browser.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
training_job_name (str): Optional. The name of the training job to pre-load in
|
|
55
|
+
TensorBoard. If nothing provided, the method just returns the TensorBoard
|
|
56
|
+
application URL. You can add training jobs later by using the SageMaker Data
|
|
57
|
+
Manager UI.
|
|
58
|
+
Default: ``None``
|
|
59
|
+
open_in_default_web_browser (bool): Optional. When True, the URL will attempt to be
|
|
60
|
+
opened in the environment's default web browser. Otherwise, the resulting URL will
|
|
61
|
+
be returned by this function.
|
|
62
|
+
Default: ``True``
|
|
63
|
+
create_presigned_domain_url (bool): Optional. Determines whether a presigned domain URL
|
|
64
|
+
should be generated instead of an unsigned URL. This only applies when called from
|
|
65
|
+
outside of a SageMaker Studio environment. If this is set to True inside of a
|
|
66
|
+
SageMaker Studio environment, it will be ignored.
|
|
67
|
+
Default: ``False``
|
|
68
|
+
domain_id (str): Optional. This parameter should be passed when a user outside of
|
|
69
|
+
Studio wants a presigned URL to the TensorBoard application. This value will map to
|
|
70
|
+
'DomainId' in the resulting create_presigned_domain_url call. Must be passed with
|
|
71
|
+
user_profile_name and create_presigned_domain_url set to True.
|
|
72
|
+
Default: ``None``
|
|
73
|
+
user_profile_name (str): Optional. This parameter should be passed when a user outside
|
|
74
|
+
of Studio wants a presigned URL to the TensorBoard application. This value will
|
|
75
|
+
map to 'UserProfileName' in the resulting create_presigned_domain_url call. Must be
|
|
76
|
+
passed with domain_id and create_presigned_domain_url set to True.
|
|
77
|
+
Default: ``None``
|
|
78
|
+
optional_create_presigned_url_kwargs (dict): Optional. This parameter
|
|
79
|
+
should be passed when a user outside of Studio wants a presigned URL to the
|
|
80
|
+
TensorBoard application and wants to modify the optional parameters of the
|
|
81
|
+
create_presigned_domain_url call.
|
|
82
|
+
Default: ``None``
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
str: A URL for TensorBoard hosted on SageMaker.
|
|
86
|
+
"""
|
|
87
|
+
if training_job_name is not None:
|
|
88
|
+
self._validate_job_name(training_job_name)
|
|
89
|
+
|
|
90
|
+
if (
|
|
91
|
+
self._in_studio_env
|
|
92
|
+
and self._validate_domain_id(self._domain_id)
|
|
93
|
+
and self._validate_user_profile_name(self._user_profile_name)
|
|
94
|
+
):
|
|
95
|
+
if domain_id or user_profile_name:
|
|
96
|
+
logger.warning(
|
|
97
|
+
"Ignoring passed in domain_id and user_profile_name for Studio set values."
|
|
98
|
+
)
|
|
99
|
+
url = (
|
|
100
|
+
f"https://{self._domain_id}.studio.{self.region}."
|
|
101
|
+
+ "sagemaker.aws/tensorboard/default"
|
|
102
|
+
)
|
|
103
|
+
if training_job_name is not None:
|
|
104
|
+
url += (
|
|
105
|
+
"/data/plugin/sagemaker_data_manager/"
|
|
106
|
+
+ f"add_folder_or_job?Redirect=True&Name={training_job_name}"
|
|
107
|
+
)
|
|
108
|
+
else:
|
|
109
|
+
url += "/#sagemaker_data_manager"
|
|
110
|
+
|
|
111
|
+
elif (
|
|
112
|
+
not self._in_studio_env
|
|
113
|
+
and create_presigned_domain_url
|
|
114
|
+
and self._validate_domain_id(domain_id)
|
|
115
|
+
and self._validate_user_profile_name(user_profile_name)
|
|
116
|
+
):
|
|
117
|
+
if optional_create_presigned_url_kwargs is None:
|
|
118
|
+
optional_create_presigned_url_kwargs = {}
|
|
119
|
+
optional_create_presigned_url_kwargs["DomainId"] = domain_id
|
|
120
|
+
optional_create_presigned_url_kwargs["UserProfileName"] = user_profile_name
|
|
121
|
+
|
|
122
|
+
redirect = "TensorBoard"
|
|
123
|
+
state_to_encode = None
|
|
124
|
+
if training_job_name is not None:
|
|
125
|
+
state_to_encode = (
|
|
126
|
+
"/tensorboard/default/data/plugin/sagemaker_data_manager/"
|
|
127
|
+
+ f"add_folder_or_job?Redirect=True&Name={training_job_name}"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
url = self._get_presigned_url(
|
|
131
|
+
optional_create_presigned_url_kwargs, redirect, state_to_encode
|
|
132
|
+
)
|
|
133
|
+
else:
|
|
134
|
+
if not self._in_studio_env and create_presigned_domain_url:
|
|
135
|
+
logger.warning(
|
|
136
|
+
"A valid domain ID and user profile name were not provided. "
|
|
137
|
+
"Providing default landing page URL as a result."
|
|
138
|
+
)
|
|
139
|
+
url = (
|
|
140
|
+
f"https://{self.region}.console.aws.amazon.com/sagemaker/home"
|
|
141
|
+
+ f"?region={self.region}#/tensor-board-landing"
|
|
142
|
+
)
|
|
143
|
+
if training_job_name is not None:
|
|
144
|
+
url += f"/{training_job_name}"
|
|
145
|
+
|
|
146
|
+
if open_in_default_web_browser:
|
|
147
|
+
self._open_url_in_web_browser(url)
|
|
148
|
+
url = ""
|
|
149
|
+
return url
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
|
5
|
+
# the License is located at
|
|
6
|
+
#
|
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
|
8
|
+
#
|
|
9
|
+
# or in the "license" file accompanying this file. This file is
|
|
10
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
11
|
+
# ANY KIND, either express or implied. See the License for the specific
|
|
12
|
+
# language governing permissions and limitations under the License.
|
|
13
|
+
"""Implements iterators for deserializing data returned from an inference streaming endpoint."""
|
|
14
|
+
from __future__ import absolute_import
|
|
15
|
+
|
|
16
|
+
from abc import ABC, abstractmethod
|
|
17
|
+
import io
|
|
18
|
+
|
|
19
|
+
from sagemaker.core.exceptions import ModelStreamError, InternalStreamFailure
|
|
20
|
+
from sagemaker.core.common_utils import _MAX_BUFFER_SIZE
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def handle_stream_errors(chunk):
|
|
24
|
+
"""Handle API Response errors within `invoke_endpoint_with_response_stream` API if any.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
chunk (dict): A chunk of response received as part of `botocore.eventstream.EventStream`
|
|
28
|
+
response object.
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
ModelStreamError: If `ModelStreamError` error is detected in a chunk of
|
|
32
|
+
`botocore.eventstream.EventStream` response object.
|
|
33
|
+
InternalStreamFailure: If `InternalStreamFailure` error is detected in a chunk of
|
|
34
|
+
`botocore.eventstream.EventStream` response object.
|
|
35
|
+
"""
|
|
36
|
+
if "ModelStreamError" in chunk:
|
|
37
|
+
raise ModelStreamError(
|
|
38
|
+
chunk["ModelStreamError"]["Message"], code=chunk["ModelStreamError"]["ErrorCode"]
|
|
39
|
+
)
|
|
40
|
+
if "InternalStreamFailure" in chunk:
|
|
41
|
+
raise InternalStreamFailure(chunk["InternalStreamFailure"]["Message"])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class BaseIterator(ABC):
|
|
45
|
+
"""Abstract base class for Inference Streaming iterators.
|
|
46
|
+
|
|
47
|
+
Provides a skeleton for customization requiring the overriding of iterator methods
|
|
48
|
+
__iter__ and __next__.
|
|
49
|
+
|
|
50
|
+
Tenets of iterator class for Streaming Inference API Response
|
|
51
|
+
(https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/
|
|
52
|
+
sagemaker-runtime/client/invoke_endpoint_with_response_stream.html):
|
|
53
|
+
1. Needs to accept an botocore.eventstream.EventStream response.
|
|
54
|
+
2. Needs to implement logic in __next__ to:
|
|
55
|
+
2.1. Concatenate and provide next chunk of response from botocore.eventstream.EventStream.
|
|
56
|
+
While doing so parse the response_chunk["PayloadPart"]["Bytes"].
|
|
57
|
+
2.2. If PayloadPart not in EventStream response, handle Errors
|
|
58
|
+
[Recommended to use `iterators.handle_stream_errors` method].
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, event_stream):
|
|
62
|
+
"""Initialises a Iterator object to help parse the byte event stream input.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
event_stream: (botocore.eventstream.EventStream): Event Stream object to be iterated.
|
|
66
|
+
"""
|
|
67
|
+
self.event_stream = event_stream
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def __iter__(self):
|
|
71
|
+
"""Abstract method, returns an iterator object itself"""
|
|
72
|
+
return self
|
|
73
|
+
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def __next__(self):
|
|
76
|
+
"""Abstract method, is responsible for returning the next element in the iteration"""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ByteIterator(BaseIterator):
|
|
80
|
+
"""A helper class for parsing the byte Event Stream input to provide Byte iteration."""
|
|
81
|
+
|
|
82
|
+
def __init__(self, event_stream):
|
|
83
|
+
"""Initialises a BytesIterator Iterator object
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
event_stream: (botocore.eventstream.EventStream): Event Stream object to be iterated.
|
|
87
|
+
"""
|
|
88
|
+
super().__init__(event_stream)
|
|
89
|
+
self.byte_iterator = iter(event_stream)
|
|
90
|
+
|
|
91
|
+
def __iter__(self):
|
|
92
|
+
"""Returns an iterator object itself, which allows the object to be iterated.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
iter : object
|
|
96
|
+
An iterator object representing the iterable.
|
|
97
|
+
"""
|
|
98
|
+
return self
|
|
99
|
+
|
|
100
|
+
def __next__(self):
|
|
101
|
+
"""Returns the next chunk of Byte directly."""
|
|
102
|
+
# Even with "while True" loop the function still behaves like a generator
|
|
103
|
+
# and sends the next new byte chunk.
|
|
104
|
+
while True:
|
|
105
|
+
chunk = next(self.byte_iterator)
|
|
106
|
+
if "PayloadPart" not in chunk:
|
|
107
|
+
# handle API response errors and force terminate.
|
|
108
|
+
handle_stream_errors(chunk)
|
|
109
|
+
# print and move on to next response byte
|
|
110
|
+
print("Unknown event type:" + chunk)
|
|
111
|
+
continue
|
|
112
|
+
return chunk["PayloadPart"]["Bytes"]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class LineIterator(BaseIterator):
|
|
116
|
+
"""A helper class for parsing the byte Event Stream input to provide Line iteration."""
|
|
117
|
+
|
|
118
|
+
def __init__(self, event_stream):
|
|
119
|
+
"""Initialises a LineIterator Iterator object
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
event_stream: (botocore.eventstream.EventStream): Event Stream object to be iterated.
|
|
123
|
+
"""
|
|
124
|
+
super().__init__(event_stream)
|
|
125
|
+
self.byte_iterator = iter(self.event_stream)
|
|
126
|
+
self.buffer = io.BytesIO()
|
|
127
|
+
self.read_pos = 0
|
|
128
|
+
|
|
129
|
+
def __iter__(self):
|
|
130
|
+
"""Returns an iterator object itself, which allows the object to be iterated.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
iter : object
|
|
134
|
+
An iterator object representing the iterable.
|
|
135
|
+
"""
|
|
136
|
+
return self
|
|
137
|
+
|
|
138
|
+
def __next__(self):
|
|
139
|
+
r"""Returns the next Line for an Line iterable.
|
|
140
|
+
|
|
141
|
+
The output of the event stream will be in the following format:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
b'{"outputs": [" a"]}\n'
|
|
145
|
+
b'{"outputs": [" challenging"]}\n'
|
|
146
|
+
b'{"outputs": [" problem"]}\n'
|
|
147
|
+
...
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
While usually each PayloadPart event from the event stream will contain a byte array
|
|
151
|
+
with a full json, this is not guaranteed and some of the json objects may be split across
|
|
152
|
+
PayloadPart events. For example:
|
|
153
|
+
```
|
|
154
|
+
{'PayloadPart': {'Bytes': b'{"outputs": '}}
|
|
155
|
+
{'PayloadPart': {'Bytes': b'[" problem"]}\n'}}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
This class accounts for this by concatenating bytes written via the 'write' function
|
|
159
|
+
and then exposing a method which will return lines (ending with a '\n' character) within
|
|
160
|
+
the buffer via the 'scan_lines' function. It maintains the position of the last read
|
|
161
|
+
position to ensure that previous bytes are not exposed again.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
str: Read and return one line from the event stream.
|
|
165
|
+
"""
|
|
166
|
+
# Even with "while True" loop the function still behaves like a generator
|
|
167
|
+
# and sends the next new concatenated line
|
|
168
|
+
while True:
|
|
169
|
+
self.buffer.seek(self.read_pos)
|
|
170
|
+
line = self.buffer.readline()
|
|
171
|
+
if line and line[-1] == ord("\n"):
|
|
172
|
+
self.read_pos += len(line)
|
|
173
|
+
return line[:-1]
|
|
174
|
+
try:
|
|
175
|
+
chunk = next(self.byte_iterator)
|
|
176
|
+
except StopIteration:
|
|
177
|
+
if self.read_pos < self.buffer.getbuffer().nbytes:
|
|
178
|
+
continue
|
|
179
|
+
raise
|
|
180
|
+
if "PayloadPart" not in chunk:
|
|
181
|
+
# handle API response errors and force terminate.
|
|
182
|
+
handle_stream_errors(chunk)
|
|
183
|
+
# print and move on to next response byte
|
|
184
|
+
print("Unknown event type:" + chunk)
|
|
185
|
+
continue
|
|
186
|
+
|
|
187
|
+
# Check buffer size before writing to prevent unbounded memory consumption
|
|
188
|
+
chunk_size = len(chunk["PayloadPart"]["Bytes"])
|
|
189
|
+
current_size = self.buffer.getbuffer().nbytes
|
|
190
|
+
if current_size + chunk_size > _MAX_BUFFER_SIZE:
|
|
191
|
+
raise RuntimeError(
|
|
192
|
+
f"Line buffer exceeded maximum size of {_MAX_BUFFER_SIZE} bytes. "
|
|
193
|
+
f"No newline found in stream."
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
self.buffer.seek(0, io.SEEK_END)
|
|
197
|
+
self.buffer.write(chunk["PayloadPart"]["Bytes"])
|