Skip to content

Commit f4f403f

Browse files
w-javedazure-sdkweshaggardnagkumar91Nagkumar Arkalgud
authored andcommitted
Multi-Modal-Content-Safety-Evaluators (Azure#38002)
* Initial-Commit-multimodal * Fix * Sync eng/common directory with azure-sdk-tools for PR 9092 (Azure#37713) * Export the subscription data from the service connection * Update deploy-test-resources.yml --------- Co-authored-by: Wes Haggard <[email protected]> Co-authored-by: Wes Haggard <[email protected]> * Removing private parameter from __call__ of AdversarialSimulator (Azure#37709) * Update task_query_response.prompty remove required keys * Update task_simulate.prompty * Update task_query_response.prompty * Update task_simulate.prompty * Remove private variable and use kwargs * Add experimental tag to adv sim --------- Co-authored-by: Nagkumar Arkalgud <[email protected]> * Enabling option to disable response payload on writes (Azure#37365) * Initial draft * Adding tests * Renaming parameter * Update container.py * Renaming test file * Fixing LINT issues * Update container.py * Update _base.py * Update _base.py * Fixing tests * Fixing tests * Adding support to disable response payload on write for AIO * Update CHANGELOG.md * Update _cosmos_client.py * Reacting to code review comments * Addressing code review feedback * Addressed CR feedback * Fixing pyLint errors * Fixing pylint errors * Update test_crud.py * Fixing svc regression * Update sdk/cosmos/azure-cosmos/azure/cosmos/aio/_container.py Co-authored-by: Anna Tisch <[email protected]> * Reacting to code review feedback. * Update container.py * Update test_query_vector_similarity.py --------- Co-authored-by: Anna Tisch <[email protected]> * deprecate azure_germany (Azure#37654) * deprecate azure_germany * update * update * Update sdk/identity/azure-identity/azure/identity/_constants.py Co-authored-by: Paul Van Eck <[email protected]> * update --------- Co-authored-by: Paul Van Eck <[email protected]> * Add default impl to handle token challenges (Azure#37652) * Add default impl to handle token challenges * update version * update * update * update * update * Update sdk/core/azure-core/azure/core/pipeline/policies/_utils.py Co-authored-by: Paul Van Eck <[email protected]> * Update sdk/core/azure-core/azure/core/pipeline/policies/_utils.py Co-authored-by: Paul Van Eck <[email protected]> * update * Update sdk/core/azure-core/tests/test_utils.py Co-authored-by: Paul Van Eck <[email protected]> * Update sdk/core/azure-core/azure/core/pipeline/policies/_utils.py Co-authored-by: Paul Van Eck <[email protected]> * update --------- Co-authored-by: Paul Van Eck <[email protected]> * Make Credentials Required for Content Safety and Protected Materials Evaluators (Azure#37707) * Make Credentials Required for Content Safety Evaluators * fix a typo * lint, fix content safety evaluator * revert test change * remove credential from rai_service * addFeedRangesAndUseFeedRangeInQueryChangeFeed (Azure#37687) * Add getFeedRanges API * Add feedRange support in query changeFeed Co-authored-by: annie-mac <[email protected]> * Update release date for core (Azure#37723) * Improvements to mindependency dev_requirement conflict resolution (Azure#37669) * during mindependency runs, dev_requirements on local relative paths are now checked for conflict with the targeted set of minimum dependencies * multiple type clarifications within azure-sdk-tools * added tests for new conflict resolution logic --------- Co-authored-by: McCoy Patiño <[email protected]> * Need to add environment to subscription configuration (Azure#37726) Co-authored-by: Wes Haggard <[email protected]> * Enable samples for formrecognizer (Azure#37676) * multi-modal-changes * fixes * Fix with latest * dict-fix * adding-protected-material * adding-protected-material * adding-protected-material * bumping-version * adding assets * Added image in simulator * Added image in simulator * bumping-version * push-asset * assets * pushing asset * remove-containt-on-key * asset * asset2 * asset3 * asset4 * adding conftest * conftest * cred fix * asset-new * fix * asset * adding multi-modal-without-tests * asset-from-main * asset-from-main * fix * adding one test only * new asset * tests,fix: Sanitizer should replace with enum value not enum name * test-asset * [AutoRelease] t2-containerservicefleet-2024-09-24-42036(can only be merged by SDK owner) (Azure#37538) * code and test * Update CHANGELOG.md * update-testcase --------- Co-authored-by: azure-sdk <PythonSdkPipelines> Co-authored-by: ChenxiJiang333 <[email protected]> Co-authored-by: ChenxiJiang333 <[email protected]> * [AutoRelease] t2-dns-2024-09-25-81486(can only be merged by SDK owner) (Azure#37560) * code and test * update-testcase * Update CHANGELOG.md * Update test_mgmt_dns_test.py --------- Co-authored-by: azure-sdk <PythonSdkPipelines> Co-authored-by: ChenxiJiang333 <[email protected]> Co-authored-by: ChenxiJiang333 <[email protected]> * [AutoRelease] t2-appconfiguration-2024-10-09-68726(can only be merged by SDK owner) (Azure#37800) * code and test * update-testcase * Update pyproject.toml --------- Co-authored-by: azure-sdk <PythonSdkPipelines> Co-authored-by: ChenxiJiang333 <[email protected]> Co-authored-by: Yuchao Yan <[email protected]> * code and test (Azure#37855) Co-authored-by: azure-sdk <PythonSdkPipelines> * [AutoRelease] t2-servicefabricmanagedclusters-2024-10-08-57405(can only be merged by SDK owner) (Azure#37768) * code and test * update-testcase * update-testcases --------- Co-authored-by: azure-sdk <PythonSdkPipelines> Co-authored-by: ChenxiJiang333 <[email protected]> * [AutoRelease] t2-containerinstance-2024-10-21-66631(can only be merged by SDK owner) (Azure#38005) * code and test * update-testcase * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md --------- Co-authored-by: azure-sdk <PythonSdkPipelines> Co-authored-by: ChenxiJiang333 <[email protected]> Co-authored-by: ChenxiJiang333 <[email protected]> * [sdk generation pipeline] bump typespec-python 0.36.1 (Azure#38008) * update version * update package.json * [AutoRelease] t2-dnsresolver-2024-10-12-16936(can only be merged by SDK owner) (Azure#37864) * code and test * update-testcase * Update CHANGELOG.md * Update CHANGELOG.md --------- Co-authored-by: azure-sdk <PythonSdkPipelines> Co-authored-by: ChenxiJiang333 <[email protected]> Co-authored-by: ChenxiJiang333 <[email protected]> Co-authored-by: Yuchao Yan <[email protected]> * new asset after fix in conftest * asset * chore: Update assets.json * Move perf pipelines to TME subscription (Azure#38020) Co-authored-by: Wes Haggard <[email protected]> * fix * after-comments * fix * asset * new asset with 1 test recording only * chore: Update assets.json * conftest fix * assets change * new test * few changes * removing proxy start * added all tests * asset * fixes * fixes with asset * asset-after-tax * enabling 2 more tests * unit test fix * asset * new asset * fixes per comments * changes by black * merge fix * pylint fix * pylint fix * ground test fix * fixes - pylint, black, mypy * more tests * docstring fixes * doc string fix * asset * few updates after Nagkumar review --------- Co-authored-by: Azure SDK Bot <[email protected]> Co-authored-by: Wes Haggard <[email protected]> Co-authored-by: Wes Haggard <[email protected]> Co-authored-by: Nagkumar Arkalgud <[email protected]> Co-authored-by: Nagkumar Arkalgud <[email protected]> Co-authored-by: Fabian Meiswinkel <[email protected]> Co-authored-by: Anna Tisch <[email protected]> Co-authored-by: Xiang Yan <[email protected]> Co-authored-by: Paul Van Eck <[email protected]> Co-authored-by: Neehar Duvvuri <[email protected]> Co-authored-by: Annie Liang <[email protected]> Co-authored-by: annie-mac <[email protected]> Co-authored-by: Scott Beddall <[email protected]> Co-authored-by: McCoy Patiño <[email protected]> Co-authored-by: kdestin <[email protected]> Co-authored-by: ChenxiJiang333 <[email protected]> Co-authored-by: ChenxiJiang333 <[email protected]> Co-authored-by: Yuchao Yan <[email protected]>
1 parent 30bf0a3 commit f4f403f

28 files changed

+1680
-31
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# Release History
22

3-
43
## 1.0.0b5 (Unreleased)
54

65
### Features Added
@@ -23,6 +22,7 @@ outputs = asyncio.run(custom_simulator(
2322
max_conversation_turns=1,
2423
))
2524
```
25+
- Adding evaluator for multimodal use cases
2626

2727
### Breaking Changes
2828
- Renamed environment variable `PF_EVALS_BATCH_USE_ASYNC` to `AI_EVALS_BATCH_USE_ASYNC`.

sdk/evaluation/azure-ai-evaluation/assets.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/evaluation/azure-ai-evaluation",
5-
"Tag": "python/evaluation/azure-ai-evaluation_f0444ef220"
5+
"Tag": "python/evaluation/azure-ai-evaluation_eb4989f81d"
66
}

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/__init__.py

+14
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212
SexualEvaluator,
1313
ViolenceEvaluator,
1414
)
15+
from ._evaluators._multimodal._content_safety_multimodal import (
16+
ContentSafetyMultimodalEvaluator,
17+
HateUnfairnessMultimodalEvaluator,
18+
SelfHarmMultimodalEvaluator,
19+
SexualMultimodalEvaluator,
20+
ViolenceMultimodalEvaluator,
21+
)
22+
from ._evaluators._multimodal._protected_material import ProtectedMaterialMultimodalEvaluator
1523
from ._evaluators._f1_score import F1ScoreEvaluator
1624
from ._evaluators._fluency import FluencyEvaluator
1725
from ._evaluators._gleu import GleuScoreEvaluator
@@ -65,4 +73,10 @@
6573
"Conversation",
6674
"Message",
6775
"EvaluationResult",
76+
"ContentSafetyMultimodalEvaluator",
77+
"HateUnfairnessMultimodalEvaluator",
78+
"SelfHarmMultimodalEvaluator",
79+
"SexualMultimodalEvaluator",
80+
"ViolenceMultimodalEvaluator",
81+
"ProtectedMaterialMultimodalEvaluator",
6882
]

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_common/rai_service.py

+116-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313

1414
import jwt
1515

16+
from promptflow.core._errors import MissingRequiredPackage
1617
from azure.ai.evaluation._exceptions import ErrorBlame, ErrorCategory, ErrorTarget, EvaluationException
1718
from azure.ai.evaluation._http_utils import AsyncHttpPipeline, get_async_http_client
1819
from azure.ai.evaluation._model_configurations import AzureAIProject
1920
from azure.core.credentials import TokenCredential
21+
from azure.core.exceptions import HttpResponseError
2022
from azure.core.pipeline.policies import AsyncRetryPolicy
2123

2224
from .constants import (
@@ -26,7 +28,7 @@
2628
Tasks,
2729
_InternalEvaluationMetrics,
2830
)
29-
from .utils import get_harm_severity_level
31+
from .utils import get_harm_severity_level, retrieve_content_type
3032

3133
try:
3234
version = importlib.metadata.version("azure-ai-evaluation")
@@ -281,7 +283,11 @@ def _parse_content_harm_response(
281283
if key == EvaluationMetrics.HATE_FAIRNESS:
282284
key = EvaluationMetrics.HATE_UNFAIRNESS
283285

284-
result: Dict[str, Union[str, float]] = {key: math.nan, key + "_score": math.nan, key + "_reason": ""}
286+
result: Dict[str, Union[str, float]] = {
287+
(key.value if hasattr(key, "value") else key): math.nan,
288+
f"{(key.value if hasattr(key, 'value') else key)}_score": math.nan,
289+
f"{(key.value if hasattr(key, 'value') else key)}_reason": math.nan,
290+
}
285291

286292
response = batch_response[0]
287293
if metric_name not in response:
@@ -330,6 +336,8 @@ def _parse_content_harm_response(
330336
reason = ""
331337

332338
harm_score = metric_value
339+
if metric_value == "n/a":
340+
return result
333341
if not math.isnan(metric_value):
334342
# int(math.nan) causes a value error, and math.nan is already handled
335343
# by get_harm_severity_level
@@ -465,3 +473,109 @@ async def evaluate_with_rai_service(
465473
result = parse_response(annotation_response, metric_name, metric_display_name)
466474

467475
return result
476+
477+
478+
def generate_payload_multimodal(content_type: str, messages, metric: str) -> Dict:
479+
"""Generate the payload for the annotation request
480+
:param content_type: The type of the content representing multimodal or images.
481+
:type content_type: str
482+
:param messages: The normalized list of messages to be entered as the "Contents" in the payload.
483+
:type messages: str
484+
:param metric: The evaluation metric to use. This determines the task type, and whether a "MetricList" is needed
485+
in the payload.
486+
:type metric: str
487+
:return: The payload for the annotation request.
488+
:rtype: Dict
489+
"""
490+
include_metric = True
491+
task = Tasks.CONTENT_HARM
492+
if metric == EvaluationMetrics.PROTECTED_MATERIAL:
493+
task = Tasks.PROTECTED_MATERIAL
494+
include_metric = False
495+
496+
if include_metric:
497+
return {
498+
"ContentType": content_type,
499+
"Contents": [{"messages": messages}],
500+
"AnnotationTask": task,
501+
"MetricList": [metric],
502+
}
503+
return {
504+
"ContentType": content_type,
505+
"Contents": [{"messages": messages}],
506+
"AnnotationTask": task,
507+
}
508+
509+
510+
async def submit_multimodal_request(messages, metric: str, rai_svc_url: str, token: str) -> str:
511+
"""Submit request to Responsible AI service for evaluation and return operation ID
512+
:param messages: The normalized list of messages to be entered as the "Contents" in the payload.
513+
:type messages: str
514+
:param metric: The evaluation metric to use.
515+
:type metric: str
516+
:param rai_svc_url: The Responsible AI service URL.
517+
:type rai_svc_url: str
518+
:param token: The Azure authentication token.
519+
:type token: str
520+
:return: The operation ID.
521+
:rtype: str
522+
"""
523+
## handle json payload and payload from inference sdk strongly type messages
524+
if len(messages) > 0 and not isinstance(messages[0], dict):
525+
try:
526+
from azure.ai.inference.models import ChatRequestMessage
527+
except ImportError as ex:
528+
error_message = (
529+
"Please install 'azure-ai-inference' package to use SystemMessage, UserMessage, AssistantMessage"
530+
)
531+
raise MissingRequiredPackage(message=error_message) from ex
532+
if len(messages) > 0 and isinstance(messages[0], ChatRequestMessage):
533+
messages = [message.as_dict() for message in messages]
534+
535+
filtered_messages = [message for message in messages if message["role"] != "system"]
536+
assistant_messages = [message for message in messages if message["role"] == "assistant"]
537+
content_type = retrieve_content_type(assistant_messages, metric)
538+
payload = generate_payload_multimodal(content_type, filtered_messages, metric)
539+
540+
## calling rai service for annotation
541+
url = rai_svc_url + "/submitannotation"
542+
headers = get_common_headers(token)
543+
async with get_async_http_client() as client:
544+
response = await client.post( # pylint: disable=too-many-function-args,unexpected-keyword-arg
545+
url, json=payload, headers=headers
546+
)
547+
if response.status_code != 202:
548+
raise HttpResponseError(
549+
message=f"Received unexpected HTTP status: {response.status_code} {response.text()}", response=response
550+
)
551+
result = response.json()
552+
operation_id = result["location"].split("/")[-1]
553+
return operation_id
554+
555+
556+
async def evaluate_with_rai_service_multimodal(
557+
messages, metric_name: str, project_scope: AzureAIProject, credential: TokenCredential
558+
):
559+
""" "Evaluate the content safety of the response using Responsible AI service
560+
:param messages: The normalized list of messages.
561+
:type messages: str
562+
:param metric_name: The evaluation metric to use.
563+
:type metric_name: str
564+
:param project_scope: The Azure AI project scope details.
565+
:type project_scope: Dict
566+
:param credential: The Azure authentication credential.
567+
:type credential:
568+
~azure.core.credentials.TokenCredential
569+
:return: The parsed annotation result.
570+
:rtype: List[List[Dict]]
571+
"""
572+
573+
# Get RAI service URL from discovery service and check service availability
574+
token = await fetch_or_reuse_token(credential)
575+
rai_svc_url = await get_rai_svc_url(project_scope, token)
576+
await ensure_service_availability(rai_svc_url, token, Tasks.CONTENT_HARM)
577+
# Submit annotation request and fetch result
578+
operation_id = await submit_multimodal_request(messages, metric_name, rai_svc_url, token)
579+
annotation_response = cast(List[Dict], await fetch_result(operation_id, rai_svc_url, credential, token))
580+
result = parse_response(annotation_response, metric_name)
581+
return result

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_common/utils.py

+99-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99

1010
import nltk
1111
from typing_extensions import NotRequired, Required, TypeGuard
12-
12+
from promptflow.core._errors import MissingRequiredPackage
1313
from azure.ai.evaluation._constants import AZURE_OPENAI_TYPE, OPENAI_TYPE
14-
from azure.ai.evaluation._exceptions import ErrorBlame, ErrorCategory, EvaluationException
14+
from azure.ai.evaluation._exceptions import ErrorBlame, ErrorCategory, ErrorTarget, EvaluationException
1515
from azure.ai.evaluation._model_configurations import (
1616
AzureAIProject,
1717
AzureOpenAIModelConfiguration,
@@ -312,3 +312,100 @@ def remove_optional_singletons(eval_class, singletons):
312312
if param in singletons:
313313
del required_singletons[param]
314314
return required_singletons
315+
316+
317+
def retrieve_content_type(assistant_messages: List, metric: str) -> str:
318+
"""Get the content type for service payload.
319+
320+
:param assistant_messages: The list of messages to be annotated by evaluation service
321+
:type assistant_messages: list
322+
:param metric: A string representing the metric type
323+
:type metric: str
324+
:return: A text representing the content type. Example: 'text', or 'image'
325+
:rtype: str
326+
"""
327+
# Check if metric is "protected_material"
328+
if metric == "protected_material":
329+
return "image"
330+
331+
# Iterate through each message
332+
for item in assistant_messages:
333+
# Ensure "content" exists in the message and is iterable
334+
content = item.get("content", [])
335+
for message in content:
336+
if message.get("type", "") == "image_url":
337+
return "image"
338+
# Default return if no image was found
339+
return "text"
340+
341+
342+
def validate_conversation(conversation):
343+
def raise_exception(msg, target):
344+
raise EvaluationException(
345+
message=msg,
346+
internal_message=msg,
347+
target=target,
348+
category=ErrorCategory.INVALID_VALUE,
349+
blame=ErrorBlame.USER_ERROR,
350+
)
351+
352+
if not conversation or "messages" not in conversation:
353+
raise_exception(
354+
"Attribute 'messages' is missing in the request",
355+
ErrorTarget.CONTENT_SAFETY_CHAT_EVALUATOR,
356+
)
357+
messages = conversation["messages"]
358+
if not isinstance(messages, list):
359+
raise_exception(
360+
"'messages' parameter must be a JSON-compatible list of chat messages",
361+
ErrorTarget.CONTENT_SAFETY_MULTIMODAL_EVALUATOR,
362+
)
363+
expected_roles = {"user", "assistant", "system"}
364+
image_found = False
365+
for num, message in enumerate(messages, 1):
366+
if not isinstance(message, dict):
367+
try:
368+
from azure.ai.inference.models import (
369+
ChatRequestMessage,
370+
UserMessage,
371+
AssistantMessage,
372+
SystemMessage,
373+
ImageContentItem,
374+
)
375+
except ImportError as ex:
376+
raise MissingRequiredPackage(
377+
message="Please install 'azure-ai-inference' package to use SystemMessage, AssistantMessage"
378+
) from ex
379+
380+
if isinstance(messages[0], ChatRequestMessage) and not isinstance(
381+
message, (UserMessage, AssistantMessage, SystemMessage)
382+
):
383+
raise_exception(
384+
f"Messages must be a strongly typed class of ChatRequestMessage. Message number: {num}",
385+
ErrorTarget.CONTENT_SAFETY_MULTIMODAL_EVALUATOR,
386+
)
387+
388+
if isinstance(message.content, list) and any(
389+
isinstance(item, ImageContentItem) for item in message.content
390+
):
391+
image_found = True
392+
continue
393+
if message.get("role") not in expected_roles:
394+
raise_exception(
395+
f"Invalid role provided: {message.get('role')}. Message number: {num}",
396+
ErrorTarget.CONTENT_SAFETY_MULTIMODAL_EVALUATOR,
397+
)
398+
content = message.get("content")
399+
if not isinstance(content, (str, list)):
400+
raise_exception(
401+
f"Content in each turn must be a string or array. Message number: {num}",
402+
ErrorTarget.CONTENT_SAFETY_MULTIMODAL_EVALUATOR,
403+
)
404+
if isinstance(content, list):
405+
if any(item.get("type") == "image_url" and "url" in item.get("image_url", {}) for item in content):
406+
image_found = True
407+
if not image_found:
408+
raise_exception(
409+
"Message needs to have multi-modal input like images.",
410+
ErrorTarget.CONTENT_SAFETY_MULTIMODAL_EVALUATOR,
411+
)

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_evaluate/_utils.py

+38
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import tempfile
99
from pathlib import Path
1010
from typing import Any, Dict, NamedTuple, Optional, Tuple, Union
11+
import uuid
12+
import base64
1113

1214
import pandas as pd
1315
from promptflow.client import PFClient
@@ -81,6 +83,33 @@ def _azure_pf_client_and_triad(trace_destination) -> Tuple[PFClient, AzureMLWork
8183
return azure_pf_client, ws_triad
8284

8385

86+
def _store_multimodal_content(messages, tmpdir: str):
87+
# verify if images folder exists
88+
images_folder_path = os.path.join(tmpdir, "images")
89+
os.makedirs(images_folder_path, exist_ok=True)
90+
91+
# traverse all messages and replace base64 image data with new file name.
92+
for message in messages:
93+
for content in message.get("content", []):
94+
if content.get("type") == "image_url":
95+
image_url = content.get("image_url")
96+
if image_url and "url" in image_url and image_url["url"].startswith("data:image/jpg;base64,"):
97+
# Extract the base64 string
98+
base64image = image_url["url"].replace("data:image/jpg;base64,", "")
99+
100+
# Generate a unique filename
101+
image_file_name = f"{str(uuid.uuid4())}.jpg"
102+
image_url["url"] = f"images/{image_file_name}" # Replace the base64 URL with the file path
103+
104+
# Decode the base64 string to binary image data
105+
image_data_binary = base64.b64decode(base64image)
106+
107+
# Write the binary image data to the file
108+
image_file_path = os.path.join(images_folder_path, image_file_name)
109+
with open(image_file_path, "wb") as f:
110+
f.write(image_data_binary)
111+
112+
84113
def _log_metrics_and_instance_results(
85114
metrics: Dict[str, Any],
86115
instance_results: pd.DataFrame,
@@ -110,6 +139,15 @@ def _log_metrics_and_instance_results(
110139
artifact_name = EvalRun.EVALUATION_ARTIFACT if run else EvalRun.EVALUATION_ARTIFACT_DUMMY_RUN
111140

112141
with tempfile.TemporaryDirectory() as tmpdir:
142+
# storing multi_modal images if exists
143+
col_name = "inputs.conversation"
144+
if col_name in instance_results.columns:
145+
for item in instance_results[col_name].items():
146+
value = item[1]
147+
if "messages" in value:
148+
_store_multimodal_content(value["messages"], tmpdir)
149+
150+
# storing artifact result
113151
tmp_path = os.path.join(tmpdir, artifact_name)
114152

115153
with open(tmp_path, "w", encoding=DefaultOpenEncoding.WRITE) as f:

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_evaluators/_content_safety/_content_safety_chat.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,10 @@ def __init__(
9999
self._eval_last_turn = eval_last_turn
100100
self._parallel = parallel
101101
self._evaluators: List[Callable[..., Dict[str, Union[str, float]]]] = [
102-
ViolenceEvaluator(azure_ai_project, credential),
103-
SexualEvaluator(azure_ai_project, credential),
104-
SelfHarmEvaluator(azure_ai_project, credential),
105-
HateUnfairnessEvaluator(azure_ai_project, credential),
102+
ViolenceEvaluator(credential, azure_ai_project),
103+
SexualEvaluator(credential, azure_ai_project),
104+
SelfHarmEvaluator(credential, azure_ai_project),
105+
HateUnfairnessEvaluator(credential, azure_ai_project),
106106
]
107107

108108
def __call__(self, *, conversation: list, **kwargs):

0 commit comments

Comments
 (0)