1
- from dotenv import load_dotenv
2
- import os
3
1
import asyncio
4
- from typing import Optional
2
+ import os
3
+ from dotenv import load_dotenv
4
+
5
5
from 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
8
9
from 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.
25
10
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 )
32
11
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
+
52
91
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 ,
55
106
)
56
107
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
- )
65
108
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 ,
86
114
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 ,
89
120
)
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
91
140
92
141
async def main ():
93
- load_dotenv ()
94
-
95
- browser , context = await setup_browser ()
96
- session = await context .get_session ()
97
-
98
- print ("Session:" , session )
99
-
100
142
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
103
158
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
+
107
161
108
162
if __name__ == "__main__" :
109
163
asyncio .run (main ())
0 commit comments