import os from typing import TYPE_CHECKING from browser_use.logging_config import setup_logging # Only set up logging if not in MCP mode or if explicitly requested if os.environ.get('BROWSER_USE_SETUP_LOGGING', 'true').lower() != 'false': from browser_use.config import CONFIG # Get log file paths from config/environment debug_log_file = getattr(CONFIG, 'BROWSER_USE_DEBUG_LOG_FILE', None) info_log_file = getattr(CONFIG, 'BROWSER_USE_INFO_LOG_FILE', None) # Set up logging with file handlers if specified logger = setup_logging(debug_log_file=debug_log_file, info_log_file=info_log_file) else: import logging logger = logging.getLogger('browser_use') # Monkeypatch BaseSubprocessTransport.__del__ to handle closed event loops gracefully from asyncio import base_subprocess _original_del = base_subprocess.BaseSubprocessTransport.__del__ def _patched_del(self): """Patched __del__ that handles closed event loops without throwing noisy red-herring errors like RuntimeError: Event loop is closed""" try: # Check if the event loop is closed before calling the original if hasattr(self, '_loop') and self._loop and self._loop.is_closed(): # Event loop is closed, skip cleanup that requires the loop return _original_del(self) except RuntimeError as e: if 'Event loop is closed' in str(e): # Silently ignore this specific error pass else: raise base_subprocess.BaseSubprocessTransport.__del__ = _patched_del # Type stubs for lazy imports - fixes linter warnings if TYPE_CHECKING: from browser_use.agent.prompts import SystemPrompt from browser_use.agent.service import Agent # from browser_use.agent.service import Agent from browser_use.agent.views import ActionModel, ActionResult, AgentHistoryList from browser_use.browser import BrowserProfile, BrowserSession from browser_use.browser import BrowserSession as Browser from browser_use.code_use.service import CodeAgent from browser_use.dom.service import DomService from browser_use.llm import models from browser_use.llm.anthropic.chat import ChatAnthropic from browser_use.llm.azure.chat import ChatAzureOpenAI from browser_use.llm.browser_use.chat import ChatBrowserUse from browser_use.llm.google.chat import ChatGoogle from browser_use.llm.groq.chat import ChatGroq from browser_use.llm.oci_raw.chat import ChatOCIRaw from browser_use.llm.ollama.chat import ChatOllama from browser_use.llm.openai.chat import ChatOpenAI from browser_use.sandbox import sandbox from browser_use.tools.service import Controller, Tools # Lazy imports mapping - only import when actually accessed _LAZY_IMPORTS = { # Agent service (heavy due to dependencies) # 'Agent': ('browser_use.agent.service', 'Agent'), # Code-use agent (Jupyter notebook-like execution) 'CodeAgent': ('browser_use.code_use.service', 'CodeAgent'), 'Agent': ('browser_use.agent.service', 'Agent'), # System prompt (moderate weight due to agent.views imports) 'SystemPrompt': ('browser_use.agent.prompts', 'SystemPrompt'), # Agent views (very heavy - over 1 second!) 'ActionModel': ('browser_use.agent.views', 'ActionModel'), 'ActionResult': ('browser_use.agent.views', 'ActionResult'), 'AgentHistoryList': ('browser_use.agent.views', 'AgentHistoryList'), 'BrowserSession': ('browser_use.browser', 'BrowserSession'), 'Browser': ('browser_use.browser', 'BrowserSession'), # Alias for BrowserSession 'BrowserProfile': ('browser_use.browser', 'BrowserProfile'), # Tools (moderate weight) 'Tools': ('browser_use.tools.service', 'Tools'), 'Controller': ('browser_use.tools.service', 'Controller'), # alias # DOM service (moderate weight) 'DomService': ('browser_use.dom.service', 'DomService'), # Chat models (very heavy imports) 'ChatOpenAI': ('browser_use.llm.openai.chat', 'ChatOpenAI'), 'ChatGoogle': ('browser_use.llm.google.chat', 'ChatGoogle'), 'ChatAnthropic': ('browser_use.llm.anthropic.chat', 'ChatAnthropic'), 'ChatBrowserUse': ('browser_use.llm.browser_use.chat', 'ChatBrowserUse'), 'ChatGroq': ('browser_use.llm.groq.chat', 'ChatGroq'), 'ChatAzureOpenAI': ('browser_use.llm.azure.chat', 'ChatAzureOpenAI'), 'ChatOCIRaw': ('browser_use.llm.oci_raw.chat', 'ChatOCIRaw'), 'ChatOllama': ('browser_use.llm.ollama.chat', 'ChatOllama'), # LLM models module 'models': ('browser_use.llm.models', None), # Sandbox execution 'sandbox': ('browser_use.sandbox', 'sandbox'), } def __getattr__(name: str): """Lazy import mechanism - only import modules when they're actually accessed.""" if name in _LAZY_IMPORTS: module_path, attr_name = _LAZY_IMPORTS[name] try: from importlib import import_module module = import_module(module_path) if attr_name is None: # For modules like 'models', return the module itself attr = module else: attr = getattr(module, attr_name) # Cache the imported attribute in the module's globals globals()[name] = attr return attr except ImportError as e: raise ImportError(f'Failed to import {name} from {module_path}: {e}') from e raise AttributeError(f"module '{__name__}' has no attribute '{name}'") __all__ = [ 'Agent', 'CodeAgent', # 'CodeAgent', 'BrowserSession', 'Browser', # Alias for BrowserSession 'BrowserProfile', 'Controller', 'DomService', 'SystemPrompt', 'ActionResult', 'ActionModel', 'AgentHistoryList', # Chat models 'ChatOpenAI', 'ChatGoogle', 'ChatAnthropic', 'ChatBrowserUse', 'ChatGroq', 'ChatAzureOpenAI', 'ChatOCIRaw', 'ChatOllama', 'Tools', 'Controller', # LLM models module 'models', # Sandbox execution 'sandbox', ]