sunholo 0.71.11__tar.gz → 0.71.13__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.
Files changed (135) hide show
  1. {sunholo-0.71.11 → sunholo-0.71.13}/PKG-INFO +12 -2
  2. {sunholo-0.71.11 → sunholo-0.71.13}/README.md +10 -0
  3. {sunholo-0.71.11 → sunholo-0.71.13}/setup.py +1 -1
  4. sunholo-0.71.13/sunholo/tools/__init__.py +0 -0
  5. sunholo-0.71.13/sunholo/tools/web_browser.py +169 -0
  6. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/vertex/__init__.py +2 -2
  7. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/vertex/init.py +13 -0
  8. sunholo-0.71.13/sunholo/vertex/safety.py +66 -0
  9. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo.egg-info/PKG-INFO +12 -2
  10. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo.egg-info/SOURCES.txt +2 -0
  11. sunholo-0.71.11/sunholo/vertex/safety.py +0 -35
  12. {sunholo-0.71.11 → sunholo-0.71.13}/LICENSE.txt +0 -0
  13. {sunholo-0.71.11 → sunholo-0.71.13}/MANIFEST.in +0 -0
  14. {sunholo-0.71.11 → sunholo-0.71.13}/setup.cfg +0 -0
  15. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/__init__.py +0 -0
  16. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/__init__.py +0 -0
  17. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/chat_history.py +0 -0
  18. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/dispatch_to_qa.py +0 -0
  19. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/fastapi/__init__.py +0 -0
  20. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/fastapi/base.py +0 -0
  21. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/fastapi/qna_routes.py +0 -0
  22. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/flask/__init__.py +0 -0
  23. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/flask/base.py +0 -0
  24. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/flask/qna_routes.py +0 -0
  25. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/flask/vac_routes.py +0 -0
  26. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/langserve.py +0 -0
  27. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/pubsub.py +0 -0
  28. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/route.py +0 -0
  29. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/special_commands.py +0 -0
  30. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/agents/swagger.py +0 -0
  31. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/archive/__init__.py +0 -0
  32. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/archive/archive.py +0 -0
  33. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/auth/__init__.py +0 -0
  34. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/auth/run.py +0 -0
  35. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/bots/__init__.py +0 -0
  36. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/bots/discord.py +0 -0
  37. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/bots/github_webhook.py +0 -0
  38. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/bots/webapp.py +0 -0
  39. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/chunker/__init__.py +0 -0
  40. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/chunker/data_to_embed_pubsub.py +0 -0
  41. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/chunker/doc_handling.py +0 -0
  42. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/chunker/images.py +0 -0
  43. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/chunker/loaders.py +0 -0
  44. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/chunker/message_data.py +0 -0
  45. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/chunker/pdfs.py +0 -0
  46. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/chunker/publish.py +0 -0
  47. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/chunker/splitter.py +0 -0
  48. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/cli/__init__.py +0 -0
  49. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/cli/chat_vac.py +0 -0
  50. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/cli/cli.py +0 -0
  51. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/cli/cli_init.py +0 -0
  52. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/cli/configs.py +0 -0
  53. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/cli/deploy.py +0 -0
  54. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/cli/embedder.py +0 -0
  55. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/cli/merge_texts.py +0 -0
  56. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/cli/run_proxy.py +0 -0
  57. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/cli/sun_rich.py +0 -0
  58. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/cli/swagger.py +0 -0
  59. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/components/__init__.py +0 -0
  60. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/components/llm.py +0 -0
  61. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/components/retriever.py +0 -0
  62. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/components/vectorstore.py +0 -0
  63. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/__init__.py +0 -0
  64. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/alloydb.py +0 -0
  65. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/alloydb_client.py +0 -0
  66. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/database.py +0 -0
  67. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/lancedb.py +0 -0
  68. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/sql/sb/create_function.sql +0 -0
  69. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/sql/sb/create_function_time.sql +0 -0
  70. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/sql/sb/create_table.sql +0 -0
  71. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/sql/sb/delete_source_row.sql +0 -0
  72. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/sql/sb/return_sources.sql +0 -0
  73. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/sql/sb/setup.sql +0 -0
  74. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/static_dbs.py +0 -0
  75. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/database/uuid.py +0 -0
  76. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/discovery_engine/__init__.py +0 -0
  77. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/discovery_engine/chunker_handler.py +0 -0
  78. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/discovery_engine/create_new.py +0 -0
  79. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/discovery_engine/discovery_engine_client.py +0 -0
  80. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/embedder/__init__.py +0 -0
  81. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/embedder/embed_chunk.py +0 -0
  82. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/gcs/__init__.py +0 -0
  83. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/gcs/add_file.py +0 -0
  84. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/gcs/download_url.py +0 -0
  85. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/gcs/metadata.py +0 -0
  86. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/langfuse/__init__.py +0 -0
  87. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/langfuse/callback.py +0 -0
  88. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/langfuse/prompts.py +0 -0
  89. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/llamaindex/__init__.py +0 -0
  90. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/llamaindex/generate.py +0 -0
  91. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/llamaindex/get_files.py +0 -0
  92. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/llamaindex/import_files.py +0 -0
  93. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/logging.py +0 -0
  94. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/lookup/__init__.py +0 -0
  95. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/lookup/model_lookup.yaml +0 -0
  96. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/patches/__init__.py +0 -0
  97. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/patches/langchain/__init__.py +0 -0
  98. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/patches/langchain/lancedb.py +0 -0
  99. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/patches/langchain/vertexai.py +0 -0
  100. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/pubsub/__init__.py +0 -0
  101. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/pubsub/process_pubsub.py +0 -0
  102. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/pubsub/pubsub_manager.py +0 -0
  103. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/qna/__init__.py +0 -0
  104. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/qna/parsers.py +0 -0
  105. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/qna/retry.py +0 -0
  106. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/streaming/__init__.py +0 -0
  107. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/streaming/content_buffer.py +0 -0
  108. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/streaming/langserve.py +0 -0
  109. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/streaming/stream_lookup.py +0 -0
  110. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/streaming/streaming.py +0 -0
  111. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/summarise/__init__.py +0 -0
  112. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/summarise/summarise.py +0 -0
  113. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/utils/__init__.py +0 -0
  114. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/utils/api_key.py +0 -0
  115. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/utils/big_context.py +0 -0
  116. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/utils/config.py +0 -0
  117. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/utils/config_schema.py +0 -0
  118. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/utils/gcp.py +0 -0
  119. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/utils/gcp_project.py +0 -0
  120. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/utils/parsers.py +0 -0
  121. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/utils/timedelta.py +0 -0
  122. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/utils/user_ids.py +0 -0
  123. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/utils/version.py +0 -0
  124. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/vertex/extensions.py +0 -0
  125. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/vertex/extensions_class.py +0 -0
  126. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo/vertex/memory_tools.py +0 -0
  127. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo.egg-info/dependency_links.txt +0 -0
  128. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo.egg-info/entry_points.txt +0 -0
  129. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo.egg-info/requires.txt +0 -0
  130. {sunholo-0.71.11 → sunholo-0.71.13}/sunholo.egg-info/top_level.txt +0 -0
  131. {sunholo-0.71.11 → sunholo-0.71.13}/tests/test_chat_history.py +0 -0
  132. {sunholo-0.71.11 → sunholo-0.71.13}/tests/test_chunker.py +0 -0
  133. {sunholo-0.71.11 → sunholo-0.71.13}/tests/test_config.py +0 -0
  134. {sunholo-0.71.11 → sunholo-0.71.13}/tests/test_dispatch_to_qa.py +0 -0
  135. {sunholo-0.71.11 → sunholo-0.71.13}/tests/test_swagger.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.71.11
