1- from dotenv import load_dotenv
2- import os
31import asyncio
4- from typing import Optional
2+ import os
3+ from dotenv import load_dotenv
4+
55from browserbase import Browserbase
6- from browser_use import Agent , Browser , BrowserConfig
7- from browser_use .browser .context import BrowserContext , BrowserContextConfig , BrowserSession
6+ from browser_use import Agent
7+ from browser_use .browser .session import BrowserSession
8+ from browser_use .browser import BrowserProfile
89from langchain_anthropic import ChatAnthropic
9- from playwright .async_api import Page , BrowserContext as PlaywrightContext
10-
11- class ExtendedBrowserSession (BrowserSession ):
12- """Extended version of BrowserSession that includes current_page"""
13- def __init__ (
14- self ,
15- context : PlaywrightContext ,
16- cached_state : Optional [dict ] = None ,
17- current_page : Optional [Page ] = None
18- ):
19- super ().__init__ (context = context , cached_state = cached_state )
20- self .current_page = current_page
21-
22- class UseBrowserbaseContext (BrowserContext ):
23- async def _initialize_session (self ) -> ExtendedBrowserSession :
24- """Initialize a browser session using existing Browserbase page.
2510
26- Returns:
27- ExtendedBrowserSession: The initialized browser session with current page.
28- """
29- playwright_browser = await self .browser .get_playwright_browser ()
30- context = await self ._create_context (playwright_browser )
31- self ._add_new_page_listener (context )
3211
33- self .session = ExtendedBrowserSession (
34- context = context ,
35- cached_state = None ,
36- )
37-
38- # Get existing page or create new one
39- self .session .current_page = context .pages [0 ] if context .pages else await context .new_page ()
40-
41- # Initialize session state
42- self .session .cached_state = await self ._update_state ()
43-
44- return self .session
45-
46- async def setup_browser () -> tuple [Browser , UseBrowserbaseContext ]:
47- """Set up browser and context configurations.
48-
49- Returns:
50- tuple[Browser, UseBrowserbaseContext]: Configured browser and context.
51- """
12+ class ManagedBrowserSession :
13+ """Context manager for proper BrowserSession lifecycle management"""
14+
15+ def __init__ (self , cdp_url : str , browser_profile : BrowserProfile ):
16+ self .cdp_url = cdp_url
17+ self .browser_profile = browser_profile
18+ self .browser_session = None
19+
20+ async def __aenter__ (self ) -> BrowserSession :
21+ try :
22+ self .browser_session = BrowserSession (
23+ cdp_url = self .cdp_url ,
24+ browser_profile = self .browser_profile ,
25+ keep_alive = False , # Essential for proper cleanup
26+ initialized = False ,
27+ )
28+
29+ await self .browser_session .start ()
30+ print ("✅ Browser session initialized successfully" )
31+ return self .browser_session
32+
33+ except Exception as e :
34+ print (f"❌ Failed to initialize browser session: { e } " )
35+ await self ._emergency_cleanup ()
36+ raise
37+
38+ async def __aexit__ (self , exc_type , exc_val , exc_tb ):
39+ await self ._close_session_properly ()
40+
41+ async def _close_session_properly (self ):
42+ playwright_instance = None
43+
44+ try :
45+ if self .browser_session :
46+ # Get playwright instance before closing session
47+ if hasattr (self .browser_session , 'playwright' ):
48+ playwright_instance = self .browser_session .playwright
49+
50+ # Close browser session first
51+ if self .browser_session .initialized :
52+ await self .browser_session .stop ()
53+ print ("✅ Browser session closed successfully" )
54+
55+ except Exception as e :
56+ error_msg = str (e ).lower ()
57+ if "browser is closed" in error_msg or "disconnected" in error_msg :
58+ print ("ℹ️ Browser session was already closed (expected behavior)" )
59+ else :
60+ print (f"⚠️ Error during browser session closure: { e } " )
61+
62+ finally :
63+ # Stop playwright instance - critical for preventing hanging processes
64+ if playwright_instance :
65+ try :
66+ await playwright_instance .stop ()
67+ print ("✅ Playwright instance stopped successfully" )
68+ except Exception as e :
69+ print (f"⚠️ Error stopping Playwright: { e } " )
70+
71+ await self ._final_cleanup ()
72+
73+ async def _emergency_cleanup (self ):
74+ try :
75+ if self .browser_session :
76+ if hasattr (self .browser_session , 'playwright' ):
77+ await self .browser_session .playwright .stop ()
78+ if self .browser_session .initialized :
79+ await self .browser_session .stop ()
80+ except Exception as e :
81+ print (f"⚠️ Emergency cleanup error: { e } " )
82+ finally :
83+ await self ._final_cleanup ()
84+
85+ async def _final_cleanup (self ):
86+ self .browser_session = None
87+
88+ async def create_browserbase_session ():
89+ load_dotenv ()
90+
5291 bb = Browserbase (api_key = os .environ ["BROWSERBASE_API_KEY" ])
53- bb_session = bb .sessions .create (
54- project_id = os .environ ["BROWSERBASE_PROJECT_ID" ],
92+ session = bb .sessions .create (project_id = os .environ ["BROWSERBASE_PROJECT_ID" ])
93+
94+ print (f"Session ID: { session .id } " )
95+ print (f"Debug URL: https://www.browserbase.com/sessions/{ session .id } " )
96+
97+ return session
98+
99+
100+ def create_browser_profile () -> BrowserProfile :
101+ return BrowserProfile (
102+ keep_alive = False , # Essential for proper cleanup
103+ wait_between_actions = 2.0 ,
104+ default_timeout = 30000 ,
105+ default_navigation_timeout = 30000 ,
55106 )
56107
57- browser = Browser (config = BrowserConfig (cdp_url = bb_session .connect_url ))
58- context = UseBrowserbaseContext (
59- browser ,
60- BrowserContextConfig (
61- wait_for_network_idle_page_load_time = 10.0 ,
62- highlight_elements = True ,
63- )
64- )
65108
66- return browser , context
67-
68- async def setup_agent (browser : Browser , context : UseBrowserbaseContext ) -> Agent :
69- """Set up the browser automation agent.
70-
71- Args:
72- browser: Configured browser instance
73- context: Browser context for the agent
74-
75- Returns:
76- Agent: Configured automation agent
77- """
78- llm = ChatAnthropic (
79- model_name = "claude-3-5-sonnet-20240620" ,
80- temperature = 0.0 ,
81- timeout = 100 ,
82- )
83-
84- return Agent (
85- task = "go to https://www.macrumors.com/contact.php and fill in the form. Make sure to use the selectors and submit the form" ,
109+ async def run_automation_task (browser_session : BrowserSession , task : str ) -> str :
110+ llm = ChatAnthropic (model = "claude-3-5-sonnet-20240620" , temperature = 0.0 )
111+
112+ agent = Agent (
113+ task = task ,
86114 llm = llm ,
87- browser = browser ,
88- browser_context = context ,
115+ browser_session = browser_session ,
116+ enable_memory = False ,
117+ max_failures = 5 ,
118+ retry_delay = 5 ,
119+ max_actions_per_step = 1 ,
89120 )
90-
121+
122+ try :
123+ print ("🚀 Starting agent task..." )
124+ result = await agent .run (max_steps = 20 )
125+ print ("🎉 Task completed successfully!" )
126+ return str (result )
127+
128+ except Exception as e :
129+ # Handle expected browser disconnection after successful completion
130+ error_msg = str (e ).lower ()
131+ if "browser is closed" in error_msg or "disconnected" in error_msg :
132+ print ("✅ Task completed - Browser session ended normally" )
133+ return "Task completed successfully (session ended normally)"
134+ else :
135+ print (f"❌ Agent execution error: { e } " )
136+ raise
137+
138+ finally :
139+ del agent
91140
92141async def main ():
93- load_dotenv ()
94-
95- browser , context = await setup_browser ()
96- session = await context .get_session ()
97-
98- print ("Session:" , session )
99-
100142 try :
101- agent = await setup_agent (browser , context )
102- await agent .run ()
143+ session = await create_browserbase_session ()
144+ browser_profile = create_browser_profile ()
145+
146+ task = ("Go to https://www.macrumors.com/contact.php and fill in the form. "
147+ "Make sure to use the selectors and submit the form" )
148+
149+ async with ManagedBrowserSession (session .connect_url , browser_profile ) as browser_session :
150+ result = await run_automation_task (browser_session , task )
151+ print (f"Final result: { result } " )
152+
153+ except KeyboardInterrupt :
154+ print ("\n ⏹️ Process interrupted by user" )
155+ except Exception as e :
156+ print (f"💥 Fatal error: { e } " )
157+ raise
103158 finally :
104- # Simplified cleanup - just close the browser
105- # This will automatically close all contexts and pages
106- await browser .close ()
159+ print ("🏁 Application shutdown complete" )
160+
107161
108162if __name__ == "__main__" :
109163 asyncio .run (main ())
0 commit comments