sunholo 0.74.8__py3-none-any.whl → 0.74.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -213,6 +213,35 @@ class BrowseWebWithImagePromptsBot:
213
213
  except Exception as err:
214
214
  log.warning(f"navigate failed with {str(err)}")
215
215
  self.action_log.append(f"Tried to navigate to {url} but got an error")
216
+
217
+ def get_locator_via_roles_and_placeholder(self, selector: str):
218
+ interactive_roles = ["button", "link", "menuitem", "menuitemcheckbox", "menuitemradio", "tab", "option"]
219
+
220
+ for role in interactive_roles:
221
+ log.info(f'Trying role {role} for selector {selector}')
222
+ elements = self.page.get_by_role(role).get_by_placeholder(selector).locator("visible=true").all()
223
+ if elements:
224
+ log.info(f"Got {len(elements)} elements for selector {selector} with role {role}")
225
+ for element in elements:
226
+ try:
227
+ log.info(f"Trying {selector} with element.hover locator: {element}")
228
+ try:
229
+ element.hover(timeout=10000, trial=True)
230
+ self.action_log.append(f"Successfully found element via selector: {selector}")
231
+
232
+ return element
233
+
234
+ except Exception as err:
235
+ log.warning(f"Could not hover over element: {element} {str(err)} - trying next element")
236
+ except Exception as e:
237
+ log.error(f"Failed to get locator for selector '{selector}' with role {role}: {str(e)}")
238
+
239
+ time.sleep(0.5) # Wait for a bit before retrying
240
+
241
+ log.info(f"No elements for '{selector}' within role '{role}'")
242
+
243
+ self.action_log.append(f"FAILED: Using page.get_by_role('role').get_by_placeholder('{selector}').locator('visible=true') could not find any valid element. Try something else.")
244
+ return None
216
245
 
217
246
  def get_locator_via_roles_and_text(self, selector: str):
218
247
  interactive_roles = ["button", "link", "menuitem", "menuitemcheckbox", "menuitemradio", "tab", "option"]
@@ -240,7 +269,7 @@ class BrowseWebWithImagePromptsBot:
240
269
 
241
270
  log.info(f"No elements for '{selector}' within role '{role}'")
242
271
 
243
- self.action_log.append(f"FAILED: Using page.get_by_role('role').locator('text={selector}').locator('visible=true') could not find any valid element. Try something else.")
272
+ self.action_log.append(f"FAILED: Using page.get_by_role('role').get_by_text('{selector}').locator('visible=true') could not find any valid element. Try something else.")
244
273
  return None
245
274
 
246
275
  def get_locator(self, selector, by_text=True):
@@ -256,10 +285,10 @@ class BrowseWebWithImagePromptsBot:
256
285
 
257
286
  return None
258
287
 
259
- def click(self, selector, by_text=True):
288
+ def click(self, selector):
260
289
  (x,y)=(0,0)
261
290
 
262
- element = self.get_locator(selector, by_text=by_text)
291
+ element = self.get_locator_via_roles_and_text(selector)
263
292
  if element is None:
264
293
  self.action_log.append(f"Tried to click on text {selector} but it was not a valid location to click")
265
294
  return (x,y)
@@ -303,9 +332,9 @@ class BrowseWebWithImagePromptsBot:
303
332
  log.warning(f"Scrolled failed with {str(err)}")
304
333
  self.action_log.append(f"Tried to scroll {direction} by {amount} pixels but got an error")
305
334
 
306
- def type_text(self, selector, text, by_text=True):
335
+ def type_text(self, selector, text):
307
336
  (x,y)=(0,0)
308
- element = self.get_locator(selector, by_text=by_text)
337
+ element = self.get_locator_via_roles_and_placeholder(selector)
309
338
  if element is None:
310
339
  self.action_log.append(f"Tried to type {text} via website text: {selector} but it was not a valid element to add text")
311
340
  return (x,y)
@@ -344,6 +373,10 @@ class BrowseWebWithImagePromptsBot:
344
373
  url_path = "index.html"
345
374
  else:
346
375
  url_path = url_path.replace("/","_")
376
+
377
+ if get_clean_website_name(url_path) != self.website_name:
378
+ url_path = f"{get_clean_website_name(url_path)}_{url_path}"
379
+
347
380
  screenshot_path = os.path.join(self.screenshot_dir, f"{timestamp}_{url_path}.png")
348
381
  screenshot_bytes = self.page.screenshot(full_page=full_page, scale='css')
349
382
 
@@ -358,7 +391,7 @@ class BrowseWebWithImagePromptsBot:
358
391
  #self.action_log.append(f"Screenshot {self.page.url} taken and saved to {screenshot_path}")
359
392
  self.session_screenshots.append(screenshot_path)
360
393
 
361
- return screenshot_path
394
+ return screenshot_bytes
362
395
 
363
396
  def mark_screenshot(self, screenshot_bytes, mark_action):
