Team Ai
Apppublic

anu151105/agentic-browser

sourceHugging Facemitupdated 1y agoView on Hugging Face
2likes
orchestrator.py419 linesDownload Raw Back to src
1#!/usr/bin/env python32"""3Agent Orchestrator module for the Enhanced AI Agentic Browser Agent Architecture.4 5This module serves as the central coordinator for all layers in the architecture,6managing task execution flow and communication between components.7"""8 9import asyncio10import json11import logging12import os13import time14import uuid15from typing import Dict, List, Optional, Any16 17from fastapi import WebSocket18 19from src.perception.multimodal_processor import MultimodalProcessor20from src.browser_control.browser_controller import BrowserController21from src.action_execution.action_executor import ActionExecutor, APIInteractionModule22from src.planning.task_planner import TaskPlanner23from src.memory.continuous_memory import ContinuousMemory24from src.user_interaction.hybrid_executor import HybridExecutor25from src.a2a_protocol.agent_communication import A2AProtocol26from src.security.ethical_guardian import EthicalGuardian27from src.monitoring.metrics_collector import MetricsCollector28 29# Configure logging30logging.basicConfig(31    level=logging.INFO,32    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",33)34logger = logging.getLogger(__name__)35 36class AgentOrchestrator:37    """38    Orchestrates all layers of the AI Agent Architecture.39    40    This class manages the lifecycle of tasks, coordinates between different 41    layers, and handles communication with external systems.42    """43    44    def __init__(self):45        """Initialize the Agent Orchestrator."""46        self.perception = None47        self.browser_control = None48        self.action_executor = None49        self.api_module = None50        self.task_planner = None51        self.memory = None52        self.hybrid_executor = None53        self.a2a_protocol = None54        self.ethical_guardian = None55        self.metrics = None56        57        # Task tracking58        self.tasks = {}  # task_id -> task_info59        self.websockets = {}  # task_id -> List[WebSocket]60        61        logger.info("Agent Orchestrator instance created")62    63    @classmethod64    async def initialize(cls):65        """66        Initialize all components of the architecture.67        68        Returns:69            AgentOrchestrator: Initialized orchestrator instance70        """71        instance = cls()72        73        # Initialize components in parallel for efficiency74        await asyncio.gather(75            instance._init_perception(),76            instance._init_browser_control(),77            instance._init_action_execution(),78            instance._init_planning(),79            instance._init_memory(),80            instance._init_user_interaction(),81            instance._init_a2a_protocol(),82            instance._init_security(),83            instance._init_monitoring(),84        )85        86        logger.info("All components initialized successfully")87        return instance88    89    async def _init_perception(self):90        """Initialize the Perception & Understanding Layer."""91        self.perception = MultimodalProcessor()92        await self.perception.initialize()93        logger.info("Perception & Understanding Layer initialized")94    95    async def _init_browser_control(self):96        """Initialize the Browser Control Layer."""97        self.browser_control = BrowserController()98        await self.browser_control.initialize()99        logger.info("Browser Control Layer initialized")100    101    async def _init_action_execution(self):102        """Initialize the Action Execution Layer."""103        self.api_module = APIInteractionModule()104        self.action_executor = ActionExecutor(self.browser_control, self.api_module)105        await self.action_executor.initialize()106        logger.info("Action Execution Layer initialized")107    108    async def _init_planning(self):109        """Initialize the Planning & Reasoning Layer."""110        self.task_planner = TaskPlanner()111        await self.task_planner.initialize()112        logger.info("Planning & Reasoning Layer initialized")113    114    async def _init_memory(self):115        """Initialize the Memory & Learning Layer."""116        self.memory = ContinuousMemory()117        await self.memory.initialize()118        logger.info("Memory & Learning Layer initialized")119    120    async def _init_user_interaction(self):121        """Initialize the User Interaction Layer."""122        self.hybrid_executor = HybridExecutor(self.action_executor)123        await self.hybrid_executor.initialize()124        logger.info("User Interaction Layer initialized")125    126    async def _init_a2a_protocol(self):127        """Initialize the Agent-to-Agent Protocol."""128        self.a2a_protocol = A2AProtocol()129        await self.a2a_protocol.initialize()130        logger.info("Agent-to-Agent Protocol initialized")131    132    async def _init_security(self):133        """Initialize the Security & Ethics components."""134        self.ethical_guardian = EthicalGuardian()135        await self.ethical_guardian.initialize()136        logger.info("Security & Ethics components initialized")137    138    async def _init_monitoring(self):139        """Initialize the Monitoring & Analytics components."""140        self.metrics = MetricsCollector()141        await self.metrics.initialize()142        logger.info("Monitoring & Analytics components initialized")143    144    async def create_task(self, task_config: Dict) -> str:145        """146        Create a new task with the given configuration.147        148        Args:149            task_config: Dictionary containing task configuration150            151        Returns:152            str: Task ID153        """154        task_id = str(uuid.uuid4())155        156        # Validate the task with ethical guardian157        is_valid, reason = await self.ethical_guardian.validate_task(task_config["task_description"])158        if not is_valid:159            self.tasks[task_id] = {160                "status": "rejected",161                "error": f"Task rejected due to ethical concerns: {reason}",162                "config": task_config,163                "result": None,164                "created_at": time.time()165            }166            return task_id167        168        # Create and store task169        self.tasks[task_id] = {170            "status": "created",171            "config": task_config,172            "result": None,173            "created_at": time.time()174        }175        176        logger.info(f"Created task {task_id}: {task_config['task_description']}")177        self.metrics.record_task_created()178        179        return task_id180    181    async def execute_task(self, task_id: str):182        """183        Execute a task with the given ID.184        185        Args:186            task_id: ID of the task to execute187        """188        if task_id not in self.tasks:189            logger.error(f"Task {task_id} not found")190            return191        192        task = self.tasks[task_id]193        task["status"] = "running"194        await self._notify_task_update(task_id, {"status": "running"})195        196        try:197            # Record start time for metrics198            start_time = time.time()199            200            # Retrieve task configuration201            task_config = task["config"]202            human_assisted = task_config.get("human_assisted", False)203            204            # 1. Planning phase205            await self._notify_task_update(task_id, {"status": "planning"})206            plan = await self.task_planner.decompose_task(207                task_config["task_description"],208                urls=task_config.get("urls", [])209            )210            211            # 2. Check similar past tasks in memory212            similar_tasks = await self.memory.retrieve_similar_tasks(task_config["task_description"])213            214            # 3. Execute plan steps215            results = []216            for step_idx, step in enumerate(plan["steps"]):217                # Update task status with current step218                step_status = f"Executing step {step_idx+1}/{len(plan['steps'])}: {step['description']}"219                await self._notify_task_update(task_id, {"status": step_status})220                221                # Process current web page if browser is initialized222                if self.browser_control.is_page_loaded():223                    page_data = await self.browser_control.get_page_data()224                    understanding = await self.perception.analyze_page(225                        page_data["screenshot"], 226                        page_data["dom_text"], 227                        step["description"]228                    )229                230                # Execute the step based on operation mode231                if human_assisted:232                    step_result = await self.hybrid_executor.execute_task(233                        step, 234                        human_assist=True235                    )236                else:237                    step_result = await self.action_executor.execute_action(step)238                239                # Record results240                results.append({241                    "step": step["description"],242                    "result": step_result243                })244                245                # Check for cancellation after each step246                if task["status"] == "cancelling":247                    task["status"] = "cancelled"248                    await self._notify_task_update(task_id, {"status": "cancelled"})249                    return250            251            # 4. Store experience in memory252            await self.memory.store_experience(253                task_config["task_description"],254                plan["steps"],255                {256                    "success": True,257                    "results": results258                }259            )260            261            # 5. Update task status to completed262            task["status"] = "completed"263            task["result"] = {264                "steps_executed": len(results),265                "results": results,266                "execution_time": time.time() - start_time267            }268            269            # Record metrics270            self.metrics.record_task_completed(time.time() - start_time)271            272            await self._notify_task_update(task_id, {273                "status": "completed",274                "result": task["result"]275            })276            277            logger.info(f"Task {task_id} completed successfully")278            279        except Exception as e:280            # Handle errors and update task status281            logger.exception(f"Error executing task {task_id}: {str(e)}")282            task["status"] = "failed"283            task["error"] = str(e)284            285            # Record metrics286            self.metrics.record_task_failed()287            288            await self._notify_task_update(task_id, {289                "status": "failed",290                "error": str(e)291            })292    293    async def cancel_task(self, task_id: str) -> bool:294        """295        Cancel a running task.296        297        Args:298            task_id: ID of the task to cancel299            300        Returns:301            bool: True if task was found and cancellation was initiated, False otherwise302        """303        if task_id not in self.tasks:304            return False305        306        task = self.tasks[task_id]307        if task["status"] == "running":308            task["status"] = "cancelling"309            await self._notify_task_update(task_id, {"status": "cancelling"})310            logger.info(f"Initiated cancellation of task {task_id}")311            return True312        313        return False314    315    async def get_task_status(self, task_id: str) -> Optional[Dict]:316        """317        Get the current status of a task.318        319        Args:320            task_id: ID of the task321            322        Returns:323            Optional[Dict]: Task status and results if found, None otherwise324        """325        if task_id not in self.tasks:326            return None327        328        task = self.tasks[task_id]329        return {330            "task_id": task_id,331            "status": task["status"],332            "result": task.get("result"),333            "error": task.get("error")334        }335    336    async def register_websocket(self, task_id: str, websocket: WebSocket):337        """338        Register a WebSocket connection for real-time task updates.339        340        Args:341            task_id: ID of the task to subscribe to342            websocket: WebSocket connection343        """344        if task_id not in self.websockets:345            self.websockets[task_id] = []346        347        self.websockets[task_id].append(websocket)348        349        # Send initial status350        status = await self.get_task_status(task_id)351        if status:352            await websocket.send_text(json.dumps(status))353    354    async def unregister_websocket(self, task_id: str, websocket: WebSocket):355        """356        Unregister a WebSocket connection.357        358        Args:359            task_id: ID of the task360            websocket: WebSocket connection to unregister361        """362        if task_id in self.websockets:363            try:364                self.websockets[task_id].remove(websocket)365            except ValueError:366                pass367    368    async def _notify_task_update(self, task_id: str, update: Dict):369        """370        Send task updates to all registered WebSocket connections.371        372        Args:373            task_id: ID of the task374            update: Update data to send375        """376        if task_id not in self.websockets:377            return378        379        full_update = {"task_id": task_id, **update}380        381        dead_sockets = []382        for websocket in self.websockets[task_id]:383            try:384                await websocket.send_text(json.dumps(full_update))385            except Exception:386                dead_sockets.append(websocket)387        388        # Clean up dead connections389        for dead in dead_sockets:390            try:391                self.websockets[task_id].remove(dead)392            except ValueError:393                pass394    395    async def shutdown(self):396        """Clean up resources when the application is shutting down."""397        # Cancel all running tasks398        for task_id, task in self.tasks.items():399            if task["status"] == "running":400                task["status"] = "cancelled"401        402        # Close all websocket connections403        for task_id, sockets in self.websockets.items():404            for socket in sockets:405                try:406                    await socket.close()407                except Exception:408                    pass409        410        # Shutdown all components411        await asyncio.gather(412            self.browser_control.shutdown(),413            self.action_executor.shutdown(),414            self.memory.shutdown(),415            self.metrics.shutdown(),416        )417        418        logger.info("Agent Orchestrator shut down successfully")419