# ---------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# ---------------------------------------------------------
# pylint: disable=protected-access
from os import PathLike
from typing import Any, Dict, Iterable, Optional, Union, cast
from azure.ai.ml._restclient.v2021_10_01_dataplanepreview import (
AzureMachineLearningWorkspaces as ServiceClient102021Dataplane,
)
from azure.ai.ml._restclient.v2023_08_01_preview import AzureMachineLearningWorkspaces as ServiceClient082023Preview
from azure.ai.ml._restclient.v2023_08_01_preview.models import ListViewType
from azure.ai.ml._scope_dependent_operations import (
OperationConfig,
OperationsContainer,
OperationScope,
_ScopeDependentOperations,
)
from azure.ai.ml._telemetry import ActivityType, monitor_with_activity
from azure.ai.ml._utils._logger_utils import OpsLogger
from azure.ai.ml._utils.utils import _get_evaluator_properties, _is_evaluator
from azure.ai.ml.entities._assets import Model
from azure.ai.ml.entities._assets.workspace_asset_reference import WorkspaceAssetReference
from azure.ai.ml.exceptions import UnsupportedOperationError
from azure.ai.ml.operations._datastore_operations import DatastoreOperations
from azure.ai.ml.operations._model_operations import ModelOperations
from azure.core.exceptions import ResourceNotFoundError
ops_logger = OpsLogger(__name__)
module_logger = ops_logger.module_logger
[docs]
class EvaluatorOperations(_ScopeDependentOperations):
"""EvaluatorOperations.
You should not instantiate this class directly. Instead, you should create an MLClient instance that instantiates it
for you and attaches it as an attribute.
:param operation_scope: Scope variables for the operations classes of an MLClient object.
:type operation_scope: ~azure.ai.ml._scope_dependent_operations.OperationScope
:param operation_config: Common configuration for operations classes of an MLClient object.
:type operation_config: ~azure.ai.ml._scope_dependent_operations.OperationConfig
:param service_client: Service client to allow end users to operate on Azure Machine Learning Workspace
resources (ServiceClient082023Preview or ServiceClient102021Dataplane).
:type service_client: typing.Union[
azure.ai.ml._restclient.v2023_04_01_preview._azure_machine_learning_workspaces.AzureMachineLearningWorkspaces,
azure.ai.ml._restclient.v2021_10_01_dataplanepreview._azure_machine_learning_workspaces.
AzureMachineLearningWorkspaces]
:param datastore_operations: Represents a client for performing operations on Datastores.
:type datastore_operations: ~azure.ai.ml.operations._datastore_operations.DatastoreOperations
:param all_operations: All operations classes of an MLClient object.
:type all_operations: ~azure.ai.ml._scope_dependent_operations.OperationsContainer
:param kwargs: A dictionary of additional configuration parameters.
:type kwargs: dict
"""
# pylint: disable=unused-argument
def __init__(
self,
operation_scope: OperationScope,
operation_config: OperationConfig,
service_client: Union[ServiceClient082023Preview, ServiceClient102021Dataplane],
datastore_operations: DatastoreOperations,
all_operations: Optional[OperationsContainer] = None,
**kwargs,
):
super(EvaluatorOperations, self).__init__(operation_scope, operation_config)
ops_logger.update_filter()
self._model_op = ModelOperations(
operation_scope=operation_scope,
operation_config=operation_config,
service_client=service_client,
datastore_operations=datastore_operations,
all_operations=all_operations,
**{ModelOperations._IS_EVALUATOR: True},
**kwargs,
)
self._operation_scope = self._model_op._operation_scope
self._datastore_operation = self._model_op._datastore_operation
[docs]
@monitor_with_activity(ops_logger, "Evaluator.CreateOrUpdate", ActivityType.PUBLICAPI)
def create_or_update( # type: ignore
self, model: Union[Model, WorkspaceAssetReference], **kwargs: Any
) -> Model: # TODO: Are we going to implement job_name?
"""Returns created or updated model asset.
:param model: Model asset object.
:type model: ~azure.ai.ml.entities.Model
:raises ~azure.ai.ml.exceptions.AssetPathException: Raised when the Model artifact path is
already linked to another asset
:raises ~azure.ai.ml.exceptions.ValidationException: Raised if Model cannot be successfully validated.
Details will be provided in the error message.
:raises ~azure.ai.ml.exceptions.EmptyDirectoryError: Raised if local path provided points to an empty directory.
:return: Model asset object.
:rtype: ~azure.ai.ml.entities.Model
"""
model.properties.update(_get_evaluator_properties())
return self._model_op.create_or_update(model)
def _raise_if_not_evaluator(self, properties: Optional[Dict[str, Any]], message: str) -> None:
"""
:param properties: The properties of a model.
:type properties: dict[str, str]
:param message: The message to be set on exception.
:type message: str
:raises ~azure.ai.ml.exceptions.ValidationException: Raised if model is not an
evaluator.
"""
if properties is not None and not _is_evaluator(properties):
raise ResourceNotFoundError(
message=message,
response=None,
)
[docs]
@monitor_with_activity(ops_logger, "Evaluator.Get", ActivityType.PUBLICAPI)
def get(self, name: str, *, version: Optional[str] = None, label: Optional[str] = None, **kwargs) -> Model:
"""Returns information about the specified model asset.
:param name: Name of the model.
:type name: str
:keyword version: Version of the model.
:paramtype version: str
:keyword label: Label of the model. (mutually exclusive with version)
:paramtype label: str
:raises ~azure.ai.ml.exceptions.ValidationException: Raised if Model cannot be successfully validated.
Details will be provided in the error message.
:return: Model asset object.
:rtype: ~azure.ai.ml.entities.Model
"""
model = self._model_op.get(name, version, label)
properties = None if model is None else model.properties
self._raise_if_not_evaluator(
properties,
f"Evaluator {name} with version {version} not found.",
)
return model
[docs]
@monitor_with_activity(ops_logger, "Evaluator.Download", ActivityType.PUBLICAPI)
def download(self, name: str, version: str, download_path: Union[PathLike, str] = ".", **kwargs: Any) -> None:
"""Download files related to a model.
:param name: Name of the model.
:type name: str
:param version: Version of the model.
:type version: str
:param download_path: Local path as download destination, defaults to current working directory of the current
user. Contents will be overwritten.
:type download_path: Union[PathLike, str]
:raises ResourceNotFoundError: if can't find a model matching provided name.
"""
self._model_op.download(name, version, download_path)
[docs]
@monitor_with_activity(ops_logger, "Evaluator.List", ActivityType.PUBLICAPI)
def list(
self,
name: str,
stage: Optional[str] = None,
*,
list_view_type: ListViewType = ListViewType.ACTIVE_ONLY,
**kwargs: Any,
) -> Iterable[Model]:
"""List all model assets in workspace.
:param name: Name of the model.
:type name: str
:param stage: The Model stage
:type stage: Optional[str]
:keyword list_view_type: View type for including/excluding (for example) archived models.
Defaults to :attr:`ListViewType.ACTIVE_ONLY`.
:paramtype list_view_type: ListViewType
:return: An iterator like instance of Model objects
:rtype: ~azure.core.paging.ItemPaged[~azure.ai.ml.entities.Model]
"""
properties_str = "is-promptflow=true,is-evaluator=true"
if name:
return cast(
Iterable[Model],
(
self._model_op._model_versions_operation.list(
name=name,
registry_name=self._model_op._registry_name,
cls=lambda objs: [Model._from_rest_object(obj) for obj in objs],
properties=properties_str,
**self._model_op._scope_kwargs,
)
if self._registry_name
else self._model_op._model_versions_operation.list(
name=name,
workspace_name=self._model_op._workspace_name,
cls=lambda objs: [Model._from_rest_object(obj) for obj in objs],
list_view_type=list_view_type,
properties=properties_str,
stage=stage,
**self._model_op._scope_kwargs,
)
),
)
# ModelContainer object does not carry properties.
raise UnsupportedOperationError("list on evaluation operations without name provided")
# TODO: Implement filtering of the ModelContainerOperations list output
# return cast(
# Iterable[Model], (
# self._model_container_operation.list(
# registry_name=self._registry_name,
# cls=lambda objs: [Model._from_container_rest_object(obj) for obj in objs],
# list_view_type=list_view_type,
# **self._scope_kwargs,
# )
# if self._registry_name
# else self._model_container_operation.list(
# workspace_name=self._workspace_name,
# cls=lambda objs: [Model._from_container_rest_object(obj) for obj in objs],
# list_view_type=list_view_type,
# **self._scope_kwargs,
# )
# )
# )