azure-ai-evaluation 1.7.0__py3-none-any.whl → 1.9.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.
- azure/ai/evaluation/__init__.py +13 -2
- azure/ai/evaluation/_aoai/__init__.py +1 -1
- azure/ai/evaluation/_aoai/aoai_grader.py +21 -11
- azure/ai/evaluation/_aoai/label_grader.py +3 -2
- azure/ai/evaluation/_aoai/score_model_grader.py +90 -0
- azure/ai/evaluation/_aoai/string_check_grader.py +3 -2
- azure/ai/evaluation/_aoai/text_similarity_grader.py +3 -2
- azure/ai/evaluation/_azure/_envs.py +9 -10
- azure/ai/evaluation/_azure/_token_manager.py +7 -1
- azure/ai/evaluation/_common/constants.py +11 -2
- azure/ai/evaluation/_common/evaluation_onedp_client.py +32 -26
- azure/ai/evaluation/_common/onedp/__init__.py +32 -32
- azure/ai/evaluation/_common/onedp/_client.py +136 -139
- azure/ai/evaluation/_common/onedp/_configuration.py +70 -73
- azure/ai/evaluation/_common/onedp/_patch.py +21 -21
- azure/ai/evaluation/_common/onedp/_utils/__init__.py +6 -0
- azure/ai/evaluation/_common/onedp/_utils/model_base.py +1232 -0
- azure/ai/evaluation/_common/onedp/_utils/serialization.py +2032 -0
- azure/ai/evaluation/_common/onedp/_validation.py +50 -50
- azure/ai/evaluation/_common/onedp/_version.py +9 -9
- azure/ai/evaluation/_common/onedp/aio/__init__.py +29 -29
- azure/ai/evaluation/_common/onedp/aio/_client.py +138 -143
- azure/ai/evaluation/_common/onedp/aio/_configuration.py +70 -75
- azure/ai/evaluation/_common/onedp/aio/_patch.py +21 -21
- azure/ai/evaluation/_common/onedp/aio/operations/__init__.py +37 -39
- azure/ai/evaluation/_common/onedp/aio/operations/_operations.py +4832 -4494
- azure/ai/evaluation/_common/onedp/aio/operations/_patch.py +21 -21
- azure/ai/evaluation/_common/onedp/models/__init__.py +168 -142
- azure/ai/evaluation/_common/onedp/models/_enums.py +230 -162
- azure/ai/evaluation/_common/onedp/models/_models.py +2685 -2228
- azure/ai/evaluation/_common/onedp/models/_patch.py +21 -21
- azure/ai/evaluation/_common/onedp/operations/__init__.py +37 -39
- azure/ai/evaluation/_common/onedp/operations/_operations.py +6106 -5655
- azure/ai/evaluation/_common/onedp/operations/_patch.py +21 -21
- azure/ai/evaluation/_common/rai_service.py +86 -50
- azure/ai/evaluation/_common/raiclient/__init__.py +1 -1
- azure/ai/evaluation/_common/raiclient/operations/_operations.py +14 -1
- azure/ai/evaluation/_common/utils.py +124 -3
- azure/ai/evaluation/_constants.py +2 -1
- azure/ai/evaluation/_converters/__init__.py +1 -1
- azure/ai/evaluation/_converters/_ai_services.py +9 -8
- azure/ai/evaluation/_converters/_models.py +46 -0
- azure/ai/evaluation/_converters/_sk_services.py +495 -0
- azure/ai/evaluation/_eval_mapping.py +2 -2
- azure/ai/evaluation/_evaluate/_batch_run/_run_submitter_client.py +4 -4
- azure/ai/evaluation/_evaluate/_batch_run/eval_run_context.py +2 -2
- azure/ai/evaluation/_evaluate/_evaluate.py +64 -58
- azure/ai/evaluation/_evaluate/_evaluate_aoai.py +130 -89
- azure/ai/evaluation/_evaluate/_telemetry/__init__.py +0 -1
- azure/ai/evaluation/_evaluate/_utils.py +24 -15
- azure/ai/evaluation/_evaluators/_bleu/_bleu.py +3 -3
- azure/ai/evaluation/_evaluators/_code_vulnerability/_code_vulnerability.py +12 -11
- azure/ai/evaluation/_evaluators/_coherence/_coherence.py +5 -5
- azure/ai/evaluation/_evaluators/_common/_base_eval.py +15 -5
- azure/ai/evaluation/_evaluators/_common/_base_prompty_eval.py +24 -9
- azure/ai/evaluation/_evaluators/_common/_base_rai_svc_eval.py +6 -1
- azure/ai/evaluation/_evaluators/_content_safety/_content_safety.py +13 -13
- azure/ai/evaluation/_evaluators/_content_safety/_hate_unfairness.py +7 -7
- azure/ai/evaluation/_evaluators/_content_safety/_self_harm.py +7 -7
- azure/ai/evaluation/_evaluators/_content_safety/_sexual.py +7 -7
- azure/ai/evaluation/_evaluators/_content_safety/_violence.py +6 -6
- azure/ai/evaluation/_evaluators/_document_retrieval/__init__.py +1 -5
- azure/ai/evaluation/_evaluators/_document_retrieval/_document_retrieval.py +34 -64
- azure/ai/evaluation/_evaluators/_eci/_eci.py +3 -3
- azure/ai/evaluation/_evaluators/_f1_score/_f1_score.py +4 -4
- azure/ai/evaluation/_evaluators/_fluency/_fluency.py +2 -2
- azure/ai/evaluation/_evaluators/_gleu/_gleu.py +3 -3
- azure/ai/evaluation/_evaluators/_groundedness/_groundedness.py +11 -7
- azure/ai/evaluation/_evaluators/_intent_resolution/_intent_resolution.py +30 -25
- azure/ai/evaluation/_evaluators/_intent_resolution/intent_resolution.prompty +210 -96
- azure/ai/evaluation/_evaluators/_meteor/_meteor.py +2 -3
- azure/ai/evaluation/_evaluators/_protected_material/_protected_material.py +6 -6
- azure/ai/evaluation/_evaluators/_qa/_qa.py +4 -4
- azure/ai/evaluation/_evaluators/_relevance/_relevance.py +8 -13
- azure/ai/evaluation/_evaluators/_response_completeness/_response_completeness.py +20 -25
- azure/ai/evaluation/_evaluators/_retrieval/_retrieval.py +4 -4
- azure/ai/evaluation/_evaluators/_rouge/_rouge.py +25 -25
- azure/ai/evaluation/_evaluators/_service_groundedness/_service_groundedness.py +5 -5
- azure/ai/evaluation/_evaluators/_similarity/_similarity.py +3 -3
- azure/ai/evaluation/_evaluators/_task_adherence/_task_adherence.py +11 -14
- azure/ai/evaluation/_evaluators/_tool_call_accuracy/_tool_call_accuracy.py +43 -34
- azure/ai/evaluation/_evaluators/_tool_call_accuracy/tool_call_accuracy.prompty +3 -3
- azure/ai/evaluation/_evaluators/_ungrounded_attributes/_ungrounded_attributes.py +12 -11
- azure/ai/evaluation/_evaluators/_xpia/xpia.py +6 -6
- azure/ai/evaluation/_exceptions.py +10 -0
- azure/ai/evaluation/_http_utils.py +3 -3
- azure/ai/evaluation/_legacy/_batch_engine/_engine.py +3 -3
- azure/ai/evaluation/_legacy/_batch_engine/_openai_injector.py +5 -2
- azure/ai/evaluation/_legacy/_batch_engine/_run_submitter.py +5 -10
- azure/ai/evaluation/_legacy/_batch_engine/_utils.py +1 -4
- azure/ai/evaluation/_legacy/_common/_async_token_provider.py +12 -19
- azure/ai/evaluation/_legacy/_common/_thread_pool_executor_with_context.py +2 -0
- azure/ai/evaluation/_legacy/prompty/_prompty.py +11 -5
- azure/ai/evaluation/_safety_evaluation/__init__.py +1 -1
- azure/ai/evaluation/_safety_evaluation/_safety_evaluation.py +193 -111
- azure/ai/evaluation/_user_agent.py +32 -1
- azure/ai/evaluation/_version.py +1 -1
- azure/ai/evaluation/red_team/__init__.py +3 -1
- azure/ai/evaluation/red_team/_agent/__init__.py +3 -0
- azure/ai/evaluation/red_team/_agent/_agent_functions.py +261 -0
- azure/ai/evaluation/red_team/_agent/_agent_tools.py +461 -0
- azure/ai/evaluation/red_team/_agent/_agent_utils.py +89 -0
- azure/ai/evaluation/red_team/_agent/_semantic_kernel_plugin.py +228 -0
- azure/ai/evaluation/red_team/_attack_objective_generator.py +94 -52
- azure/ai/evaluation/red_team/_attack_strategy.py +4 -1
- azure/ai/evaluation/red_team/_callback_chat_target.py +4 -9
- azure/ai/evaluation/red_team/_default_converter.py +1 -1
- azure/ai/evaluation/red_team/_red_team.py +1622 -765
- azure/ai/evaluation/red_team/_red_team_result.py +43 -38
- azure/ai/evaluation/red_team/_utils/__init__.py +1 -1
- azure/ai/evaluation/red_team/_utils/_rai_service_eval_chat_target.py +121 -0
- azure/ai/evaluation/red_team/_utils/_rai_service_target.py +595 -0
- azure/ai/evaluation/red_team/_utils/_rai_service_true_false_scorer.py +108 -0
- azure/ai/evaluation/red_team/_utils/constants.py +6 -12
- azure/ai/evaluation/red_team/_utils/formatting_utils.py +41 -44
- azure/ai/evaluation/red_team/_utils/logging_utils.py +17 -17
- azure/ai/evaluation/red_team/_utils/metric_mapping.py +33 -6
- azure/ai/evaluation/red_team/_utils/strategy_utils.py +35 -25
- azure/ai/evaluation/simulator/_adversarial_scenario.py +2 -0
- azure/ai/evaluation/simulator/_adversarial_simulator.py +34 -16
- azure/ai/evaluation/simulator/_conversation/__init__.py +2 -2
- azure/ai/evaluation/simulator/_direct_attack_simulator.py +8 -8
- azure/ai/evaluation/simulator/_indirect_attack_simulator.py +5 -5
- azure/ai/evaluation/simulator/_model_tools/_generated_rai_client.py +54 -23
- azure/ai/evaluation/simulator/_model_tools/_identity_manager.py +7 -1
- azure/ai/evaluation/simulator/_model_tools/_proxy_completion_model.py +25 -15
- azure/ai/evaluation/simulator/_model_tools/_rai_client.py +19 -31
- azure/ai/evaluation/simulator/_model_tools/_template_handler.py +20 -6
- azure/ai/evaluation/simulator/_model_tools/models.py +1 -1
- azure/ai/evaluation/simulator/_simulator.py +9 -8
- {azure_ai_evaluation-1.7.0.dist-info → azure_ai_evaluation-1.9.0.dist-info}/METADATA +24 -1
- {azure_ai_evaluation-1.7.0.dist-info → azure_ai_evaluation-1.9.0.dist-info}/RECORD +135 -123
- azure/ai/evaluation/_common/onedp/aio/_vendor.py +0 -40
- {azure_ai_evaluation-1.7.0.dist-info → azure_ai_evaluation-1.9.0.dist-info}/NOTICE.txt +0 -0
- {azure_ai_evaluation-1.7.0.dist-info → azure_ai_evaluation-1.9.0.dist-info}/WHEEL +0 -0
- {azure_ai_evaluation-1.7.0.dist-info → azure_ai_evaluation-1.9.0.dist-info}/top_level.txt +0 -0
|
@@ -49,8 +49,8 @@ class AdversarialSimulator:
|
|
|
49
49
|
"""
|
|
50
50
|
Initializes the adversarial simulator with a project scope.
|
|
51
51
|
|
|
52
|
-
:param azure_ai_project: The Azure AI project, which can either be a string representing the project endpoint
|
|
53
|
-
or an instance of AzureAIProject. It contains subscription id, resource group, and project name.
|
|
52
|
+
:param azure_ai_project: The Azure AI project, which can either be a string representing the project endpoint
|
|
53
|
+
or an instance of AzureAIProject. It contains subscription id, resource group, and project name.
|
|
54
54
|
:type azure_ai_project: Union[str, AzureAIProject]
|
|
55
55
|
:param credential: The credential for connecting to Azure AI project.
|
|
56
56
|
:type credential: ~azure.core.credentials.TokenCredential
|
|
@@ -77,7 +77,7 @@ class AdversarialSimulator:
|
|
|
77
77
|
logger=logging.getLogger("AdversarialSimulator"),
|
|
78
78
|
credential=self.credential,
|
|
79
79
|
)
|
|
80
|
-
self.rai_client
|
|
80
|
+
self.rai_client = AIProjectClient(endpoint=azure_ai_project, credential=credential)
|
|
81
81
|
else:
|
|
82
82
|
try:
|
|
83
83
|
self.azure_ai_project = validate_azure_ai_project(azure_ai_project)
|
|
@@ -96,7 +96,7 @@ class AdversarialSimulator:
|
|
|
96
96
|
credential=self.credential,
|
|
97
97
|
)
|
|
98
98
|
self.rai_client = RAIClient(azure_ai_project=self.azure_ai_project, token_manager=self.token_manager)
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
self.adversarial_template_handler = AdversarialTemplateHandler(
|
|
101
101
|
azure_ai_project=self.azure_ai_project, rai_client=self.rai_client
|
|
102
102
|
)
|
|
@@ -264,12 +264,21 @@ class AdversarialSimulator:
|
|
|
264
264
|
|
|
265
265
|
# Limit to max_simulation_results if needed
|
|
266
266
|
if len(template_parameter_pairs) > max_simulation_results:
|
|
267
|
-
template_parameter_pairs = template_parameter_pairs[
|
|
267
|
+
template_parameter_pairs = template_parameter_pairs[
|
|
268
|
+
:max_simulation_results
|
|
269
|
+
] # Create a seeded random instance for jailbreak selection if randomization_seed is provided
|
|
270
|
+
jailbreak_random = None
|
|
271
|
+
if _jailbreak_type == "upia" and randomization_seed is not None:
|
|
272
|
+
jailbreak_random = random.Random(randomization_seed)
|
|
268
273
|
|
|
269
274
|
# Single task append loop for all scenarios
|
|
270
275
|
for template, parameter in template_parameter_pairs:
|
|
271
276
|
if _jailbreak_type == "upia":
|
|
272
|
-
|
|
277
|
+
if jailbreak_random is not None:
|
|
278
|
+
selected_jailbreak = jailbreak_random.choice(jailbreak_dataset)
|
|
279
|
+
else:
|
|
280
|
+
selected_jailbreak = random.choice(jailbreak_dataset)
|
|
281
|
+
parameter = self._add_jailbreak_parameter(parameter, selected_jailbreak)
|
|
273
282
|
|
|
274
283
|
tasks.append(
|
|
275
284
|
asyncio.create_task(
|
|
@@ -357,10 +366,21 @@ class AdversarialSimulator:
|
|
|
357
366
|
target=target, role=ConversationRole.ASSISTANT, template=template, parameters=parameters, scenario=scenario
|
|
358
367
|
)
|
|
359
368
|
bots = [user_bot, system_bot]
|
|
360
|
-
|
|
369
|
+
|
|
370
|
+
async def run_simulation(session_obj):
|
|
371
|
+
async with semaphore:
|
|
372
|
+
_, conversation_history = await simulate_conversation(
|
|
373
|
+
bots=bots,
|
|
374
|
+
session=session_obj,
|
|
375
|
+
turn_limit=max_conversation_turns,
|
|
376
|
+
api_call_delay_sec=api_call_delay_sec,
|
|
377
|
+
language=language,
|
|
378
|
+
)
|
|
379
|
+
return conversation_history
|
|
380
|
+
|
|
361
381
|
if isinstance(self.rai_client, AIProjectClient):
|
|
362
382
|
session = self.rai_client
|
|
363
|
-
else:
|
|
383
|
+
else:
|
|
364
384
|
session = get_async_http_client().with_policies(
|
|
365
385
|
retry_policy=AsyncRetryPolicy(
|
|
366
386
|
retry_total=api_call_retry_limit,
|
|
@@ -368,13 +388,7 @@ class AdversarialSimulator:
|
|
|
368
388
|
retry_mode=RetryMode.Fixed,
|
|
369
389
|
)
|
|
370
390
|
)
|
|
371
|
-
|
|
372
|
-
bots=bots,
|
|
373
|
-
session=session,
|
|
374
|
-
turn_limit=max_conversation_turns,
|
|
375
|
-
api_call_delay_sec=api_call_delay_sec,
|
|
376
|
-
language=language,
|
|
377
|
-
)
|
|
391
|
+
conversation_history = await run_simulation(session)
|
|
378
392
|
|
|
379
393
|
return self._to_chat_protocol(
|
|
380
394
|
conversation_history=conversation_history,
|
|
@@ -384,7 +398,11 @@ class AdversarialSimulator:
|
|
|
384
398
|
def _get_user_proxy_completion_model(
|
|
385
399
|
self, template_key: str, template_parameters: TemplateParameters, simulation_id: str = ""
|
|
386
400
|
) -> ProxyChatCompletionsModel:
|
|
387
|
-
endpoint_url =
|
|
401
|
+
endpoint_url = (
|
|
402
|
+
self.rai_client._config.endpoint + "/redTeams/simulation/chat/completions/submit"
|
|
403
|
+
if isinstance(self.rai_client, AIProjectClient)
|
|
404
|
+
else self.rai_client.simulation_submit_endpoint
|
|
405
|
+
)
|
|
388
406
|
return ProxyChatCompletionsModel(
|
|
389
407
|
name="raisvc_proxy_model",
|
|
390
408
|
template_key=template_key,
|
|
@@ -419,13 +419,13 @@ class MultiModalConversationBot(ConversationBot):
|
|
|
419
419
|
contents = []
|
|
420
420
|
for msg in messages:
|
|
421
421
|
if msg.startswith("image_understanding/"):
|
|
422
|
-
if
|
|
422
|
+
if isinstance(self.rai_client, RAIClient):
|
|
423
423
|
encoded_image = await self.rai_client.get_image_data(msg)
|
|
424
424
|
else:
|
|
425
425
|
response = self.rai_client.red_teams.get_template_parameters_image(path=msg, stream="true")
|
|
426
426
|
image_data = b"".join(response)
|
|
427
427
|
encoded_image = base64.b64encode(image_data).decode("utf-8")
|
|
428
|
-
|
|
428
|
+
|
|
429
429
|
contents.append(
|
|
430
430
|
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{encoded_image}"}},
|
|
431
431
|
)
|
|
@@ -28,8 +28,8 @@ class DirectAttackSimulator:
|
|
|
28
28
|
Initialize a UPIA (user prompt injected attack) jailbreak adversarial simulator with a project scope.
|
|
29
29
|
This simulator converses with your AI system using prompts designed to interrupt normal functionality.
|
|
30
30
|
|
|
31
|
-
:param azure_ai_project: The Azure AI project, which can either be a string representing the project endpoint
|
|
32
|
-
or an instance of AzureAIProject. It contains subscription id, resource group, and project name.
|
|
31
|
+
:param azure_ai_project: The Azure AI project, which can either be a string representing the project endpoint
|
|
32
|
+
or an instance of AzureAIProject. It contains subscription id, resource group, and project name.
|
|
33
33
|
:type azure_ai_project: Union[str, AzureAIProject]
|
|
34
34
|
:param credential: The credential for connecting to Azure AI project.
|
|
35
35
|
:type credential: ~azure.core.credentials.TokenCredential
|
|
@@ -46,16 +46,16 @@ class DirectAttackSimulator:
|
|
|
46
46
|
|
|
47
47
|
def __init__(self, *, azure_ai_project: Union[str, AzureAIProject], credential: TokenCredential):
|
|
48
48
|
"""Constructor."""
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
if is_onedp_project(azure_ai_project):
|
|
51
51
|
self.azure_ai_project = azure_ai_project
|
|
52
|
-
self.credential=cast(TokenCredential, credential)
|
|
52
|
+
self.credential = cast(TokenCredential, credential)
|
|
53
53
|
self.token_manager = ManagedIdentityAPITokenManager(
|
|
54
54
|
token_scope=TokenScope.COGNITIVE_SERVICES_MANAGEMENT,
|
|
55
55
|
logger=logging.getLogger("AdversarialSimulator"),
|
|
56
|
-
credential=self.credential
|
|
56
|
+
credential=self.credential,
|
|
57
57
|
)
|
|
58
|
-
self.rai_client
|
|
58
|
+
self.rai_client = AIProjectClient(endpoint=azure_ai_project, credential=credential)
|
|
59
59
|
else:
|
|
60
60
|
try:
|
|
61
61
|
self.azure_ai_project = validate_azure_ai_project(azure_ai_project)
|
|
@@ -67,14 +67,14 @@ class DirectAttackSimulator:
|
|
|
67
67
|
category=e.category,
|
|
68
68
|
blame=e.blame,
|
|
69
69
|
) from e
|
|
70
|
-
self.credential = cast(TokenCredential, credential)
|
|
70
|
+
self.credential = cast(TokenCredential, credential)
|
|
71
71
|
self.token_manager = ManagedIdentityAPITokenManager(
|
|
72
72
|
token_scope=TokenScope.DEFAULT_AZURE_MANAGEMENT,
|
|
73
73
|
logger=logging.getLogger("AdversarialSimulator"),
|
|
74
74
|
credential=self.credential,
|
|
75
75
|
)
|
|
76
76
|
self.rai_client = RAIClient(azure_ai_project=self.azure_ai_project, token_manager=self.token_manager)
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
self.adversarial_template_handler = AdversarialTemplateHandler(
|
|
79
79
|
azure_ai_project=self.azure_ai_project, rai_client=self.rai_client
|
|
80
80
|
)
|
|
@@ -30,8 +30,8 @@ class IndirectAttackSimulator(AdversarialSimulator):
|
|
|
30
30
|
"""
|
|
31
31
|
Initializes the XPIA (cross domain prompt injected attack) jailbreak adversarial simulator with a project scope.
|
|
32
32
|
|
|
33
|
-
:param azure_ai_project: The Azure AI project, which can either be a string representing the project endpoint
|
|
34
|
-
or an instance of AzureAIProject. It contains subscription id, resource group, and project name.
|
|
33
|
+
:param azure_ai_project: The Azure AI project, which can either be a string representing the project endpoint
|
|
34
|
+
or an instance of AzureAIProject. It contains subscription id, resource group, and project name.
|
|
35
35
|
:type azure_ai_project: Union[str, AzureAIProject]
|
|
36
36
|
:param credential: The credential for connecting to Azure AI project.
|
|
37
37
|
:type credential: ~azure.core.credentials.TokenCredential
|
|
@@ -51,13 +51,13 @@ class IndirectAttackSimulator(AdversarialSimulator):
|
|
|
51
51
|
|
|
52
52
|
if is_onedp_project(azure_ai_project):
|
|
53
53
|
self.azure_ai_project = azure_ai_project
|
|
54
|
-
self.credential=cast(TokenCredential, credential)
|
|
54
|
+
self.credential = cast(TokenCredential, credential)
|
|
55
55
|
self.token_manager = ManagedIdentityAPITokenManager(
|
|
56
56
|
token_scope=TokenScope.COGNITIVE_SERVICES_MANAGEMENT,
|
|
57
57
|
logger=logging.getLogger("AdversarialSimulator"),
|
|
58
|
-
credential=self.credential
|
|
58
|
+
credential=self.credential,
|
|
59
59
|
)
|
|
60
|
-
self.rai_client
|
|
60
|
+
self.rai_client = AIProjectClient(endpoint=azure_ai_project, credential=credential)
|
|
61
61
|
self.adversarial_template_handler = AdversarialTemplateHandler(
|
|
62
62
|
azure_ai_project=self.azure_ai_project, rai_client=self.rai_client
|
|
63
63
|
)
|
|
@@ -6,6 +6,7 @@ import os
|
|
|
6
6
|
from typing import Dict, List, Optional, Union
|
|
7
7
|
|
|
8
8
|
from azure.core.credentials import TokenCredential
|
|
9
|
+
from azure.core.pipeline.policies import UserAgentPolicy
|
|
9
10
|
from azure.ai.evaluation._model_configurations import AzureAIProject
|
|
10
11
|
from azure.ai.evaluation.simulator._model_tools import ManagedIdentityAPITokenManager
|
|
11
12
|
from azure.ai.evaluation._common.raiclient import MachineLearningServicesClient
|
|
@@ -13,30 +14,35 @@ from azure.ai.evaluation._constants import TokenScope
|
|
|
13
14
|
from azure.ai.evaluation._common.utils import is_onedp_project
|
|
14
15
|
from azure.ai.evaluation._common.onedp import AIProjectClient
|
|
15
16
|
from azure.ai.evaluation._common import EvaluationServiceOneDPClient
|
|
17
|
+
from azure.ai.evaluation._user_agent import UserAgentSingleton
|
|
16
18
|
import jwt
|
|
17
19
|
import time
|
|
18
20
|
import ast
|
|
19
21
|
|
|
22
|
+
|
|
20
23
|
class GeneratedRAIClient:
|
|
21
24
|
"""Client for the Responsible AI Service using the auto-generated MachineLearningServicesClient.
|
|
22
|
-
|
|
23
|
-
:param azure_ai_project: The
|
|
24
|
-
|
|
25
|
+
|
|
26
|
+
:param azure_ai_project: The Azure AI project, which can either be a string representing the project endpoint
|
|
27
|
+
or an instance of AzureAIProject. It contains subscription id, resource group, and project name.
|
|
28
|
+
:type azure_ai_project: Union[str, ~azure.ai.evaluation.AzureAIProject]
|
|
25
29
|
:param token_manager: The token manager
|
|
26
30
|
:type token_manager: ~azure.ai.evaluation.simulator._model_tools._identity_manager.APITokenManager
|
|
27
31
|
"""
|
|
28
|
-
|
|
32
|
+
|
|
29
33
|
def __init__(self, azure_ai_project: Union[AzureAIProject, str], token_manager: ManagedIdentityAPITokenManager):
|
|
30
34
|
self.azure_ai_project = azure_ai_project
|
|
31
35
|
self.token_manager = token_manager
|
|
32
|
-
|
|
36
|
+
|
|
37
|
+
user_agent_policy = UserAgentPolicy(base_user_agent=UserAgentSingleton().value)
|
|
38
|
+
|
|
33
39
|
if not is_onedp_project(azure_ai_project):
|
|
34
40
|
# Service URL construction
|
|
35
41
|
if "RAI_SVC_URL" in os.environ:
|
|
36
42
|
endpoint = os.environ["RAI_SVC_URL"].rstrip("/")
|
|
37
43
|
else:
|
|
38
44
|
endpoint = self._get_service_discovery_url()
|
|
39
|
-
|
|
45
|
+
|
|
40
46
|
# Create the autogenerated client
|
|
41
47
|
self._client = MachineLearningServicesClient(
|
|
42
48
|
endpoint=endpoint,
|
|
@@ -46,19 +52,24 @@ class GeneratedRAIClient:
|
|
|
46
52
|
credential=self.token_manager,
|
|
47
53
|
).rai_svc
|
|
48
54
|
else:
|
|
49
|
-
self._client = AIProjectClient(
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
self._client = AIProjectClient(
|
|
56
|
+
endpoint=azure_ai_project, credential=token_manager, user_agent_policy=user_agent_policy
|
|
57
|
+
).red_teams
|
|
58
|
+
self._evaluation_onedp_client = EvaluationServiceOneDPClient(
|
|
59
|
+
endpoint=azure_ai_project, credential=token_manager, user_agent_policy=user_agent_policy
|
|
60
|
+
)
|
|
61
|
+
|
|
52
62
|
def _get_service_discovery_url(self):
|
|
53
63
|
"""Get the service discovery URL.
|
|
54
|
-
|
|
64
|
+
|
|
55
65
|
:return: The service discovery URL
|
|
56
66
|
:rtype: str
|
|
57
67
|
"""
|
|
58
68
|
import requests
|
|
69
|
+
|
|
59
70
|
bearer_token = self._fetch_or_reuse_token(self.token_manager)
|
|
60
71
|
headers = {"Authorization": f"Bearer {bearer_token}", "Content-Type": "application/json"}
|
|
61
|
-
|
|
72
|
+
|
|
62
73
|
response = requests.get(
|
|
63
74
|
f"https://management.azure.com/subscriptions/{self.azure_ai_project['subscription_id']}/"
|
|
64
75
|
f"resourceGroups/{self.azure_ai_project['resource_group_name']}/"
|
|
@@ -67,7 +78,7 @@ class GeneratedRAIClient:
|
|
|
67
78
|
headers=headers,
|
|
68
79
|
timeout=5,
|
|
69
80
|
)
|
|
70
|
-
|
|
81
|
+
|
|
71
82
|
if response.status_code != 200:
|
|
72
83
|
msg = (
|
|
73
84
|
f"Failed to connect to your Azure AI project. Please check if the project scope is configured "
|
|
@@ -78,51 +89,71 @@ class GeneratedRAIClient:
|
|
|
78
89
|
|
|
79
90
|
# Parse the discovery URL
|
|
80
91
|
from urllib.parse import urlparse
|
|
92
|
+
|
|
81
93
|
base_url = urlparse(response.json()["properties"]["discoveryUrl"])
|
|
82
94
|
return f"{base_url.scheme}://{base_url.netloc}"
|
|
83
|
-
|
|
84
|
-
async def get_attack_objectives(
|
|
95
|
+
|
|
96
|
+
async def get_attack_objectives(
|
|
97
|
+
self,
|
|
98
|
+
*,
|
|
99
|
+
risk_type: Optional[str] = None,
|
|
100
|
+
risk_category: Optional[str] = None,
|
|
101
|
+
application_scenario: str = None,
|
|
102
|
+
strategy: Optional[str] = None,
|
|
103
|
+
scan_session_id: Optional[str] = None,
|
|
104
|
+
) -> Dict:
|
|
85
105
|
"""Get attack objectives using the auto-generated operations.
|
|
86
|
-
|
|
106
|
+
|
|
107
|
+
:param risk_type: Optional risk type to filter the attack objectives
|
|
108
|
+
:type risk_type: Optional[str]
|
|
87
109
|
:param risk_category: Optional risk category to filter the attack objectives
|
|
88
110
|
:type risk_category: Optional[str]
|
|
89
111
|
:param application_scenario: Optional description of the application scenario for context
|
|
90
112
|
:type application_scenario: str
|
|
91
113
|
:param strategy: Optional strategy to filter the attack objectives
|
|
92
114
|
:type strategy: Optional[str]
|
|
115
|
+
:param scan_session_id: Optional unique session ID for the scan
|
|
116
|
+
:type scan_session_id: Optional[str]
|
|
93
117
|
:return: The attack objectives
|
|
94
118
|
:rtype: Dict
|
|
95
|
-
"""
|
|
119
|
+
"""
|
|
96
120
|
try:
|
|
97
121
|
# Send the request using the autogenerated client
|
|
98
122
|
response = self._client.get_attack_objectives(
|
|
99
|
-
risk_types=[
|
|
123
|
+
risk_types=[risk_type],
|
|
124
|
+
risk_category=risk_category,
|
|
100
125
|
lang="en",
|
|
101
126
|
strategy=strategy,
|
|
127
|
+
headers={"client_request_id": scan_session_id},
|
|
102
128
|
)
|
|
103
129
|
return response
|
|
104
|
-
|
|
130
|
+
|
|
105
131
|
except Exception as e:
|
|
106
132
|
# Log the exception for debugging purposes
|
|
107
133
|
import logging
|
|
134
|
+
|
|
108
135
|
logging.error(f"Error in get_attack_objectives: {str(e)}")
|
|
109
136
|
raise
|
|
110
|
-
|
|
111
|
-
async def get_jailbreak_prefixes(self) -> List[str]:
|
|
137
|
+
|
|
138
|
+
async def get_jailbreak_prefixes(self, scan_session_id: Optional[str] = None) -> List[str]:
|
|
112
139
|
"""Get jailbreak prefixes using the auto-generated operations.
|
|
113
|
-
|
|
140
|
+
|
|
141
|
+
:param scan_session_id: Optional unique session ID for the scan
|
|
142
|
+
:type scan_session_id: Optional[str]
|
|
114
143
|
:return: The jailbreak prefixes
|
|
115
144
|
:rtype: List[str]
|
|
116
145
|
"""
|
|
117
146
|
try:
|
|
118
147
|
# Send the request using the autogenerated client
|
|
119
|
-
response = self._client.get_jail_break_dataset_with_type(
|
|
148
|
+
response = self._client.get_jail_break_dataset_with_type(
|
|
149
|
+
type="upia", headers={"client_request_id": scan_session_id}
|
|
150
|
+
)
|
|
120
151
|
if isinstance(response, list):
|
|
121
152
|
return response
|
|
122
153
|
else:
|
|
123
154
|
self.logger.error("Unexpected response format from get_jail_break_dataset_with_type")
|
|
124
155
|
raise ValueError("Unexpected response format from get_jail_break_dataset_with_type")
|
|
125
|
-
|
|
156
|
+
|
|
126
157
|
except Exception as e:
|
|
127
158
|
return [""]
|
|
128
159
|
|
|
@@ -78,7 +78,13 @@ class APITokenManager(ABC):
|
|
|
78
78
|
|
|
79
79
|
@abstractmethod
|
|
80
80
|
def get_token(
|
|
81
|
-
|
|
81
|
+
self,
|
|
82
|
+
scopes: Union[str, None] = None,
|
|
83
|
+
claims: Union[str, None] = None,
|
|
84
|
+
tenant_id: Union[str, None] = None,
|
|
85
|
+
enable_cae: bool = False,
|
|
86
|
+
**kwargs: Any,
|
|
87
|
+
) -> AccessToken:
|
|
82
88
|
"""Async method to get the API token. Subclasses should implement this method.
|
|
83
89
|
|
|
84
90
|
:return: API token
|
|
@@ -9,7 +9,7 @@ import uuid
|
|
|
9
9
|
from typing import Any, Dict, List, Optional, cast, Union
|
|
10
10
|
|
|
11
11
|
from azure.ai.evaluation._http_utils import AsyncHttpPipeline, get_async_http_client
|
|
12
|
-
from azure.ai.evaluation._user_agent import
|
|
12
|
+
from azure.ai.evaluation._user_agent import UserAgentSingleton
|
|
13
13
|
from azure.core.exceptions import HttpResponseError
|
|
14
14
|
from azure.core.pipeline.policies import AsyncRetryPolicy, RetryMode
|
|
15
15
|
from azure.ai.evaluation._common.onedp._client import AIProjectClient
|
|
@@ -43,15 +43,15 @@ class SimulationRequestDTO:
|
|
|
43
43
|
headers: Dict[str, str],
|
|
44
44
|
payload: Dict[str, Any],
|
|
45
45
|
params: Dict[str, str],
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
templateKey: str,
|
|
47
|
+
templateParameters: Optional[TemplateParameters],
|
|
48
48
|
):
|
|
49
49
|
self.url = url
|
|
50
50
|
self.headers = headers
|
|
51
51
|
self.json = json.dumps(payload)
|
|
52
52
|
self.params = params
|
|
53
|
-
self.
|
|
54
|
-
self.templateParameters =
|
|
53
|
+
self.templateKey = templateKey
|
|
54
|
+
self.templateParameters = templateParameters
|
|
55
55
|
|
|
56
56
|
def to_dict(self) -> Dict:
|
|
57
57
|
"""Convert the DTO to a dictionary.
|
|
@@ -166,7 +166,7 @@ class ProxyChatCompletionsModel(OpenAIChatCompletionsModel):
|
|
|
166
166
|
proxy_headers = {
|
|
167
167
|
"Authorization": f"Bearer {token}",
|
|
168
168
|
"Content-Type": "application/json",
|
|
169
|
-
"User-Agent":
|
|
169
|
+
"User-Agent": UserAgentSingleton().value,
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
headers = {
|
|
@@ -186,14 +186,14 @@ class ProxyChatCompletionsModel(OpenAIChatCompletionsModel):
|
|
|
186
186
|
headers=headers,
|
|
187
187
|
payload=request_data,
|
|
188
188
|
params=params,
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
templateKey=self.tkey,
|
|
190
|
+
templateParameters=self.tparam,
|
|
191
191
|
)
|
|
192
192
|
|
|
193
193
|
time_start = time.time()
|
|
194
194
|
full_response = None
|
|
195
195
|
|
|
196
|
-
if
|
|
196
|
+
if isinstance(session, AIProjectClient):
|
|
197
197
|
sim_request_dto = SimulationDTO(
|
|
198
198
|
headers=headers,
|
|
199
199
|
params=params,
|
|
@@ -203,11 +203,20 @@ class ProxyChatCompletionsModel(OpenAIChatCompletionsModel):
|
|
|
203
203
|
)
|
|
204
204
|
response_data = session.red_teams.submit_simulation(sim_request_dto, headers=headers, params=params)
|
|
205
205
|
operation_id = response_data["location"].split("/")[-1]
|
|
206
|
-
|
|
206
|
+
|
|
207
207
|
request_count = 0
|
|
208
208
|
flag = True
|
|
209
209
|
while flag:
|
|
210
|
-
|
|
210
|
+
try:
|
|
211
|
+
response = session.evaluations.operation_results(operation_id, headers=headers)
|
|
212
|
+
except Exception as e:
|
|
213
|
+
from types import SimpleNamespace # pylint: disable=forgotten-debug-statement
|
|
214
|
+
|
|
215
|
+
response = SimpleNamespace(status_code=202, text=str(e), json=lambda: {"error": str(e)})
|
|
216
|
+
if isinstance(response, dict):
|
|
217
|
+
response_data = response
|
|
218
|
+
flag = False
|
|
219
|
+
break
|
|
211
220
|
if response.status_code == 200:
|
|
212
221
|
response_data = cast(List[Dict], response.json())
|
|
213
222
|
flag = False
|
|
@@ -220,10 +229,11 @@ class ProxyChatCompletionsModel(OpenAIChatCompletionsModel):
|
|
|
220
229
|
# response.raise_for_status()
|
|
221
230
|
if response.status_code != 202:
|
|
222
231
|
raise HttpResponseError(
|
|
223
|
-
message=f"Received unexpected HTTP status: {response.status_code} {response.text()}",
|
|
232
|
+
message=f"Received unexpected HTTP status: {response.status_code} {response.text()}",
|
|
233
|
+
response=response,
|
|
224
234
|
)
|
|
225
235
|
response_data = response.json()
|
|
226
|
-
|
|
236
|
+
|
|
227
237
|
self.result_url = cast(str, response_data["location"])
|
|
228
238
|
retry_policy = AsyncRetryPolicy( # set up retry configuration
|
|
229
239
|
retry_on_status_codes=[202], # on which statuses to retry
|
|
@@ -244,14 +254,14 @@ class ProxyChatCompletionsModel(OpenAIChatCompletionsModel):
|
|
|
244
254
|
proxy_headers = {
|
|
245
255
|
"Authorization": f"Bearer {token}",
|
|
246
256
|
"Content-Type": "application/json",
|
|
247
|
-
"User-Agent":
|
|
257
|
+
"User-Agent": UserAgentSingleton().value,
|
|
248
258
|
}
|
|
249
259
|
response = await exp_retry_client.get( # pylint: disable=too-many-function-args,unexpected-keyword-arg
|
|
250
260
|
self.result_url, headers=proxy_headers
|
|
251
261
|
)
|
|
252
262
|
response.raise_for_status()
|
|
253
263
|
response_data = response.json()
|
|
254
|
-
|
|
264
|
+
|
|
255
265
|
self.logger.info("Response: %s", response_data)
|
|
256
266
|
|
|
257
267
|
# Copy the full response and return it to be saved in jsonl.
|
|
@@ -10,7 +10,7 @@ import json
|
|
|
10
10
|
from azure.ai.evaluation._exceptions import ErrorBlame, ErrorCategory, ErrorTarget, EvaluationException
|
|
11
11
|
from azure.ai.evaluation._http_utils import AsyncHttpPipeline, get_async_http_client, get_http_client
|
|
12
12
|
from azure.ai.evaluation._model_configurations import AzureAIProject
|
|
13
|
-
from azure.ai.evaluation._user_agent import
|
|
13
|
+
from azure.ai.evaluation._user_agent import UserAgentSingleton
|
|
14
14
|
from azure.core.pipeline.policies import AsyncRetryPolicy, RetryMode
|
|
15
15
|
|
|
16
16
|
from ._identity_manager import APITokenManager
|
|
@@ -148,7 +148,7 @@ class RAIClient: # pylint: disable=client-accepts-api-version-keyword
|
|
|
148
148
|
headers = {
|
|
149
149
|
"Authorization": f"Bearer {token}",
|
|
150
150
|
"Content-Type": "application/json",
|
|
151
|
-
"User-Agent":
|
|
151
|
+
"User-Agent": UserAgentSingleton().value,
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
session = self._create_async_client()
|
|
@@ -184,7 +184,7 @@ class RAIClient: # pylint: disable=client-accepts-api-version-keyword
|
|
|
184
184
|
headers = {
|
|
185
185
|
"Authorization": f"Bearer {token}",
|
|
186
186
|
"Content-Type": "application/json",
|
|
187
|
-
"User-Agent":
|
|
187
|
+
"User-Agent": UserAgentSingleton().value,
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
session = self._create_async_client()
|
|
@@ -209,9 +209,11 @@ class RAIClient: # pylint: disable=client-accepts-api-version-keyword
|
|
|
209
209
|
blame=ErrorBlame.USER_ERROR,
|
|
210
210
|
)
|
|
211
211
|
|
|
212
|
-
async def get_attack_objectives(
|
|
212
|
+
async def get_attack_objectives(
|
|
213
|
+
self, risk_categories: List[str], application_scenario: str = None, strategy: str = None
|
|
214
|
+
) -> Any:
|
|
213
215
|
"""Get the attack objectives based on risk categories and application scenario
|
|
214
|
-
|
|
216
|
+
|
|
215
217
|
:param risk_categories: List of risk categories to generate attack objectives for
|
|
216
218
|
:type risk_categories: List[str]
|
|
217
219
|
:param application_scenario: Optional description of the application scenario for context
|
|
@@ -224,17 +226,17 @@ class RAIClient: # pylint: disable=client-accepts-api-version-keyword
|
|
|
224
226
|
# Create query parameters for the request
|
|
225
227
|
if application_scenario:
|
|
226
228
|
raise NotImplementedError("Application scenario is not supported yet")
|
|
227
|
-
|
|
229
|
+
|
|
228
230
|
params = {
|
|
229
231
|
"api-version": "2022-11-01-preview",
|
|
230
232
|
"riskTypes": ",".join(risk_categories),
|
|
231
|
-
"lang": "en" # Default to English
|
|
233
|
+
"lang": "en", # Default to English
|
|
232
234
|
}
|
|
233
|
-
|
|
235
|
+
|
|
234
236
|
# Add strategy parameter if provided
|
|
235
237
|
if strategy:
|
|
236
238
|
params["strategy"] = strategy
|
|
237
|
-
|
|
239
|
+
|
|
238
240
|
try:
|
|
239
241
|
# Make the request using the existing get method
|
|
240
242
|
result = await self.get(self.attack_objectives_endpoint)
|
|
@@ -254,25 +256,11 @@ class RAIClient: # pylint: disable=client-accepts-api-version-keyword
|
|
|
254
256
|
except Exception:
|
|
255
257
|
# If the API fails or isn't implemented yet, return a mock response
|
|
256
258
|
# This is temporary until the API endpoint is fully implemented
|
|
257
|
-
return [
|
|
258
|
-
|
|
259
|
-
"lang": "en",
|
|
260
|
-
"
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
]
|
|
266
|
-
},
|
|
267
|
-
"messages": [
|
|
268
|
-
{
|
|
269
|
-
"role": "user",
|
|
270
|
-
"content": "Risky content"
|
|
271
|
-
}
|
|
272
|
-
],
|
|
273
|
-
"modality": "text",
|
|
274
|
-
"source": [
|
|
275
|
-
"source"
|
|
276
|
-
]
|
|
277
|
-
}]
|
|
278
|
-
|
|
259
|
+
return [
|
|
260
|
+
{
|
|
261
|
+
"metadata": {"lang": "en", "target_harms": [{"risk-type": "violence", "risk-subtype": ""}]},
|
|
262
|
+
"messages": [{"role": "user", "content": "Risky content"}],
|
|
263
|
+
"modality": "text",
|
|
264
|
+
"source": ["source"],
|
|
265
|
+
}
|
|
266
|
+
]
|
|
@@ -8,6 +8,7 @@ from typing_extensions import NotRequired
|
|
|
8
8
|
|
|
9
9
|
from azure.ai.evaluation._model_configurations import AzureAIProject
|
|
10
10
|
from azure.ai.evaluation._common.onedp._client import AIProjectClient
|
|
11
|
+
from azure.ai.evaluation.simulator._adversarial_scenario import AdversarialScenario
|
|
11
12
|
|
|
12
13
|
from ._rai_client import RAIClient
|
|
13
14
|
|
|
@@ -148,14 +149,16 @@ class AdversarialTemplateHandler:
|
|
|
148
149
|
"""
|
|
149
150
|
Initialize the AdversarialTemplateHandler.
|
|
150
151
|
|
|
151
|
-
:param azure_ai_project: The Azure AI project, which can either be a string representing the project endpoint
|
|
152
|
-
or an instance of AzureAIProject. It contains subscription id, resource group, and project name.
|
|
152
|
+
:param azure_ai_project: The Azure AI project, which can either be a string representing the project endpoint
|
|
153
|
+
or an instance of AzureAIProject. It contains subscription id, resource group, and project name.
|
|
153
154
|
:type azure_ai_project: Union[str, AzureAIProject]
|
|
154
155
|
:param rai_client: The RAI client or AI Project client used for fetching parameters.
|
|
155
156
|
:type rai_client: Union[~azure.ai.evaluation.simulator._model_tools.RAIClient, ~azure.ai.evaluation._common.onedp._client.AIProjectClient]
|
|
156
157
|
"""
|
|
157
158
|
|
|
158
|
-
def __init__(
|
|
159
|
+
def __init__(
|
|
160
|
+
self, azure_ai_project: Union[str, AzureAIProject], rai_client: Union[RAIClient, AIProjectClient]
|
|
161
|
+
) -> None:
|
|
159
162
|
self.azure_ai_project = azure_ai_project
|
|
160
163
|
self.categorized_ch_parameters: Optional[Dict[str, _CategorizedParameter]] = None
|
|
161
164
|
self.rai_client = rai_client
|
|
@@ -164,12 +167,11 @@ class AdversarialTemplateHandler:
|
|
|
164
167
|
if self.categorized_ch_parameters is None:
|
|
165
168
|
categorized_parameters: Dict[str, _CategorizedParameter] = {}
|
|
166
169
|
util = ContentHarmTemplatesUtils
|
|
167
|
-
|
|
168
170
|
if isinstance(self.rai_client, RAIClient):
|
|
169
171
|
parameters = await self.rai_client.get_contentharm_parameters()
|
|
170
172
|
elif isinstance(self.rai_client, AIProjectClient):
|
|
171
173
|
parameters = literal_eval(self.rai_client.red_teams.get_template_parameters())
|
|
172
|
-
|
|
174
|
+
|
|
173
175
|
for k in parameters.keys():
|
|
174
176
|
template_key = util.get_template_key(k)
|
|
175
177
|
categorized_parameters[template_key] = {
|
|
@@ -181,17 +183,29 @@ class AdversarialTemplateHandler:
|
|
|
181
183
|
|
|
182
184
|
template_category = collection_key.split("adv_")[-1]
|
|
183
185
|
|
|
186
|
+
# Handle both qa_enterprise and qa_documents mapping to qa
|
|
187
|
+
if template_category in ["qa_enterprise", "qa_documents"]:
|
|
188
|
+
template_category = "qa"
|
|
189
|
+
|
|
184
190
|
plist = self.categorized_ch_parameters
|
|
185
191
|
ch_templates = []
|
|
192
|
+
|
|
186
193
|
for key, value in plist.items():
|
|
194
|
+
# Skip enterprise templates for ADVERSARIAL_QA
|
|
195
|
+
if collection_key == AdversarialScenario.ADVERSARIAL_QA.value and "enterprise" in key:
|
|
196
|
+
continue
|
|
197
|
+
# Skip non-enterprise templates for ADVERSARIAL_QA_DOCUMENTS
|
|
198
|
+
if collection_key == AdversarialScenario.ADVERSARIAL_QA_DOCUMENTS.value and "enterprise" not in key:
|
|
199
|
+
continue
|
|
200
|
+
|
|
187
201
|
if value["category"] == template_category:
|
|
188
202
|
params = value["parameters"]
|
|
189
203
|
for p in params:
|
|
190
204
|
p.update({"ch_template_placeholder": "{{ch_template_placeholder}}"})
|
|
191
205
|
|
|
192
206
|
template = AdversarialTemplate(template_name=key, text=None, context_key=[], template_parameters=params)
|
|
193
|
-
|
|
194
207
|
ch_templates.append(template)
|
|
208
|
+
|
|
195
209
|
return ch_templates
|
|
196
210
|
|
|
197
211
|
def get_template(self, template_name: str) -> Optional[AdversarialTemplate]:
|