sunholo 0.74.8__tar.gz → 0.75.0__tar.gz
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.
- {sunholo-0.74.8 → sunholo-0.75.0}/PKG-INFO +2 -2
- {sunholo-0.74.8 → sunholo-0.75.0}/setup.py +1 -1
- sunholo-0.75.0/sunholo/auth/__init__.py +2 -0
- sunholo-0.75.0/sunholo/auth/gcloud.py +14 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/auth/run.py +4 -12
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/tools/web_browser.py +110 -11
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/utils/parsers.py +1 -1
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/vertex/extensions_class.py +19 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo.egg-info/PKG-INFO +2 -2
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo.egg-info/SOURCES.txt +1 -0
- sunholo-0.74.8/sunholo/auth/__init__.py +0 -1
- {sunholo-0.74.8 → sunholo-0.75.0}/LICENSE.txt +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/MANIFEST.in +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/README.md +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/setup.cfg +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/chat_history.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/dispatch_to_qa.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/fastapi/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/fastapi/base.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/fastapi/qna_routes.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/flask/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/flask/base.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/flask/qna_routes.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/flask/vac_routes.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/langserve.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/pubsub.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/route.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/special_commands.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/agents/swagger.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/archive/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/archive/archive.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/bots/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/bots/discord.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/bots/github_webhook.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/bots/webapp.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/chunker/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/chunker/data_to_embed_pubsub.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/chunker/doc_handling.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/chunker/images.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/chunker/loaders.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/chunker/message_data.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/chunker/pdfs.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/chunker/publish.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/chunker/splitter.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/cli/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/cli/chat_vac.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/cli/cli.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/cli/cli_init.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/cli/configs.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/cli/deploy.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/cli/embedder.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/cli/merge_texts.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/cli/run_proxy.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/cli/sun_rich.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/cli/swagger.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/components/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/components/llm.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/components/retriever.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/components/vectorstore.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/alloydb.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/alloydb_client.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/database.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/lancedb.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/sql/sb/create_function.sql +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/sql/sb/create_function_time.sql +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/sql/sb/create_table.sql +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/sql/sb/delete_source_row.sql +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/sql/sb/return_sources.sql +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/sql/sb/setup.sql +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/static_dbs.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/database/uuid.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/discovery_engine/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/discovery_engine/chunker_handler.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/discovery_engine/create_new.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/discovery_engine/discovery_engine_client.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/embedder/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/embedder/embed_chunk.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/gcs/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/gcs/add_file.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/gcs/download_url.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/gcs/metadata.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/invoke/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/invoke/invoke_vac_utils.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/langfuse/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/langfuse/callback.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/langfuse/prompts.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/llamaindex/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/llamaindex/generate.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/llamaindex/get_files.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/llamaindex/import_files.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/logging.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/lookup/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/lookup/model_lookup.yaml +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/patches/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/patches/langchain/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/patches/langchain/lancedb.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/patches/langchain/vertexai.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/pubsub/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/pubsub/process_pubsub.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/pubsub/pubsub_manager.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/qna/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/qna/parsers.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/qna/retry.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/streaming/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/streaming/content_buffer.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/streaming/langserve.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/streaming/stream_lookup.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/streaming/streaming.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/summarise/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/summarise/summarise.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/tools/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/utils/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/utils/api_key.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/utils/big_context.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/utils/config.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/utils/config_class.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/utils/config_schema.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/utils/gcp.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/utils/gcp_project.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/utils/timedelta.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/utils/user_ids.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/utils/version.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/vertex/__init__.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/vertex/init.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/vertex/memory_tools.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo/vertex/safety.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo.egg-info/dependency_links.txt +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo.egg-info/entry_points.txt +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo.egg-info/requires.txt +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/sunholo.egg-info/top_level.txt +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/tests/test_chat_history.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/tests/test_chunker.py +0 -0
- {sunholo-0.74.8 → sunholo-0.75.0}/tests/test_config.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.75.0
|
|
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.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.75.0.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -7,6 +7,7 @@ from ..utils.gcp import is_running_on_cloudrun
|
|
|
7
7
|
from ..utils.api_key import has_multivac_api_key, get_multivac_api_key
|
|
8
8
|
from ..logging import log
|
|
9
9
|
from ..agents.route import route_vac
|
|
10
|
+
from .gcloud import get_local_gcloud_token
|
|
10
11
|
|
|
11
12
|
def get_run_url(vector_name=None):
|
|
12
13
|
|
|
@@ -33,20 +34,11 @@ def get_id_token(url: str) -> str:
|
|
|
33
34
|
import google.oauth2.id_token # type: ignore
|
|
34
35
|
auth_req = google.auth.transport.requests.Request()
|
|
35
36
|
log.info(f'Got id_token for {url}')
|
|
37
|
+
|
|
36
38
|
return google.oauth2.id_token.fetch_id_token(auth_req, url)
|
|
37
|
-
else:
|
|
38
|
-
# Use gcloud credentials locally
|
|
39
|
-
import subprocess
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
["gcloud", "auth", "print-identity-token"],
|
|
44
|
-
stdout=subprocess.PIPE,
|
|
45
|
-
check=True,
|
|
46
|
-
)
|
|
47
|
-
.stdout.strip()
|
|
48
|
-
.decode()
|
|
49
|
-
)
|
|
40
|
+
return get_local_gcloud_token()
|
|
41
|
+
|
|
50
42
|
|
|
51
43
|
def get_header(vector_name) -> Optional[dict]:
|
|
52
44
|
if has_multivac_api_key():
|
|
@@ -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').
|
|
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
|
|
288
|
+
def click(self, selector):
|
|
260
289
|
(x,y)=(0,0)
|
|
261
290
|
|
|
262
|
-
element = self.
|
|
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
|
|
335
|
+
def type_text(self, selector, text):
|
|
307
336
|
(x,y)=(0,0)
|
|
308
|
-
element = self.
|
|
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)
|
|
@@ -332,6 +361,68 @@ class BrowseWebWithImagePromptsBot:
|
|
|
332
361
|
|
|
333
362
|
return (x, y)
|
|
334
363
|
|
|
364
|
+
def execute_custom_command(self, command):
|
|
365
|
+
"""
|
|
366
|
+
Executes a custom command on the page object.
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
command (str): The command string to be executed.
|
|
370
|
+
"""
|
|
371
|
+
try:
|
|
372
|
+
element_part = command.get('get_locator')
|
|
373
|
+
operation = command.get('operation')
|
|
374
|
+
|
|
375
|
+
if not element_part or not operation:
|
|
376
|
+
raise ValueError("Both 'element_part' and 'operation' must be provided in the command")
|
|
377
|
+
|
|
378
|
+
# Dynamically get the method and its parameters
|
|
379
|
+
method_name, params = self.parse_element_part(element_part)
|
|
380
|
+
method = getattr(self.page, method_name)
|
|
381
|
+
element = method(*params)
|
|
382
|
+
|
|
383
|
+
if not element:
|
|
384
|
+
raise ValueError(f"Element not found for selector: {element_part}")
|
|
385
|
+
|
|
386
|
+
# Execute the operation
|
|
387
|
+
exec(f"element.{operation}")
|
|
388
|
+
|
|
389
|
+
# Mark the action on the screenshot
|
|
390
|
+
bounding_box = element.bounding_box()
|
|
391
|
+
if bounding_box:
|
|
392
|
+
x = bounding_box['x'] + bounding_box['width'] / 2
|
|
393
|
+
y = bounding_box['y'] + bounding_box['height'] / 2
|
|
394
|
+
mark_action = {'type': operation, 'position': (x, y)}
|
|
395
|
+
self.take_screenshot(mark_action=mark_action)
|
|
396
|
+
else:
|
|
397
|
+
self.take_screenshot()
|
|
398
|
+
|
|
399
|
+
log.info(f"Executed custom command on element: {element_part} with operation: {operation}")
|
|
400
|
+
self.action_log.append(f"Executed custom command on element: {element_part} with operation: {operation}")
|
|
401
|
+
|
|
402
|
+
except Exception as e:
|
|
403
|
+
log.error(f"Failed to execute custom command: {command}. Error: {str(e)}")
|
|
404
|
+
self.action_log.append(f"Failed to execute custom command: {command}. Error: {str(e)}")
|
|
405
|
+
|
|
406
|
+
def parse_element_part(self, element_part):
|
|
407
|
+
"""
|
|
408
|
+
Parses the element_part string to extract the method name and its parameters.
|
|
409
|
+
|
|
410
|
+
Args:
|
|
411
|
+
element_part (str): The element part string (e.g., "get_by_role('button')")
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
tuple: A tuple containing the method name and a list of parameters.
|
|
415
|
+
"""
|
|
416
|
+
try:
|
|
417
|
+
# Extract the method name and parameters
|
|
418
|
+
method_name = element_part.split('(')[0]
|
|
419
|
+
params_str = element_part.split('(')[1].rstrip(')')
|
|
420
|
+
params = eval(f'[{params_str}]') # Safely evaluate parameters
|
|
421
|
+
|
|
422
|
+
return method_name, params
|
|
423
|
+
except Exception as e:
|
|
424
|
+
raise ValueError(f"Failed to parse element part: {element_part}. Error: {str(e)}")
|
|
425
|
+
|
|
335
426
|
def take_screenshot(self, full_page=False, mark_action=None):
|
|
336
427
|
|
|
337
428
|
from PIL import Image
|
|
@@ -344,6 +435,10 @@ class BrowseWebWithImagePromptsBot:
|
|
|
344
435
|
url_path = "index.html"
|
|
345
436
|
else:
|
|
346
437
|
url_path = url_path.replace("/","_")
|
|
438
|
+
|
|
439
|
+
if get_clean_website_name(url_path) != self.website_name:
|
|
440
|
+
url_path = f"{get_clean_website_name(url_path)}_{url_path}"
|
|
441
|
+
|
|
347
442
|
screenshot_path = os.path.join(self.screenshot_dir, f"{timestamp}_{url_path}.png")
|
|
348
443
|
screenshot_bytes = self.page.screenshot(full_page=full_page, scale='css')
|
|
349
444
|
|
|
@@ -358,7 +453,7 @@ class BrowseWebWithImagePromptsBot:
|
|
|
358
453
|
#self.action_log.append(f"Screenshot {self.page.url} taken and saved to {screenshot_path}")
|
|
359
454
|
self.session_screenshots.append(screenshot_path)
|
|
360
455
|
|
|
361
|
-
return
|
|
456
|
+
return screenshot_bytes
|
|
362
457
|
|
|
363
458
|
def mark_screenshot(self, screenshot_bytes, mark_action):
|
|
364
459
|
"""
|
|
@@ -433,9 +528,9 @@ class BrowseWebWithImagePromptsBot:
|
|
|
433
528
|
|
|
434
529
|
return output
|
|
435
530
|
|
|
436
|
-
def send_screenshot_to_llm(self,
|
|
437
|
-
|
|
438
|
-
|
|
531
|
+
def send_screenshot_to_llm(self, screenshot_bytes, last_message):
|
|
532
|
+
|
|
533
|
+
encoded_image = base64.b64encode(screenshot_bytes).decode('utf-8')
|
|
439
534
|
|
|
440
535
|
prompt_vars = self.create_prompt_vars(last_message)
|
|
441
536
|
response = self.send_prompt_to_llm(prompt_vars, encoded_image) # Sending prompt and image separately
|
|
@@ -490,14 +585,18 @@ This method should be implemented by subclasses: `def send_prompt_to_llm(self, p
|
|
|
490
585
|
x,y = self.type_text(instruction['selector'], instruction['text'])
|
|
491
586
|
if (x,y) != (0,0):
|
|
492
587
|
mark_action = {'type':'type', 'position': (x,y)}
|
|
588
|
+
elif action == 'execute':
|
|
589
|
+
x,y,mark = self.execute_custom_command(instruction['command'])
|
|
590
|
+
if mark:
|
|
591
|
+
mark_action = {'type': mark, 'position': (x,y)}
|
|
493
592
|
self.steps += 1
|
|
494
593
|
if self.steps >= self.max_steps:
|
|
495
594
|
log.warning(f"Reached the maximum number of steps: {self.max_steps}")
|
|
496
595
|
return
|
|
497
596
|
time.sleep(2)
|
|
498
|
-
|
|
597
|
+
screenshot_bytes = self.take_screenshot(mark_action=mark_action)
|
|
499
598
|
next_browser_instructions = self.send_screenshot_to_llm(
|
|
500
|
-
|
|
599
|
+
screenshot_bytes,
|
|
501
600
|
last_message=last_message)
|
|
502
601
|
|
|
503
602
|
return next_browser_instructions
|
|
@@ -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
|
|
@@ -236,6 +236,7 @@ class VertexAIExtensions:
|
|
|
236
236
|
if extension_name is None:
|
|
237
237
|
raise ValueError("Must specify extension_id or init one with class")
|
|
238
238
|
else:
|
|
239
|
+
extension_id = str(extension_id)
|
|
239
240
|
if not extension_id.startswith("projects/"):
|
|
240
241
|
project_id = get_gcp_project()
|
|
241
242
|
extension_name = f"projects/{project_id}/locations/{self.location}/extensions/{extension_id}"
|
|
@@ -244,11 +245,29 @@ class VertexAIExtensions:
|
|
|
244
245
|
|
|
245
246
|
extension = extensions.Extension(extension_name)
|
|
246
247
|
|
|
248
|
+
log.info(f"Executing extension {extension_name=} with {operation_id=} and {operation_params=}")
|
|
249
|
+
|
|
250
|
+
# local testing auth
|
|
251
|
+
from ..utils.gcp import is_running_on_cloudrun
|
|
252
|
+
auth_config=None # on cloud run it sorts itself out via default creds(?)
|
|
253
|
+
|
|
254
|
+
if not is_running_on_cloudrun():
|
|
255
|
+
from ..auth import get_local_gcloud_token
|
|
256
|
+
log.warning("Using local authentication via gcloud")
|
|
257
|
+
auth_config = {
|
|
258
|
+
"authType": "OAUTH",
|
|
259
|
+
"oauth_config": {"access_token": f"'{get_local_gcloud_token()}'"}
|
|
260
|
+
}
|
|
261
|
+
log.info(auth_config)
|
|
262
|
+
|
|
247
263
|
response = extension.execute(
|
|
248
264
|
operation_id=operation_id,
|
|
249
265
|
operation_params=operation_params,
|
|
266
|
+
runtime_auth_config=auth_config
|
|
250
267
|
)
|
|
251
268
|
|
|
269
|
+
log.info(f"Extension {extension_name=} {response=}")
|
|
270
|
+
|
|
252
271
|
return response
|
|
253
272
|
|
|
254
273
|
def execute_code_extension(self,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.75.0
|
|
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.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.75.0.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .run import get_header
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|