@@ -195,19 +195,33 @@ def _prepare_attack_params(self, attack_config: Dict[str, Any]) -> Dict[str, Any
195195 # Check for direct goals first
196196 goals = attack_config .get ("goals" )
197197 dataset_config = attack_config .get ("dataset" )
198+ intents_config = attack_config .get ("intents" )
199+ goal_labels_by_index : Optional [Dict [int , Dict [str , str ]]] = None
198200
199201 if goals is not None and dataset_config is not None :
200202 logger .warning (
201203 "Both 'goals' and 'dataset' provided. Using 'goals' directly."
202204 )
203205 dataset_config = None
206+ if goals is not None and intents_config is not None :
207+ logger .warning (
208+ "Both 'goals' and 'intents' provided. Using 'goals' directly."
209+ )
210+ intents_config = None
211+
212+ if intents_config is not None and dataset_config is not None :
213+ logger .warning ("Both 'intents' and 'dataset' provided. Using 'intents'." )
214+ dataset_config = None
204215
205- if dataset_config is not None :
216+ if intents_config is not None :
217+ goals , goal_labels_by_index = self ._load_goals_from_intents (intents_config )
218+ elif dataset_config is not None :
206219 # Load goals from dataset source
207220 goals = self ._load_goals_from_dataset (dataset_config )
208221 elif goals is None :
209222 raise ValueError (
210- f"'{ self .attack_type } ' requires either 'goals' (list) or 'dataset' (config)"
223+ f"'{ self .attack_type } ' requires either 'goals' (list), "
224+ "'dataset' (config), or 'intents' (config)"
211225 )
212226
213227 if not isinstance (goals , list ):
@@ -217,7 +231,10 @@ def _prepare_attack_params(self, attack_config: Dict[str, Any]) -> Dict[str, Any
217231 raise ValueError (f"'goals' list is empty for { self .attack_type } " )
218232
219233 logger .info (f"Prepared { len (goals )} goals for { self .attack_type } attack" )
220- return {"goals" : goals }
234+ params : Dict [str , Any ] = {"goals" : goals }
235+ if goal_labels_by_index :
236+ params ["_goal_labels_by_index" ] = goal_labels_by_index
237+ return params
221238
222239 @staticmethod
223240 def _uses_default_category_classifier (attack_config : Dict [str , Any ]) -> bool :
@@ -354,6 +371,26 @@ def _load_goals_from_dataset(self, dataset_config: Dict[str, Any]) -> list:
354371 logger .error (f"Failed to load goals from dataset: { e } " , exc_info = True )
355372 raise ValueError (f"Failed to load goals from dataset: { e } " ) from e
356373
374+ def _load_goals_from_intents (
375+ self , intents_config : Any
376+ ) -> Tuple [List [str ], Dict [int , Dict [str , str ]]]:
377+ """Load goals from intent taxonomy labels and sample selectors."""
378+ from hackagent .datasets .intents import load_goals_from_intents_config
379+
380+ logger .info ("Loading goals from intents taxonomy config" )
381+
382+ try :
383+ goals , goal_labels_by_index = load_goals_from_intents_config (intents_config )
384+ logger .info (
385+ "Loaded %s goals from intents across %s labeled entries" ,
386+ len (goals ),
387+ len (goal_labels_by_index ),
388+ )
389+ return goals , goal_labels_by_index
390+ except Exception as e :
391+ logger .error (f"Failed to load goals from intents: { e } " , exc_info = True )
392+ raise ValueError (f"Failed to load goals from intents: { e } " ) from e
393+
357394 def _get_attack_impl_kwargs (
358395 self ,
359396 attack_config : Dict [str , Any ],
@@ -664,9 +701,16 @@ def execute(
664701 """
665702 # 1. Validate parameters
666703 attack_params = self ._prepare_attack_params (attack_config )
704+ goal_labels_by_index = attack_params .pop ("_goal_labels_by_index" , None )
667705
668706 # Fail-fast preflight before creating Attack/Run DB records.
669- self ._validate_default_category_classifier_requirements (attack_config )
707+ # Skip this when intents already provide explicit category labels.
708+ if goal_labels_by_index :
709+ logger .info (
710+ "Using explicit intents taxonomy labels: category classifier preflight skipped"
711+ )
712+ else :
713+ self ._validate_default_category_classifier_requirements (attack_config )
670714
671715 # Enrich run config with expected goal cardinality so downstream views
672716 # can keep RUNNING until all expected goals are fully tracked.
@@ -710,6 +754,13 @@ def execute(
710754 except Exception as e :
711755 logger .warning (f"Failed to update run status to RUNNING: { e } " )
712756
757+ if goal_labels_by_index :
758+ attack_config = {
759+ ** attack_config ,
760+ "_goal_labels_by_index" : goal_labels_by_index ,
761+ "_disable_goal_category_classifier" : True ,
762+ }
763+
713764 # Make the event bus available to the technique impl and to the
714765 # tracker via the shared config bag (alongside _run_id / _backend).
715766 if _tui_event_bus is not None :
0 commit comments