Spaces:
Runtime error
Runtime error
| """ | |
| ActionExecutorAgent - Prepares actionable buttons for user | |
| Determines what actions are available based on analysis context | |
| """ | |
| from typing import Dict | |
| from .base import Agent, AgentConfig, AgentResult | |
| import time | |
| class ActionExecutorAgent(Agent): | |
| """ | |
| Prepares action buttons based on context. | |
| Does NOT execute — just plans available actions. | |
| Actual execution happens in /api/copilot/action endpoint. | |
| """ | |
| AVAILABLE_ACTIONS = { | |
| "CREATE_CASE": { | |
| "label": "Create Investigation Case", | |
| "icon": "📁", | |
| "category": "DIRECT_ACTION", | |
| "requires_confirmation": False, | |
| "priority": 1, | |
| }, | |
| "GENERATE_STR": { | |
| "label": "Generate STR Report (PDF)", | |
| "icon": "📄", | |
| "category": "DATA_ACTION", | |
| "requires_confirmation": False, | |
| "priority": 2, | |
| }, | |
| "OPEN_STR_BUILDER": { | |
| "label": "Open STR Builder", | |
| "icon": "🔨", | |
| "category": "NAVIGATION", | |
| "requires_confirmation": False, | |
| "priority": 3, | |
| }, | |
| "VIEW_GRAPH": { | |
| "label": "Open Network Graph", | |
| "icon": "🕸", | |
| "category": "NAVIGATION", | |
| "requires_confirmation": False, | |
| "priority": 4, | |
| }, | |
| "ADD_NOTES": { | |
| "label": "Add Investigation Notes", | |
| "icon": "📝", | |
| "category": "DIRECT_ACTION", | |
| "requires_confirmation": False, | |
| "priority": 7, | |
| }, | |
| } | |
| def __init__(self, api_pool): | |
| config = AgentConfig( | |
| name="ActionExecutorAgent", | |
| model="llama-3.1-8b-instant", | |
| temperature=0.0, | |
| max_tokens=500, | |
| timeout_ms=5000, | |
| ) | |
| super().__init__(config, api_pool) | |
| def _build_prompt(self, **inputs) -> str: | |
| return "" | |
| def _parse_response(self, response_text: str) -> Dict: | |
| return {} | |
| async def invoke( | |
| self, | |
| account_id: str = None, | |
| intent: str = "GENERAL", | |
| risk_assessment: Dict = None, | |
| alert_data: Dict = None, | |
| **kwargs, | |
| ) -> AgentResult: | |
| """Prepare list of actions to show user based on context.""" | |
| start_time = time.time() | |
| try: | |
| actions = [] | |
| risk_score = 0 | |
| risk_tier = "LOW" | |
| if risk_assessment: | |
| recommendation = risk_assessment.get("recommendation", {}) | |
| risk_tier = recommendation.get("priority", "MEDIUM") | |
| if alert_data: | |
| risk_score = alert_data.get("risk_score", 0) | |
| risk_tier = alert_data.get("risk_tier", risk_tier) | |
| is_high_risk = risk_tier == "CRITICAL" | |
| is_medium_risk = risk_tier in ["HIGH", "MEDIUM"] | |
| # Create case for medium/high risk accounts | |
| if account_id and (is_high_risk or is_medium_risk): | |
| actions.append(self._build_action("CREATE_CASE", { | |
| "account_id": account_id, | |
| "alert_id": alert_data.get("alert_id") if alert_data else None, | |
| "description": f"Investigation for {account_id}", | |
| "priority": "CRITICAL" if is_high_risk else "HIGH", | |
| })) | |
| # STR/SAR generation for NARRATIVE intent or high-risk | |
| if account_id and (intent == "NARRATIVE" or is_high_risk): | |
| actions.append(self._build_action("GENERATE_STR", { | |
| "account_id": account_id, | |
| "alert_id": alert_data.get("alert_id") if alert_data else None, | |
| })) | |
| actions.append(self._build_action("OPEN_STR_BUILDER", { | |
| "account_id": account_id, | |
| "url": f"#str-builder?account={account_id}", | |
| })) | |
| # Navigation — always available when account_id is known | |
| if account_id: | |
| actions.append(self._build_action("VIEW_GRAPH", { | |
| "account_id": account_id, | |
| "url": f"#investigation?account={account_id}", | |
| "hops": 2, | |
| "max_nodes": 50, | |
| })) | |
| # Add notes — only if a case exists (medium/high risk context) | |
| if account_id and (is_high_risk or is_medium_risk): | |
| actions.append(self._build_action("ADD_NOTES", { | |
| "account_id": account_id, | |
| "url": f"#cases?account={account_id}&action=new_note", | |
| })) | |
| actions.sort(key=lambda x: x.get("priority", 99)) | |
| result_data = { | |
| "actions": actions, | |
| "actions_count": len(actions), | |
| "context_analyzed": { | |
| "account_id": account_id, | |
| "intent": intent, | |
| "risk_score": risk_score, | |
| "risk_tier": risk_tier, | |
| }, | |
| } | |
| self.logger.info(f"[OK] {self.config.name}: Prepared {len(actions)} actions") | |
| return await self._create_result( | |
| success=True, | |
| data=result_data, | |
| tokens_input=0, | |
| tokens_output=0, | |
| start_time=start_time, | |
| ) | |
| except Exception as e: | |
| self.logger.error(f"[FAIL] {self.config.name}: {e}") | |
| return await self._create_result( | |
| success=False, | |
| data={"actions": []}, | |
| error=str(e), | |
| start_time=start_time, | |
| ) | |
| def _build_action(self, action_type: str, payload: Dict) -> Dict: | |
| """Build an action object with full metadata.""" | |
| if action_type not in self.AVAILABLE_ACTIONS: | |
| return {} | |
| cfg = self.AVAILABLE_ACTIONS[action_type] | |
| return { | |
| "id": f"action_{action_type.lower()}_{int(time.time() * 1000)}", | |
| "type": action_type, | |
| "label": cfg["label"], | |
| "icon": cfg.get("icon", ""), | |
| "category": cfg["category"], | |
| "priority": cfg["priority"], | |
| "requires_confirmation": cfg.get("requires_confirmation", False), | |
| "confirmation_message": cfg.get("confirmation_message", ""), | |
| "enabled": True, | |
| "payload": payload, | |
| } | |