3
+ Version: 0.71.13
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.71.11.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.71.13.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -124,6 +124,16 @@ This is the Sunholo Python project, a comprehensive toolkit for working with lan
124
124
 
125
125
  Please refer to the website for full documentation at https://dev.sunholo.com/
126
126
 
127
+ ## Tests via pytest
128
+
129
+ If loading from GitHub, run tests:
130
+
131
+ ```bash
132
+ pip install pytest
133
+ pip install . --use-feature=in-tree-build
134
+ pytest tests
135
+ ```
136
+
127
137
  ## Demos
128
138
 
129
139
  Using https://github.com/charmbracelet/vhs
@@ -3,6 +3,16 @@ This is the Sunholo Python project, a comprehensive toolkit for working with lan
3
3
 
4
4
  Please refer to the website for full documentation at https://dev.sunholo.com/
5
5
 
6
+ ## Tests via pytest
7
+
8
+ If loading from GitHub, run tests:
9
+
10
+ ```bash
11
+ pip install pytest
12
+ pip install . --use-feature=in-tree-build
13
+ pytest tests
14
+ ```
15
+
6
16
  ## Demos
7
17
 
8
18
  Using https://github.com/charmbracelet/vhs
@@ -1,7 +1,7 @@
1
1
  from setuptools import setup, find_packages
