Spaces:
Sleeping
Sleeping
| import subprocess | |
| import asyncio | |
| from contextlib import contextmanager | |
| from typing import List, Optional | |
| from threading import Lock | |
| from smolagents.tools import Tool | |
| from langchain_community.tools.playwright.utils import ( | |
| create_async_playwright_browser, | |
| ) | |
| from langchain_community.agent_toolkits import PlayWrightBrowserToolkit | |
| import logging | |
| from playwright.async_api import async_playwright | |
| logging.basicConfig(level=logging.DEBUG) | |
| logger = logging.getLogger(__name__) | |
| class BrowserManager: | |
| _instance = None | |
| _lock = Lock() | |
| _browser_tools: Optional[List[Tool]] = None | |
| _loop: Optional[asyncio.AbstractEventLoop] = None | |
| def __new__(cls): | |
| if cls._instance is None: | |
| with cls._lock: | |
| if cls._instance is None: | |
| cls._instance = super(BrowserManager, cls).__new__(cls) | |
| return cls._instance | |
| def __init__(self): | |
| if not hasattr(self, 'initialized'): | |
| # Run setup script | |
| subprocess.run(["bash", "scripts.sh"]) | |
| self.initialized = True | |
| def _create_browser_tools(self): | |
| """Create browser tools in a new event loop""" | |
| # Create a new event loop for browser tools | |
| loop = asyncio.new_event_loop() | |
| asyncio.set_event_loop(loop) | |
| try: | |
| logger.debug("Creating async browser...") | |
| # Create browsers in the new loop | |
| async def create_browser(): | |
| playwright = await async_playwright().start() | |
| return await playwright.chromium.launch(headless=True) | |
| async_browser = loop.run_until_complete(create_browser()) | |
| logger.debug("Async browser created successfully") | |
| # Create toolkit and tools | |
| logger.debug("Creating browser toolkit...") | |
| browser_toolkit = PlayWrightBrowserToolkit.from_browser( | |
| async_browser=async_browser, | |
| sync_browser=None # Don't use sync browser | |
| ) | |
| logger.debug("Browser toolkit created successfully") | |
| logger.debug("Converting tools...") | |
| tools = [] | |
| for tool in browser_toolkit.get_tools(): | |
| logger.debug(f"Converting tool: {tool.name}") | |
| try: | |
| converted_tool = Tool.from_langchain(tool) | |
| converted_tool._browser = async_browser | |
| converted_tool._loop = loop | |
| tools.append(converted_tool) | |
| logger.debug(f"Successfully converted tool: {tool.name}") | |
| except Exception as e: | |
| logger.error(f"Error converting tool {tool.name}: {e}") | |
| raise | |
| logger.debug(f"Successfully created {len(tools)} browser tools") | |
| return tools | |
| except Exception as e: | |
| logger.error(f"Error creating browser tools: {e}") | |
| if loop.is_running(): | |
| loop.stop() | |
| loop.close() | |
| raise | |
| def get_browser_tools(self): | |
| """Get browser tools in a context that ensures proper cleanup""" | |
| try: | |
| if self._browser_tools is None: | |
| with self._lock: | |
| if self._browser_tools is None: | |
| logger.debug("Creating new browser tools...") | |
| self._browser_tools = self._create_browser_tools() | |
| logger.debug("Browser tools created successfully") | |
| yield self._browser_tools | |
| except Exception as e: | |
| logger.error(f"Error in browser context: {e}") | |
| # Reset tools on error | |
| self._browser_tools = None | |
| raise | |
| finally: | |
| # Cleanup if needed | |
| if self._browser_tools: | |
| for tool in self._browser_tools: | |
| if hasattr(tool, '_browser') and hasattr(tool, '_loop'): | |
| try: | |
| loop = tool._loop | |
| if loop and not loop.is_closed(): | |
| logger.debug("Cleaning up browser...") | |
| loop.run_until_complete(tool._browser.close()) | |
| loop.close() | |
| logger.debug("Browser cleanup completed") | |
| except Exception as e: | |
| logger.error(f"Error during browser cleanup: {e}") | |
| self._browser_tools = None | |
| # Create a singleton instance | |
| browser_manager = BrowserManager() | |
| # For backward compatibility, but prefer using browser_manager.get_browser_tools() | |
| def get_browser_tools(): | |
| """Get browser tools (use with context manager)""" | |
| return browser_manager.get_browser_tools() | |