From 55ba423fc751152756e08feec408a653e68f394c Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Thu, 1 May 2025 23:27:18 +0000 Subject: [PATCH 01/11] Port to redteaming --- .devcontainer/devcontainer.json | 3 +- .vscode/launch.json | 8 +++ evals/requirements.txt | 4 +- evals/safety_evaluation.py | 108 ++++++++++++++++---------------- infra/main.bicep | 30 +++++++++ 5 files changed, 95 insertions(+), 58 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 3a19de41..92bea28a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -33,7 +33,8 @@ "mtxr.sqltools", "mtxr.sqltools-driver-pg", "ms-vscode.vscode-node-azure-pack", - "esbenp.prettier-vscode" + "esbenp.prettier-vscode", + "twixes.pypi-assistant" ], // Set *default* container specific settings.json values on container create. "settings": { diff --git a/.vscode/launch.json b/.vscode/launch.json index 4c233e69..d6c07eaf 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -21,6 +21,14 @@ "module": "uvicorn", "args": ["fastapi_app:create_app", "--factory", "--reload"], "justMyCode": false + }, + { + "name": "Python: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "justMyCode": false } ], "compounds": [ diff --git a/evals/requirements.txt b/evals/requirements.txt index ef8aea4d..cd98f541 100644 --- a/evals/requirements.txt +++ b/evals/requirements.txt @@ -1,4 +1,4 @@ -git+https://github.com/Azure-Samples/ai-rag-chat-evaluator/@2025-02-06b -azure-ai-evaluation +git+https://github.com/Azure-Samples/ai-rag-chat-evaluator/@2025-05-01 +azure-ai-evaluation[redteam]>=1.5.0 rich dotenv-azd diff --git a/evals/safety_evaluation.py b/evals/safety_evaluation.py index 18fc404f..37781e47 100644 --- a/evals/safety_evaluation.py +++ b/evals/safety_evaluation.py @@ -1,25 +1,23 @@ import argparse import asyncio -import json import logging import os import pathlib +import sys from enum import Enum import requests -from azure.ai.evaluation import AzureAIProject, ContentSafetyEvaluator -from azure.ai.evaluation.simulator import ( - AdversarialScenario, - AdversarialSimulator, - SupportedLanguages, -) +from azure.ai.evaluation import AzureAIProject +from azure.ai.evaluation.red_team import AttackStrategy, RedTeam, RiskCategory from azure.identity import AzureDeveloperCliCredential from dotenv_azd import load_azd_env from rich.logging import RichHandler -from rich.progress import track logger = logging.getLogger("ragapp") +# Configure logging to capture and display warnings with tracebacks +logging.captureWarnings(True) # Capture warnings as log messages + root_dir = pathlib.Path(__file__).parent @@ -47,11 +45,10 @@ def get_azure_credential(): async def callback( - messages: dict, + messages: list, target_url: str = "http://127.0.0.1:8000/chat", ): - messages_list = messages["messages"] - query = messages_list[-1]["content"] + query = messages[-1].content headers = {"Content-Type": "application/json"} body = { "messages": [{"content": query, "role": "user"}], @@ -65,7 +62,7 @@ async def callback( message = {"content": response["error"], "role": "assistant"} else: message = response["message"] - return {"messages": messages_list + [message]} + return {"messages": messages + [message]} async def run_simulator(target_url: str, max_simulations: int): @@ -75,50 +72,35 @@ async def run_simulator(target_url: str, max_simulations: int): "resource_group_name": os.environ["AZURE_RESOURCE_GROUP"], "project_name": os.environ["AZURE_AI_PROJECT"], } - - # Simulate single-turn question-and-answering against the app - scenario = AdversarialScenario.ADVERSARIAL_QA - adversarial_simulator = AdversarialSimulator(azure_ai_project=azure_ai_project, credential=credential) - - outputs = await adversarial_simulator( - scenario=scenario, + model_red_team = RedTeam( + azure_ai_project=azure_ai_project, + credential=credential, + risk_categories=[ + RiskCategory.Violence, + # RiskCategory.HateUnfairness, + # RiskCategory.Sexual, + # RiskCategory.SelfHarm, + ], + num_objectives=1, + ) + await model_red_team.scan( target=lambda messages, stream=False, session_state=None, context=None: callback(messages, target_url), - max_simulation_results=max_simulations, - language=SupportedLanguages.English, # Match this to your app language - randomization_seed=1, # For more consistent results, use a fixed seed + scan_name="Advanced-Callback-Scan", + attack_strategies=[ + AttackStrategy.EASY, # Group of easy complexity attacks + # AttackStrategy.MODERATE, # Group of moderate complexity attacks + # AttackStrategy.CharacterSpace, # Add character spaces + # AttackStrategy.ROT13, # Use ROT13 encoding + # AttackStrategy.UnicodeConfusable, # Use confusable Unicode characters + # AttackStrategy.CharSwap, # Swap characters in prompts + # AttackStrategy.Morse, # Encode prompts in Morse code + # AttackStrategy.Leetspeak, # Use Leetspeak + # AttackStrategy.Url, # Use URLs in prompts + # AttackStrategy.Binary, # Encode prompts in binary + ], + output_path="Advanced-Callback-Scan.json", ) - # Run safety evaluation on the outputs and save the scores - # Do not save the outputs, as they may contain disturbing content - evaluators = ["hate_unfairness", "sexual", "violence", "self_harm"] - summary_scores = { - evaluator: {"low_count": 0, "score_total": 0.0, "mean_score": 0.0, "low_rate": 0.0} for evaluator in evaluators - } - for output in track(outputs, description="Evaluating simulated responses..."): - query = output["messages"][0]["content"] - answer = output["messages"][1]["content"] - safety_eval = ContentSafetyEvaluator(credential=credential, azure_ai_project=azure_ai_project) - eval_score = safety_eval(query=query, response=answer) - for evaluator in evaluators: - severity_level = HarmSeverityLevel(eval_score[evaluator]) - if severity_level == HarmSeverityLevel.VeryLow or severity_level == HarmSeverityLevel.Low.name: - summary_scores[evaluator]["low_count"] += 1 - else: - logger.info(f"Failing score from:\nQ: {query}\nA: {answer}\n{evaluator} score: {eval_score}") - numeric_severity_score = eval_score[f"{evaluator}_score"] - if isinstance(numeric_severity_score, float) or isinstance(numeric_severity_score, int): - summary_scores[evaluator]["score_total"] += numeric_severity_score - - # Compute the overall statistics - for evaluator in evaluators: - if len(outputs) > 0: - summary_scores[evaluator]["mean_score"] = summary_scores[evaluator]["score_total"] / len(outputs) - summary_scores[evaluator]["low_rate"] = summary_scores[evaluator]["low_count"] / len(outputs) - - # Save summary scores - with open(root_dir / "safety_results.json", "w") as f: - json.dump(summary_scores, f, indent=2) - if __name__ == "__main__": parser = argparse.ArgumentParser(description="Run safety evaluation simulator.") @@ -130,10 +112,26 @@ async def run_simulator(target_url: str, max_simulations: int): ) args = parser.parse_args() + # Configure logging to show tracebacks for warnings and above logging.basicConfig( - level=logging.WARNING, format="%(message)s", datefmt="[%X]", handlers=[RichHandler(rich_tracebacks=True)] + level=logging.WARNING, + format="%(message)s", + datefmt="[%X]", + handlers=[RichHandler(rich_tracebacks=True, show_path=True)], ) + + # Set urllib3 and azure libraries to WARNING level to see connection issues + logging.getLogger("urllib3").setLevel(logging.WARNING) + logging.getLogger("azure").setLevel(logging.DEBUG) + logging.getLogger("RedTeamLogger").setLevel(logging.DEBUG) + + # Set our application logger to INFO level logger.setLevel(logging.INFO) + load_azd_env() - asyncio.run(run_simulator(args.target_url, args.max_simulations)) + try: + asyncio.run(run_simulator(args.target_url, args.max_simulations)) + except Exception: + logging.exception("Unhandled exception in safety evaluation") + sys.exit(1) diff --git a/infra/main.bicep b/infra/main.bicep index a55c5c8b..509e025b 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -408,6 +408,24 @@ module openAI 'core/ai/cognitiveservices.bicep' = if (deployAzureOpenAI) { } } +module storage 'br/public:avm/res/storage/storage-account:0.9.1' = if (useAiProject) { + name: 'storage' + scope: resourceGroup + params: { + name: '${take(replace(prefix, '-', ''), 17)}storage' + location: location + tags: tags + kind: 'StorageV2' + skuName: 'Standard_LRS' + networkAcls: { + defaultAction: 'Allow' + bypass: 'AzureServices' + } + allowBlobPublicAccess: false + allowSharedKeyAccess: false + } +} + module ai 'core/ai/ai-environment.bicep' = if (useAiProject) { name: 'ai' scope: resourceGroup @@ -417,6 +435,7 @@ module ai 'core/ai/ai-environment.bicep' = if (useAiProject) { hubName: 'aihub-${resourceToken}' projectName: 'aiproj-${resourceToken}' applicationInsightsId: monitoring.outputs.applicationInsightsId + storageAccountId: storage.outputs.resourceId } } @@ -442,6 +461,17 @@ module openAIRoleBackend 'core/security/role.bicep' = { } } +// Application Insights Reader role for web app +module appInsightsReaderRole 'core/security/role.bicep' = { + scope: resourceGroup + name: 'appinsights-reader-role' + params: { + principalId: principalId + roleDefinitionId: '43d0d8ad-25c7-4714-9337-8ba259a9fe05' // Application Insights Component Reader + principalType: 'User' + } +} + output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId output AZURE_RESOURCE_GROUP string = resourceGroup.name From b78bc41a1c5facd8aba29ddb45e2e0a2515d0269 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Mon, 5 May 2025 17:10:20 +0000 Subject: [PATCH 02/11] add red teaming changes --- evals/safety_evaluation.py | 2 +- infra/main.bicep | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/evals/safety_evaluation.py b/evals/safety_evaluation.py index 37781e47..66d1703e 100644 --- a/evals/safety_evaluation.py +++ b/evals/safety_evaluation.py @@ -117,7 +117,7 @@ async def run_simulator(target_url: str, max_simulations: int): level=logging.WARNING, format="%(message)s", datefmt="[%X]", - handlers=[RichHandler(rich_tracebacks=True, show_path=True)], + handlers=[RichHandler(rich_tracebacks=False, show_path=True)], ) # Set urllib3 and azure libraries to WARNING level to see connection issues diff --git a/infra/main.bicep b/infra/main.bicep index 509e025b..01a9041e 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -423,6 +423,52 @@ module storage 'br/public:avm/res/storage/storage-account:0.9.1' = if (useAiProj } allowBlobPublicAccess: false allowSharedKeyAccess: false + roleAssignments: [ + { + principalId: principalId + principalType: 'User' + roleDefinitionIdOrName: 'Storage Blob Data Contributor' + } + ] + blobServices: { + containers: [ + { + name: 'default' + publicAccess: 'None' + } + ] + cors: { + corsRules: [ + { + allowedOrigins: [ + 'https://mlworkspace.azure.ai' + 'https://ml.azure.com' + 'https://*.ml.azure.com' + 'https://ai.azure.com' + 'https://*.ai.azure.com' + 'https://mlworkspacecanary.azure.ai' + 'https://mlworkspace.azureml-test.net' + ] + allowedMethods: [ + 'GET' + 'HEAD' + 'POST' + 'PUT' + 'DELETE' + 'OPTIONS' + 'PATCH' + ] + maxAgeInSeconds: 1800 + exposedHeaders: [ + '*' + ] + allowedHeaders: [ + '*' + ] + } + ] + } + } } } From 5830c0fee6e7f6281c98f84646f29675764c71c7 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Wed, 7 May 2025 18:04:48 +0000 Subject: [PATCH 03/11] Try better project --- .devcontainer/devcontainer.json | 3 ++- evals/safety_evaluation.py | 21 ++++----------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 92bea28a..6c8f410f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -34,7 +34,8 @@ "mtxr.sqltools-driver-pg", "ms-vscode.vscode-node-azure-pack", "esbenp.prettier-vscode", - "twixes.pypi-assistant" + "twixes.pypi-assistant", + "ms-python.vscode-python-envs" ], // Set *default* container specific settings.json values on container create. "settings": { diff --git a/evals/safety_evaluation.py b/evals/safety_evaluation.py index 66d1703e..36b27db4 100644 --- a/evals/safety_evaluation.py +++ b/evals/safety_evaluation.py @@ -4,7 +4,6 @@ import os import pathlib import sys -from enum import Enum import requests from azure.ai.evaluation import AzureAIProject @@ -21,18 +20,6 @@ root_dir = pathlib.Path(__file__).parent -class HarmSeverityLevel(Enum): - """Harm severity levels reported by the Azure AI Evaluator service. - These constants have been copied from the azure-ai-evaluation package, - where they're currently in a private module. - """ - - VeryLow = "Very low" - Low = "Low" - Medium = "Medium" - High = "High" - - def get_azure_credential(): AZURE_TENANT_ID = os.getenv("AZURE_TENANT_ID") if AZURE_TENANT_ID: @@ -68,9 +55,9 @@ async def callback( async def run_simulator(target_url: str, max_simulations: int): credential = get_azure_credential() azure_ai_project: AzureAIProject = { - "subscription_id": os.environ["AZURE_SUBSCRIPTION_ID"], - "resource_group_name": os.environ["AZURE_RESOURCE_GROUP"], - "project_name": os.environ["AZURE_AI_PROJECT"], + "subscription_id": os.getenv("AZURE_SUBSCRIPTION_ID"), + "resource_group_name": os.getenv("AZURE_RESOURCE_GROUP"), + "project_name": "pf-testprojforaisaety", } model_red_team = RedTeam( azure_ai_project=azure_ai_project, @@ -114,7 +101,7 @@ async def run_simulator(target_url: str, max_simulations: int): # Configure logging to show tracebacks for warnings and above logging.basicConfig( - level=logging.WARNING, + level=logging.DEBUG, format="%(message)s", datefmt="[%X]", handlers=[RichHandler(rich_tracebacks=False, show_path=True)], From cf542dc95adca6eadfbd5d5f90dc6444fe5acab4 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Tue, 20 May 2025 12:23:36 +0000 Subject: [PATCH 04/11] AI Foundry changes --- evals/safety_evaluation.py | 86 ++++++++++-------------- infra/core/ai/ai-foundry.bicep | 117 +++++++++++++++++++++++++++++++++ infra/main.bicep | 25 +++++-- 3 files changed, 169 insertions(+), 59 deletions(-) create mode 100644 infra/core/ai/ai-foundry.bicep diff --git a/evals/safety_evaluation.py b/evals/safety_evaluation.py index cede5266..63678b24 100644 --- a/evals/safety_evaluation.py +++ b/evals/safety_evaluation.py @@ -8,16 +8,9 @@ from typing import Optional import requests -from azure.ai.evaluation import AzureAIProject from azure.ai.evaluation.red_team import AttackStrategy, RedTeam, RiskCategory from azure.identity import AzureDeveloperCliCredential from dotenv_azd import load_azd_env -from rich.logging import RichHandler - -logger = logging.getLogger("ragapp") - -# Configure logging to capture and display warnings with tracebacks -logging.captureWarnings(True) # Capture warnings as log messages root_dir = pathlib.Path(__file__).parent @@ -25,68 +18,69 @@ def get_azure_credential(): AZURE_TENANT_ID = os.getenv("AZURE_TENANT_ID") if AZURE_TENANT_ID: - logger.info("Setting up Azure credential using AzureDeveloperCliCredential with tenant_id %s", AZURE_TENANT_ID) + print("Setting up Azure credential using AzureDeveloperCliCredential with tenant_id %s", AZURE_TENANT_ID) azure_credential = AzureDeveloperCliCredential(tenant_id=AZURE_TENANT_ID, process_timeout=60) else: - logger.info("Setting up Azure credential using AzureDeveloperCliCredential for home tenant") + print("Setting up Azure credential using AzureDeveloperCliCredential for home tenant") azure_credential = AzureDeveloperCliCredential(process_timeout=60) return azure_credential -async def callback( - messages: list, +def callback( + question: str, target_url: str = "http://127.0.0.1:8000/chat", ): - query = messages[-1].content headers = {"Content-Type": "application/json"} body = { - "messages": [{"content": query, "role": "user"}], + "messages": [{"content": question, "role": "user"}], "stream": False, - "context": {"overrides": {"use_advanced_flow": True, "top": 3, "retrieval_mode": "hybrid", "temperature": 0.3}}, + "context": { + "overrides": {"use_advanced_flow": False, "top": 3, "retrieval_mode": "hybrid", "temperature": 0.3} + }, } url = target_url r = requests.post(url, headers=headers, json=body) response = r.json() if "error" in response: - message = {"content": response["error"], "role": "assistant"} + return f"Error received: {response['error']}" else: - message = response["message"] - return {"messages": messages + [message]} + return response["message"]["content"] -async def run_simulator(target_url: str, max_simulations: int, scan_name: Optional[str] = None): - credential = get_azure_credential() - azure_ai_project: AzureAIProject = { - "subscription_id": os.getenv("AZURE_SUBSCRIPTION_ID"), - "resource_group_name": os.getenv("AZURE_RESOURCE_GROUP"), - "project_name": "pf-testprojforaisaety", - } +async def run_redteaming(target_url: str, questions_per_category: int = 1, scan_name: Optional[str] = None): + AZURE_AI_FOUNDRY = os.getenv("AZURE_AI_FOUNDRY") + AZURE_AI_PROJECT = os.getenv("AZURE_AI_PROJECT") model_red_team = RedTeam( - azure_ai_project=azure_ai_project, - credential=credential, + azure_ai_project=f"https://{AZURE_AI_FOUNDRY}.services.ai.azure.com/api/projects/{AZURE_AI_PROJECT}", + credential=get_azure_credential(), risk_categories=[ RiskCategory.Violence, RiskCategory.HateUnfairness, RiskCategory.Sexual, RiskCategory.SelfHarm, ], - num_objectives=1, + num_objectives=questions_per_category, ) + if scan_name is None: timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") scan_name = f"Safety evaluation {timestamp}" + await model_red_team.scan( - target=lambda messages, stream=False, session_state=None, context=None: callback(messages, target_url), scan_name=scan_name, + output_path=f"{root_dir}/redteams/{scan_name}.json", attack_strategies=[ - AttackStrategy.DIFFICULT, AttackStrategy.Baseline, - AttackStrategy.UnicodeConfusable, # Use confusable Unicode characters - AttackStrategy.Morse, # Encode prompts in Morse code - AttackStrategy.Leetspeak, # Use Leetspeak - AttackStrategy.Url, # Use URLs in prompts + # Easy Complexity: + AttackStrategy.Morse, + AttackStrategy.UnicodeConfusable, + AttackStrategy.Url, + # Moderate Complexity: + AttackStrategy.Tense, + # Difficult Complexity: + AttackStrategy.Compose([AttackStrategy.Tense, AttackStrategy.Url]), ], - output_path="Advanced-Callback-Scan.json", + target=lambda query: callback(query, target_url), ) @@ -96,31 +90,17 @@ async def run_simulator(target_url: str, max_simulations: int, scan_name: Option "--target_url", type=str, default="http://127.0.0.1:8000/chat", help="Target URL for the callback." ) parser.add_argument( - "--max_simulations", type=int, default=200, help="Maximum number of simulations (question/response pairs)." + "--questions_per_category", + type=int, + default=1, + help="Number of questions per risk category to ask during the scan.", ) - # argument for the name parser.add_argument("--scan_name", type=str, default=None, help="Name of the safety evaluation (optional).") args = parser.parse_args() - # Configure logging to show tracebacks for warnings and above - logging.basicConfig( - level=logging.WARNING, - format="%(message)s", - datefmt="[%X]", - handlers=[RichHandler(rich_tracebacks=False, show_path=True)], - ) - - # Set urllib3 and azure libraries to WARNING level to see connection issues - logging.getLogger("urllib3").setLevel(logging.WARNING) - logging.getLogger("azure").setLevel(logging.WARNING) - - # Set our application logger to INFO level - logger.setLevel(logging.INFO) - load_azd_env() - try: - asyncio.run(run_simulator(args.target_url, args.max_simulations, args.scan_name)) + asyncio.run(run_redteaming(args.target_url, args.questions_per_category, args.scan_name)) except Exception: logging.exception("Unhandled exception in safety evaluation") sys.exit(1) diff --git a/infra/core/ai/ai-foundry.bicep b/infra/core/ai/ai-foundry.bicep new file mode 100644 index 00000000..cc787a77 --- /dev/null +++ b/infra/core/ai/ai-foundry.bicep @@ -0,0 +1,117 @@ +@minLength(1) +@description('Primary location for all resources') +param location string + +@description('The AI Foundry resource name.') +param foundryName string + +@description('The AI Project resource name.') +param projectName string = foundryName + +param projectDescription string = '' +param projectDisplayName string = projectName + +@description('The Storage Account resource name.') +param storageAccountName string + +param principalId string +param principalType string + +param tags object = {} + +// Step 1: Create an AI Foundry resource +resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { + name: foundryName + location: location + tags: tags + sku: { + name: 'S0' + } + kind: 'AIServices' + identity: { + type: 'SystemAssigned' + } + properties: { + allowProjectManagement: true + customSubDomainName: toLower(foundryName) + networkAcls: { + defaultAction: 'Allow' + virtualNetworkRules: [] + ipRules: [] + } + publicNetworkAccess: 'Enabled' + disableLocalAuth: false + } +} + +// Step 2: Create an AI Foundry project +resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { + parent: account + name: projectName + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + description: projectDescription + displayName: projectDisplayName + } +} + +// Step 4: Create a storage account, needed for evaluations +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' existing = { + name: storageAccountName +} + +// Create a storage account connection for the foundry resource +resource storageAccountConnection 'Microsoft.CognitiveServices/accounts/connections@2025-04-01-preview' = { + parent: account + name: 'default-storage' + properties: { + authType: 'AAD' + category: 'AzureStorageAccount' + isSharedToAll: true + target: storageAccount.properties.primaryEndpoints.blob + metadata: { + ApiType: 'Azure' + ResourceId: storageAccount.id + } + } +} + +// Assign a role to the project's managed identity for the storage account +resource storageRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(storageAccount.id, 'Storage Blob Data Contributor', project.name) + scope: storageAccount + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') // Storage Blob Data Contributor + principalId: project.identity.principalId + principalType: 'ServicePrincipal' + } +} + +// Assign a role to the calling user for the AI Foundry project (needed for projects (including agents) API) +resource projectRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(project.id, 'Azure AI User', principalId) + scope: project + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '53ca6127-db72-4b80-b1b0-d745d6d5456d') // Azure AI User + principalId: principalId + principalType: 'User' + } +} + +// Assign a role to the calling user for the AI Foundry account (needed for Azure OpenAI API) +resource accountRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(account.id, 'Azure AI User', principalId) + scope: account + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '53ca6127-db72-4b80-b1b0-d745d6d5456d') // Azure AI User + principalId: principalId + principalType: 'User' + } +} + +output foundryName string = account.name +output projectName string = project.name diff --git a/infra/main.bicep b/infra/main.bicep index a06e0d52..6fc222fd 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -472,16 +472,17 @@ module storage 'br/public:avm/res/storage/storage-account:0.9.1' = if (useAiProj } } -module ai 'core/ai/ai-environment.bicep' = if (useAiProject) { +module ai 'core/ai/ai-foundry.bicep' = if (useAiProject) { name: 'ai' scope: resourceGroup params: { location: 'swedencentral' tags: tags - hubName: 'aihub-${resourceToken}' - projectName: 'aiproj-${resourceToken}' - applicationInsightsId: monitoring.outputs.applicationInsightsId - storageAccountId: storage.outputs.resourceId + foundryName: 'aifoundry-${resourceToken}' + projectName: 'aiproject-${resourceToken}' + storageAccountName: storage.outputs.name + principalId: principalId + principalType: empty(runningOnGh) ? 'User' : 'ServicePrincipal' } } @@ -491,11 +492,22 @@ module openAIRoleUser 'core/security/role.bicep' = { name: 'openai-role-user' params: { principalId: principalId - roleDefinitionId: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' + roleDefinitionId: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' // Cognitive Services OpenAI User principalType: empty(runningOnGh) ? 'User' : 'ServicePrincipal' } } +module azureAiUserRole 'core/security/role.bicep' = if (useAiProject && resourceGroup.name != openAIResourceGroup.name) { + name: 'azureai-role-user' + scope: resourceGroup + params: { + principalId: principalId + roleDefinitionId: '53ca6127-db72-4b80-b1b0-d745d6d5456d' // Azure AI User + principalType: empty(runningOnGh) ? 'User' : 'ServicePrincipal' + } +} + + // Backend roles module openAIRoleBackend 'core/security/role.bicep' = { scope: openAIResourceGroup @@ -560,6 +572,7 @@ output AZURE_OPENAI_EVAL_DEPLOYMENT_CAPACITY string = deployAzureOpenAI ? evalDe output AZURE_OPENAI_EVAL_DEPLOYMENT_SKU string = deployAzureOpenAI ? evalDeploymentSku : '' output AZURE_OPENAI_EVAL_MODEL string = deployAzureOpenAI ? evalModelName : '' +output AZURE_AI_FOUNDRY string = useAiProject ? ai.outputs.foundryName : '' output AZURE_AI_PROJECT string = useAiProject ? ai.outputs.projectName : '' output POSTGRES_HOST string = postgresServer.outputs.POSTGRES_DOMAIN_NAME From 2cbf19e0b9bb7f75d82b6b7fc757ca559ed71482 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Mon, 2 Jun 2025 19:08:58 +0000 Subject: [PATCH 05/11] Add evals requirements --- evals/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evals/requirements.txt b/evals/requirements.txt index cd98f541..a5a311ad 100644 --- a/evals/requirements.txt +++ b/evals/requirements.txt @@ -1,4 +1,4 @@ -git+https://github.com/Azure-Samples/ai-rag-chat-evaluator/@2025-05-01 -azure-ai-evaluation[redteam]>=1.5.0 +git+https://github.com/Azure-Samples/ai-rag-chat-evaluator/@2025-06-02 +azure-ai-evaluation[redteam]>=1.8.0 rich dotenv-azd From 75d0645fa71137b2c354bd9cab8e89e125d19b47 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Mon, 2 Jun 2025 19:28:08 +0000 Subject: [PATCH 06/11] Update documentation --- docs/images/redteam_dashboard.png | Bin 0 -> 35093 bytes docs/images/redteam_logs.png | Bin 0 -> 82292 bytes docs/safety_evaluation.md | 70 +++++++++++------------------- evals/redteams/.gitkeep | 0 evals/safety_evaluation.py | 2 +- 5 files changed, 27 insertions(+), 45 deletions(-) create mode 100644 docs/images/redteam_dashboard.png create mode 100644 docs/images/redteam_logs.png create mode 100644 evals/redteams/.gitkeep diff --git a/docs/images/redteam_dashboard.png b/docs/images/redteam_dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..639f90b4d34470ead2535658b589a0825f61daf9 GIT binary patch literal 35093 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 --max_simulations +python evals/safety_evaluation.py --target_url --questions_per_category ``` * `--target_url`: The target URL for the callback. Default is `http://127.0.0.1:8000/chat`, so make sure that the RAG app is running locally. If you are running the RAG app in a different environment, set this to the correct URL. -* `--max_simulations`: The maximum number of simulated user queries. Default is `200`. The higher the number, the longer the evaluation will take. The default of `200` simulations will take about 25 minutes to run, which includes both the time to generate the simulated data and the time to evaluate it. - -## Review the safety evaluation results - -That command will generate a report in `safety_results.json`. -Results will look like this: - -```json -{ - "hate_unfairness": { - "low_count": 20, - "score_total": 8, - "mean_score": 0.4, - "low_rate": 1.0 - }, - "sexual": { - "low_count": 20, - "score_total": 9, - "mean_score": 0.45, - "low_rate": 1.0 - }, - "violence": { - "low_count": 20, - "score_total": 9, - "mean_score": 0.45, - "low_rate": 1.0 - }, - "self_harm": { - "low_count": 20, - "score_total": 10, - "mean_score": 0.5, - "low_rate": 1.0 - } -} -``` +* `--questions_per_category`: The number of questions per risk category to ask during the scan. There are four risk categories (`hate_unfairness`, `sexual`, `violence`, and `self_harm`). The default is 5 questions per category, but you can decrease this number to 1 for test runs, or increase it for a more thorough evaluation. Note that the total questions asked will be higher, as the red teaming agent is configured to try [multiple attack strategies](https://learn.microsoft.com/azure/ai-foundry/how-to/develop/run-scans-ai-red-teaming-agent#supported-attack-strategies), where it transforms the original question in different ways. + +## Review the red team results + +That command will save results both in Azure AI Foundry and in the local `evals/redteams` directory. + +### Review results in Azure AI Foundry + +Navigate from the URL in the terminal output to the results in Azure AI Foundry. +First you'll see overall attack metrics: + +![Red Teaming Agent Results](images/redteam_dashboard.png) + +Then you can dig into the data and see the questions and answers received from your app: + +![Red Teaming Agent Results](images/redteam_logs.png) -The ideal score is `low_rate` of 1.0 and `mean_score` of 0.0. The `low_rate` indicates the fraction of answers that were reported as "Low" or "Very low" by an evaluator. The `mean_score` is the average score of all the answers, where 0 is a very safe answer and 7 is a very unsafe answer. +Note that the questions are intentionally adversarial, so you should mentally prepare for that before looking at the results. The goal is to see if your app can handle these adversarial queries and provide safe answers. Even if your scan results in a 0% attack success rate, you should still review the questions and answers to ensure that you're happy with the way your app responds to these adversarial queries. ## Resources -To learn more about the Azure AI services used in this project, look through the script and reference the following documentation: +To learn more about the Azure AI services used in this project, look through the script, documentation, and videos below: -* [Generate simulated data for evaluation](https://learn.microsoft.com/azure/ai-studio/how-to/develop/simulator-interaction-data) -* [Evaluate with the Azure AI Evaluation SDK](https://learn.microsoft.com/azure/ai-studio/how-to/develop/evaluate-sdk) +* [safety_evaluation.py](evals/safety_evaluation.py) +* [Run automated safety scans with AI Red Teaming Agent](https://learn.microsoft.com/azure/ai-foundry/how-to/develop/run-scans-ai-red-teaming-agent) +* [Build 2025: Red-teaming Demo](https://www.youtube.com/watch?v=sZzcSX7BFVA) diff --git a/evals/redteams/.gitkeep b/evals/redteams/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/evals/safety_evaluation.py b/evals/safety_evaluation.py index 63678b24..acf9b015 100644 --- a/evals/safety_evaluation.py +++ b/evals/safety_evaluation.py @@ -92,7 +92,7 @@ async def run_redteaming(target_url: str, questions_per_category: int = 1, scan_ parser.add_argument( "--questions_per_category", type=int, - default=1, + default=5, help="Number of questions per risk category to ask during the scan.", ) parser.add_argument("--scan_name", type=str, default=None, help="Name of the safety evaluation (optional).") From 4d2977aa6aaa667451da4733a9b972cad8f7e645 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Mon, 2 Jun 2025 19:32:01 +0000 Subject: [PATCH 07/11] Remove unused bicep --- infra/core/ai/ai-environment.bicep | 46 ------------------ infra/core/ai/hub.bicep | 78 ------------------------------ infra/core/ai/project.bicep | 66 ------------------------- 3 files changed, 190 deletions(-) delete mode 100644 infra/core/ai/ai-environment.bicep delete mode 100644 infra/core/ai/hub.bicep delete mode 100644 infra/core/ai/project.bicep diff --git a/infra/core/ai/ai-environment.bicep b/infra/core/ai/ai-environment.bicep deleted file mode 100644 index 56c705d1..00000000 --- a/infra/core/ai/ai-environment.bicep +++ /dev/null @@ -1,46 +0,0 @@ -@minLength(1) -@description('Primary location for all resources') -param location string - -@description('The AI Hub resource name.') -param hubName string -@description('The AI Project resource name.') -param projectName string -@description('The Storage Account resource ID.') -param storageAccountId string = '' -@description('The Application Insights resource ID.') -param applicationInsightsId string = '' -@description('The Azure Search resource name.') -param searchServiceName string = '' -@description('The Azure Search connection name.') -param searchConnectionName string = '' -param tags object = {} - -module hub './hub.bicep' = { - name: 'hub' - params: { - location: location - tags: tags - name: hubName - displayName: hubName - storageAccountId: storageAccountId - containerRegistryId: null - applicationInsightsId: applicationInsightsId - aiSearchName: searchServiceName - aiSearchConnectionName: searchConnectionName - } -} - -module project './project.bicep' = { - name: 'project' - params: { - location: location - tags: tags - name: projectName - displayName: projectName - hubName: hub.outputs.name - } -} - - -output projectName string = project.outputs.name diff --git a/infra/core/ai/hub.bicep b/infra/core/ai/hub.bicep deleted file mode 100644 index fd9f68bb..00000000 --- a/infra/core/ai/hub.bicep +++ /dev/null @@ -1,78 +0,0 @@ -@description('The AI Foundry Hub Resource name') -param name string -@description('The display name of the AI Foundry Hub Resource') -param displayName string = name -@description('The storage account ID to use for the AI Foundry Hub Resource') -param storageAccountId string = '' - -@description('The application insights ID to use for the AI Foundry Hub Resource') -param applicationInsightsId string = '' -@description('The container registry ID to use for the AI Foundry Hub Resource') -param containerRegistryId string = '' - -@description('The Azure Cognitive Search service name to use for the AI Foundry Hub Resource') -param aiSearchName string = '' -@description('The Azure Cognitive Search service connection name to use for the AI Foundry Hub Resource') -param aiSearchConnectionName string = '' - - -@description('The SKU name to use for the AI Foundry Hub Resource') -param skuName string = 'Basic' -@description('The SKU tier to use for the AI Foundry Hub Resource') -@allowed(['Basic', 'Free', 'Premium', 'Standard']) -param skuTier string = 'Basic' -@description('The public network access setting to use for the AI Foundry Hub Resource') -@allowed(['Enabled','Disabled']) -param publicNetworkAccess string = 'Enabled' - -param location string = resourceGroup().location -param tags object = {} - -resource hub 'Microsoft.MachineLearningServices/workspaces@2024-07-01-preview' = { - name: name - location: location - tags: tags - sku: { - name: skuName - tier: skuTier - } - kind: 'Hub' - identity: { - type: 'SystemAssigned' - } - properties: { - friendlyName: displayName - storageAccount: !empty(storageAccountId) ? storageAccountId : null - applicationInsights: !empty(applicationInsightsId) ? applicationInsightsId : null - containerRegistry: !empty(containerRegistryId) ? containerRegistryId : null - hbiWorkspace: false - managedNetwork: { - isolationMode: 'Disabled' - } - v1LegacyMode: false - publicNetworkAccess: publicNetworkAccess - } - - resource searchConnection 'connections' = - if (!empty(aiSearchName)) { - name: aiSearchConnectionName - properties: { - category: 'CognitiveSearch' - authType: 'ApiKey' - isSharedToAll: true - target: 'https://${search.name}.search.windows.net/' - credentials: { - key: !empty(aiSearchName) ? search.listAdminKeys().primaryKey : '' - } - } - } -} - -resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = - if (!empty(aiSearchName)) { - name: aiSearchName - } - -output name string = hub.name -output id string = hub.id -output principalId string = hub.identity.principalId diff --git a/infra/core/ai/project.bicep b/infra/core/ai/project.bicep deleted file mode 100644 index 34fe7663..00000000 --- a/infra/core/ai/project.bicep +++ /dev/null @@ -1,66 +0,0 @@ -@description('The AI Foundry Hub Resource name') -param name string -@description('The display name of the AI Foundry Hub Resource') -param displayName string = name -@description('The name of the AI Foundry Hub Resource where this project should be created') -param hubName string - -@description('The SKU name to use for the AI Foundry Hub Resource') -param skuName string = 'Basic' -@description('The SKU tier to use for the AI Foundry Hub Resource') -@allowed(['Basic', 'Free', 'Premium', 'Standard']) -param skuTier string = 'Basic' -@description('The public network access setting to use for the AI Foundry Hub Resource') -@allowed(['Enabled','Disabled']) -param publicNetworkAccess string = 'Enabled' - -param location string = resourceGroup().location -param tags object = {} - -resource project 'Microsoft.MachineLearningServices/workspaces@2024-01-01-preview' = { - name: name - location: location - tags: tags - sku: { - name: skuName - tier: skuTier - } - kind: 'Project' - identity: { - type: 'SystemAssigned' - } - properties: { - friendlyName: displayName - hbiWorkspace: false - v1LegacyMode: false - publicNetworkAccess: publicNetworkAccess - hubResourceId: hub.id - } -} - -module mlServiceRoleDataScientist '../security/role.bicep' = { - name: 'ml-service-role-data-scientist' - params: { - principalId: project.identity.principalId - roleDefinitionId: 'f6c7c914-8db3-469d-8ca1-694a8f32e121' - principalType: 'ServicePrincipal' - } -} - -module mlServiceRoleSecretsReader '../security/role.bicep' = { - name: 'ml-service-role-secrets-reader' - params: { - principalId: project.identity.principalId - roleDefinitionId: 'ea01e6af-a1c1-4350-9563-ad00f8c72ec5' - principalType: 'ServicePrincipal' - } -} - -resource hub 'Microsoft.MachineLearningServices/workspaces@2024-01-01-preview' existing = { - name: hubName -} - -output id string = project.id -output name string = project.name -output principalId string = project.identity.principalId -output discoveryUrl string = project.properties.discoveryUrl From 59c482867bc48122c3337810d261b2d391f08b2c Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Mon, 2 Jun 2025 20:41:42 +0000 Subject: [PATCH 08/11] Update documentation --- docs/safety_evaluation.md | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/docs/safety_evaluation.md b/docs/safety_evaluation.md index ab17e83f..419366ca 100644 --- a/docs/safety_evaluation.md +++ b/docs/safety_evaluation.md @@ -4,8 +4,8 @@ When deploying a RAG app to production, you should evaluate the safety of the an * [Deploy an Azure AI project](#deploy-an-azure-ai-project) * [Setup the evaluation environment](#setup-the-evaluation-environment) -* [Run safety scan](#simulate-and-evaluate-adversarial-users) -* [Review the safety evaluation results](#review-the-safety-evaluation-results) +* [Run red teaming agent](#run-red-teaming-agent) +* [Review the red teaming results](#review-the-red-team-results) ## Deploy an Azure AI project @@ -45,25 +45,27 @@ In order to use the Red Teaming agent, you need an Azure AI project inside Azure .evalenv\Scripts\activate ``` -1. Install the dependencies for the safety evaluation script: +3. Install the dependencies for the safety evaluation script: ```bash pip install uv uv pip install -r evals/requirements.txt ``` -## Simulate and evaluate adversarial users +## Run the red teaming agent -Run the following command to simulate adversarial queries and evaluate the safety of the answers generated in response to those queries: +To run the red teaming agent, you need to have the RAG app running at a deployed URL or local URL. Consult the main README for deployment instructions local server instructions. + +In the same terminal where you activated `.evalenv`, run this command to perform a red teaming scan: ```shell python evals/safety_evaluation.py --target_url --questions_per_category ``` -* `--target_url`: The target URL for the callback. Default is `http://127.0.0.1:8000/chat`, so make sure that the RAG app is running locally. If you are running the RAG app in a different environment, set this to the correct URL. +* `--target_url`: The target URL for the callback. Default is `http://127.0.0.1:8000/chat`, which assumes the RAG app is running locally. If you want to scan a deployed app instead, set this to `https://DEPLOYEDURL/chat`. * `--questions_per_category`: The number of questions per risk category to ask during the scan. There are four risk categories (`hate_unfairness`, `sexual`, `violence`, and `self_harm`). The default is 5 questions per category, but you can decrease this number to 1 for test runs, or increase it for a more thorough evaluation. Note that the total questions asked will be higher, as the red teaming agent is configured to try [multiple attack strategies](https://learn.microsoft.com/azure/ai-foundry/how-to/develop/run-scans-ai-red-teaming-agent#supported-attack-strategies), where it transforms the original question in different ways. -## Review the red team results +## Review the red teaming results That command will save results both in Azure AI Foundry and in the local `evals/redteams` directory. @@ -80,10 +82,25 @@ Then you can dig into the data and see the questions and answers received from y Note that the questions are intentionally adversarial, so you should mentally prepare for that before looking at the results. The goal is to see if your app can handle these adversarial queries and provide safe answers. Even if your scan results in a 0% attack success rate, you should still review the questions and answers to ensure that you're happy with the way your app responds to these adversarial queries. +Learn more in the [Red Teaming Agent documentation](https://learn.microsoft.com/azure/ai-foundry/how-to/develop/run-scans-ai-red-teaming-agent#viewing-your-results-in-azure-ai-foundry-project). + +## Review results locally + +In addition to the results in Azure AI Foundry, you can also review the results locally in the `evals/redteams` directory. The results are saved in JSON format. + +Each file in this directory corresponds to a single red teaming scan, and contains the following fields: + +* `redteaming_scorecard`: A summary of the scan results, including the attack success rate and the number of questions asked. +* `redteaming_parameters`: The parameters used for the scan, including the risk categories and attack strategies. +* `redteaming_data`: A list of the questions asked during the scan, along with the answers received from your app. +* `studio_url`: A link to the Azure AI Foundry studio where you can view the results in more detail. + +Learn more in the [Red Teaming Agent documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/develop/run-scans-ai-red-teaming-agent#results-from-your-automated-scans). + ## Resources -To learn more about the Azure AI services used in this project, look through the script, documentation, and videos below: +To learn more about the red team scanning, look through the script, documentation, and videos below: -* [safety_evaluation.py](evals/safety_evaluation.py) +* [safety_evaluation.py](/evals/safety_evaluation.py) * [Run automated safety scans with AI Red Teaming Agent](https://learn.microsoft.com/azure/ai-foundry/how-to/develop/run-scans-ai-red-teaming-agent) * [Build 2025: Red-teaming Demo](https://www.youtube.com/watch?v=sZzcSX7BFVA) From 78e4dbbf87ca20e4ad20bb8ce26089fdd7bcce7c Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Mon, 2 Jun 2025 20:45:49 +0000 Subject: [PATCH 09/11] Deprecate 3.9 support --- .github/workflows/app-tests.yaml | 4 +--- README.md | 2 +- src/backend/requirements.txt | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/app-tests.yaml b/.github/workflows/app-tests.yaml index b432baa3..3c1ca9c5 100644 --- a/.github/workflows/app-tests.yaml +++ b/.github/workflows/app-tests.yaml @@ -28,10 +28,8 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest", "macos-latest-xlarge", "macos-13", "windows-latest"] - python_version: ["3.9", "3.10", "3.11", "3.12"] + python_version: ["3.10", "3.11", "3.12"] exclude: - - os: macos-latest-xlarge - python_version: "3.9" - os: macos-latest-xlarge python_version: "3.10" env: diff --git a/README.md b/README.md index 53635a83..0415ca58 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ A related option is VS Code Dev Containers, which will open the project in your * [Azure Developer CLI (azd)](https://aka.ms/install-azd) * [Node.js 18+](https://nodejs.org/download/) - * [Python 3.9+](https://www.python.org/downloads/) + * [Python 3.10+](https://www.python.org/downloads/) * [PostgreSQL 14+](https://www.postgresql.org/download/) * [pgvector](https://github.com/pgvector/pgvector) * [Docker Desktop](https://www.docker.com/products/docker-desktop/) diff --git a/src/backend/requirements.txt b/src/backend/requirements.txt index f7c76310..05889d3a 100644 --- a/src/backend/requirements.txt +++ b/src/backend/requirements.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv pip compile pyproject.toml -o requirements.txt --python-version 3.9 +# uv pip compile pyproject.toml -o requirements.txt --python-version 3.10 aiohappyeyeballs==2.4.4 # via aiohttp aiohttp==3.11.18 From 1a58493f5931dc7da2f50cdf18a5116dbd073fea Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Mon, 2 Jun 2025 20:48:25 +0000 Subject: [PATCH 10/11] Update docs --- docs/safety_evaluation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/safety_evaluation.md b/docs/safety_evaluation.md index 419366ca..4eb4028e 100644 --- a/docs/safety_evaluation.md +++ b/docs/safety_evaluation.md @@ -5,7 +5,7 @@ When deploying a RAG app to production, you should evaluate the safety of the an * [Deploy an Azure AI project](#deploy-an-azure-ai-project) * [Setup the evaluation environment](#setup-the-evaluation-environment) * [Run red teaming agent](#run-red-teaming-agent) -* [Review the red teaming results](#review-the-red-team-results) +* [Review the red teaming results](#review-the-red-teaming-results) ## Deploy an Azure AI project @@ -90,9 +90,9 @@ In addition to the results in Azure AI Foundry, you can also review the results Each file in this directory corresponds to a single red teaming scan, and contains the following fields: -* `redteaming_scorecard`: A summary of the scan results, including the attack success rate and the number of questions asked. -* `redteaming_parameters`: The parameters used for the scan, including the risk categories and attack strategies. -* `redteaming_data`: A list of the questions asked during the scan, along with the answers received from your app. +* `scorecard`: A summary of the scan results, including the attack success rate and the number of questions asked. +* `parameters`: The parameters used for the scan, including the risk categories and attack strategies. +* `attack_details`: A list of the questions asked during the scan, along with the answers received from your app. * `studio_url`: A link to the Azure AI Foundry studio where you can view the results in more detail. Learn more in the [Red Teaming Agent documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/develop/run-scans-ai-red-teaming-agent#results-from-your-automated-scans). From 949f3042a264bbbe8b868056a773f740f321921f Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Mon, 2 Jun 2025 21:09:37 +0000 Subject: [PATCH 11/11] Update scope syntax to be correct --- infra/core/host/container-apps.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/core/host/container-apps.bicep b/infra/core/host/container-apps.bicep index 1c656e28..74db9bd3 100644 --- a/infra/core/host/container-apps.bicep +++ b/infra/core/host/container-apps.bicep @@ -23,7 +23,7 @@ module containerAppsEnvironment 'container-apps-environment.bicep' = { module containerRegistry 'container-registry.bicep' = { name: '${name}-container-registry' - scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() + scope: resourceGroup(!empty(containerRegistryResourceGroupName) ? containerRegistryResourceGroupName : resourceGroup().name) params: { name: containerRegistryName location: location