364
397
  """
@@ -433,9 +466,9 @@ class BrowseWebWithImagePromptsBot:
433
466
 
434
467
  return output
435
468
 
436
- def send_screenshot_to_llm(self, screenshot_path, last_message):
437
- with open(screenshot_path, "rb") as image_file:
438
- encoded_image = base64.b64encode(image_file.read()).decode('utf-8')
469
+ def send_screenshot_to_llm(self, screenshot_bytes, last_message):
470
+
471
+ encoded_image = base64.b64encode(screenshot_bytes).decode('utf-8')
439
472
 
440
473
  prompt_vars = self.create_prompt_vars(last_message)
441
474
  response = self.send_prompt_to_llm(prompt_vars, encoded_image) # Sending prompt and image separately
@@ -495,9 +528,9 @@ This method should be implemented by subclasses: `def send_prompt_to_llm(self, p
495
528
  log.warning(f"Reached the maximum number of steps: {self.max_steps}")
496
529
  return
497
530
  time.sleep(2)
498
- screenshot_path = self.take_screenshot(mark_action=mark_action)
531
+ screenshot_bytes = self.take_screenshot(mark_action=mark_action)
499
532
  next_browser_instructions = self.send_screenshot_to_llm(
500
- screenshot_path,
533
+ screenshot_bytes,
501
534
  last_message=last_message)
502
535
 
503
536
  return next_browser_instructions
sunholo/utils/parsers.py CHANGED
@@ -186,6 +186,6 @@ def escape_braces(text):
186
186
  text = re.sub(r'(?<!})}(?!})', '}}', text) # Replace '}' with '}}' if not already double braced
187
187
  return text
188
188
 
189
- def get_clean_website_name(url):
189
+ def get_clean_website_name(url: str):
190
190
  parsed_url = urllib.parse.urlparse(url)
191
191
  return parsed_url.netloc
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.74.8
3
+ Version: 0.74.9
4
4
  Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
5
5
  Home-page: https://github.com/sunholo-data/sunholo-py
6
- Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.74.8.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.74.9.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -99,7 +99,7 @@ sunholo/streaming/streaming.py,sha256=9z6pXINEopuL_Z1RnmgXAoZJum9dzyuOxqYtEYnjf8
99
99
  sunholo/summarise/__init__.py,sha256=MZk3dblUMODcPb1crq4v-Z508NrFIpkSWNf9FIO8BcU,38
100
100
  sunholo/summarise/summarise.py,sha256=C3HhjepTjUhUC8FLk4jMQIBvq1BcORniwuTFHjPVhVo,3784
101
101
  sunholo/tools/__init__.py,sha256=5NuYpwwTX81qGUWvgwfItoSLXteNnp7KjgD7IPZUFjI,53
102
- sunholo/tools/web_browser.py,sha256=-zfuaxnRXTEzPUyBT0a26uhe2D9T7BtQhH3696gy4xo,24542
102
+ sunholo/tools/web_browser.py,sha256=wms9vHndrbD2NaAvNpYG291YiGP8wVHJ6iljsMVZc5w,26292
103
103
  sunholo/utils/__init__.py,sha256=Hv02T5L2zYWvCso5hzzwm8FQogwBq0OgtUbN_7Quzqc,89
104
104
  sunholo/utils/api_key.py,sha256=Ct4bIAQZxzPEw14hP586LpVxBAVi_W9Serpy0BK-7KI,244
105
105
  sunholo/utils/big_context.py,sha256=gJIP7_ZL-YSLhOMq8jmFTMqH1wq8eB1NK7oKPeZAq2s,5578
@@ -108,7 +108,7 @@ sunholo/utils/config_class.py,sha256=4fm2Bwn_zFhVJBiUnMBzfCA5LKhTcBMU3mzhf5seXrw
108
108
  sunholo/utils/config_schema.py,sha256=Wv-ncitzljOhgbDaq9qnFqH5LCuxNv59dTGDWgd1qdk,4189
109
109
  sunholo/utils/gcp.py,sha256=uueODEpA-P6O15-t0hmcGC9dONLO_hLfzSsSoQnkUss,4854
110
110
  sunholo/utils/gcp_project.py,sha256=0ozs6tzI4qEvEeXb8MxLnCdEVoWKxlM6OH05htj7_tc,1325
111
- sunholo/utils/parsers.py,sha256=aCIT08VjVbu8E3BAxepIiqFQa8zwu4bTgEstU_qjyg8,5414
111
+ sunholo/utils/parsers.py,sha256=akLSZLdvHf5T9OKVj5C3bo4g3Y8puATd8rxnbB4tWDs,5419
112
112
  sunholo/utils/timedelta.py,sha256=BbLabEx7_rbErj_YbNM0MBcaFN76DC4PTe4zD2ucezg,493
113
113
  sunholo/utils/user_ids.py,sha256=SQd5_H7FE7vcTZp9AQuQDWBXd4FEEd7TeVMQe1H4Ny8,292
114
114
  sunholo/utils/version.py,sha256=P1QAJQdZfT2cMqdTSmXmcxrD2PssMPEGM-WI6083Fck,237
@@ -117,9 +117,9 @@ sunholo/vertex/extensions_class.py,sha256=4PsUM9dSYrIPpq9bZ3K2rL9MRb_rlqAgnMsW0o
117
117
  sunholo/vertex/init.py,sha256=-w7b9GKsyJnAJpYHYz6_zBUtmeJeLXlEkgOfwoe4DEI,2715
118
118
  sunholo/vertex/memory_tools.py,sha256=pomHrDKqvY8MZxfUqoEwhdlpCvSGP6KmFJMVKOimXjs,6842
119
119
  sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
120
- sunholo-0.74.8.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
121
- sunholo-0.74.8.dist-info/METADATA,sha256=aQlv95LPMIjodfSejFXHiaTmfGWAcIuvlDg2hLx-xys,7010
122
- sunholo-0.74.8.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
123
- sunholo-0.74.8.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
124
- sunholo-0.74.8.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
125
- sunholo-0.74.8.dist-info/RECORD,,
120
+ sunholo-0.74.9.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
121
+ sunholo-0.74.9.dist-info/METADATA,sha256=tf6DzIGnmFeb47QUwocgunQWJD2WEmYI3Kv73pcCNy4,7010
122
+ sunholo-0.74.9.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
123
+ sunholo-0.74.9.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
124
+ sunholo-0.74.9.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
125
+ sunholo-0.74.9.dist-info/RECORD,,