1010
1111logger = logging .getLogger (__name__ )
1212
13- # Global variables to cache browser initialization
14- _browser = None
15- _controller = None
16-
17-
18- def init_browser ():
19- """
20- Initialize and cache browser and controller instances.
21-
22- This function uses a singleton pattern to ensure we only create one browser
23- instance throughout the application lifecycle, which saves resources.
24-
25- Returns:
26- tuple: (Browser, Controller) instances for web automation
27- """
28- global _browser , _controller
29-
30- # Return cached instances if already initialized
31- if _browser is not None and _controller is not None :
32- return _browser , _controller
33-
34- from browser_use import Browser , BrowserConfig , BrowserContextConfig , Controller
35- from browser_use .agent .views import ActionResult
36- from browser_use .browser .context import BrowserContext
37-
38- # Set up downloads directory for browser operations
39- downloads_path = os .path .join (os .getcwd (), "downloads" )
40- if not os .path .exists (downloads_path ):
41- os .makedirs (downloads_path )
42-
43- context_config = BrowserContextConfig (save_downloads_path = downloads_path )
44- config = BrowserConfig (headless = True , disable_security = True , new_context_config = context_config )
45- controller = Controller ()
46-
47- # Register custom action to upload files to web elements
48- @controller .action (
49- description = "Upload file to interactive element with file path" ,
50- )
51- async def upload_file (index : int , path : str , browser : BrowserContext ):
52- """
53- Upload a file to a file input element identified by its index.
54-
55- Args:
56- index: The DOM element index to target
57- path: Local file path to upload
58- browser: Browser context for interaction
59-
60- Returns:
61- ActionResult: Result of the upload operation
62- """
63- if not os .path .exists (path ):
64- return ActionResult (error = f"File { path } does not exist" )
65-
66- dom_el = await browser .get_dom_element_by_index (index )
67- file_upload_dom_el = dom_el .get_file_upload_element ()
68-
69- if file_upload_dom_el is None :
70- msg = f"No file upload element found at index { index } . The element may be hidden or not an input type file"
71- logger .info (msg )
72- return ActionResult (error = msg )
73-
74- file_upload_el = await browser .get_locate_element (file_upload_dom_el )
75-
76- if file_upload_el is None :
77- msg = f"No file upload element found at index { index } . The element may be hidden or not an input type file"
78- logger .info (msg )
79- return ActionResult (error = msg )
80-
81- try :
82- await file_upload_el .set_input_files (path )
83- msg = f"Successfully uploaded file to index { index } "
84- logger .info (msg )
85- return ActionResult (extracted_content = msg , include_in_memory = True )
86- except Exception as e :
87- msg = f"Failed to upload file to index { index } : { str (e )} "
88- logger .info (msg )
89- return ActionResult (error = msg )
90-
91- # Register custom action to read file contents
92- @controller .action (description = "Read the file content of a file given a path" )
93- async def read_file (path : str ):
94- """
95- Read and return the contents of a file at the specified path.
96-
97- Args:
98- path: Path to the file to read
99-
100- Returns:
101- ActionResult: File contents or error message
102- """
103- if not os .path .exists (path ):
104- return ActionResult (error = f"File { path } does not exist" )
105-
106- with open (path , "r" ) as f :
107- content = f .read ()
108- msg = f"File content: { content } "
109- logger .info (msg )
110- return ActionResult (extracted_content = msg , include_in_memory = True )
111-
112- # Cache the initialized instances
113- _browser = Browser (config = config )
114- _controller = controller
115-
116- return _browser , _controller
117-
11813
11914class BrowserUse (Step , input_class = BrowserUseInputs , output_class = BrowserUseOutputs ):
12015 """
@@ -155,9 +50,13 @@ def __init__(self, inputs):
15550 api_key = self .inputs ["anthropic_api_key" ],
15651 )
15752
53+ gifs_base_path = os .path .join (os .path .dirname (__file__ ), "../../../tmp/gifs" )
54+ if not os .path .exists (gifs_base_path ):
55+ os .makedirs (gifs_base_path )
56+
15857 # Configure GIF generation for debugging/visualization
15958 self .generate_gif = (
160- f"agent_history_{ datetime .now ().strftime ('%Y-%m-%d_%H-%M-%S' )} .gif"
59+ f"{ gifs_base_path } / agent_history_{ datetime .now ().strftime ('%Y-%m-%d_%H-%M-%S' )} .gif"
16160 if ("generate_gif" in self .inputs and self .inputs ["generate_gif" ])
16261 or ("debug" in self .inputs and self .inputs ["debug" ])
16362 else False
@@ -173,21 +72,37 @@ def run(self) -> dict:
17372 Returns:
17473 dict: Results of the browser automation task
17574 """
176- from browser_use import Agent
75+ from browser_use import Agent , BrowserConfig
76+
77+ from patchwork .common .utils .browser_initializer import BrowserInitializer
78+
79+ browser_config = BrowserConfig (
80+ headless = self .inputs .get ("headless" , True ),
81+ disable_security = True ,
82+ )
83+ browser_context = BrowserInitializer .init_browser_context (
84+ browser_config , self .inputs .get ("downloads_path" , None )
85+ )
86+ controller = BrowserInitializer .init_controller ()
87+ logger .info ("Browser initialized" )
17788
178- browser , controller = init_browser ()
17989 agent = Agent (
180- browser = browser ,
90+ browser_context = browser_context ,
18191 controller = controller ,
18292 task = mustache_render (self .inputs ["task" ], self .inputs ["task_value" ]),
18393 llm = self .llm ,
18494 generate_gif = self .generate_gif ,
18595 validate_output = True ,
96+ initial_actions = self .inputs .get ("initial_actions" , None ),
97+ use_vision = self .inputs .get ("use_vision" , True ),
18698 )
18799
188100 # Run the agent in an event loop
189101 loop = asyncio .new_event_loop ()
190102 self .history = loop .run_until_complete (agent .run ())
103+ loop .run_until_complete (browser_context .close ())
104+ loop .run_until_complete (browser_context .browser .close ())
105+ loop .close ()
191106
192107 # Format results as JSON if schema provided
193108 if "example_json" in self .inputs :
0 commit comments