Implement asynchronous browser initialization (#7328)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Xingyao Wang 2025-03-18 15:34:57 -04:00 committed by GitHub
parent a594595fea
commit f2a54f4e23
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -31,6 +31,7 @@ from starlette.background import BackgroundTask
from starlette.exceptions import HTTPException as StarletteHTTPException
from uvicorn import run
from openhands.core.exceptions import BrowserUnavailableException
from openhands.core.logger import openhands_logger as logger
from openhands.events.action import (
Action,
@ -159,7 +160,9 @@ class ActionExecutor:
self.lock = asyncio.Lock()
self.plugins: dict[str, Plugin] = {}
self.file_editor = OHEditor(workspace_root=self._initial_cwd)
self.browser = BrowserEnv(browsergym_eval_env)
self.browser: BrowserEnv | None = None
self.browser_init_task: asyncio.Task | None = None
self.browsergym_eval_env = browsergym_eval_env
self.start_time = time.time()
self.last_execution_time = self.start_time
self._initialized = False
@ -183,6 +186,38 @@ class ActionExecutor:
def initial_cwd(self):
return self._initial_cwd
async def _init_browser_async(self):
"""Initialize the browser asynchronously."""
logger.debug('Initializing browser asynchronously')
try:
self.browser = BrowserEnv(self.browsergym_eval_env)
logger.debug('Browser initialized asynchronously')
except Exception as e:
logger.error(f'Failed to initialize browser: {e}')
self.browser = None
async def _ensure_browser_ready(self):
"""Ensure the browser is ready for use."""
if self.browser is None:
if self.browser_init_task is None:
# Start browser initialization if it hasn't been started
self.browser_init_task = asyncio.create_task(self._init_browser_async())
elif self.browser_init_task.done():
# If the task is done but browser is still None, restart initialization
self.browser_init_task = asyncio.create_task(self._init_browser_async())
# Wait for browser to be initialized
if self.browser_init_task:
logger.debug('Waiting for browser to be ready...')
await self.browser_init_task
# Check if browser was successfully initialized
if self.browser is None:
raise BrowserUnavailableException('Browser initialization failed')
# If we get here, the browser is ready
logger.debug('Browser is ready')
async def ainit(self):
# bash needs to be initialized first
logger.debug('Initializing bash session')
@ -197,6 +232,10 @@ class ActionExecutor:
self.bash_session.initialize()
logger.debug('Bash session initialized')
# Start browser initialization in the background
self.browser_init_task = asyncio.create_task(self._init_browser_async())
logger.debug('Browser initialization started in background')
await wait_all(
(self._init_plugin(plugin) for plugin in self.plugins_to_load),
timeout=30,
@ -459,16 +498,19 @@ class ActionExecutor:
)
async def browse(self, action: BrowseURLAction) -> Observation:
await self._ensure_browser_ready()
return await browse(action, self.browser)
async def browse_interactive(self, action: BrowseInteractiveAction) -> Observation:
await self._ensure_browser_ready()
return await browse(action, self.browser)
def close(self):
self.memory_monitor.stop_monitoring()
if self.bash_session is not None:
self.bash_session.close()
self.browser.close()
if self.browser is not None:
self.browser.close()
if __name__ == '__main__':