2
2
 
3
3
  # Define your base version
4
- version = '0.71.11'
4
+ version = '0.71.13'
5
5
 
6
6
  setup(
7
7
  name='sunholo',
File without changes
@@ -0,0 +1,169 @@
1
+ import os
2
+ import base64
3
+ import json
4
+ from datetime import datetime
5
+ try:
6
+ from playwright.sync_api import sync_playwright
7
+ except ImportError:
8
+ sync_playwright = None
9
+
10
+ class BrowseWebWithImagePromptsBot:
11
+ """
12
+ Examples:
13
+
14
+ ```python
15
+ class ProductionBot(BrowseWebWithImagePromptsBot):
16
+ def send_prompt_to_llm(self, prompt, screenshot_base64):
17
+ # Implement the actual logic to send the prompt and screenshot to the LLM and return the response
18
+ api_url = "https://api.example.com/process" # Replace with the actual LLM API endpoint
19
+ headers = {"Content-Type": "application/json"}
20
+ data = {
21
+ "prompt": prompt,
22
+ "screenshot": screenshot_base64
23
+ }
24
+ response = requests.post(api_url, headers=headers, data=json.dumps(data))
25
+ return response.text # Assuming the response is in JSON format
26
+
27
+ @app.route('/run-bot', methods=['POST'])
28
+ def run_bot():
29
+ data = request.json
30
+ session_id = data.get('session_id')
31
+ website_name = data.get('website_name')
32
+ browser_type = data.get('browser_type', 'chromium')
33
+ current_action_description = data.get('current_action_description', "")
34
+ next_goal = data.get('next_goal', "")
35
+
36
+ bot = ProductionBot(session_id=session_id, website_name=website_name, browser_type=browser_type, headless=True)
37
+
38
+ # Check if initial instructions are provided
39
+ initial_instructions = data.get('instructions')
40
+ if initial_instructions:
41
+ bot.execute_instructions(initial_instructions)
42
+
43
+ # Take initial screenshot and send to LLM if no instructions provided
44
+ if not initial_instructions:
45
+ screenshot_path = bot.take_screenshot()
46
+ new_instructions = bot.send_screenshot_to_llm(screenshot_path, current_action_description, next_goal)
47
+ bot.execute_instructions(new_instructions)
48
+
49
+ # Take final screenshot
50
+ bot.take_screenshot()
51
+
52
+ bot.close()
53
+
54
+ return jsonify({"status": "completed", "new_instructions": new_instructions})
55
+
56
+ if __name__ == "__main__":
57
+ app.run(host='0.0.0.0', port=8080)
58
+ ```
59
+ """
60
+ def __init__(self, session_id, website_name, browser_type='chromium', headless=True):
61
+ if not sync_playwright:
62
+ raise ImportError("playright needed for BrowseWebWithImagePromptsBot class - install via `pip install sunholo[tools]`")
63
+ self.session_id = session_id
64
+ self.website_name = website_name
65
+ self.browser_type = browser_type
66
+ self.screenshot_dir = f"{website_name}_{session_id}"
67
+ os.makedirs(self.screenshot_dir, exist_ok=True)
68
+ self.cookie_file = os.path.join(self.screenshot_dir, "cookies.json")
69
+ self.playwright = sync_playwright().start()
70
+
71
+ if browser_type == 'chromium':
72
+ self.browser = self.playwright.chromium.launch(headless=headless)
73
+ elif browser_type == 'firefox':
74
+ self.browser = self.playwright.firefox.launch(headless=headless)
75
+ elif browser_type == 'webkit':
76
+ self.browser = self.playwright.webkit.launch(headless=headless)
77
+ else:
78
+ raise ValueError(f"Unsupported browser type: {browser_type}")
79
+
80
+ self.context = self.browser.new_context()
81
+ self.page = self.context.new_page()
82
+ self.load_cookies()
83
+
84
+ def load_cookies(self):
85
+ if os.path.exists(self.cookie_file):
86
+ with open(self.cookie_file, 'r') as f:
87
+ cookies = json.load(f)
88
+ self.context.add_cookies(cookies)
89
+
90
+ def save_cookies(self):
91
+ cookies = self.context.cookies()
92
+ with open(self.cookie_file, 'w') as f:
93
+ json.dump(cookies, f)
94
+
95
+ def navigate(self, url):
96
+ self.page.goto(url)
97
+
98
+ def click(self, selector):
99
+ self.page.click(selector)
100
+
101
+ def scroll(self, direction='down', amount=1):
102
+ for _ in range(amount):
103
+ if direction == 'down':
104
+ self.page.evaluate("window.scrollBy(0, window.innerHeight)")
105
+ elif direction == 'up':
106
+ self.page.evaluate("window.scrollBy(0, -window.innerHeight)")
107
+ elif direction == 'left':
108
+ self.page.evaluate("window.scrollBy(-window.innerWidth, 0)")
109
+ elif direction == 'right':
110
+ self.page.evaluate("window.scrollBy(window.innerWidth, 0)")
111
+
112
+ def type_text(self, selector, text):
113
+ self.page.fill(selector, text)
114
+
115
+ def take_screenshot(self):
116
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
117
+ screenshot_path = os.path.join(self.screenshot_dir, f"screenshot_{timestamp}.png")
118
+ self.page.screenshot(path=screenshot_path)
119
+ return screenshot_path
120
+
121
+ def get_latest_screenshot_path(self):
122
+ screenshots = sorted(
123
+ [f for f in os.listdir(self.screenshot_dir) if f.startswith('screenshot_')],
124
+ key=lambda x: os.path.getmtime(os.path.join(self.screenshot_dir, x)),
125
+ reverse=True
126
+ )
127
+ if screenshots:
128
+ return os.path.join(self.screenshot_dir, screenshots[0])
129
+ return None
130
+
131
+ def create_prompt_vars(self, current_action_description, next_goal):
132
+ prompt = {
133
+ "current_action_description": current_action_description,
134
+ "next_goal": next_goal,
135
+ }
136
+ return prompt
137
+
138
+ def send_screenshot_to_llm(self, screenshot_path, current_action_description="", next_goal=""):
139
+ with open(screenshot_path, "rb") as image_file:
140
+ encoded_image = base64.b64encode(image_file.read()).decode('utf-8')
141
+
142
+ prompt_vars = self.create_prompt(current_action_description, next_goal)
143
+ response = self.send_prompt_to_llm(prompt_vars, encoded_image) # Sending prompt and image separately
144
+ return json.loads(response)
145
+
146
+ def send_prompt_to_llm(self, prompt_vars, screenshot_base64):
147
+ raise NotImplementedError("This method should be implemented by subclasses: `def send_prompt_to_llm(self, prompt_vars, screenshot_base64)`")
148
+
149
+ def close(self):
150
+ self.save_cookies()
151
+ self.browser.close()
152
+ self.playwright.stop()
153
+
154
+ def execute_instructions(self, instructions):
155
+ for instruction in instructions:
156
+ action = instruction['action']
157
+ if action == 'navigate':
158
+ self.navigate(instruction['url'])
159
+ elif action == 'click':
160
+ self.click(instruction['selector'])
161
+ elif action == 'scroll':
162
+ self.scroll(instruction.get('direction', 'down'), instruction.get('amount', 1))
163
+ elif action == 'type':
164
+ self.type_text(instruction['selector'], instruction['text'])
165
+ screenshot_path = self.take_screenshot()
166
+ new_instructions = self.send_screenshot_to_llm(screenshot_path, instruction.get('description', ''), instruction.get('next_goal', ''))
167
+ if new_instructions:
168
+ self.execute_instructions(new_instructions)
169
+
@@ -1,3 +1,3 @@
1
- from .init import init_vertex
1
+ from .init import init_vertex, init_genai
2
2
  from .memory_tools import get_vertex_memories, print_grounding_response, get_google_search_grounding
3
- from .safety import vertex_safety
3
+ from .safety import vertex_safety, genai_safety
@@ -1,5 +1,18 @@
1
1
  from ..logging import log
2
2
  from ..utils.gcp_project import get_gcp_project
3
+ import os
4
+
5
+ def init_genai():
6
+ try:
7
+ import google.generativeai as genai
8
+ except ImportError:
9
+ raise ImportError("google.generativeai not installed, please install via 'pip install sunholo[gcp]")
10
+
11
+ GOOGLE_API_KEY=os.getenv('GOOGLE_API_KEY')
12
+ if not GOOGLE_API_KEY:
13
+ raise ValueError("google.generativeai needs GOOGLE_API_KEY set in environment variable")
14
+
15
+ genai.configure(api_key=GOOGLE_API_KEY)
3
16
 
4
17
  def init_vertex(gcp_config=None, location="eu"):
5
18
  """
@@ -0,0 +1,66 @@
1
+
2
+
3
+ def genai_safety(threshold: str = "BLOCK_ONLY_HIGH"):
4
+ """
5
+ BLOCK_ONLY_HIGH - block when high probability of unsafe content is detected
6
+ BLOCK_MEDIUM_AND_ABOVE - block when medium or high probability of content is detected
7
+ BLOCK_LOW_AND_ABOVE - block when low, medium, or high probability of unsafe content is detected
8
+ BLOCK_NONE - no block, but need to be on an allow list to use
9
+ """
10
+ from google.generativeai.types import (
11
+ HarmCategory,
12
+ HarmBlockThreshold
13
+ )
14
+
15
+ if threshold == 'BLOCK_ONLY_HIGH':
16
+ thresh = HarmBlockThreshold.BLOCK_ONLY_HIGH
17
+ elif threshold == 'BLOCK_MEDIUM_AND_ABOVE':
18
+ thresh = HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE
19
+ elif threshold == 'BLOCK_LOW_AND_ABOVE':
20
+ thresh = HarmBlockThreshold.BLOCK_LOW_AND_ABOVE
21
+ elif threshold == 'BLOCK_NONE':
22
+ thresh = HarmBlockThreshold.BLOCK_NONE
23
+ else:
24
+ raise ValueError("Invalid threshold")
25
+
26
+ safety_settings = {
27
+ HarmCategory.HARM_CATEGORY_HARASSMENT: thresh,
28
+ HarmCategory.HARM_CATEGORY_HATE_SPEECH: thresh,
29
+ HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: thresh,
30
+ HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: thresh,
31
+ }
32
+
33
+ return safety_settings
34
+
35
+
36
+ def vertex_safety(threshold: str = "BLOCK_ONLY_HIGH"):
37
+ """
38
+ BLOCK_ONLY_HIGH - block when high probability of unsafe content is detected
39
+ BLOCK_MEDIUM_AND_ABOVE - block when medium or high probability of content is detected
40
+ BLOCK_LOW_AND_ABOVE - block when low, medium, or high probability of unsafe content is detected
41
+ BLOCK_NONE - no block, but need to be on an allow list to use
42
+ """
43
+ from vertexai.generative_models import (
44
+ HarmCategory,
45
+ HarmBlockThreshold,
46
+ )
47
+
48
+ if threshold == 'BLOCK_ONLY_HIGH':
49
+ thresh = HarmBlockThreshold.BLOCK_ONLY_HIGH
50
+ elif threshold == 'BLOCK_MEDIUM_AND_ABOVE':
51
+ thresh = HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE
52
+ elif threshold == 'BLOCK_LOW_AND_ABOVE':
53
+ thresh = HarmBlockThreshold.BLOCK_LOW_AND_ABOVE
54
+ elif threshold == 'BLOCK_NONE':
55
+ thresh = HarmBlockThreshold.BLOCK_NONE
56
+ else:
57
+ raise ValueError("Invalid threshold")
58
+
59
+ safety_settings = {
60
+ HarmCategory.HARM_CATEGORY_HARASSMENT: thresh,
61
+ HarmCategory.HARM_CATEGORY_HATE_SPEECH: thresh,
62
+ HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: thresh,
63
+ HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: thresh,
64
+ }
65
+
66
+ return safety_settings
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.71.11
3
+ Version: 0.71.13
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.71.11.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.71.13.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -124,6 +124,16 @@ This is the Sunholo Python project, a comprehensive toolkit for working with lan
124
124
 
125
125
  Please refer to the website for full documentation at https://dev.sunholo.com/
126
126
 
127
+ ## Tests via pytest
128
+
129
+ If loading from GitHub, run tests:
130
+
131
+ ```bash
132
+ pip install pytest
133
+ pip install . --use-feature=in-tree-build
134
+ pytest tests
135
+ ```
136
+
127
137
  ## Demos
128
138
 
129
139
  Using https://github.com/charmbracelet/vhs
@@ -107,6 +107,8 @@ sunholo/streaming/stream_lookup.py
107
107
  sunholo/streaming/streaming.py
108
108
  sunholo/summarise/__init__.py
109
109
  sunholo/summarise/summarise.py
110
+ sunholo/tools/__init__.py
111
+ sunholo/tools/web_browser.py
110
112
  sunholo/utils/__init__.py
111
113
  sunholo/utils/api_key.py
112
114
  sunholo/utils/big_context.py
@@ -1,35 +0,0 @@
1
- try:
2
- from vertexai.generative_models import (
3
- HarmCategory,
4
- HarmBlockThreshold,
5
- )
6
- except ImportError:
7
- pass
8
-
9
- def vertex_safety(threshold: str = "BLOCK_ONLY_HIGH"):
10
- """
11
- BLOCK_ONLY_HIGH - block when high probability of unsafe content is detected
12
- BLOCK_MEDIUM_AND_ABOVE - block when medium or high probability of content is detected
13
- BLOCK_LOW_AND_ABOVE - block when low, medium, or high probability of unsafe content is detected
14
- BLOCK_NONE - no block, but need to be on an allow list to use
15
- """
16
-
17
- if threshold == 'BLOCK_ONLY_HIGH':
18
- thresh = HarmBlockThreshold.BLOCK_ONLY_HIGH
19
- elif threshold == 'BLOCK_MEDIUM_AND_ABOVE':
20
- thresh = HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE
21
- elif threshold == 'BLOCK_LOW_AND_ABOVE':
22
- thresh = HarmBlockThreshold.BLOCK_LOW_AND_ABOVE
23
- elif threshold == 'BLOCK_NONE':
24
- thresh = HarmBlockThreshold.BLOCK_NONE
25
- else:
26
- raise ValueError("Invalid threshold")
27
-
28
- safety_settings = {
29
- HarmCategory.HARM_CATEGORY_HARASSMENT: thresh,
30
- HarmCategory.HARM_CATEGORY_HATE_SPEECH: thresh,
31
- HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: thresh,
32
- HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: thresh,
33
- }
34
-
35
- return safety_settings
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes