Skip to content

Commit cb05cfe

Browse files
authored
[evaluation] feat: Configurable credentials for aoai graders (#43026)
* feat: Add credential support to graders * style: Sort imports * test: Add test validating that graders can be evaluated with a TokenCredential * fix: Resolve inverted validation logic for auth * tests,fix: Update exception message * docs: Add update docstring * chore: Update CHANGELOG.md
1 parent 724ba33 commit cb05cfe

File tree

9 files changed

+234
-40
lines changed

9 files changed

+234
-40
lines changed

sdk/evaluation/azure-ai-evaluation/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### Features Added
66

7+
- AOAI Graders now accept a "credential" parameter that can be used for authentication with an AzureOpenAIModelConfiguration
8+
79
### Breaking Changes
810

911
### Bugs Fixed

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_aoai/aoai_grader.py

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
# ---------------------------------------------------------
22
# Copyright (c) Microsoft Corporation. All rights reserved.
33
# ---------------------------------------------------------
4-
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
4+
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
5+
6+
from typing_extensions import TypeIs
57

6-
from azure.ai.evaluation._constants import DEFAULT_AOAI_API_VERSION
8+
from azure.ai.evaluation._common._experimental import experimental
9+
from azure.ai.evaluation._constants import DEFAULT_AOAI_API_VERSION, TokenScope
710
from azure.ai.evaluation._exceptions import ErrorBlame, ErrorCategory, ErrorTarget, EvaluationException
11+
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
812
from azure.ai.evaluation._user_agent import UserAgentSingleton
9-
from typing import Any, Dict, Union
10-
from azure.ai.evaluation._common._experimental import experimental
13+
from azure.core.credentials import TokenCredential
14+
15+
if TYPE_CHECKING:
16+
from openai.lib.azure import AzureADTokenProvider
1117

1218

1319
@experimental
@@ -30,6 +36,8 @@ class AzureOpenAIGrader:
3036
to be formatted as a dictionary that matches the specifications of the sub-types of
3137
the TestingCriterion alias specified in (OpenAI's SDK)[https://github.com/openai/openai-python/blob/ed53107e10e6c86754866b48f8bd862659134ca8/src/openai/types/eval_create_params.py#L151].
3238
:type grader_config: Dict[str, Any]
39+
:param credential: The credential to use to authenticate to the model. Only applicable to AzureOpenAI models.
40+
:type credential: ~azure.core.credentials.TokenCredential
3341
:param kwargs: Additional keyword arguments to pass to the grader.
3442
:type kwargs: Any
3543
@@ -43,31 +51,52 @@ def __init__(
4351
*,
4452
model_config: Union[AzureOpenAIModelConfiguration, OpenAIModelConfiguration],
4553
grader_config: Dict[str, Any],
54+
credential: Optional[TokenCredential] = None,
4655
**kwargs: Any,
4756
):
4857
self._model_config = model_config
4958
self._grader_config = grader_config
59+
self._credential = credential
5060

5161
if kwargs.get("validate", True):
5262
self._validate_model_config()
5363
self._validate_grader_config()
5464

5565
def _validate_model_config(self) -> None:
5666
"""Validate the model configuration that this grader wrapper is using."""
57-
if "api_key" not in self._model_config or not self._model_config.get("api_key"):
58-
msg = f"{type(self).__name__}: Requires an api_key in the supplied model_config."
59-
raise EvaluationException(
60-
message=msg,
61-
blame=ErrorBlame.USER_ERROR,
62-
category=ErrorCategory.INVALID_VALUE,
63-
target=ErrorTarget.AOAI_GRADER,
64-
)
67+
msg = None
68+
if self._is_azure_model_config(self._model_config):
69+
if not any(auth for auth in (self._model_config.get("api_key"), self._credential)):
70+
msg = (
71+
f"{type(self).__name__}: Requires an api_key in the supplied model_config, "
72+
+ "or providing a credential to the grader's __init__ method. "
73+
)
74+
75+
else:
76+
if "api_key" not in self._model_config or not self._model_config.get("api_key"):
77+
msg = f"{type(self).__name__}: Requires an api_key in the supplied model_config."
78+
79+
if msg is None:
80+
return
81+
82+
raise EvaluationException(
83+
message=msg,
84+
blame=ErrorBlame.USER_ERROR,
85+
category=ErrorCategory.INVALID_VALUE,
86+
target=ErrorTarget.AOAI_GRADER,
87+
)
6588

6689
def _validate_grader_config(self) -> None:
6790
"""Validate the grader configuration that this grader wrapper is using."""
6891

6992
return
7093

94+
@staticmethod
95+
def _is_azure_model_config(
96+
model_config: Union[AzureOpenAIModelConfiguration, OpenAIModelConfiguration],
97+
) -> TypeIs[AzureOpenAIModelConfiguration]:
98+
return "azure_endpoint" in model_config
99+
71100
def get_client(self) -> Any:
72101
"""Construct an appropriate OpenAI client using this grader's model configuration.
73102
Returns a slightly different client depending on whether or not this grader's model
@@ -77,23 +106,38 @@ def get_client(self) -> Any:
77106
:rtype: [~openai.OpenAI, ~openai.AzureOpenAI]
78107
"""
79108
default_headers = {"User-Agent": UserAgentSingleton().value}
80-
if "azure_endpoint" in self._model_config:
109+
model_config: Union[AzureOpenAIModelConfiguration, OpenAIModelConfiguration] = self._model_config
110+
api_key: Optional[str] = model_config.get("api_key")
111+
112+
if self._is_azure_model_config(model_config):
81113
from openai import AzureOpenAI
82114

83115
# TODO set default values?
84116
return AzureOpenAI(
85-
azure_endpoint=self._model_config["azure_endpoint"],
86-
api_key=self._model_config.get("api_key", None), # Default-style access to appease linters.
117+
azure_endpoint=model_config["azure_endpoint"],
118+
api_key=api_key, # Default-style access to appease linters.
87119
api_version=DEFAULT_AOAI_API_VERSION, # Force a known working version
88-
azure_deployment=self._model_config.get("azure_deployment", ""),
120+
azure_deployment=model_config.get("azure_deployment", ""),
121+
azure_ad_token_provider=self.get_token_provider(self._credential) if not api_key else None,
89122
default_headers=default_headers,
90123
)
91124
from openai import OpenAI
92125

93126
# TODO add default values for base_url and organization?
94127
return OpenAI(
95-
api_key=self._model_config["api_key"],
96-
base_url=self._model_config.get("base_url", ""),
97-
organization=self._model_config.get("organization", ""),
128+
api_key=api_key,
129+
base_url=model_config.get("base_url", ""),
130+
organization=model_config.get("organization", ""),
98131
default_headers=default_headers,
99132
)
133+
134+
@staticmethod
135+
def get_token_provider(cred: TokenCredential) -> "AzureADTokenProvider":
136+
"""Get the token provider the AzureOpenAI client.
137+
138+
:param TokenCredential cred: The Azure authentication credential.
139+
:return: The token provider if a credential is provided, otherwise None.
140+
:rtype: openai.lib.azure.AzureADTokenProvider
141+
"""
142+
143+
return lambda: cred.get_token(TokenScope.COGNITIVE_SERVICES_MANAGEMENT).token

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_aoai/label_grader.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# ---------------------------------------------------------
22
# Copyright (c) Microsoft Corporation. All rights reserved.
33
# ---------------------------------------------------------
4-
from typing import Any, Dict, Union, List
4+
from typing import Any, Dict, List, Optional, Union
55

6-
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
76
from openai.types.graders import LabelModelGrader
7+
88
from azure.ai.evaluation._common._experimental import experimental
9+
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
10+
from azure.core.credentials import TokenCredential
911

1012
from .aoai_grader import AzureOpenAIGrader
1113

@@ -37,6 +39,8 @@ class AzureOpenAILabelGrader(AzureOpenAIGrader):
3739
:type name: str
3840
:param passing_labels: The labels that indicate a passing result. Must be a subset of labels.
3941
:type passing_labels: List[str]
42+
:param credential: The credential to use to authenticate to the model. Only applicable to AzureOpenAI models.
43+
:type credential: ~azure.core.credentials.TokenCredential
4044
:param kwargs: Additional keyword arguments to pass to the grader.
4145
:type kwargs: Any
4246
@@ -54,6 +58,7 @@ def __init__(
5458
model: str,
5559
name: str,
5660
passing_labels: List[str],
61+
credential: Optional[TokenCredential] = None,
5762
**kwargs: Any
5863
):
5964
grader = LabelModelGrader(
@@ -64,4 +69,4 @@ def __init__(
6469
passing_labels=passing_labels,
6570
type="label_model",
6671
)
67-
super().__init__(model_config=model_config, grader_config=grader, **kwargs)
72+
super().__init__(model_config=model_config, grader_config=grader, credential=credential, **kwargs)

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_aoai/python_grader.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# ---------------------------------------------------------
22
# Copyright (c) Microsoft Corporation. All rights reserved.
33
# ---------------------------------------------------------
4-
from typing import Any, Dict, Union, Optional
4+
from typing import Any, Dict, Optional, Union
55

6-
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
76
from openai.types.graders import PythonGrader
7+
88
from azure.ai.evaluation._common._experimental import experimental
9+
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
10+
from azure.core.credentials import TokenCredential
911

1012
from .aoai_grader import AzureOpenAIGrader
1113

@@ -39,6 +41,8 @@ class AzureOpenAIPythonGrader(AzureOpenAIGrader):
3941
:param source: Python source code containing the grade function.
4042
Must define: def grade(sample: dict, item: dict) -> float
4143
:type source: str
44+
:param credential: The credential to use to authenticate to the model. Only applicable to AzureOpenAI models.
45+
:type credential: ~azure.core.credentials.TokenCredential
4246
:param kwargs: Additional keyword arguments to pass to the grader.
4347
:type kwargs: Any
4448
@@ -63,6 +67,7 @@ def __init__(
6367
image_tag: str,
6468
pass_threshold: float,
6569
source: str,
70+
credential: Optional[TokenCredential] = None,
6671
**kwargs: Any,
6772
):
6873
# Validate pass_threshold
@@ -81,4 +86,4 @@ def __init__(
8186
type="python",
8287
)
8388

84-
super().__init__(model_config=model_config, grader_config=grader, **kwargs)
89+
super().__init__(model_config=model_config, grader_config=grader, credential=credential, **kwargs)

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_aoai/score_model_grader.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# ---------------------------------------------------------
22
# Copyright (c) Microsoft Corporation. All rights reserved.
33
# ---------------------------------------------------------
4-
from typing import Any, Dict, Union, List, Optional
4+
from typing import Any, Dict, List, Optional, Union
55

6-
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
76
from openai.types.graders import ScoreModelGrader
7+
88
from azure.ai.evaluation._common._experimental import experimental
9+
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
10+
from azure.core.credentials import TokenCredential
911

1012
from .aoai_grader import AzureOpenAIGrader
1113

@@ -43,6 +45,8 @@ class AzureOpenAIScoreModelGrader(AzureOpenAIGrader):
4345
:type pass_threshold: Optional[float]
4446
:param sampling_params: The sampling parameters for the model.
4547
:type sampling_params: Optional[Dict[str, Any]]
48+
:param credential: The credential to use to authenticate to the model. Only applicable to AzureOpenAI models.
49+
:type credential: ~azure.core.credentials.TokenCredential
4650
:param kwargs: Additional keyword arguments to pass to the grader.
4751
:type kwargs: Any
4852
"""
@@ -59,6 +63,7 @@ def __init__(
5963
range: Optional[List[float]] = None,
6064
pass_threshold: Optional[float] = None,
6165
sampling_params: Optional[Dict[str, Any]] = None,
66+
credential: Optional[TokenCredential] = None,
6267
**kwargs: Any,
6368
):
6469
# Validate range and pass_threshold
@@ -88,4 +93,4 @@ def __init__(
8893

8994
grader = ScoreModelGrader(**grader_kwargs)
9095

91-
super().__init__(model_config=model_config, grader_config=grader, **kwargs)
96+
super().__init__(model_config=model_config, grader_config=grader, credential=credential, **kwargs)

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_aoai/string_check_grader.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
# ---------------------------------------------------------
22
# Copyright (c) Microsoft Corporation. All rights reserved.
33
# ---------------------------------------------------------
4-
from typing import Any, Dict, Union
5-
from typing_extensions import Literal
4+
from typing import Any, Dict, Optional, Union
65

7-
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
86
from openai.types.graders import StringCheckGrader
7+
from typing_extensions import Literal
8+
99
from azure.ai.evaluation._common._experimental import experimental
10+
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
11+
from azure.core.credentials import TokenCredential
1012

1113
from .aoai_grader import AzureOpenAIGrader
1214

@@ -33,6 +35,8 @@ class AzureOpenAIStringCheckGrader(AzureOpenAIGrader):
3335
:type operation: Literal["eq", "ne", "like", "ilike"]
3436
:param reference: The reference text. This may include template strings.
3537
:type reference: str
38+
:param credential: The credential to use to authenticate to the model. Only applicable to AzureOpenAI models.
39+
:type credential: ~azure.core.credentials.TokenCredential
3640
:param kwargs: Additional keyword arguments to pass to the grader.
3741
:type kwargs: Any
3842
@@ -54,6 +58,7 @@ def __init__(
5458
"ilike",
5559
],
5660
reference: str,
61+
credential: Optional[TokenCredential] = None,
5762
**kwargs: Any
5863
):
5964
grader = StringCheckGrader(
@@ -63,4 +68,4 @@ def __init__(
6368
reference=reference,
6469
type="string_check",
6570
)
66-
super().__init__(model_config=model_config, grader_config=grader, **kwargs)
71+
super().__init__(model_config=model_config, grader_config=grader, credential=credential, **kwargs)

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_aoai/text_similarity_grader.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
# ---------------------------------------------------------
22
# Copyright (c) Microsoft Corporation. All rights reserved.
33
# ---------------------------------------------------------
4-
from typing import Any, Dict, Union
5-
from typing_extensions import Literal
4+
from typing import Any, Dict, Optional, Union
65

7-
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
86
from openai.types.graders import TextSimilarityGrader
7+
from typing_extensions import Literal
8+
99
from azure.ai.evaluation._common._experimental import experimental
10+
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
11+
from azure.core.credentials import TokenCredential
1012

1113
from .aoai_grader import AzureOpenAIGrader
1214

@@ -47,6 +49,8 @@ class AzureOpenAITextSimilarityGrader(AzureOpenAIGrader):
4749
:type reference: str
4850
:param name: The name of the grader.
4951
:type name: str
52+
:param credential: The credential to use to authenticate to the model. Only applicable to AzureOpenAI models.
53+
:type credential: ~azure.core.credentials.TokenCredential
5054
:param kwargs: Additional keyword arguments to pass to the grader.
5155
:type kwargs: Any
5256
@@ -76,6 +80,7 @@ def __init__(
7680
pass_threshold: float,
7781
reference: str,
7882
name: str,
83+
credential: Optional[TokenCredential] = None,
7984
**kwargs: Any
8085
):
8186
grader = TextSimilarityGrader(
@@ -86,4 +91,4 @@ def __init__(
8691
reference=reference,
8792
type="text_similarity",
8893
)
89-
super().__init__(model_config=model_config, grader_config=grader, **kwargs)
94+
super().__init__(model_config=model_config, grader_config=grader, credential=credential, **kwargs)

0 commit comments

Comments
 (0)