@@ -283,14 +283,14 @@ def _start_redteam_mlflow_run(
283
283
284
284
async def _log_redteam_results_to_mlflow (
285
285
self ,
286
- redteam_output : RedTeamResult ,
286
+ redteam_result : RedTeamResult ,
287
287
eval_run : EvalRun ,
288
288
data_only : bool = False ,
289
289
) -> Optional [str ]:
290
290
"""Log the Red Team Agent results to MLFlow.
291
291
292
- :param redteam_output : The output from the red team agent evaluation
293
- :type redteam_output : ~azure.ai.evaluation.RedTeamOutput
292
+ :param redteam_result : The output from the red team agent evaluation
293
+ :type redteam_result : ~azure.ai.evaluation.RedTeamResult
294
294
:param eval_run: The MLFlow run object
295
295
:type eval_run: ~azure.ai.evaluation._evaluate._eval_run.EvalRun
296
296
:param data_only: Whether to log only data without evaluation results
@@ -308,21 +308,21 @@ async def _log_redteam_results_to_mlflow(
308
308
with open (artifact_path , "w" , encoding = DefaultOpenEncoding .WRITE ) as f :
309
309
if data_only :
310
310
# In data_only mode, we write the conversations in conversation/messages format
311
- f .write (json .dumps ({"conversations" : redteam_output .attack_details or []}))
312
- elif redteam_output .scan_result :
311
+ f .write (json .dumps ({"conversations" : redteam_result .attack_details or []}))
312
+ elif redteam_result .scan_result :
313
313
# Create a copy to avoid modifying the original scan result
314
- result_with_conversations = redteam_output .scan_result .copy () if isinstance (redteam_output .scan_result , dict ) else {}
314
+ result_with_conversations = redteam_result .scan_result .copy () if isinstance (redteam_result .scan_result , dict ) else {}
315
315
316
316
# Preserve all original fields needed for scorecard generation
317
317
result_with_conversations ["scorecard" ] = result_with_conversations .get ("scorecard" , {})
318
318
result_with_conversations ["parameters" ] = result_with_conversations .get ("parameters" , {})
319
319
320
320
# Add conversations field with all conversation data including user messages
321
- result_with_conversations ["conversations" ] = redteam_output .attack_details or []
321
+ result_with_conversations ["conversations" ] = redteam_result .attack_details or []
322
322
323
323
# Keep original attack_details field to preserve compatibility with existing code
324
- if "attack_details" not in result_with_conversations and redteam_output .attack_details is not None :
325
- result_with_conversations ["attack_details" ] = redteam_output .attack_details
324
+ if "attack_details" not in result_with_conversations and redteam_result .attack_details is not None :
325
+ result_with_conversations ["attack_details" ] = redteam_result .attack_details
326
326
327
327
json .dump (result_with_conversations , f )
328
328
@@ -340,10 +340,10 @@ async def _log_redteam_results_to_mlflow(
340
340
f .write (json .dumps (red_team_info_logged ))
341
341
342
342
# Also save a human-readable scorecard if available
343
- if not data_only and redteam_output .scan_result :
343
+ if not data_only and redteam_result .scan_result :
344
344
scorecard_path = os .path .join (self .scan_output_dir , "scorecard.txt" )
345
345
with open (scorecard_path , "w" , encoding = DefaultOpenEncoding .WRITE ) as f :
346
- f .write (self ._to_scorecard (redteam_output .scan_result ))
346
+ f .write (self ._to_scorecard (redteam_result .scan_result ))
347
347
self .logger .debug (f"Saved scorecard to: { scorecard_path } " )
348
348
349
349
# Create a dedicated artifacts directory with proper structure for MLFlow
@@ -354,13 +354,13 @@ async def _log_redteam_results_to_mlflow(
354
354
# First, create the main artifact file that MLFlow expects
355
355
with open (os .path .join (tmpdir , artifact_name ), "w" , encoding = DefaultOpenEncoding .WRITE ) as f :
356
356
if data_only :
357
- f .write (json .dumps ({"conversations" : redteam_output .attack_details or []}))
358
- elif redteam_output .scan_result :
359
- redteam_output .scan_result ["redteaming_scorecard" ] = redteam_output .scan_result .get ("scorecard" , None )
360
- redteam_output .scan_result ["redteaming_parameters" ] = redteam_output .scan_result .get ("parameters" , None )
361
- redteam_output .scan_result ["redteaming_data" ] = redteam_output .scan_result .get ("attack_details" , None )
357
+ f .write (json .dumps ({"conversations" : redteam_result .attack_details or []}))
358
+ elif redteam_result .scan_result :
359
+ redteam_result .scan_result ["redteaming_scorecard" ] = redteam_result .scan_result .get ("scorecard" , None )
360
+ redteam_result .scan_result ["redteaming_parameters" ] = redteam_result .scan_result .get ("parameters" , None )
361
+ redteam_result .scan_result ["redteaming_data" ] = redteam_result .scan_result .get ("attack_details" , None )
362
362
363
- json .dump (redteam_output .scan_result , f )
363
+ json .dump (redteam_result .scan_result , f )
364
364
365
365
# Copy all relevant files to the temp directory
366
366
import shutil
@@ -401,9 +401,9 @@ async def _log_redteam_results_to_mlflow(
401
401
artifact_file = Path (tmpdir ) / artifact_name
402
402
with open (artifact_file , "w" , encoding = DefaultOpenEncoding .WRITE ) as f :
403
403
if data_only :
404
- f .write (json .dumps ({"conversations" : redteam_output .attack_details or []}))
405
- elif redteam_output .scan_result :
406
- json .dump (redteam_output .scan_result , f )
404
+ f .write (json .dumps ({"conversations" : redteam_result .attack_details or []}))
405
+ elif redteam_result .scan_result :
406
+ json .dump (redteam_result .scan_result , f )
407
407
eval_run .log_artifact (tmpdir , artifact_name )
408
408
self .logger .debug (f"Logged artifact: { artifact_name } " )
409
409
@@ -414,8 +414,8 @@ async def _log_redteam_results_to_mlflow(
414
414
"_azureml.evaluate_artifacts" : json .dumps ([{"path" : artifact_name , "type" : "table" }]),
415
415
})
416
416
417
- if redteam_output .scan_result :
418
- scorecard = redteam_output .scan_result ["scorecard" ]
417
+ if redteam_result .scan_result :
418
+ scorecard = redteam_result .scan_result ["scorecard" ]
419
419
joint_attack_summary = scorecard ["joint_risk_attack_summary" ]
420
420
421
421
if joint_attack_summary :
@@ -1641,7 +1641,7 @@ async def scan(
1641
1641
:param timeout: The timeout in seconds for API calls (default: 120)
1642
1642
:type timeout: int
1643
1643
:return: The output from the red team scan
1644
- :rtype: RedTeamOutput
1644
+ :rtype: RedTeamResult
1645
1645
"""
1646
1646
# Start timing for performance tracking
1647
1647
self .start_time = time .time ()
@@ -1673,7 +1673,7 @@ def filter(self, record):
1673
1673
return False
1674
1674
if 'The path to the artifact is either not a directory or does not exist' in record .getMessage ():
1675
1675
return False
1676
- if 'RedTeamOutput object at' in record .getMessage ():
1676
+ if 'RedTeamResult object at' in record .getMessage ():
1677
1677
return False
1678
1678
if 'timeout won\' t take effect' in record .getMessage ():
1679
1679
return False
@@ -2006,7 +2006,7 @@ def filter(self, record):
2006
2006
# Log results to MLFlow
2007
2007
self .logger .info ("Logging results to MLFlow" )
2008
2008
await self ._log_redteam_results_to_mlflow (
2009
- redteam_output = output ,
2009
+ redteam_result = output ,
2010
2010
eval_run = eval_run ,
2011
2011
data_only = data_only
2012
2012
)
0 commit comments