8
8
from selenium .webdriver .chrome .options import Options
9
9
from selenium .webdriver .chrome .service import Service
10
10
from webdriver_manager .chrome import ChromeDriverManager
11
+ from selenium .common .exceptions import WebDriverException
11
12
12
13
load_dotenv ()
13
14
api_key = os .getenv ('OPENAI_API_KEY' )
14
15
15
- # Hard-coded coordinates to open web-wand side panel
16
- extensions_pos = (1060 , 110 )
17
- web_wand_pos = (900 , 280 )
18
-
19
16
# Place to store task execution results
20
17
results_dir = 'results'
21
18
os .makedirs (results_dir , exist_ok = True )
@@ -26,6 +23,8 @@ def setup_driver():
26
23
chrome_options .add_argument ("--load-extension=./dist" )
27
24
service = Service (ChromeDriverManager ().install ())
28
25
driver = webdriver .Chrome (service = service , options = chrome_options )
26
+ # Set script timeout to 240 seconds
27
+ driver .set_script_timeout (240 )
29
28
return driver
30
29
31
30
def dispatch_event (driver , event_name , event ):
@@ -35,57 +34,84 @@ def dispatch_event(driver, event_name, event):
35
34
"""
36
35
driver .execute_script (script )
37
36
38
- def add_task_listener (driver , task_id ):
39
- # Async script to add event listeners for taskStatus, taskHistory, and screenshot events
40
- script = """
37
+ def add_task_listener (driver , task_id , max_retries = 3 ):
38
+ print ('add_task_listener' , task_id )
39
+ """
40
+ Add event listeners for task history and screenshot events. Both events include task status.
41
+ Then process those events as they are captured.
42
+ """
43
+
44
+ script = f"""
41
45
var callback = arguments[0];
42
- var keepListening = true;
43
- var historyData = null;
44
-
45
- var statusListener = function (e) {
46
- if (e.detail.status !== 'running' && e.detail.status !== 'idle') {
47
- keepListening = false;
48
- document.removeEventListener('TaskStatusUpdate', statusListener);
49
- document.removeEventListener('ScreenshotUpdate', screenshotListener);
50
- document.removeEventListener('TaskHistoryUpdate', historyListener);
51
- callback({type: 'status', data: 'stopped', history: historyData});
52
- }
53
- };
54
-
55
- var screenshotListener = function (e) {
56
- callback({type: 'screenshot', data: e.detail.imgData});
57
- };
58
-
59
- var historyListener = function (e) {
60
- historyData = e.detail.history;
61
- };
62
-
63
- document.addEventListener('TaskStatusUpdate', statusListener);
64
- document.addEventListener('ScreenshotUpdate', screenshotListener);
65
- document.addEventListener('TaskHistoryUpdate', historyListener);
66
-
67
- // To keep the async script alive
68
- var checkInterval = setInterval(function () {
69
- if (!keepListening) {
70
- clearInterval(checkInterval);
71
- }
72
- }, 1000);
46
+ var eventListener = function (e) {{
47
+ if (e.detail.type == 'history') {{
48
+ console.log("event listener received history event");
49
+ if (e.detail.status === 'success' || e.detail.status === 'error') {{
50
+ callback({{status: e.detail.status, type: 'history', data: e.detail.data}});
51
+ document.removeEventListener('TaskUpdate', eventListener);
52
+ console.log("event listener removed");
53
+ }}
54
+ // Does not do anything when the status is 'running' or 'idle'.
55
+ // The status 'interrupted' will never be triggered automatically.
56
+ }} else if (e.detail.type == 'screenshot') {{
57
+ console.log("event listener received screenshot event");
58
+ callback({{status: e.detail.status, type: 'screenshot', data: e.detail.data}});
59
+ document.removeEventListener('TaskUpdate', eventListener);
60
+ console.log("event listener removed");
61
+ }} else {{
62
+ throw new Error("Invalid event type received: " + e.detail.type);
63
+ }}
64
+ }};
65
+
66
+ document.addEventListener('TaskUpdate', eventListener);
67
+ console.log("added event listener");
73
68
"""
74
69
75
- try :
76
- while True :
77
- event_data = driver .execute_async_script (script )
78
- if event_data :
79
- if event_data ['type' ] == 'screenshot' :
80
- write_screenshots (task_id , event_data ['data' ])
81
- elif event_data ['type' ] == 'status' and event_data ['data' ] == 'stopped' :
82
- if event_data .get ('history' ):
83
- write_history (task_id , event_data ['history' ])
84
- break
85
- except Exception as e :
86
- print (f"Error while listening for updates: { e } " )
70
+ completed = {'status' : None }
71
+ attempts = 0
72
+
73
+ def handle_event (event_data ):
74
+ nonlocal attempts
75
+ if not event_data :
76
+ print ("no event data" )
77
+ return
78
+ if event_data ['type' ] == 'history' :
79
+ # Record history when task stops
80
+ completed ['status' ] = event_data ['status' ]
81
+ write_history (task_id , event_data ['data' ])
82
+ return
83
+ if event_data ['type' ] == 'screenshot' :
84
+ write_screenshots (task_id , event_data ['data' ])
85
+ # Task is still running. Continue to listen for events
86
+ handle_event (driver .execute_async_script (script ))
87
+ else :
88
+ raise ValueError (f"Unhandled event data type: { event_data ['type' ]} " )
89
+ attempts = 0
90
+ print ("reset attempts to zero" )
91
+
92
+ while attempts < max_retries :
93
+ try :
94
+ handle_event (driver .execute_async_script (script ))
95
+ break
96
+ except WebDriverException as e :
97
+ if "javascript error: document unloaded while waiting for result" in str (e ):
98
+ print (f"Document unloaded error: { e } " )
99
+ attempts += 1
100
+ print (f"Attempt { attempts } : Document unloaded error. Retrying..." )
101
+ if attempts == max_retries :
102
+ print ("Maximum retry attempts reached. Cannot recover from document unloaded error." )
103
+ else :
104
+ print (f"Other WebDriver error: { e } " )
105
+ break
106
+ except Exception as e :
107
+ print (f"Error while listening for updates: { e } " )
108
+ break
109
+
110
+ print ("completed['status']" , completed ['status' ])
111
+ return completed ['status' ]
87
112
88
113
def write_history (task_id , task_history ):
114
+ print ('write_history' , task_id )
89
115
task_dir = os .path .join (results_dir , f"test{ task_id } " )
90
116
os .makedirs (task_dir , exist_ok = True )
91
117
file_path = os .path .join (task_dir , 'interact_messages.json' )
@@ -94,21 +120,33 @@ def write_history(task_id, task_history):
94
120
json .dump (task_history , file , indent = 4 )
95
121
96
122
def write_screenshots (task_id , image_data ):
123
+ print ('write_screenshots' , task_id )
97
124
image_bytes = base64 .b64decode (image_data )
98
-
99
125
task_dir = os .path .join (results_dir , f"test{ task_id } " )
100
126
os .makedirs (task_dir , exist_ok = True )
101
127
timestamp = int (time .time ())
102
128
file_path = os .path .join (task_dir , f'screenshot_{ timestamp } .png' )
103
-
104
129
with open (file_path , 'wb' ) as file :
105
130
file .write (image_bytes )
106
131
107
132
def run_webwand_task (driver , task_id , task_description ):
133
+ print ('run_webwand_task' , task_id , task_description )
108
134
dispatch_event (driver , 'SetAPIKey' , {"value" : api_key })
109
135
dispatch_event (driver , 'SetTask' , {"value" : task_description })
110
136
dispatch_event (driver , 'RunTask' , {})
111
- add_task_listener (driver , task_id )
137
+ task_status = add_task_listener (driver , task_id )
138
+ return task_status
139
+
140
+ def click_extensions_icon (driver ):
141
+ # Simulate click to open side panel
142
+ window_position = driver .get_window_rect ()
143
+ top = window_position ['y' ]
144
+ right = window_position ['x' ] + window_position ['width' ]
145
+ # click Extensions icon
146
+ pyautogui .click (right - 150 , top + 50 )
147
+
148
+ # click webwand
149
+ pyautogui .click (right - 300 , top + 210 )
112
150
113
151
def main ():
114
152
driver = setup_driver ()
@@ -121,12 +159,13 @@ def main():
121
159
driver .get (task ['web' ])
122
160
123
161
if initial_load :
124
- # Simulate click to open side panel
125
- pyautogui .click (extensions_pos )
126
- pyautogui .click (web_wand_pos )
162
+ click_extensions_icon (driver )
127
163
initial_load = False
128
164
129
- run_webwand_task (driver , task_id , task ['ques' ])
165
+ task_status = run_webwand_task (driver , task_id , task ['ques' ])
166
+ while task_status not in ['success' , 'error' ]:
167
+ print ("wait task_status" , task_status )
168
+ time .sleep (3 ) # Wait for 3 seconds till the current task completes
130
169
driver .quit ()
131
170
132
171
if __name__ == "__main__" :
0 commit comments