azure-ai-evaluation 1.0.0b5__py3-none-any.whl → 1.1.0__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.

Potentially problematic release.


This version of azure-ai-evaluation might be problematic. Click here for more details.

Files changed (72) hide show
  1. azure/ai/evaluation/_azure/__init__.py +3 -0
  2. azure/ai/evaluation/_azure/_clients.py +188 -0
  3. azure/ai/evaluation/_azure/_models.py +227 -0
  4. azure/ai/evaluation/_azure/_token_manager.py +118 -0
  5. azure/ai/evaluation/_common/_experimental.py +4 -0
  6. azure/ai/evaluation/_common/math.py +62 -2
  7. azure/ai/evaluation/_common/rai_service.py +110 -50
  8. azure/ai/evaluation/_common/utils.py +50 -16
  9. azure/ai/evaluation/_constants.py +2 -0
  10. azure/ai/evaluation/_evaluate/_batch_run/eval_run_context.py +9 -0
  11. azure/ai/evaluation/_evaluate/_batch_run/proxy_client.py +13 -3
  12. azure/ai/evaluation/_evaluate/_batch_run/target_run_context.py +12 -1
  13. azure/ai/evaluation/_evaluate/_eval_run.py +38 -43
  14. azure/ai/evaluation/_evaluate/_evaluate.py +62 -131
  15. azure/ai/evaluation/_evaluate/_telemetry/__init__.py +2 -1
  16. azure/ai/evaluation/_evaluate/_utils.py +72 -38
  17. azure/ai/evaluation/_evaluators/_bleu/_bleu.py +16 -17
  18. azure/ai/evaluation/_evaluators/_coherence/_coherence.py +60 -29
  19. azure/ai/evaluation/_evaluators/_common/_base_eval.py +88 -6
  20. azure/ai/evaluation/_evaluators/_common/_base_prompty_eval.py +16 -3
  21. azure/ai/evaluation/_evaluators/_common/_base_rai_svc_eval.py +39 -10
  22. azure/ai/evaluation/_evaluators/_content_safety/_content_safety.py +58 -52
  23. azure/ai/evaluation/_evaluators/_content_safety/_hate_unfairness.py +79 -34
  24. azure/ai/evaluation/_evaluators/_content_safety/_self_harm.py +73 -34
  25. azure/ai/evaluation/_evaluators/_content_safety/_sexual.py +74 -33
  26. azure/ai/evaluation/_evaluators/_content_safety/_violence.py +76 -34
  27. azure/ai/evaluation/_evaluators/_eci/_eci.py +28 -3
  28. azure/ai/evaluation/_evaluators/_f1_score/_f1_score.py +20 -13
  29. azure/ai/evaluation/_evaluators/_fluency/_fluency.py +57 -26
  30. azure/ai/evaluation/_evaluators/_gleu/_gleu.py +13 -15
  31. azure/ai/evaluation/_evaluators/_groundedness/_groundedness.py +68 -30
  32. azure/ai/evaluation/_evaluators/_meteor/_meteor.py +17 -20
  33. azure/ai/evaluation/_evaluators/_multimodal/_content_safety_multimodal.py +10 -8
  34. azure/ai/evaluation/_evaluators/_multimodal/_content_safety_multimodal_base.py +0 -2
  35. azure/ai/evaluation/_evaluators/_multimodal/_hate_unfairness.py +6 -2
  36. azure/ai/evaluation/_evaluators/_multimodal/_protected_material.py +10 -6
  37. azure/ai/evaluation/_evaluators/_multimodal/_self_harm.py +6 -2
  38. azure/ai/evaluation/_evaluators/_multimodal/_sexual.py +6 -2
  39. azure/ai/evaluation/_evaluators/_multimodal/_violence.py +6 -2
  40. azure/ai/evaluation/_evaluators/_protected_material/_protected_material.py +57 -34
  41. azure/ai/evaluation/_evaluators/_qa/_qa.py +25 -37
  42. azure/ai/evaluation/_evaluators/_relevance/_relevance.py +63 -29
  43. azure/ai/evaluation/_evaluators/_retrieval/_retrieval.py +76 -161
  44. azure/ai/evaluation/_evaluators/_rouge/_rouge.py +24 -25
  45. azure/ai/evaluation/_evaluators/_service_groundedness/_service_groundedness.py +65 -67
  46. azure/ai/evaluation/_evaluators/_similarity/_similarity.py +26 -20
  47. azure/ai/evaluation/_evaluators/_xpia/xpia.py +74 -40
  48. azure/ai/evaluation/_exceptions.py +2 -0
  49. azure/ai/evaluation/_http_utils.py +6 -4
  50. azure/ai/evaluation/_model_configurations.py +65 -14
  51. azure/ai/evaluation/_vendor/rouge_score/rouge_scorer.py +0 -4
  52. azure/ai/evaluation/_vendor/rouge_score/scoring.py +0 -4
  53. azure/ai/evaluation/_vendor/rouge_score/tokenize.py +0 -4
  54. azure/ai/evaluation/_version.py +1 -1
  55. azure/ai/evaluation/simulator/_adversarial_scenario.py +17 -1
  56. azure/ai/evaluation/simulator/_adversarial_simulator.py +57 -47
  57. azure/ai/evaluation/simulator/_constants.py +11 -1
  58. azure/ai/evaluation/simulator/_conversation/__init__.py +128 -7
  59. azure/ai/evaluation/simulator/_conversation/_conversation.py +0 -1
  60. azure/ai/evaluation/simulator/_direct_attack_simulator.py +16 -8
  61. azure/ai/evaluation/simulator/_indirect_attack_simulator.py +12 -1
  62. azure/ai/evaluation/simulator/_model_tools/_identity_manager.py +3 -1
  63. azure/ai/evaluation/simulator/_model_tools/_rai_client.py +48 -4
  64. azure/ai/evaluation/simulator/_model_tools/_template_handler.py +1 -0
  65. azure/ai/evaluation/simulator/_simulator.py +54 -45
  66. azure/ai/evaluation/simulator/_utils.py +25 -7
  67. {azure_ai_evaluation-1.0.0b5.dist-info → azure_ai_evaluation-1.1.0.dist-info}/METADATA +240 -327
  68. {azure_ai_evaluation-1.0.0b5.dist-info → azure_ai_evaluation-1.1.0.dist-info}/RECORD +71 -68
  69. azure/ai/evaluation/_evaluators/_content_safety/_content_safety_chat.py +0 -322
  70. {azure_ai_evaluation-1.0.0b5.dist-info → azure_ai_evaluation-1.1.0.dist-info}/NOTICE.txt +0 -0
  71. {azure_ai_evaluation-1.0.0b5.dist-info → azure_ai_evaluation-1.1.0.dist-info}/WHEEL +0 -0
  72. {azure_ai_evaluation-1.0.0b5.dist-info → azure_ai_evaluation-1.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,3 @@
1
+ # ---------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # ---------------------------------------------------------
@@ -0,0 +1,188 @@
1
+ # ---------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # ---------------------------------------------------------
4
+
5
+ from logging import Logger
6
+ from typing import Any, Dict, Final, Optional, Set, Union, cast
7
+ from threading import Lock
8
+ from urllib.parse import quote
9
+ from json.decoder import JSONDecodeError
10
+
11
+ from azure.core.credentials import TokenCredential, AzureSasCredential
12
+ from azure.core.rest import HttpResponse
13
+ from azure.ai.evaluation._exceptions import ErrorBlame, ErrorCategory, ErrorTarget, EvaluationException
14
+ from azure.ai.evaluation._http_utils import HttpPipeline, get_http_client
15
+ from azure.ai.evaluation._azure._token_manager import AzureMLTokenManager
16
+ from azure.ai.evaluation.simulator._model_tools._identity_manager import TokenScope
17
+ from ._models import BlobStoreInfo, Workspace
18
+
19
+
20
+ API_VERSION: Final[str] = "2024-10-01"
21
+ QUERY_KEY_API_VERSION: Final[str] = "api-version"
22
+ PATH_ML_WORKSPACES = ("providers", "Microsoft.MachineLearningServices", "workspaces")
23
+
24
+
25
+ class LiteMLClient:
26
+ """A lightweight Azure ML API client.
27
+
28
+ :param subscription_id: Azure subscription ID
29
+ :type subscription_id: str
30
+ :param resource_group: Azure resource group name
31
+ :type resource_group: str
32
+ :param logger: Logger object
33
+ :type logger: logging.Logger
34
+ :keyword credential: Azure credentials
35
+ :paramtype credential: TokenCredential
36
+ :keyword kwargs: Additional keyword arguments
37
+ :paramtype kwargs: Dict
38
+ :keyword str api_version: The API version. Default is 2024-10-01
39
+ """
40
+
41
+ def __init__(
42
+ self,
43
+ subscription_id: str,
44
+ resource_group: str,
45
+ logger: Logger,
46
+ credential: Optional[TokenCredential] = None,
47
+ **kwargs: Any,
48
+ ) -> None:
49
+ subscription_id = quote(subscription_id, safe="")
50
+ resource_group = quote(resource_group, safe="")
51
+
52
+ self._base_url: Final[str] = (
53
+ f"https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/{resource_group}"
54
+ )
55
+ self._logger: Final[Logger] = logger
56
+ self._api_version: Final[str] = kwargs.get("api_version", API_VERSION)
57
+ self._http_client: Final[HttpPipeline] = get_http_client(**kwargs)
58
+ self._lock: Final[Lock] = Lock()
59
+
60
+ # things that can change under lock
61
+ self._token_manager: Optional[AzureMLTokenManager] = None
62
+ self._credential: Optional[TokenCredential] = credential
63
+
64
+ def get_token(self) -> str:
65
+ return self._get_token_manager().get_token()
66
+
67
+ def get_credential(self) -> TokenCredential:
68
+ # load the token manager to get the credential if needed
69
+ self._get_token_manager()
70
+ return cast(TokenCredential, self._credential)
71
+
72
+ def workspace_get_default_datastore(self, workspace_name: str, include_credentials: bool = False) -> BlobStoreInfo:
73
+ # 1. Get the default blob store
74
+ # REST API documentation:
75
+ # https://learn.microsoft.com/rest/api/azureml/datastores/list?view=rest-azureml-2024-10-01
76
+ url = self._generate_path( # pylint: disable=specify-parameter-names-in-call
77
+ *PATH_ML_WORKSPACES, workspace_name, "datastores"
78
+ )
79
+ headers = self._get_headers()
80
+
81
+ stores_response = self._http_client.request(
82
+ method="GET",
83
+ url=url,
84
+ params={QUERY_KEY_API_VERSION: self._api_version, "isDefault": True, "count": 1, "orderByAsc": "false"},
85
+ headers=headers,
86
+ )
87
+ self._throw_on_http_error(stores_response, "list default workspace datastore")
88
+
89
+ json = stores_response.json()["value"][0]
90
+ props_json = json["properties"]
91
+ name = json["name"]
92
+ account_name = props_json["accountName"]
93
+ endpoint = props_json["endpoint"]
94
+ container_name = props_json["containerName"]
95
+
96
+ # 2. Get the SAS token to use for accessing the blob store
97
+ # REST API documentation:
98
+ # https://learn.microsoft.com/rest/api/azureml/datastores/list-secrets?view=rest-azureml-2024-10-01
99
+ blob_store_credential: Optional[Union[AzureSasCredential, str]] = None
100
+ if include_credentials:
101
+ url = self._generate_path(
102
+ *PATH_ML_WORKSPACES, workspace_name, "datastores", "workspaceblobstore", "listSecrets"
103
+ )
104
+ secrets_response = self._http_client.request(
105
+ method="POST",
106
+ url=url,
107
+ params={
108
+ QUERY_KEY_API_VERSION: self._api_version,
109
+ },
110
+ headers=headers,
111
+ )
112
+ self._throw_on_http_error(secrets_response, "workspace datastore secrets")
113
+
114
+ secrets_json = secrets_response.json()
115
+ secrets_type = secrets_json["secretsType"].lower()
116
+
117
+ if secrets_type == "sas":
118
+ blob_store_credential = AzureSasCredential(secrets_json["sasToken"])
119
+ elif secrets_type == "accountkey":
120
+ # To support olders versions of azure-storage-blob better, we return a string here instead of
121
+ # an AzureNamedKeyCredential
122
+ blob_store_credential = secrets_json["key"]
123
+ else:
124
+ raise EvaluationException(
125
+ message=f"The '{account_name}' blob store does not use a recognized credential type.",
126
+ internal_message=f"The credential type is '{secrets_type}'",
127
+ target=ErrorTarget.EVALUATE,
128
+ category=ErrorCategory.INVALID_VALUE,
129
+ blame=ErrorBlame.SYSTEM_ERROR,
130
+ )
131
+
132
+ return BlobStoreInfo(name, account_name, endpoint, container_name, blob_store_credential)
133
+
134
+ def workspace_get_info(self, workspace_name: str) -> Workspace:
135
+ # https://learn.microsoft.com/rest/api/azureml/workspaces/get?view=rest-azureml-2024-10-01
136
+ workspace_response = self._http_client.request(
137
+ "GET",
138
+ self._generate_path(*PATH_ML_WORKSPACES, workspace_name),
139
+ params={QUERY_KEY_API_VERSION: self._api_version},
140
+ headers=self._get_headers(),
141
+ )
142
+
143
+ self._throw_on_http_error(workspace_response, f"get '{workspace_name}' workspace")
144
+ workspace = Workspace.deserialize(workspace_response)
145
+ return workspace
146
+
147
+ def _get_token_manager(self) -> AzureMLTokenManager:
148
+ # Lazy init since getting credentials in the constructor can take a long time in some situations
149
+ if self._token_manager is None:
150
+ with self._lock:
151
+ if self._token_manager is None:
152
+ self._token_manager = AzureMLTokenManager(
153
+ TokenScope.DEFAULT_AZURE_MANAGEMENT.value, self._logger, credential=self._credential
154
+ )
155
+ self._credential = self._token_manager.credential
156
+
157
+ return self._token_manager
158
+
159
+ @staticmethod
160
+ def _throw_on_http_error(response: HttpResponse, description: str, valid_status: Optional[Set[int]] = None) -> None:
161
+ if valid_status and (response.status_code in valid_status):
162
+ return
163
+ if response.status_code >= 200 and response.status_code < 300:
164
+ # nothing to see here, move along
165
+ return
166
+
167
+ additional_info: Optional[str] = None
168
+ try:
169
+ error_json = response.json()["error"]
170
+ additional_info = f"({error_json['code']}) {error_json['message']}"
171
+ except (JSONDecodeError, ValueError, KeyError):
172
+ pass
173
+
174
+ raise EvaluationException(
175
+ message=f"The {description} request failed with HTTP {response.status_code}",
176
+ target=ErrorTarget.EVALUATE,
177
+ category=ErrorCategory.FAILED_EXECUTION,
178
+ blame=ErrorBlame.SYSTEM_ERROR,
179
+ internal_message=additional_info,
180
+ )
181
+
182
+ def _generate_path(self, *paths: str) -> str:
183
+ sanitized_paths = [quote(path, safe="") for path in paths]
184
+ url = self._base_url + "/" + str.join("/", sanitized_paths)
185
+ return url
186
+
187
+ def _get_headers(self) -> Dict[str, str]:
188
+ return {"Authorization": f"Bearer {self.get_token()}", "Content-Type": "application/json"}
@@ -0,0 +1,227 @@
1
+ # ---------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # ---------------------------------------------------------
4
+
5
+ # pylint: disable=too-many-instance-attributes
6
+ # pylint: disable=too-many-locals
7
+ # pylint: disable=line-too-long
8
+
9
+ from typing import Dict, List, NamedTuple, Optional, Union
10
+ from msrest.serialization import Model
11
+ from azure.core.credentials import AzureSasCredential
12
+
13
+
14
+ class BlobStoreInfo(NamedTuple):
15
+ name: str
16
+ account_name: str
17
+ endpoint: str
18
+ container_name: str
19
+ credential: Optional[Union[AzureSasCredential, str]]
20
+
21
+
22
+ class WorkspaceHubConfig(Model):
23
+ """WorkspaceHub's configuration object."""
24
+
25
+ _attribute_map = {
26
+ "additional_workspace_storage_accounts": {"key": "additionalWorkspaceStorageAccounts", "type": "[str]"},
27
+ "default_workspace_resource_group": {"key": "defaultWorkspaceResourceGroup", "type": "str"},
28
+ }
29
+
30
+ def __init__(
31
+ self,
32
+ *,
33
+ additional_workspace_storage_accounts: Optional[List[str]] = None,
34
+ default_workspace_resource_group: Optional[str] = None,
35
+ **kwargs
36
+ ):
37
+ super(WorkspaceHubConfig, self).__init__(**kwargs)
38
+ self.additional_workspace_storage_accounts = additional_workspace_storage_accounts
39
+ self.default_workspace_resource_group = default_workspace_resource_group
40
+
41
+
42
+ class Workspace(Model):
43
+ """An object that represents a machine learning workspace.
44
+
45
+ Variables are only populated by the server, and will be ignored when sending a request."""
46
+
47
+ _validation = {
48
+ "id": {"readonly": True},
49
+ "name": {"readonly": True},
50
+ "type": {"readonly": True},
51
+ #'system_data': {'readonly': True},
52
+ "agents_endpoint_uri": {"readonly": True},
53
+ "ml_flow_tracking_uri": {"readonly": True},
54
+ #'notebook_info': {'readonly': True},
55
+ "private_endpoint_connections": {"readonly": True},
56
+ #'private_link_count': {'readonly': True},
57
+ "provisioning_state": {"readonly": True},
58
+ "service_provisioned_resource_group": {"readonly": True},
59
+ "storage_hns_enabled": {"readonly": True},
60
+ "tenant_id": {"readonly": True},
61
+ "workspace_id": {"readonly": True},
62
+ }
63
+
64
+ _attribute_map = {
65
+ "id": {"key": "id", "type": "str"},
66
+ "name": {"key": "name", "type": "str"},
67
+ "type": {"key": "type", "type": "str"},
68
+ #'system_data': {'key': 'systemData', 'type': 'SystemData'},
69
+ #'identity': {'key': 'identity', 'type': 'ManagedServiceIdentity'},
70
+ "kind": {"key": "kind", "type": "str"},
71
+ "location": {"key": "location", "type": "str"},
72
+ #'sku': {'key': 'sku', 'type': 'Sku'},
73
+ "tags": {"key": "tags", "type": "{str}"},
74
+ "agents_endpoint_uri": {"key": "properties.agentsEndpointUri", "type": "str"},
75
+ "allow_public_access_when_behind_vnet": {"key": "properties.allowPublicAccessWhenBehindVnet", "type": "bool"},
76
+ "allow_role_assignment_on_rg": {"key": "properties.allowRoleAssignmentOnRG", "type": "bool"},
77
+ "application_insights": {"key": "properties.applicationInsights", "type": "str"},
78
+ "associated_workspaces": {"key": "properties.associatedWorkspaces", "type": "[str]"},
79
+ "container_registries": {"key": "properties.containerRegistries", "type": "[str]"},
80
+ "container_registry": {"key": "properties.containerRegistry", "type": "str"},
81
+ "description": {"key": "properties.description", "type": "str"},
82
+ "discovery_url": {"key": "properties.discoveryUrl", "type": "str"},
83
+ "enable_data_isolation": {"key": "properties.enableDataIsolation", "type": "bool"},
84
+ "enable_service_side_cmk_encryption": {"key": "properties.enableServiceSideCMKEncryption", "type": "bool"},
85
+ "enable_simplified_cmk": {"key": "properties.enableSimplifiedCmk", "type": "bool"},
86
+ "enable_software_bill_of_materials": {"key": "properties.enableSoftwareBillOfMaterials", "type": "bool"},
87
+ #'encryption': {'key': 'properties.encryption', 'type': 'EncryptionProperty'},
88
+ "existing_workspaces": {"key": "properties.existingWorkspaces", "type": "[str]"},
89
+ #'feature_store_settings': {'key': 'properties.featureStoreSettings', 'type': 'FeatureStoreSettings'},
90
+ "friendly_name": {"key": "properties.friendlyName", "type": "str"},
91
+ "hbi_workspace": {"key": "properties.hbiWorkspace", "type": "bool"},
92
+ "hub_resource_id": {"key": "properties.hubResourceId", "type": "str"},
93
+ "image_build_compute": {"key": "properties.imageBuildCompute", "type": "str"},
94
+ "ip_allowlist": {"key": "properties.ipAllowlist", "type": "[str]"},
95
+ "key_vault": {"key": "properties.keyVault", "type": "str"},
96
+ "key_vaults": {"key": "properties.keyVaults", "type": "[str]"},
97
+ #'managed_network': {'key': 'properties.managedNetwork', 'type': 'ManagedNetworkSettings'},
98
+ "ml_flow_tracking_uri": {"key": "properties.mlFlowTrackingUri", "type": "str"},
99
+ #'network_acls': {'key': 'properties.networkAcls', 'type': 'NetworkAcls'},
100
+ #'notebook_info': {'key': 'properties.notebookInfo', 'type': 'NotebookResourceInfo'},
101
+ "primary_user_assigned_identity": {"key": "properties.primaryUserAssignedIdentity", "type": "str"},
102
+ "private_endpoint_connections": {
103
+ "key": "properties.privateEndpointConnections",
104
+ "type": "[PrivateEndpointConnection]",
105
+ },
106
+ "private_link_count": {"key": "properties.privateLinkCount", "type": "int"},
107
+ "provision_network_now": {"key": "properties.provisionNetworkNow", "type": "bool"},
108
+ "provisioning_state": {"key": "properties.provisioningState", "type": "str"},
109
+ #'public_network_access': {'key': 'properties.publicNetworkAccess', 'type': 'str'},
110
+ #'serverless_compute_settings': {'key': 'properties.serverlessComputeSettings', 'type': 'ServerlessComputeSettings'},
111
+ #'service_managed_resources_settings': {'key': 'properties.serviceManagedResourcesSettings', 'type': 'ServiceManagedResourcesSettings'},
112
+ "service_provisioned_resource_group": {"key": "properties.serviceProvisionedResourceGroup", "type": "str"},
113
+ #'shared_private_link_resources': {'key': 'properties.sharedPrivateLinkResources', 'type': '[SharedPrivateLinkResource]'},
114
+ "soft_delete_retention_in_days": {"key": "properties.softDeleteRetentionInDays", "type": "int"},
115
+ "storage_account": {"key": "properties.storageAccount", "type": "str"},
116
+ "storage_accounts": {"key": "properties.storageAccounts", "type": "[str]"},
117
+ "storage_hns_enabled": {"key": "properties.storageHnsEnabled", "type": "bool"},
118
+ #'system_datastores_auth_mode': {'key': 'properties.systemDatastoresAuthMode', 'type': 'str'},
119
+ "tenant_id": {"key": "properties.tenantId", "type": "str"},
120
+ "v1_legacy_mode": {"key": "properties.v1LegacyMode", "type": "bool"},
121
+ "workspace_hub_config": {"key": "properties.workspaceHubConfig", "type": "WorkspaceHubConfig"},
122
+ "workspace_id": {"key": "properties.workspaceId", "type": "str"},
123
+ }
124
+
125
+ def __init__(
126
+ self,
127
+ *,
128
+ # system_data: Optional[SystemData] = None,
129
+ # identity: Optional["ManagedServiceIdentity"] = None,
130
+ kind: Optional[str] = None,
131
+ location: Optional[str] = None,
132
+ # sku: Optional["Sku"] = None,
133
+ tags: Optional[Dict[str, str]] = None,
134
+ allow_public_access_when_behind_vnet: Optional[bool] = None,
135
+ allow_role_assignment_on_rg: Optional[bool] = None,
136
+ application_insights: Optional[str] = None,
137
+ associated_workspaces: Optional[List[str]] = None,
138
+ container_registries: Optional[List[str]] = None,
139
+ container_registry: Optional[str] = None,
140
+ description: Optional[str] = None,
141
+ discovery_url: Optional[str] = None,
142
+ enable_data_isolation: Optional[bool] = None,
143
+ enable_service_side_cmk_encryption: Optional[bool] = None,
144
+ enable_simplified_cmk: Optional[bool] = None,
145
+ enable_software_bill_of_materials: Optional[bool] = None,
146
+ # encryption: Optional["EncryptionProperty"] = None,
147
+ existing_workspaces: Optional[List[str]] = None,
148
+ # feature_store_settings: Optional["FeatureStoreSettings"] = None,
149
+ friendly_name: Optional[str] = None,
150
+ hbi_workspace: Optional[bool] = None,
151
+ hub_resource_id: Optional[str] = None,
152
+ image_build_compute: Optional[str] = None,
153
+ ip_allowlist: Optional[List[str]] = None,
154
+ key_vault: Optional[str] = None,
155
+ key_vaults: Optional[List[str]] = None,
156
+ # managed_network: Optional["ManagedNetworkSettings"] = None,
157
+ # network_acls: Optional["NetworkAcls"] = None,
158
+ primary_user_assigned_identity: Optional[str] = None,
159
+ provision_network_now: Optional[bool] = None,
160
+ # public_network_access: Optional[Union[str, "PublicNetworkAccessType"]] = None,
161
+ # serverless_compute_settings: Optional["ServerlessComputeSettings"] = None,
162
+ # service_managed_resources_settings: Optional["ServiceManagedResourcesSettings"] = None,
163
+ # shared_private_link_resources: Optional[List["SharedPrivateLinkResource"]] = None,
164
+ soft_delete_retention_in_days: Optional[int] = None,
165
+ storage_account: Optional[str] = None,
166
+ storage_accounts: Optional[List[str]] = None,
167
+ # system_datastores_auth_mode: Optional[Union[str, "SystemDatastoresAuthMode"]] = None,
168
+ v1_legacy_mode: Optional[bool] = None,
169
+ workspace_hub_config: Optional["WorkspaceHubConfig"] = None,
170
+ **kwargs
171
+ ):
172
+ super(Workspace, self).__init__(**kwargs)
173
+ self.id: Optional[str] = None
174
+ self.name: Optional[str] = None
175
+ self.type: Optional[str] = None
176
+ # self.system_data = system_data
177
+ # self.identity = identity
178
+ self.kind = kind
179
+ self.location = location
180
+ # self.sku = sku
181
+ self.tags = tags
182
+ self.agents_endpoint_uri = None
183
+ self.allow_public_access_when_behind_vnet = allow_public_access_when_behind_vnet
184
+ self.allow_role_assignment_on_rg = allow_role_assignment_on_rg
185
+ self.application_insights = application_insights
186
+ self.associated_workspaces = associated_workspaces
187
+ self.container_registries = container_registries
188
+ self.container_registry = container_registry
189
+ self.description = description
190
+ self.discovery_url = discovery_url
191
+ self.enable_data_isolation = enable_data_isolation
192
+ self.enable_service_side_cmk_encryption = enable_service_side_cmk_encryption
193
+ self.enable_simplified_cmk = enable_simplified_cmk
194
+ self.enable_software_bill_of_materials = enable_software_bill_of_materials
195
+ # self.encryption = encryption
196
+ self.existing_workspaces = existing_workspaces
197
+ # self.feature_store_settings = feature_store_settings
198
+ self.friendly_name = friendly_name
199
+ self.hbi_workspace = hbi_workspace
200
+ self.hub_resource_id = hub_resource_id
201
+ self.image_build_compute = image_build_compute
202
+ self.ip_allowlist = ip_allowlist
203
+ self.key_vault = key_vault
204
+ self.key_vaults = key_vaults
205
+ # self.managed_network = managed_network
206
+ self.ml_flow_tracking_uri = None
207
+ # self.network_acls = network_acls
208
+ # self.notebook_info = None
209
+ self.primary_user_assigned_identity = primary_user_assigned_identity
210
+ self.private_endpoint_connections = None
211
+ self.private_link_count = None
212
+ self.provision_network_now = provision_network_now
213
+ self.provisioning_state = None
214
+ # self.public_network_access = public_network_access
215
+ # self.serverless_compute_settings = serverless_compute_settings
216
+ # self.service_managed_resources_settings = service_managed_resources_settings
217
+ self.service_provisioned_resource_group = None
218
+ # self.shared_private_link_resources = shared_private_link_resources
219
+ self.soft_delete_retention_in_days = soft_delete_retention_in_days
220
+ self.storage_account = storage_account
221
+ self.storage_accounts = storage_accounts
222
+ self.storage_hns_enabled = None
223
+ # self.system_datastores_auth_mode = system_datastores_auth_mode
224
+ self.tenant_id = None
225
+ self.v1_legacy_mode = v1_legacy_mode
226
+ self.workspace_hub_config = workspace_hub_config
227
+ self.workspace_id = None
@@ -0,0 +1,118 @@
1
+ # ---------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # ---------------------------------------------------------
4
+ import os
5
+ import logging
6
+ import time
7
+ import inspect
8
+ from typing import cast, Optional, Union
9
+
10
+ from azure.core.credentials import TokenCredential, AccessToken
11
+ from azure.identity import AzureCliCredential, DefaultAzureCredential, ManagedIdentityCredential
12
+ from azure.ai.evaluation._exceptions import ErrorBlame, ErrorCategory, ErrorTarget, EvaluationException
13
+ from ..simulator._model_tools._identity_manager import APITokenManager, AZURE_TOKEN_REFRESH_INTERVAL
14
+
15
+
16
+ class AzureMLTokenManager(APITokenManager):
17
+ """API Token manager for Azure Management API.
18
+
19
+ :param token_scope: Token scopes for Azure endpoint
20
+ :type token_scope: str
21
+ :param logger: Logger object
22
+ :type logger: logging.Logger
23
+ :keyword kwargs: Additional keyword arguments
24
+ :paramtype kwargs: Dict
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ token_scope: str,
30
+ logger: logging.Logger,
31
+ credential: Optional[TokenCredential] = None,
32
+ ):
33
+ super().__init__(logger, credential=credential)
34
+ self.token_scope = token_scope
35
+ self.token_expiry_time: Optional[int] = None
36
+
37
+ def get_aad_credential(self) -> Union[DefaultAzureCredential, ManagedIdentityCredential]:
38
+ """Get the Azure credentials to use for the management APIs.
39
+
40
+ :return: Azure credentials
41
+ :rtype: DefaultAzureCredential or ManagedIdentityCredential
42
+ """
43
+ # Adds some of the additional types credentials that the previous Azure AI ML code used
44
+ # These may or may not be needed but kept here for backwards compatibility
45
+
46
+ if os.getenv("AZUREML_OBO_ENABLED"):
47
+ # using Azure on behalf of credentials requires the use of the azure-ai-ml package
48
+ try:
49
+ from azure.ai.ml.identity import AzureMLOnBehalfOfCredential
50
+
51
+ self.logger.debug("User identity is configured, use OBO credential.")
52
+ return AzureMLOnBehalfOfCredential() # type: ignore
53
+ except (ModuleNotFoundError, ImportError):
54
+ raise EvaluationException( # pylint: disable=raise-missing-from
55
+ message=(
56
+ "The required packages for OBO credentials are missing.\n"
57
+ 'To resolve this, please install them by running "pip install azure-ai-ml".'
58
+ ),
59
+ target=ErrorTarget.EVALUATE,
60
+ category=ErrorCategory.MISSING_PACKAGE,
61
+ blame=ErrorBlame.USER_ERROR,
62
+ )
63
+ elif os.environ.get("PF_USE_AZURE_CLI_CREDENTIAL", "false").lower() == "true":
64
+ self.logger.debug("Use azure cli credential since specified in environment variable.")
65
+ return AzureCliCredential() # type: ignore
66
+ elif os.environ.get("IS_IN_CI_PIPELINE", "false").lower() == "true":
67
+ # use managed identity when executing in CI pipeline.
68
+ self.logger.debug("Use azure cli credential since in CI pipeline.")
69
+ return AzureCliCredential() # type: ignore
70
+ else:
71
+ # Fall back to using the parent implementation
72
+ return super().get_aad_credential()
73
+
74
+ def get_token(self) -> str:
75
+ """Get the API token. If the token is not available or has expired, refresh the token.
76
+
77
+ :return: API token
78
+ :rtype: str
79
+ """
80
+ if self._token_needs_update():
81
+ credential = cast(TokenCredential, self.credential)
82
+ access_token = credential.get_token(self.token_scope)
83
+ self._update_token(access_token)
84
+
85
+ return cast(str, self.token) # check for none is hidden in the _token_needs_update method
86
+
87
+ async def get_token_async(self) -> str:
88
+ """Get the API token asynchronously. If the token is not available or has expired, refresh it.
89
+
90
+ :return: API token
91
+ :rtype: str
92
+ """
93
+ if self._token_needs_update():
94
+ credential = cast(TokenCredential, self.credential)
95
+ get_token_method = credential.get_token(self.token_scope)
96
+ if inspect.isawaitable(get_token_method):
97
+ access_token = await get_token_method
98
+ else:
99
+ access_token = get_token_method
100
+ self._update_token(access_token)
101
+
102
+ return cast(str, self.token) # check for none is hidden in the _token_needs_update method
103
+
104
+ def _token_needs_update(self) -> bool:
105
+ current_time = time.time()
106
+ return (
107
+ self.token is None
108
+ or self.last_refresh_time is None
109
+ or self.token_expiry_time is None
110
+ or self.token_expiry_time - current_time < AZURE_TOKEN_REFRESH_INTERVAL
111
+ or current_time - self.last_refresh_time > AZURE_TOKEN_REFRESH_INTERVAL
112
+ )
113
+
114
+ def _update_token(self, access_token: AccessToken) -> None:
115
+ self.token = cast(str, access_token.token)
116
+ self.token_expiry_time = access_token.expires_on
117
+ self.last_refresh_time = time.time()
118
+ self.logger.info("Refreshed Azure management token.")
@@ -2,6 +2,7 @@
2
2
  # Copyright (c) Microsoft Corporation. All rights reserved.
3
3
  # ---------------------------------------------------------
4
4
 
5
+ import os
5
6
  import functools
6
7
  import inspect
7
8
  import logging
@@ -149,6 +150,9 @@ def _get_indentation_size(doc_string: str) -> int:
149
150
  def _should_skip_warning():
150
151
  skip_warning_msg = False
151
152
 
153
+ if os.getenv("AI_EVALS_DISABLE_EXPERIMENTAL_WARNING", "false").lower() == "true":
154
+ skip_warning_msg = True
155
+
152
156
  # Cases where we want to suppress the warning:
153
157
  # 1. When converting from REST object to SDK object
154
158
  for frame in inspect.stack():
@@ -3,20 +3,44 @@
3
3
  # ---------------------------------------------------------
4
4
 
5
5
  import math
6
- from typing import List
6
+ from typing import List, Callable, Any
7
7
 
8
8
  from azure.ai.evaluation._exceptions import EvaluationException, ErrorBlame, ErrorCategory, ErrorTarget
9
9
 
10
10
 
11
11
  def list_sum(lst: List[float]) -> float:
12
+ """Given a list of floats, return the sum of the values.
13
+
14
+ :param lst: A list of floats.
15
+ :type lst: List[float]
16
+ :return: The sum of the values in the list.
17
+ :rtype: float
18
+ """
19
+
12
20
  return sum(lst)
13
21
 
14
22
 
15
23
  def list_mean(lst: List[float]) -> float:
24
+ """Given a list of floats, calculate the mean of the values.
25
+
26
+ :param lst: A list of floats.
27
+ :type lst: List[float]
28
+ :return: The mean of the values in the list.
29
+ :rtype: float
30
+ """
31
+
16
32
  return list_sum(lst) / len(lst)
17
33
 
18
34
 
19
35
  def list_mean_nan_safe(lst: List[float]) -> float:
36
+ """Given a list of floats, remove all nan or None values, then calculate the mean of the remaining values.
37
+
38
+ :param lst: A list of floats.
39
+ :type lst: List[float]
40
+ :return: The mean of the values in the list.
41
+ :rtype: float
42
+ """
43
+
20
44
  msg = "All score values are NaN. The mean cannot be calculated."
21
45
  if all(math.isnan(l) for l in lst):
22
46
  raise EvaluationException(
@@ -26,4 +50,40 @@ def list_mean_nan_safe(lst: List[float]) -> float:
26
50
  category=ErrorCategory.INVALID_VALUE,
27
51
  target=ErrorTarget.CONVERSATION,
28
52
  )
29
- return list_mean([l for l in lst if not math.isnan(l)])
53
+ return list_mean([l for l in lst if not is_none_or_nan(l)])
54
+
55
+
56
+ def apply_transform_nan_safe(lst: List[float], transform_fn: Callable[[float], Any]) -> List[Any]:
57
+ """Given a list of floats, remove all nan values, then apply the inputted transform function
58
+ to the remaining values, and return the resulting list of outputted values.
59
+
60
+ :param lst: A list of floats.
61
+ :type lst: List[float]
62
+ :param transform_fn: A function that produces something when applied to a float.
63
+ :type transform_fn: Callable[[float], Any]
64
+ :return: A list of the transformed values.
65
+ :rtype: List[Any]
66
+ """
67
+
68
+ msg = "All score values are NaN. The mean cannot be calculated."
69
+ if all(math.isnan(l) for l in lst):
70
+ raise EvaluationException(
71
+ message=msg,
72
+ internal_message=msg,
73
+ blame=ErrorBlame.USER_ERROR,
74
+ category=ErrorCategory.INVALID_VALUE,
75
+ target=ErrorTarget.CONVERSATION,
76
+ )
77
+ return [transform_fn(l) for l in lst if not is_none_or_nan(l)]
78
+
79
+
80
+ def is_none_or_nan(val: float) -> bool:
81
+ """math.isnan raises an error if None is inputted. This is a more robust wrapper.
82
+
83
+ :param val: The value to check.
84
+ :type val: float
85
+ :return: Whether the value is None or NaN.
86
+ :rtype: bool
87
+ """
88
+
89
+ return val is None or math.isnan(val)