@@ -974,12 +974,12 @@ async def send_batch_with_retry():
974974
975975 # Print progress to console
976976 if batch_idx < len (batches ) - 1 : # Don't print for the last batch
977- print (f"Strategy { strategy_name } , Risk { risk_category_name } : Processed batch { batch_idx + 1 } /{ len (batches )} " )
977+ tqdm . write (f"Strategy { strategy_name } , Risk { risk_category_name } : Processed batch { batch_idx + 1 } /{ len (batches )} " )
978978
979979 except (asyncio .TimeoutError , tenacity .RetryError ):
980980 self .logger .warning (f"Batch { batch_idx + 1 } for { strategy_name } /{ risk_category_name } timed out after { timeout } seconds, continuing with partial results" )
981981 self .logger .debug (f"Timeout: Strategy { strategy_name } , Risk { risk_category_name } , Batch { batch_idx + 1 } after { timeout } seconds." , exc_info = True )
982- print (f"⚠️ TIMEOUT: Strategy { strategy_name } , Risk { risk_category_name } , Batch { batch_idx + 1 } " )
982+ tqdm . write (f"⚠️ TIMEOUT: Strategy { strategy_name } , Risk { risk_category_name } , Batch { batch_idx + 1 } " )
983983 # Set task status to TIMEOUT
984984 batch_task_key = f"{ strategy_name } _{ risk_category_name } _batch_{ batch_idx + 1 } "
985985 self .task_statuses [batch_task_key ] = TASK_STATUS ["TIMEOUT" ]
@@ -1020,7 +1020,7 @@ async def send_all_with_retry():
10201020 self .logger .debug (f"Successfully processed single batch for { strategy_name } /{ risk_category_name } in { batch_duration :.2f} seconds" )
10211021 except (asyncio .TimeoutError , tenacity .RetryError ):
10221022 self .logger .warning (f"Prompt processing for { strategy_name } /{ risk_category_name } timed out after { timeout } seconds, continuing with partial results" )
1023- print (f"⚠️ TIMEOUT: Strategy { strategy_name } , Risk { risk_category_name } " )
1023+ tqdm . write (f"⚠️ TIMEOUT: Strategy { strategy_name } , Risk { risk_category_name } " )
10241024 # Set task status to TIMEOUT
10251025 single_batch_task_key = f"{ strategy_name } _{ risk_category_name } _single_batch"
10261026 self .task_statuses [single_batch_task_key ] = TASK_STATUS ["TIMEOUT" ]
@@ -2101,7 +2101,7 @@ async def _process_attack(
21012101
21022102 try :
21032103 start_time = time .time ()
2104- print (f"▶️ Starting task: { strategy_name } strategy for { risk_category .value } risk category" )
2104+ tqdm . write (f"▶️ Starting task: { strategy_name } strategy for { risk_category .value } risk category" )
21052105 log_strategy_start (self .logger , strategy_name , risk_category .value )
21062106
21072107 converter = self ._get_converter_for_strategy (strategy )
@@ -2137,7 +2137,7 @@ async def _process_attack(
21372137 )
21382138 except Exception as e :
21392139 log_error (self .logger , f"Error during evaluation for { strategy_name } /{ risk_category .value } " , e )
2140- print (f"⚠️ Evaluation error for { strategy_name } /{ risk_category .value } : { str (e )} " )
2140+ tqdm . write (f"⚠️ Evaluation error for { strategy_name } /{ risk_category .value } : { str (e )} " )
21412141 self .red_team_info [strategy_name ][risk_category .value ]["status" ] = TASK_STATUS ["FAILED" ]
21422142 # Continue processing even if evaluation fails
21432143
@@ -2156,12 +2156,10 @@ async def _process_attack(
21562156
21572157 # Print task completion message and estimated time on separate lines
21582158 # This ensures they don't get concatenated with tqdm output
2159- print ("" ) # Empty line to separate from progress bar
2160- print (f"✅ Completed task { self .completed_tasks } /{ self .total_tasks } ({ completion_pct :.1f} %) - { strategy_name } /{ risk_category .value } in { elapsed_time :.1f} s" )
2161- print (f" Est. remaining: { est_remaining_time / 60 :.1f} minutes" )
2159+ tqdm .write (f"✅ Completed task { self .completed_tasks } /{ self .total_tasks } ({ completion_pct :.1f} %) - { strategy_name } /{ risk_category .value } in { elapsed_time :.1f} s" )
2160+ tqdm .write (f" Est. remaining: { est_remaining_time / 60 :.1f} minutes" )
21622161 else :
2163- print ("" ) # Empty line to separate from progress bar
2164- print (f"✅ Completed task { self .completed_tasks } /{ self .total_tasks } ({ completion_pct :.1f} %) - { strategy_name } /{ risk_category .value } in { elapsed_time :.1f} s" )
2162+ tqdm .write (f"✅ Completed task { self .completed_tasks } /{ self .total_tasks } ({ completion_pct :.1f} %) - { strategy_name } /{ risk_category .value } in { elapsed_time :.1f} s" )
21652163
21662164 log_strategy_completion (self .logger , strategy_name , risk_category .value , elapsed_time )
21672165 self .task_statuses [task_key ] = TASK_STATUS ["COMPLETED" ]
@@ -2279,8 +2277,8 @@ def filter(self, record):
22792277 self .logger .debug (f"Timeout: { timeout } seconds" )
22802278
22812279 # Clear, minimal output for start of scan
2282- print (f"🚀 STARTING RED TEAM SCAN: { scan_name } " )
2283- print (f"📂 Output directory: { self .scan_output_dir } " )
2280+ tqdm . write (f"🚀 STARTING RED TEAM SCAN: { scan_name } " )
2281+ tqdm . write (f"📂 Output directory: { self .scan_output_dir } " )
22842282 self .logger .info (f"Starting RED TEAM SCAN: { scan_name } " )
22852283 self .logger .info (f"Output directory: { self .scan_output_dir } " )
22862284
@@ -2307,7 +2305,7 @@ def filter(self, record):
23072305
23082306 self .risk_categories = self .attack_objective_generator .risk_categories
23092307 # Show risk categories to user
2310- print (f"📊 Risk categories: { [rc .value for rc in self .risk_categories ]} " )
2308+ tqdm . write (f"📊 Risk categories: { [rc .value for rc in self .risk_categories ]} " )
23112309 self .logger .info (f"Risk categories to process: { [rc .value for rc in self .risk_categories ]} " )
23122310
23132311 # Prepend AttackStrategy.Baseline to the attack strategy list
@@ -2329,11 +2327,11 @@ def filter(self, record):
23292327
23302328 if strategy == AttackStrategy .Jailbreak :
23312329 self .logger .warning ("Jailbreak strategy with custom attack objectives may not work as expected. The strategy will be run, but results may vary." )
2332- print ("⚠️ Warning: Jailbreak strategy with custom attack objectives may not work as expected." )
2330+ tqdm . write ("⚠️ Warning: Jailbreak strategy with custom attack objectives may not work as expected." )
23332331
23342332 if strategy == AttackStrategy .Tense :
23352333 self .logger .warning ("Tense strategy requires specific formatting in objectives and may not work correctly with custom attack objectives." )
2336- print ("⚠️ Warning: Tense strategy requires specific formatting in objectives and may not work correctly with custom attack objectives." )
2334+ tqdm . write ("⚠️ Warning: Tense strategy requires specific formatting in objectives and may not work correctly with custom attack objectives." )
23372335
23382336 # Check for redundant converters
23392337 # TODO: should this be in flattening logic?
@@ -2343,7 +2341,7 @@ def filter(self, record):
23432341
23442342 if converter_type in used_converter_types and strategy != AttackStrategy .Baseline :
23452343 self .logger .warning (f"Strategy { strategy .name } uses a converter type that has already been used. Skipping redundant strategy." )
2346- print (f"ℹ️ Skipping redundant strategy: { strategy .name } (uses same converter as another strategy)" )
2344+ tqdm . write (f"ℹ️ Skipping redundant strategy: { strategy .name } (uses same converter as another strategy)" )
23472345 strategies_to_remove .append (strategy )
23482346 else :
23492347 used_converter_types .add (converter_type )
@@ -2360,7 +2358,7 @@ def filter(self, record):
23602358 eval_run = self ._start_redteam_mlflow_run (self .azure_ai_project , scan_name )
23612359
23622360 # Show URL for tracking progress
2363- print (f"🔗 Track your red team scan in AI Foundry: { self .ai_studio_url } " )
2361+ tqdm . write (f"🔗 Track your red team scan in AI Foundry: { self .ai_studio_url } " )
23642362 self .logger .info (f"Started Uploading run: { self .ai_studio_url } " )
23652363
23662364 log_subsection_header (self .logger , "Setting up scan configuration" )
@@ -2376,7 +2374,7 @@ def filter(self, record):
23762374 # Calculate total tasks: #risk_categories * #converters
23772375 self .total_tasks = len (self .risk_categories ) * len (flattened_attack_strategies )
23782376 # Show task count for user awareness
2379- print (f"📋 Planning { self .total_tasks } total tasks" )
2377+ tqdm . write (f"📋 Planning { self .total_tasks } total tasks" )
23802378 self .logger .info (f"Total tasks: { self .total_tasks } ({ len (self .risk_categories )} risk categories * { len (flattened_attack_strategies )} strategies)" )
23812379
23822380 # Initialize our tracking dictionary early with empty structures
@@ -2412,10 +2410,10 @@ def filter(self, record):
24122410 # Log the objective source mode
24132411 if using_custom_objectives :
24142412 self .logger .info (f"Using custom attack objectives from { self .attack_objective_generator .custom_attack_seed_prompts } " )
2415- print (f"📚 Using custom attack objectives from { self .attack_objective_generator .custom_attack_seed_prompts } " )
2413+ tqdm . write (f"📚 Using custom attack objectives from { self .attack_objective_generator .custom_attack_seed_prompts } " )
24162414 else :
24172415 self .logger .info ("Using attack objectives from Azure RAI service" )
2418- print ("📚 Using attack objectives from Azure RAI service" )
2416+ tqdm . write ("📚 Using attack objectives from Azure RAI service" )
24192417
24202418 # Dictionary to store all objectives
24212419 all_objectives = {}
@@ -2434,7 +2432,7 @@ def filter(self, record):
24342432 if "baseline" not in all_objectives :
24352433 all_objectives ["baseline" ] = {}
24362434 all_objectives ["baseline" ][risk_category .value ] = baseline_objectives
2437- print (f"📝 Fetched baseline objectives for { risk_category .value } : { len (baseline_objectives )} objectives" )
2435+ tqdm . write (f"📝 Fetched baseline objectives for { risk_category .value } : { len (baseline_objectives )} objectives" )
24382436
24392437 # Then fetch objectives for other strategies
24402438 self .logger .info ("Fetching objectives for non-baseline strategies" )
@@ -2444,7 +2442,7 @@ def filter(self, record):
24442442 if strategy_name == "baseline" :
24452443 continue # Already fetched
24462444
2447- print (f"🔄 Fetching objectives for strategy { i + 1 } /{ strategy_count } : { strategy_name } " )
2445+ tqdm . write (f"🔄 Fetching objectives for strategy { i + 1 } /{ strategy_count } : { strategy_name } " )
24482446 all_objectives [strategy_name ] = {}
24492447
24502448 for risk_category in self .risk_categories :
@@ -2471,7 +2469,7 @@ def filter(self, record):
24712469
24722470 if not objectives :
24732471 self .logger .warning (f"No objectives found for { strategy_name } +{ risk_category .value } , skipping" )
2474- print (f"⚠️ No objectives found for { strategy_name } /{ risk_category .value } , skipping" )
2472+ tqdm . write (f"⚠️ No objectives found for { strategy_name } /{ risk_category .value } , skipping" )
24752473 self .red_team_info [strategy_name ][risk_category .value ]["status" ] = TASK_STATUS ["COMPLETED" ]
24762474 async with progress_bar_lock :
24772475 progress_bar .update (1 )
@@ -2496,7 +2494,7 @@ def filter(self, record):
24962494
24972495 # Process tasks in parallel with optimized batching
24982496 if parallel_execution and orchestrator_tasks :
2499- print (f"⚙️ Processing { len (orchestrator_tasks )} tasks in parallel (max { max_parallel_tasks } at a time)" )
2497+ tqdm . write (f"⚙️ Processing { len (orchestrator_tasks )} tasks in parallel (max { max_parallel_tasks } at a time)" )
25002498 self .logger .info (f"Processing { len (orchestrator_tasks )} tasks in parallel (max { max_parallel_tasks } at a time)" )
25012499
25022500 # Create batches for processing
@@ -2514,7 +2512,7 @@ def filter(self, record):
25142512 )
25152513 except asyncio .TimeoutError :
25162514 self .logger .warning (f"Batch { i // max_parallel_tasks + 1 } timed out after { timeout * 2 } seconds" )
2517- print (f"⚠️ Batch { i // max_parallel_tasks + 1 } timed out, continuing with next batch" )
2515+ tqdm . write (f"⚠️ Batch { i // max_parallel_tasks + 1 } timed out, continuing with next batch" )
25182516 # Set task status to TIMEOUT
25192517 batch_task_key = f"scan_batch_{ i // max_parallel_tasks + 1 } "
25202518 self .task_statuses [batch_task_key ] = TASK_STATUS ["TIMEOUT" ]
@@ -2526,7 +2524,7 @@ def filter(self, record):
25262524 else :
25272525 # Sequential execution
25282526 self .logger .info ("Running orchestrator processing sequentially" )
2529- print ("⚙️ Processing tasks sequentially" )
2527+ tqdm . write ("⚙️ Processing tasks sequentially" )
25302528 for i , task in enumerate (orchestrator_tasks ):
25312529 progress_bar .set_postfix ({"current" : f"task { i + 1 } /{ len (orchestrator_tasks )} " })
25322530 self .logger .debug (f"Processing task { i + 1 } /{ len (orchestrator_tasks )} " )
@@ -2536,7 +2534,7 @@ def filter(self, record):
25362534 await asyncio .wait_for (task , timeout = timeout )
25372535 except asyncio .TimeoutError :
25382536 self .logger .warning (f"Task { i + 1 } /{ len (orchestrator_tasks )} timed out after { timeout } seconds" )
2539- print (f"⚠️ Task { i + 1 } timed out, continuing with next task" )
2537+ tqdm . write (f"⚠️ Task { i + 1 } timed out, continuing with next task" )
25402538 # Set task status to TIMEOUT
25412539 task_key = f"scan_task_{ i + 1 } "
25422540 self .task_statuses [task_key ] = TASK_STATUS ["TIMEOUT" ]
@@ -2607,18 +2605,18 @@ def filter(self, record):
26072605 self .scorecard = scorecard
26082606
26092607 # Print scorecard to console for user visibility (without extra header)
2610- print (scorecard )
2608+ tqdm . write (scorecard )
26112609
26122610 # Print URL for detailed results (once only)
26132611 studio_url = output .scan_result .get ("studio_url" , "" )
26142612 if studio_url :
2615- print (f"\n Detailed results available at:\n { studio_url } " )
2613+ tqdm . write (f"\n Detailed results available at:\n { studio_url } " )
26162614
26172615 # Print the output directory path so the user can find it easily
26182616 if hasattr (self , 'scan_output_dir' ) and self .scan_output_dir :
2619- print (f"\n 📂 All scan files saved to: { self .scan_output_dir } " )
2617+ tqdm . write (f"\n 📂 All scan files saved to: { self .scan_output_dir } " )
26202618
2621- print (f"✅ Scan completed successfully!" )
2619+ tqdm . write (f"✅ Scan completed successfully!" )
26222620 self .logger .info ("Scan completed successfully" )
26232621 for handler in self .logger .handlers :
26242622 if isinstance (handler , logging .FileHandler ):
0 commit comments