From a11358f81860bc1be68a98b2afad224b70f1d4be Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Sun, 20 Apr 2025 00:40:57 +0530 Subject: [PATCH 01/14] make batch deployment subclasses inherited --- .../entities/_deployment/batch_deployment.py | 160 ++++++++++++------ .../_deployment/model_batch_deployment.py | 127 +++++--------- .../model_batch_deployment_settings.py | 6 +- .../pipeline_component_batch_deployment.py | 29 ++-- 4 files changed, 164 insertions(+), 158 deletions(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py index 59b23eb80e3e..3e961d452565 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py @@ -5,6 +5,7 @@ # pylint: disable=protected-access import logging +import warnings from os import PathLike from pathlib import Path from typing import Any, Dict, Optional, Union @@ -27,13 +28,29 @@ from .code_configuration import CodeConfiguration from .deployment import Deployment +from .model_batch_deployment_settings import ModelBatchDeploymentSettings as BatchDeploymentSettings module_logger = logging.getLogger(__name__) +SETTINGS_ATTRIBUTES = [ + "output_action", + "output_file_name", + "error_threshold", + "retry_settings", + "logging_level", + "mini_batch_size", + "max_concurrency_per_instance", + "environment_variables", +] + class BatchDeployment(Deployment): """Batch endpoint deployment entity. + **Warning** This class should not be used directly. + Please use one of the child implementations, :class:`~azure.ai.ml.entities.ModelBatchDeployment` or + :class:`azure.ai.ml.entities.PipelineComponentBatchDeployment`. + :param name: the name of the batch deployment :type name: str :param description: Description of the resource. @@ -112,10 +129,19 @@ def __init__( instance_count: Optional[int] = None, # promoted property from resources.instance_count **kwargs: Any, ) -> None: + _type = kwargs.pop("_type", None) + if _type is None: + warnings.warn( + "This class is intended as a base class and it's direct usage is deprecated. " + "Use one of the concrete implementations instead:\n" + "* ModelBatchDeployment - For model-based batch deployments\n" + "* PipelineComponentBatchDeployment - For pipeline component-based batch deployments" + ) self._provisioning_state: Optional[str] = kwargs.pop("provisioning_state", None) super(BatchDeployment, self).__init__( name=name, + type=_type, endpoint_name=endpoint_name, properties=properties, tags=tags, @@ -123,7 +149,6 @@ def __init__( model=model, code_configuration=code_configuration, environment=environment, - environment_variables=environment_variables, code_path=code_path, scoring_script=scoring_script, **kwargs, @@ -131,16 +156,41 @@ def __init__( self.compute = compute self.resources = resources - self.output_action = output_action - self.output_file_name = output_file_name - self.error_threshold = error_threshold - self.retry_settings = retry_settings - self.logging_level = logging_level - self.mini_batch_size = mini_batch_size - self.max_concurrency_per_instance = max_concurrency_per_instance - - if self.resources and instance_count: - msg = "Can't set instance_count when resources is provided." + + settings = kwargs.pop("settings", None) + self._settings = ( + settings + if settings + else BatchDeploymentSettings( + mini_batch_size=mini_batch_size, + instance_count=instance_count, + max_concurrency_per_instance=max_concurrency_per_instance, + output_action=output_action, + output_file_name=output_file_name, + retry_settings=retry_settings, + environment_variables=environment_variables, + error_threshold=error_threshold, + logging_level=logging_level, + ) + ) + + self._setup_instance_count(instance_count) + + def _setup_instance_count(self, instance_count: Optional[int]) -> None: + # Check if instance_count is being set from multiple sources + instance_count_sources = [] + if self.resources and self.resources.instance_count is not None: + instance_count_sources.append("resources.instance_count") + if self._settings.instance_count is not None: + instance_count_sources.append("settings.instance_count") + if instance_count is not None: + instance_count_sources.append("instance_count") + + if len(instance_count_sources) > 1: + msg = ( + f"Instance count can only be set from one source. " + f"Found multiple sources: {', '.join(instance_count_sources)}" + ) raise ValidationException( message=msg, target=ErrorTarget.BATCH_DEPLOYMENT, @@ -149,8 +199,34 @@ def __init__( error_type=ValidationErrorType.INVALID_VALUE, ) - if not self.resources and instance_count: - self.resources = ResourceConfiguration(instance_count=instance_count) + if not self.resources: + instance_count_value = instance_count or self._settings.instance_count + if instance_count_value: + # Set resources with instance_count if provided + self.resources = ResourceConfiguration(instance_count=instance_count_value) + # Update resources.instance_count if resources exists but instance_count not set + elif self.resources.instance_count is None and (instance_count or self._settings.instance_count): + self.resources.instance_count = instance_count or self._settings.instance_count + + def __getattr__(self, name: str) -> Optional[Any]: + # Support backwards compatibility with old BatchDeployment properties. + if name in SETTINGS_ATTRIBUTES: + try: + return getattr(self._settings, name) + except AttributeError: + pass + return super().__getattribute__(name) + + def __setattr__(self, name, value): + # Support backwards compatibility with old BatchDeployment properties. + if name in SETTINGS_ATTRIBUTES: + try: + # First confirm that this object has a compatible attr + getattr(self._settings, name) + setattr(self._settings, name, value) + except AttributeError: + pass + super().__setattr__(name, value) @property def instance_count(self) -> Optional[int]: @@ -209,42 +285,28 @@ def _to_rest_object(self, location: str) -> BatchDeploymentData: # type: ignore environment = self.environment batch_deployment: RestBatchDeployment = None - if isinstance(self.output_action, str): - batch_deployment = RestBatchDeployment( - compute=self.compute, - description=self.description, - resources=self.resources._to_rest_object() if self.resources else None, - code_configuration=code_config, - environment_id=environment, - model=model, - output_file_name=self.output_file_name, - output_action=BatchDeployment._yaml_output_action_to_rest_output_action(self.output_action), - error_threshold=self.error_threshold, - retry_settings=self.retry_settings._to_rest_object() if self.retry_settings else None, - logging_level=self.logging_level, - mini_batch_size=self.mini_batch_size, - max_concurrency_per_instance=self.max_concurrency_per_instance, - environment_variables=self.environment_variables, - properties=self.properties, - ) - else: - batch_deployment = RestBatchDeployment( - compute=self.compute, - description=self.description, - resources=self.resources._to_rest_object() if self.resources else None, - code_configuration=code_config, - environment_id=environment, - model=model, - output_file_name=self.output_file_name, - output_action=None, - error_threshold=self.error_threshold, - retry_settings=self.retry_settings._to_rest_object() if self.retry_settings else None, - logging_level=self.logging_level, - mini_batch_size=self.mini_batch_size, - max_concurrency_per_instance=self.max_concurrency_per_instance, - environment_variables=self.environment_variables, - properties=self.properties, - ) + # Create base RestBatchDeployment object with common properties + batch_deployment = RestBatchDeployment( + compute=self.compute, + description=self.description, + resources=self.resources._to_rest_object() if self.resources else None, + code_configuration=code_config, + environment_id=environment, + model=model, + output_file_name=self.output_file_name, + output_action=( + BatchDeployment._yaml_output_action_to_rest_output_action(self.output_action) + if isinstance(self.output_action, str) + else None + ), + error_threshold=self.error_threshold, + retry_settings=self.retry_settings._to_rest_object() if self.retry_settings else None, + logging_level=self.logging_level, + mini_batch_size=self.mini_batch_size, + max_concurrency_per_instance=self.max_concurrency_per_instance, + environment_variables=self.environment_variables, + properties=self.properties, + ) return BatchDeploymentData(location=location, properties=batch_deployment, tags=self.tags) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py index 0ad4fd6fea19..3304ca26377e 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py @@ -4,54 +4,60 @@ from os import PathLike from pathlib import Path -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Literal, Optional, Union from azure.ai.ml._restclient.v2022_05_01.models import BatchDeploymentData from azure.ai.ml._restclient.v2022_05_01.models import BatchDeploymentDetails as RestBatchDeployment -from azure.ai.ml._restclient.v2022_05_01.models import BatchOutputAction from azure.ai.ml._restclient.v2022_05_01.models import CodeConfiguration as RestCodeConfiguration from azure.ai.ml._restclient.v2022_05_01.models import IdAssetReference from azure.ai.ml._schema._deployment.batch.model_batch_deployment import ModelBatchDeploymentSchema -from azure.ai.ml._utils._experimental import experimental from azure.ai.ml.constants._common import BASE_PATH_CONTEXT_KEY, PARAMS_OVERRIDE_KEY -from azure.ai.ml.constants._deployment import BatchDeploymentOutputAction from azure.ai.ml.entities._assets import Environment, Model from azure.ai.ml.entities._deployment.batch_deployment import BatchDeployment -from azure.ai.ml.entities._deployment.deployment import Deployment from azure.ai.ml.entities._job.resource_configuration import ResourceConfiguration from azure.ai.ml.entities._util import load_from_dict -from azure.ai.ml.exceptions import ErrorCategory, ErrorTarget, ValidationErrorType, ValidationException from .code_configuration import CodeConfiguration from .model_batch_deployment_settings import ModelBatchDeploymentSettings +type: Literal["model"] = "model" -@experimental -class ModelBatchDeployment(Deployment): - """Job Definition entity. - :param type: Job definition type. Allowed value is: pipeline - :type type: str - :param name: Job name +class ModelBatchDeployment(BatchDeployment): + """Model Batch Deployment entity. + + :param name: Name of the deployment resource. :type name: str - :param job: Job definition - :type job: Union[Job, str] - :param component: Component definition - :type component: Union[Component, str] - :param settings: Job settings - :type settings: Dict[str, Any] - :param description: Job description. + :param endpoint_name: Name of the endpoint. + :type endpoint_name: str + :param environment: Environment to use for deployment. + :type environment: Union[str, Environment] + :param properties: The asset property dictionary. + :type properties: Dict[str, str] + :param model: Model to deploy. + :type model: Union[str, Model] + :param description: Deployment description. :type description: str - :param tags: Job tags + :param tags: Deployment tags. :type tags: Dict[str, Any] - :param properties: The asset property dictionary. - :type properties: dict[str, str] + :param settings: Deployment settings. + :type settings: ModelBatchDeploymentSettings + :param resources: Resource configuration. + :type resources: ResourceConfiguration + :param compute: Compute target to use. + :type compute: str + :param code_configuration: Code configuration for deployment. + :type code_configuration: CodeConfiguration + :param code_path: Path to the code directory. + :type code_path: Union[str, PathLike] + :param scoring_script: Path to the scoring script. + :type scoring_script: Union[str, PathLike] """ def __init__( self, *, - name: Optional[str], + name: str, endpoint_name: Optional[str] = None, environment: Optional[Union[str, Environment]] = None, properties: Optional[Dict[str, str]] = None, @@ -68,9 +74,9 @@ def __init__( ] = None, # promoted property from code_configuration.scoring_script **kwargs: Any, ): - self._provisioning_state: Optional[str] = kwargs.pop("provisioning_state", None) super().__init__( name=name, + _type=type, endpoint_name=endpoint_name, properties=properties, code_path=code_path, @@ -80,27 +86,19 @@ def __init__( description=description, tags=tags, code_configuration=code_configuration, + compute=compute, + resources=resources, + settings=settings, **kwargs, ) - self.compute = compute - self.resources = resources - if settings is not None: - self.settings = ModelBatchDeploymentSettings( - mini_batch_size=settings.mini_batch_size, - instance_count=settings.instance_count, - max_concurrency_per_instance=settings.max_concurrency_per_instance, - output_action=settings.output_action, - output_file_name=settings.output_file_name, - retry_settings=settings.retry_settings, - environment_variables=settings.environment_variables, - error_threshold=settings.error_threshold, - logging_level=settings.logging_level, - ) - if self.resources is not None: - if self.resources.instance_count is None and settings.instance_count is not None: - self.resources.instance_count = settings.instance_count - if self.resources is None and settings.instance_count is not None: - self.resources = ResourceConfiguration(instance_count=settings.instance_count) + + @property + def settings(self) -> ModelBatchDeploymentSettings: + return self._settings + + @settings.setter + def settings(self, value: ModelBatchDeploymentSettings) -> None: + self._settings = value # pylint: disable=arguments-differ def _to_rest_object(self, location: str) -> BatchDeploymentData: # type: ignore @@ -159,49 +157,6 @@ def _load( res: ModelBatchDeployment = load_from_dict(ModelBatchDeploymentSchema, data, context, **kwargs) return res - @classmethod - def _update_params(cls, params_override: Any) -> None: - for param in params_override: - endpoint_name = param.get("endpoint_name") - if isinstance(endpoint_name, str): - param["endpoint_name"] = endpoint_name.lower() - - @classmethod - def _yaml_output_action_to_rest_output_action(cls, yaml_output_action: str) -> str: - output_switcher = { - BatchDeploymentOutputAction.APPEND_ROW: BatchOutputAction.APPEND_ROW, - BatchDeploymentOutputAction.SUMMARY_ONLY: BatchOutputAction.SUMMARY_ONLY, - } - return output_switcher.get(yaml_output_action, yaml_output_action) - - @property - def provisioning_state(self) -> Optional[str]: - """Batch deployment provisioning state, readonly. - - :return: Batch deployment provisioning state. - :rtype: Optional[str] - """ - return self._provisioning_state - - def _validate(self) -> None: - self._validate_output_action() - - def _validate_output_action(self) -> None: - if ( - self.settings.output_action - and self.settings.output_action == BatchDeploymentOutputAction.SUMMARY_ONLY - and self.settings.output_file_name - ): - msg = "When output_action is set to {}, the output_file_name need not to be specified." - msg = msg.format(BatchDeploymentOutputAction.SUMMARY_ONLY) - raise ValidationException( - message=msg, - target=ErrorTarget.BATCH_DEPLOYMENT, - no_personal_data_message=msg, - error_category=ErrorCategory.USER_ERROR, - error_type=ValidationErrorType.INVALID_VALUE, - ) - def _to_dict(self) -> Dict: res: dict = ModelBatchDeploymentSchema(context={BASE_PATH_CONTEXT_KEY: "./"}).dump(self) return res diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment_settings.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment_settings.py index 36151019486a..0d004a1ed8e6 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment_settings.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment_settings.py @@ -2,16 +2,14 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # --------------------------------------------------------- -from typing import Any, Dict, Optional +from typing import Any, Dict, Optional, Union from azure.ai.ml._schema._deployment.batch.model_batch_deployment_settings import ModelBatchDeploymentSettingsSchema -from azure.ai.ml._utils._experimental import experimental from azure.ai.ml.constants._common import BASE_PATH_CONTEXT_KEY from azure.ai.ml.constants._deployment import BatchDeploymentOutputAction from azure.ai.ml.entities._deployment.deployment_settings import BatchRetrySettings -@experimental class ModelBatchDeploymentSettings: """Model Batch Deployment Settings entity. @@ -56,7 +54,7 @@ def __init__( mini_batch_size: Optional[int], instance_count: Optional[int] = None, max_concurrency_per_instance: Optional[int] = None, - output_action: Optional[BatchDeploymentOutputAction] = None, + output_action: Optional[Union[BatchDeploymentOutputAction, str]] = None, output_file_name: Optional[str] = None, retry_settings: Optional[BatchRetrySettings] = None, environment_variables: Optional[Dict[str, str]] = None, diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/pipeline_component_batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/pipeline_component_batch_deployment.py index 730bc39e37bb..6e61561cd70a 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/pipeline_component_batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/pipeline_component_batch_deployment.py @@ -4,7 +4,7 @@ from os import PathLike from pathlib import Path -from typing import IO, Any, AnyStr, Dict, Optional, Union +from typing import IO, Any, AnyStr, Dict, Literal, Optional, Union from azure.ai.ml._restclient.v2024_01_01_preview.models import BatchDeployment as RestBatchDeployment from azure.ai.ml._restclient.v2024_01_01_preview.models import ( @@ -16,24 +16,22 @@ PipelineComponentBatchDeploymentSchema, ) from azure.ai.ml._utils._arm_id_utils import _parse_endpoint_name_from_deployment_id -from azure.ai.ml._utils._experimental import experimental from azure.ai.ml._utils.utils import dump_yaml_to_file from azure.ai.ml.constants._common import BASE_PATH_CONTEXT_KEY, PARAMS_OVERRIDE_KEY from azure.ai.ml.entities import PipelineComponent from azure.ai.ml.entities._builders import BaseNode from azure.ai.ml.entities._component.component import Component -from azure.ai.ml.entities._resource import Resource +from azure.ai.ml.entities._deployment.batch_deployment import BatchDeployment from azure.ai.ml.entities._util import load_from_dict +type: Literal["pipeline"] = "pipeline" -@experimental -class PipelineComponentBatchDeployment(Resource): + +class PipelineComponentBatchDeployment(BatchDeployment): """Pipeline Component Batch Deployment entity. - :param type: Job definition type. Allowed value: "pipeline" - :type type: Optional[str] :param name: Name of the deployment resource. - :type name: Optional[str] + :type name: str :param description: Description of the deployment resource. :type description: Optional[str] :param component: Component definition. @@ -51,7 +49,7 @@ class PipelineComponentBatchDeployment(Resource): def __init__( self, *, - name: Optional[str], + name: str, endpoint_name: Optional[str] = None, component: Optional[Union[Component, str]] = None, settings: Optional[Dict[str, str]] = None, @@ -60,10 +58,10 @@ def __init__( description: Optional[str] = None, **kwargs: Any, ): - self._type = kwargs.pop("type", None) - super().__init__(name=name, tags=tags, description=description, **kwargs) + super().__init__( + name=name, _type=type, endpoint_name=endpoint_name, tags=tags, description=description, **kwargs + ) self.component = component - self.endpoint_name = endpoint_name self.settings = settings self.job_definition = job_definition @@ -112,13 +110,6 @@ def _load( ) return res - @classmethod - def _update_params(cls, params_override: Any) -> None: - for param in params_override: - endpoint_name = param.get("endpoint_name") - if isinstance(endpoint_name, str): - param["endpoint_name"] = endpoint_name.lower() - @classmethod def _from_rest_object(cls, deployment: RestBatchDeployment) -> "PipelineComponentBatchDeployment": return PipelineComponentBatchDeployment( From bcc1addda232f6064a405ffe9f80fa94b6c8c439 Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Sun, 20 Apr 2025 14:28:33 +0530 Subject: [PATCH 02/14] fix mypy issue --- .../azure-ai-ml/azure/ai/ml/entities/_deployment/deployment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/deployment.py index 2f857cfafe3d..827afa4f8931 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/deployment.py @@ -184,7 +184,7 @@ def _from_rest_object( error_category=ErrorCategory.SYSTEM_ERROR, ) - def _to_rest_object(self) -> Any: + def _to_rest_object(self, location: Optional[str] = None) -> Any: pass def _merge_with(self, other: "Deployment") -> None: From 34baa982d7160391d394e2879bc3d189c0decc59 Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Sun, 20 Apr 2025 15:06:13 +0530 Subject: [PATCH 03/14] avoid checking instance_count population --- .../entities/_deployment/batch_deployment.py | 51 +++++++------------ 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py index 3e961d452565..6100af5cb30e 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py @@ -174,39 +174,26 @@ def __init__( ) ) - self._setup_instance_count(instance_count) - - def _setup_instance_count(self, instance_count: Optional[int]) -> None: - # Check if instance_count is being set from multiple sources - instance_count_sources = [] - if self.resources and self.resources.instance_count is not None: - instance_count_sources.append("resources.instance_count") - if self._settings.instance_count is not None: - instance_count_sources.append("settings.instance_count") - if instance_count is not None: - instance_count_sources.append("instance_count") - - if len(instance_count_sources) > 1: - msg = ( - f"Instance count can only be set from one source. " - f"Found multiple sources: {', '.join(instance_count_sources)}" - ) - raise ValidationException( - message=msg, - target=ErrorTarget.BATCH_DEPLOYMENT, - no_personal_data_message=msg, - error_category=ErrorCategory.USER_ERROR, - error_type=ValidationErrorType.INVALID_VALUE, - ) + self._setup_instance_count() - if not self.resources: - instance_count_value = instance_count or self._settings.instance_count - if instance_count_value: - # Set resources with instance_count if provided - self.resources = ResourceConfiguration(instance_count=instance_count_value) - # Update resources.instance_count if resources exists but instance_count not set - elif self.resources.instance_count is None and (instance_count or self._settings.instance_count): - self.resources.instance_count = instance_count or self._settings.instance_count + def _setup_instance_count( + self, + ) -> None: # No need to check instance_count here as it's already set in self._settings during initialization + if self.resources is not None: + if self.resources.instance_count is None and self._settings.instance_count is not None: + self.resources.instance_count = self._settings.instance_count + elif self.resources.instance_count is not None and self._settings.instance_count is not None: + msg = "Can't set instance_count when resources.instance_count is provided." + raise ValidationException( + message=msg, + target=ErrorTarget.BATCH_DEPLOYMENT, + no_personal_data_message=msg, + error_category=ErrorCategory.USER_ERROR, + error_type=ValidationErrorType.INVALID_VALUE, + ) + + if not self.resources and self._settings.instance_count: + self.resources = ResourceConfiguration(instance_count=self._settings.instance_count) def __getattr__(self, name: str) -> Optional[Any]: # Support backwards compatibility with old BatchDeployment properties. From 15df88ce9a972e6afae202063efaca02751e7682 Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Sun, 20 Apr 2025 15:56:19 +0530 Subject: [PATCH 04/14] bypass mypy signature mismatch error --- .../azure/ai/ml/entities/_deployment/batch_deployment.py | 2 +- .../azure-ai-ml/azure/ai/ml/entities/_deployment/deployment.py | 2 +- .../azure/ai/ml/entities/_deployment/model_batch_deployment.py | 2 +- .../entities/_deployment/pipeline_component_batch_deployment.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py index 6100af5cb30e..43fc5842c4b8 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py @@ -258,7 +258,7 @@ def _yaml_output_action_to_rest_output_action(cls, yaml_output_action: Any) -> s return output_switcher.get(yaml_output_action, yaml_output_action) # pylint: disable=arguments-differ - def _to_rest_object(self, location: str) -> BatchDeploymentData: # type: ignore + def _to_rest_object(self, location: str) -> BatchDeploymentData: # type: ignore[override] self._validate() code_config = ( RestCodeConfiguration( diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/deployment.py index 827afa4f8931..2f857cfafe3d 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/deployment.py @@ -184,7 +184,7 @@ def _from_rest_object( error_category=ErrorCategory.SYSTEM_ERROR, ) - def _to_rest_object(self, location: Optional[str] = None) -> Any: + def _to_rest_object(self) -> Any: pass def _merge_with(self, other: "Deployment") -> None: diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py index 3304ca26377e..1f99d005cfaf 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py @@ -101,7 +101,7 @@ def settings(self, value: ModelBatchDeploymentSettings) -> None: self._settings = value # pylint: disable=arguments-differ - def _to_rest_object(self, location: str) -> BatchDeploymentData: # type: ignore + def _to_rest_object(self, location: str) -> BatchDeploymentData: # type: ignore[override] self._validate() code_config = ( RestCodeConfiguration( diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/pipeline_component_batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/pipeline_component_batch_deployment.py index 6e61561cd70a..ed28538f85b5 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/pipeline_component_batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/pipeline_component_batch_deployment.py @@ -65,7 +65,7 @@ def __init__( self.settings = settings self.job_definition = job_definition - def _to_rest_object(self, location: str) -> "RestBatchDeployment": + def _to_rest_object(self, location: str) -> "RestBatchDeployment": # type: ignore[override] if isinstance(self.component, PipelineComponent): id_asset_ref = IdAssetReference(asset_id=self.component.id) From 88dbb0e85e320af5e497fd5ffb2a5b17608b6669 Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Mon, 21 Apr 2025 13:04:51 +0530 Subject: [PATCH 05/14] fix tests --- .../entities/_deployment/batch_deployment.py | 2 +- .../unittests/test_deployment_entity.py | 35 ++++++++----------- .../pipeline_component_batch_deployment.yml | 1 - 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py index 43fc5842c4b8..cb741debbe02 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py @@ -139,6 +139,7 @@ def __init__( ) self._provisioning_state: Optional[str] = kwargs.pop("provisioning_state", None) + settings = kwargs.pop("settings", None) super(BatchDeployment, self).__init__( name=name, type=_type, @@ -157,7 +158,6 @@ def __init__( self.compute = compute self.resources = resources - settings = kwargs.pop("settings", None) self._settings = ( settings if settings diff --git a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py index 089155fa4cd5..8ccea0da0ad2 100644 --- a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py +++ b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py @@ -1,44 +1,41 @@ import copy +import json import pytest import yaml -import json from test_utilities.utils import verify_entity_load_and_dump from azure.ai.ml import load_batch_deployment, load_online_deployment -from azure.ai.ml._restclient.v2022_10_01.models import ( - BatchOutputAction, - EndpointComputeType, - BatchDeployment as BatchDeploymentData, -) +from azure.ai.ml._restclient.v2022_10_01.models import BatchDeployment as BatchDeploymentData +from azure.ai.ml._restclient.v2022_10_01.models import BatchOutputAction, EndpointComputeType +from azure.ai.ml._restclient.v2023_04_01_preview.models import BatchPipelineComponentDeploymentConfiguration from azure.ai.ml._restclient.v2023_04_01_preview.models import ( - OnlineDeployment as RestOnlineDeploymentData, - ManagedOnlineDeployment as RestManagedOnlineDeployment, KubernetesOnlineDeployment as RestKubernetesOnlineDeployment, - BatchPipelineComponentDeploymentConfiguration, ) +from azure.ai.ml._restclient.v2023_04_01_preview.models import ManagedOnlineDeployment as RestManagedOnlineDeployment +from azure.ai.ml._restclient.v2023_04_01_preview.models import OnlineDeployment as RestOnlineDeploymentData +from azure.ai.ml.constants._common import ArmConstants from azure.ai.ml.constants._deployment import BatchDeploymentOutputAction from azure.ai.ml.entities import ( BatchDeployment, - Deployment, CodeConfiguration, DefaultScaleSettings, + Deployment, KubernetesOnlineDeployment, ManagedOnlineDeployment, OnlineDeployment, OnlineEndpoint, + OnlineRequestSettings, ProbeSettings, ResourceRequirementsSettings, ResourceSettings, TargetUtilizationScaleSettings, - OnlineRequestSettings, ) -from azure.ai.ml.constants._common import ArmConstants -from azure.ai.ml.exceptions import DeploymentException, ValidationException -from azure.ai.ml.entities._assets import Environment, Model, Code +from azure.ai.ml.entities._assets import Code, Environment, Model from azure.ai.ml.entities._deployment.deployment_settings import BatchRetrySettings from azure.ai.ml.entities._deployment.run_settings import RunSettings from azure.ai.ml.entities._job.resource_configuration import ResourceConfiguration +from azure.ai.ml.exceptions import DeploymentException, ValidationException @pytest.mark.production_experiences_test @@ -661,7 +658,7 @@ def test_batch_deployment_raise_exception_when(self) -> None: BatchDeployment( name="non-mlflow-deployment", instance_count=2, resources=ResourceConfiguration(instance_count=2) ) - assert "Can't set instance_count when resources is provided." == str(exc.value) + assert "Can't set instance_count when resources.instance_count is provided." == str(exc.value) @pytest.mark.unittest @@ -817,13 +814,11 @@ def test_online_deployment_from_rest_object_unsupported_scale_settings_type(self assert str(exc.value) == "Unsupported online scale setting type Other." -from azure.ai.ml._restclient.v2022_05_01.models import ( - BatchRetrySettings as RestBatchRetrySettings, - CodeConfiguration as RestCodeConfiguration, -) +from azure.ai.ml._restclient.v2022_05_01.models import BatchRetrySettings as RestBatchRetrySettings +from azure.ai.ml._restclient.v2022_05_01.models import CodeConfiguration as RestCodeConfiguration +from azure.ai.ml.entities._deployment.data_asset import DataAsset from azure.ai.ml.entities._deployment.data_collector import DataCollector from azure.ai.ml.entities._deployment.deployment_collection import DeploymentCollection -from azure.ai.ml.entities._deployment.data_asset import DataAsset from azure.ai.ml.entities._deployment.request_logging import RequestLogging diff --git a/sdk/ml/azure-ai-ml/tests/test_configs/deployments/batch/pipeline_component_batch_deployment.yml b/sdk/ml/azure-ai-ml/tests/test_configs/deployments/batch/pipeline_component_batch_deployment.yml index 4a27a5d45cfb..8da282dca17d 100644 --- a/sdk/ml/azure-ai-ml/tests/test_configs/deployments/batch/pipeline_component_batch_deployment.yml +++ b/sdk/ml/azure-ai-ml/tests/test_configs/deployments/batch/pipeline_component_batch_deployment.yml @@ -2,7 +2,6 @@ $schema: https://azuremlschemas.azureedge.net/latest/pipelineComponentBatchDeplo name: hello-batch-dpl endpoint_name: hello-pipeline-batch description: "Batch deployment description" -type: pipeline component: azureml:hello_batch@latest settings: default_compute: batch-cluster From 4ebae6b9e423613386af3f10e99eb9c62d7fc878 Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Mon, 21 Apr 2025 13:27:08 +0530 Subject: [PATCH 06/14] keep resources and instance_count condition same for backwards compatibility --- .../entities/_deployment/batch_deployment.py | 21 +++++------ .../unittests/test_deployment_entity.py | 37 +++++++++++-------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py index cb741debbe02..a1f7ff8d31d3 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py @@ -179,18 +179,15 @@ def __init__( def _setup_instance_count( self, ) -> None: # No need to check instance_count here as it's already set in self._settings during initialization - if self.resources is not None: - if self.resources.instance_count is None and self._settings.instance_count is not None: - self.resources.instance_count = self._settings.instance_count - elif self.resources.instance_count is not None and self._settings.instance_count is not None: - msg = "Can't set instance_count when resources.instance_count is provided." - raise ValidationException( - message=msg, - target=ErrorTarget.BATCH_DEPLOYMENT, - no_personal_data_message=msg, - error_category=ErrorCategory.USER_ERROR, - error_type=ValidationErrorType.INVALID_VALUE, - ) + if self.resources and self._settings.instance_count: + msg = "Can't set instance_count when resources is provided." + raise ValidationException( + message=msg, + target=ErrorTarget.BATCH_DEPLOYMENT, + no_personal_data_message=msg, + error_category=ErrorCategory.USER_ERROR, + error_type=ValidationErrorType.INVALID_VALUE, + ) if not self.resources and self._settings.instance_count: self.resources = ResourceConfiguration(instance_count=self._settings.instance_count) diff --git a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py index 8ccea0da0ad2..d7fd0bf5b284 100644 --- a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py +++ b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py @@ -1,41 +1,44 @@ import copy -import json import pytest import yaml +import json from test_utilities.utils import verify_entity_load_and_dump from azure.ai.ml import load_batch_deployment, load_online_deployment -from azure.ai.ml._restclient.v2022_10_01.models import BatchDeployment as BatchDeploymentData -from azure.ai.ml._restclient.v2022_10_01.models import BatchOutputAction, EndpointComputeType -from azure.ai.ml._restclient.v2023_04_01_preview.models import BatchPipelineComponentDeploymentConfiguration +from azure.ai.ml._restclient.v2022_10_01.models import ( + BatchOutputAction, + EndpointComputeType, + BatchDeployment as BatchDeploymentData, +) from azure.ai.ml._restclient.v2023_04_01_preview.models import ( + OnlineDeployment as RestOnlineDeploymentData, + ManagedOnlineDeployment as RestManagedOnlineDeployment, KubernetesOnlineDeployment as RestKubernetesOnlineDeployment, + BatchPipelineComponentDeploymentConfiguration, ) -from azure.ai.ml._restclient.v2023_04_01_preview.models import ManagedOnlineDeployment as RestManagedOnlineDeployment -from azure.ai.ml._restclient.v2023_04_01_preview.models import OnlineDeployment as RestOnlineDeploymentData -from azure.ai.ml.constants._common import ArmConstants from azure.ai.ml.constants._deployment import BatchDeploymentOutputAction from azure.ai.ml.entities import ( BatchDeployment, + Deployment, CodeConfiguration, DefaultScaleSettings, - Deployment, KubernetesOnlineDeployment, ManagedOnlineDeployment, OnlineDeployment, OnlineEndpoint, - OnlineRequestSettings, ProbeSettings, ResourceRequirementsSettings, ResourceSettings, TargetUtilizationScaleSettings, + OnlineRequestSettings, ) -from azure.ai.ml.entities._assets import Code, Environment, Model +from azure.ai.ml.constants._common import ArmConstants +from azure.ai.ml.exceptions import DeploymentException, ValidationException +from azure.ai.ml.entities._assets import Environment, Model, Code from azure.ai.ml.entities._deployment.deployment_settings import BatchRetrySettings from azure.ai.ml.entities._deployment.run_settings import RunSettings from azure.ai.ml.entities._job.resource_configuration import ResourceConfiguration -from azure.ai.ml.exceptions import DeploymentException, ValidationException @pytest.mark.production_experiences_test @@ -658,7 +661,7 @@ def test_batch_deployment_raise_exception_when(self) -> None: BatchDeployment( name="non-mlflow-deployment", instance_count=2, resources=ResourceConfiguration(instance_count=2) ) - assert "Can't set instance_count when resources.instance_count is provided." == str(exc.value) + assert "Can't set instance_count when resources is provided." == str(exc.value) @pytest.mark.unittest @@ -814,11 +817,13 @@ def test_online_deployment_from_rest_object_unsupported_scale_settings_type(self assert str(exc.value) == "Unsupported online scale setting type Other." -from azure.ai.ml._restclient.v2022_05_01.models import BatchRetrySettings as RestBatchRetrySettings -from azure.ai.ml._restclient.v2022_05_01.models import CodeConfiguration as RestCodeConfiguration -from azure.ai.ml.entities._deployment.data_asset import DataAsset +from azure.ai.ml._restclient.v2022_05_01.models import ( + BatchRetrySettings as RestBatchRetrySettings, + CodeConfiguration as RestCodeConfiguration, +) from azure.ai.ml.entities._deployment.data_collector import DataCollector from azure.ai.ml.entities._deployment.deployment_collection import DeploymentCollection +from azure.ai.ml.entities._deployment.data_asset import DataAsset from azure.ai.ml.entities._deployment.request_logging import RequestLogging @@ -1082,4 +1087,4 @@ def test_ru_settings_to_dict(self): assert run_settings_dict["experiment_name"] == run_settings.experiment_name assert run_settings_dict["description"] == run_settings.description assert run_settings_dict["tags"] == run_settings.tags - assert run_settings_dict["settings"] == run_settings.settings + assert run_settings_dict["settings"] == run_settings.settings \ No newline at end of file From 5d05ab4dfe38020effcdc7cdab9a677a852d9f99 Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Mon, 21 Apr 2025 13:28:18 +0530 Subject: [PATCH 07/14] add new line --- .../batch_online_common/unittests/test_deployment_entity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py index d7fd0bf5b284..089155fa4cd5 100644 --- a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py +++ b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py @@ -1087,4 +1087,4 @@ def test_ru_settings_to_dict(self): assert run_settings_dict["experiment_name"] == run_settings.experiment_name assert run_settings_dict["description"] == run_settings.description assert run_settings_dict["tags"] == run_settings.tags - assert run_settings_dict["settings"] == run_settings.settings \ No newline at end of file + assert run_settings_dict["settings"] == run_settings.settings From 787fe82999325a6f36673bcbbd8686a1aa0116a1 Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Mon, 21 Apr 2025 13:38:21 +0530 Subject: [PATCH 08/14] Update optional tags for docstrings --- .../_deployment/model_batch_deployment.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py index 1f99d005cfaf..59afaed5a5aa 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py @@ -29,29 +29,29 @@ class ModelBatchDeployment(BatchDeployment): :param name: Name of the deployment resource. :type name: str :param endpoint_name: Name of the endpoint. - :type endpoint_name: str + :type endpoint_name: Optional[str] :param environment: Environment to use for deployment. - :type environment: Union[str, Environment] + :type environment: Optional[Union[str, Environment]] :param properties: The asset property dictionary. - :type properties: Dict[str, str] + :type properties: Optional[Dict[str, str]] :param model: Model to deploy. - :type model: Union[str, Model] + :type model: Optional[Union[str, Model]] :param description: Deployment description. - :type description: str + :type description: Optional[str] :param tags: Deployment tags. - :type tags: Dict[str, Any] + :type tags: Optional[Dict[str, Any]] :param settings: Deployment settings. - :type settings: ModelBatchDeploymentSettings + :type settings: Optional[ModelBatchDeploymentSettings] :param resources: Resource configuration. - :type resources: ResourceConfiguration + :type resources: Optional[ResourceConfiguration] :param compute: Compute target to use. - :type compute: str + :type compute: Optional[str] :param code_configuration: Code configuration for deployment. - :type code_configuration: CodeConfiguration + :type code_configuration: Optional[CodeConfiguration] :param code_path: Path to the code directory. - :type code_path: Union[str, PathLike] + :type code_path: Optional[Union[str, PathLike]] :param scoring_script: Path to the scoring script. - :type scoring_script: Union[str, PathLike] + :type scoring_script: Optional[Union[str, PathLike]] """ def __init__( From da3ffc07d9dcd074797880931676be01c80c75f7 Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Mon, 21 Apr 2025 16:42:34 +0530 Subject: [PATCH 09/14] remove unnecessary check --- .../azure/ai/ml/entities/_deployment/batch_deployment.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py index a1f7ff8d31d3..756974dc883a 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py @@ -205,8 +205,6 @@ def __setattr__(self, name, value): # Support backwards compatibility with old BatchDeployment properties. if name in SETTINGS_ATTRIBUTES: try: - # First confirm that this object has a compatible attr - getattr(self._settings, name) setattr(self._settings, name, value) except AttributeError: pass From f559bfd21d168f611a8a3e666b30c322784afca6 Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Tue, 22 Apr 2025 00:27:38 +0530 Subject: [PATCH 10/14] add more tests --- .../entities/_deployment/batch_deployment.py | 1 + .../unittests/test_deployment_entity.py | 111 +++++++++++++++--- .../unittests/test_model_batch_deployment.py | 36 +++++- ...test_pipeline_component_bach_deployment.py | 23 ++-- 4 files changed, 138 insertions(+), 33 deletions(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py index 756974dc883a..cf4f357f2790 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py @@ -150,6 +150,7 @@ def __init__( model=model, code_configuration=code_configuration, environment=environment, + environment_variables=environment_variables, # needed, otherwise Deployment.__init__() will set it to {} code_path=code_path, scoring_script=scoring_script, **kwargs, diff --git a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py index 089155fa4cd5..efaa8e1ce067 100644 --- a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py +++ b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_deployment_entity.py @@ -1,44 +1,44 @@ import copy +import json import pytest import yaml -import json from test_utilities.utils import verify_entity_load_and_dump from azure.ai.ml import load_batch_deployment, load_online_deployment -from azure.ai.ml._restclient.v2022_10_01.models import ( - BatchOutputAction, - EndpointComputeType, - BatchDeployment as BatchDeploymentData, -) +from azure.ai.ml._restclient.v2022_10_01.models import BatchDeployment as BatchDeploymentData +from azure.ai.ml._restclient.v2022_10_01.models import BatchOutputAction, EndpointComputeType +from azure.ai.ml._restclient.v2023_04_01_preview.models import BatchPipelineComponentDeploymentConfiguration from azure.ai.ml._restclient.v2023_04_01_preview.models import ( - OnlineDeployment as RestOnlineDeploymentData, - ManagedOnlineDeployment as RestManagedOnlineDeployment, KubernetesOnlineDeployment as RestKubernetesOnlineDeployment, - BatchPipelineComponentDeploymentConfiguration, ) +from azure.ai.ml._restclient.v2023_04_01_preview.models import ManagedOnlineDeployment as RestManagedOnlineDeployment +from azure.ai.ml._restclient.v2023_04_01_preview.models import OnlineDeployment as RestOnlineDeploymentData +from azure.ai.ml.constants._common import ArmConstants from azure.ai.ml.constants._deployment import BatchDeploymentOutputAction from azure.ai.ml.entities import ( BatchDeployment, - Deployment, CodeConfiguration, DefaultScaleSettings, + Deployment, KubernetesOnlineDeployment, ManagedOnlineDeployment, OnlineDeployment, OnlineEndpoint, + OnlineRequestSettings, ProbeSettings, ResourceRequirementsSettings, ResourceSettings, TargetUtilizationScaleSettings, - OnlineRequestSettings, ) -from azure.ai.ml.constants._common import ArmConstants -from azure.ai.ml.exceptions import DeploymentException, ValidationException -from azure.ai.ml.entities._assets import Environment, Model, Code +from azure.ai.ml.entities._assets import Code, Environment, Model from azure.ai.ml.entities._deployment.deployment_settings import BatchRetrySettings +from azure.ai.ml.entities._deployment.model_batch_deployment_settings import ( + ModelBatchDeploymentSettings as BatchDeploymentSettings, +) from azure.ai.ml.entities._deployment.run_settings import RunSettings from azure.ai.ml.entities._job.resource_configuration import ResourceConfiguration +from azure.ai.ml.exceptions import DeploymentException, ValidationException @pytest.mark.production_experiences_test @@ -473,6 +473,14 @@ def simple_batch_deployment_validation(deployment): load_batch_deployment, simple_batch_deployment_validation, TestBatchDeploymentSDK.DEPLOYMENT ) + def test_batch_endpoint_deployment_deprecated_warning(self): + # Test for warning when using deprecated BatchDeployment class + with pytest.warns( + UserWarning, match="This class is intended as a base class and it's direct usage is deprecated" + ): + deployment = load_batch_deployment(TestBatchDeploymentSDK.DEPLOYMENT) + assert deployment._type is None + def test_batch_endpoint_deployment_to_rest_object(self) -> None: with open(TestBatchDeploymentSDK.DEPLOYMENT, "r") as f: target = yaml.safe_load(f) @@ -488,6 +496,73 @@ def test_batch_endpoint_deployment_to_rest_object(self) -> None: assert rest_representation_properties.output_action == BatchOutputAction.APPEND_ROW assert rest_representation_properties.description == target["description"] + def test_batch_deployment_attributes_access(self) -> None: + # Create a deployment with settings properties + deployment = BatchDeployment( + name="attribute-test-deployment", + output_action=BatchDeploymentOutputAction.APPEND_ROW, + output_file_name="test.csv", + error_threshold=10, + retry_settings=BatchRetrySettings(max_retries=5, timeout=60), + logging_level="info", + mini_batch_size=20, + max_concurrency_per_instance=4, + environment_variables={"key1": "value1"}, + ) + assert isinstance( + deployment._settings, BatchDeploymentSettings + ) # Test accessing settings attributes through __getattr__ + assert deployment.output_action == BatchDeploymentOutputAction.APPEND_ROW + assert deployment.output_file_name == "test.csv" + assert deployment.error_threshold == 10 + assert deployment.retry_settings.max_retries == 5 + assert deployment.retry_settings.timeout == 60 + assert deployment.logging_level == "info" + assert deployment.mini_batch_size == 20 + assert deployment.max_concurrency_per_instance == 4 + assert deployment.environment_variables == {"key1": "value1"} + + # Test setting settings attributes through __setattr__ + deployment.output_action = BatchDeploymentOutputAction.SUMMARY_ONLY + deployment.output_file_name = "new.csv" + deployment.error_threshold = 20 + deployment.retry_settings = BatchRetrySettings(max_retries=10, timeout=120) + deployment.logging_level = "debug" + deployment.mini_batch_size = 30 + deployment.max_concurrency_per_instance = 8 + deployment.environment_variables = {"key2": "value2"} + + # Verify attributes were updated correctly + assert deployment.output_action == BatchDeploymentOutputAction.SUMMARY_ONLY + assert deployment._settings.output_action == BatchDeploymentOutputAction.SUMMARY_ONLY + assert deployment.output_file_name == "new.csv" + assert deployment._settings.output_file_name == "new.csv" + assert deployment.error_threshold == 20 + assert deployment._settings.error_threshold == 20 + assert deployment.retry_settings.max_retries == 10 + assert deployment._settings.retry_settings.max_retries == 10 + assert deployment.retry_settings.timeout == 120 + assert deployment._settings.retry_settings.timeout == 120 + assert deployment.logging_level == "debug" + assert deployment._settings.logging_level == "debug" + assert deployment.mini_batch_size == 30 + assert deployment._settings.mini_batch_size == 30 + assert deployment.max_concurrency_per_instance == 8 + assert deployment._settings.max_concurrency_per_instance == 8 + assert deployment.environment_variables == {"key2": "value2"} + assert deployment._settings.environment_variables == {"key2": "value2"} + + # Test accessing non-settings attributes + assert deployment.name == "attribute-test-deployment" + + # Test setting non-settings attributes + deployment.name = "updated-deployment" + assert deployment.name == "updated-deployment" + + # Ensure setting a non-existent attribute doesn't raise an exception + deployment.new_attribute = "test" + assert deployment.new_attribute == "test" + def test_to_rest_invalid_when_output_action_summary_and_file_name_provided(self) -> None: with open(TestBatchDeploymentSDK.DEPLOYMENT, "r") as f: target = yaml.safe_load(f) @@ -817,13 +892,11 @@ def test_online_deployment_from_rest_object_unsupported_scale_settings_type(self assert str(exc.value) == "Unsupported online scale setting type Other." -from azure.ai.ml._restclient.v2022_05_01.models import ( - BatchRetrySettings as RestBatchRetrySettings, - CodeConfiguration as RestCodeConfiguration, -) +from azure.ai.ml._restclient.v2022_05_01.models import BatchRetrySettings as RestBatchRetrySettings +from azure.ai.ml._restclient.v2022_05_01.models import CodeConfiguration as RestCodeConfiguration +from azure.ai.ml.entities._deployment.data_asset import DataAsset from azure.ai.ml.entities._deployment.data_collector import DataCollector from azure.ai.ml.entities._deployment.deployment_collection import DeploymentCollection -from azure.ai.ml.entities._deployment.data_asset import DataAsset from azure.ai.ml.entities._deployment.request_logging import RequestLogging diff --git a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_model_batch_deployment.py b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_model_batch_deployment.py index cf7b59fc0df5..e4a83e92459a 100644 --- a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_model_batch_deployment.py +++ b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_model_batch_deployment.py @@ -3,10 +3,11 @@ # --------------------------------------------------------- import pytest + +from azure.ai.ml._restclient.v2022_05_01.models import BatchOutputAction +from azure.ai.ml.constants._deployment import BatchDeploymentOutputAction from azure.ai.ml.entities import ModelBatchDeployment from azure.ai.ml.entities._load_functions import load_model_batch_deployment -from azure.ai.ml.constants._deployment import BatchDeploymentOutputAction -from azure.ai.ml._restclient.v2022_05_01.models import BatchOutputAction from azure.ai.ml.exceptions import ValidationException @@ -18,6 +19,7 @@ def test_to_rest_object(self) -> None: deployment = load_model_batch_deployment( TestModelBatchDeployment.MODEL_BATCH_DEPLOYMNET, params_override=[{"endpoint_name": "some-en-name"}] ) + assert deployment.type == "model" rest_deployment = deployment._to_rest_object(location="eastus") assert rest_deployment.location == "eastus" assert rest_deployment.properties.description == deployment.description @@ -69,3 +71,33 @@ def test_to_dict(self) -> None: assert deployment_dict["resources"]["instance_count"] == deployment.resources.instance_count assert deployment_dict["settings"]["error_threshold"] == deployment.settings.error_threshold assert deployment_dict["settings"]["output_action"] == "append_row" + + def test_settings_getter_setter(self) -> None: + """Test getter and setter for settings in ModelBatchDeployment.""" + # Create a minimal batch deployment + deployment = ModelBatchDeployment( + name="test-deployment", + endpoint_name="test-endpoint", + model="azureml:model:1", + compute="azureml:cpu-cluster", + resources={"instance_count": 2}, + ) + breakpoint() + assert deployment.compute == "azureml:cpu-cluster" + assert deployment.resources["instance_count"] == 2 + + # Test setting individual settings properties + deployment.settings.output_file_name = "results.csv" + deployment.settings.output_action = BatchDeploymentOutputAction.APPEND_ROW + deployment.settings.error_threshold = 10 + deployment.settings.max_concurrency_per_instance = 4 + deployment.settings.mini_batch_size = 5 + deployment.settings.environment_variables = {"ENV_VAR1": "value1", "ENV_VAR2": "value2"} + + # Verify individual settings can be retrieved correctly + assert deployment.settings.output_file_name == "results.csv" + assert deployment.settings.output_action == BatchDeploymentOutputAction.APPEND_ROW + assert deployment.settings.error_threshold == 10 + assert deployment.settings.max_concurrency_per_instance == 4 + assert deployment.settings.mini_batch_size == 5 + assert deployment.settings.environment_variables == {"ENV_VAR1": "value1", "ENV_VAR2": "value2"} diff --git a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_pipeline_component_bach_deployment.py b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_pipeline_component_bach_deployment.py index c18e0b4e46d1..e50c4565998d 100644 --- a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_pipeline_component_bach_deployment.py +++ b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_pipeline_component_bach_deployment.py @@ -2,23 +2,21 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # --------------------------------------------------------- -import pytest import json -from azure.ai.ml.entities._deployment.pipeline_component_batch_deployment import ( - PipelineComponentBatchDeployment, -) -from azure.ai.ml.entities import PipelineComponent -from azure.ai.ml.entities._load_functions import ( - load_pipeline_component_batch_deployment, -) + +import pytest + +from azure.ai.ml._restclient.v2024_01_01_preview.models import BatchDeployment as RestBatchDeployment from azure.ai.ml._restclient.v2024_01_01_preview.models import ( - BatchDeployment as RestBatchDeployment, - IdAssetReference, - BatchPipelineComponentDeploymentConfiguration as RestBatchPipelineComponentDeploymentConfiguration, + BatchDeploymentProperties as RestBatchDeploymentProperties, ) from azure.ai.ml._restclient.v2024_01_01_preview.models import ( - BatchDeploymentProperties as RestBatchDeploymentProperties, + BatchPipelineComponentDeploymentConfiguration as RestBatchPipelineComponentDeploymentConfiguration, ) +from azure.ai.ml._restclient.v2024_01_01_preview.models import IdAssetReference +from azure.ai.ml.entities import PipelineComponent +from azure.ai.ml.entities._deployment.pipeline_component_batch_deployment import PipelineComponentBatchDeployment +from azure.ai.ml.entities._load_functions import load_pipeline_component_batch_deployment @pytest.mark.unittest @@ -30,6 +28,7 @@ def test_to_rest_object(self) -> None: pipeline_component = load_pipeline_component_batch_deployment( TestPipelineComponentBatchDeployment.HELLO_BATCH_DEPLOYMENT ) + assert pipeline_component.type == "pipeline" pipeline_component_rest = pipeline_component._to_rest_object(location="eastus") assert pipeline_component_rest.properties.deployment_configuration.settings == pipeline_component.settings assert ( From d5c2dabe80d95da874ca4e90a8cf1fe5cff75062 Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Tue, 22 Apr 2025 11:48:41 +0530 Subject: [PATCH 11/14] handle CLI passed type param --- ...peline_component_batch_deployment_schema.py | 18 ++++++++++-------- .../pipeline_component_batch_deployment.py | 5 ++++- .../unittests/test_model_batch_deployment.py | 1 - 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/_schema/_deployment/batch/pipeline_component_batch_deployment_schema.py b/sdk/ml/azure-ai-ml/azure/ai/ml/_schema/_deployment/batch/pipeline_component_batch_deployment_schema.py index 4bc884b023b4..f580a1521751 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/_schema/_deployment/batch/pipeline_component_batch_deployment_schema.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/_schema/_deployment/batch/pipeline_component_batch_deployment_schema.py @@ -8,17 +8,17 @@ from marshmallow import INCLUDE, fields, post_load -from azure.ai.ml._schema import ( - ArmVersionedStr, - ArmStr, - UnionField, - RegistryStr, - NestedField, +from azure.ai.ml._schema import ArmStr, ArmVersionedStr, NestedField, RegistryStr, UnionField +from azure.ai.ml._schema.core.fields import ( + PathAwareSchema, + PipelineNodeNameStr, + StringTransformedEnum, + TypeSensitiveUnionField, ) -from azure.ai.ml._schema.core.fields import PipelineNodeNameStr, TypeSensitiveUnionField, PathAwareSchema from azure.ai.ml._schema.pipeline.pipeline_component import PipelineComponentFileRefField from azure.ai.ml.constants._common import AzureMLResourceType from azure.ai.ml.constants._component import NodeType +from azure.ai.ml.constants._deployment import BatchDeploymentType module_logger = logging.getLogger(__name__) @@ -35,7 +35,9 @@ class PipelineComponentBatchDeploymentSchema(PathAwareSchema): ) settings = fields.Dict() name = fields.Str() - type = fields.Str() + type = StringTransformedEnum( + allowed_values=[BatchDeploymentType.PIPELINE, BatchDeploymentType.MODEL], required=False + ) job_definition = UnionField( [ ArmStr(azureml_type=AzureMLResourceType.JOB), diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/pipeline_component_batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/pipeline_component_batch_deployment.py index ed28538f85b5..faad8d7c3286 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/pipeline_component_batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/pipeline_component_batch_deployment.py @@ -58,8 +58,11 @@ def __init__( description: Optional[str] = None, **kwargs: Any, ): + # If type not removed from kwargs, it can lead to dual type params passed to Deployment class + # Get type from kwargs if present, otherwise use the default type defined above + _type = kwargs.pop("type", type) super().__init__( - name=name, _type=type, endpoint_name=endpoint_name, tags=tags, description=description, **kwargs + name=name, _type=_type, endpoint_name=endpoint_name, tags=tags, description=description, **kwargs ) self.component = component self.settings = settings diff --git a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_model_batch_deployment.py b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_model_batch_deployment.py index e4a83e92459a..90966ec362ac 100644 --- a/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_model_batch_deployment.py +++ b/sdk/ml/azure-ai-ml/tests/batch_online_common/unittests/test_model_batch_deployment.py @@ -82,7 +82,6 @@ def test_settings_getter_setter(self) -> None: compute="azureml:cpu-cluster", resources={"instance_count": 2}, ) - breakpoint() assert deployment.compute == "azureml:cpu-cluster" assert deployment.resources["instance_count"] == 2 From 810e3776d0f75ae15c2c500e5204876d1610f163 Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Wed, 23 Apr 2025 11:51:46 +0530 Subject: [PATCH 12/14] handle type in model batch deployment --- .../ai/ml/entities/_deployment/model_batch_deployment.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py index 59afaed5a5aa..66e345c5acaf 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/model_batch_deployment.py @@ -74,9 +74,12 @@ def __init__( ] = None, # promoted property from code_configuration.scoring_script **kwargs: Any, ): + # If type not removed from kwargs, it can lead to dual type params passed to Deployment class + # Get type from kwargs if present, otherwise use the default type defined above + _type = kwargs.pop("type", type) super().__init__( name=name, - _type=type, + _type=_type, endpoint_name=endpoint_name, properties=properties, code_path=code_path, From 4429e7ac7bab3ca18c1af469d674af6600320c4f Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Wed, 23 Apr 2025 13:07:04 +0530 Subject: [PATCH 13/14] fix warning showing up in model deployment rest to python object creation --- .../ml/_schema/_deployment/batch/model_batch_deployment.py | 7 +++---- .../azure/ai/ml/entities/_deployment/batch_deployment.py | 6 +++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/_schema/_deployment/batch/model_batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/_schema/_deployment/batch/model_batch_deployment.py index 0dbd8463cfab..301d7b08a696 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/_schema/_deployment/batch/model_batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/_schema/_deployment/batch/model_batch_deployment.py @@ -9,14 +9,13 @@ from marshmallow import fields, post_load +from azure.ai.ml._schema._deployment.deployment import DeploymentSchema from azure.ai.ml._schema.core.fields import ComputeField, NestedField, StringTransformedEnum from azure.ai.ml._schema.job_resource_configuration import JobResourceConfigurationSchema -from azure.ai.ml._schema._deployment.deployment import DeploymentSchema from azure.ai.ml.constants._common import BASE_PATH_CONTEXT_KEY from azure.ai.ml.constants._deployment import BatchDeploymentType -from azure.ai.ml._schema import ExperimentalField -from .model_batch_deployment_settings import ModelBatchDeploymentSettingsSchema +from .model_batch_deployment_settings import ModelBatchDeploymentSettingsSchema module_logger = logging.getLogger(__name__) @@ -37,7 +36,7 @@ class ModelBatchDeploymentSchema(DeploymentSchema): allowed_values=[BatchDeploymentType.PIPELINE, BatchDeploymentType.MODEL], required=False ) - settings = ExperimentalField(NestedField(ModelBatchDeploymentSettingsSchema)) + settings = NestedField(ModelBatchDeploymentSettingsSchema) @post_load def make(self, data: Any, **kwargs: Any) -> Any: diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py index cf4f357f2790..dc2852c6cccf 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/entities/_deployment/batch_deployment.py @@ -130,7 +130,10 @@ def __init__( **kwargs: Any, ) -> None: _type = kwargs.pop("_type", None) - if _type is None: + + # Suppresses deprecation warning when object is created from REST responses + # This is needed to avoid false deprecation warning on model batch deployment + if _type is None and not kwargs.pop("_from_rest", False): warnings.warn( "This class is intended as a base class and it's direct usage is deprecated. " "Use one of the concrete implementations instead:\n" @@ -351,6 +354,7 @@ def _from_rest_object( # pylint: disable=arguments-renamed properties=properties, creation_context=SystemData._from_rest_object(deployment.system_data), provisioning_state=deployment.properties.provisioning_state, + _from_rest=True, ) return deployment From 155b19dc24f0fac16d2f68067c42ccea20fe2f9a Mon Sep 17 00:00:00 2001 From: Pratibha Shrivastav Date: Wed, 23 Apr 2025 13:34:09 +0530 Subject: [PATCH 14/14] add changelog --- sdk/ml/azure-ai-ml/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/ml/azure-ai-ml/CHANGELOG.md b/sdk/ml/azure-ai-ml/CHANGELOG.md index cc4c445d7d81..b96deee3a7c5 100644 --- a/sdk/ml/azure-ai-ml/CHANGELOG.md +++ b/sdk/ml/azure-ai-ml/CHANGELOG.md @@ -12,6 +12,7 @@ - Hub and Project are officially GA'd and no longer experimental. - Restrict major version auto updates for external dependencies to ensure stability and prevent build failures for breaking changes. +- PipelineComponentBatchDeployment, ModelBatchDeployment, ModelBatchDeploymentSettings are GA ## 1.26.3 (2025-04-17)