sunholo 0.71.10__tar.gz → 0.71.12__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.71.10 → sunholo-0.71.12}/PKG-INFO +12 -2
- {sunholo-0.71.10 → sunholo-0.71.12}/README.md +10 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/setup.py +1 -1
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/discovery_engine/discovery_engine_client.py +19 -5
- sunholo-0.71.12/sunholo/tools/__init__.py +0 -0
- sunholo-0.71.12/sunholo/tools/web_browser.py +169 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/vertex/__init__.py +1 -1
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/vertex/memory_tools.py +4 -0
- sunholo-0.71.12/sunholo/vertex/safety.py +66 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo.egg-info/PKG-INFO +12 -2
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo.egg-info/SOURCES.txt +2 -0
- sunholo-0.71.10/sunholo/vertex/safety.py +0 -35
- {sunholo-0.71.10 → sunholo-0.71.12}/LICENSE.txt +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/MANIFEST.in +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/setup.cfg +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/chat_history.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/dispatch_to_qa.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/fastapi/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/fastapi/base.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/fastapi/qna_routes.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/flask/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/flask/base.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/flask/qna_routes.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/flask/vac_routes.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/langserve.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/pubsub.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/route.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/special_commands.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/agents/swagger.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/archive/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/archive/archive.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/auth/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/auth/run.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/bots/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/bots/discord.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/bots/github_webhook.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/bots/webapp.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/chunker/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/chunker/data_to_embed_pubsub.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/chunker/doc_handling.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/chunker/images.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/chunker/loaders.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/chunker/message_data.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/chunker/pdfs.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/chunker/publish.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/chunker/splitter.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/cli/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/cli/chat_vac.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/cli/cli.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/cli/cli_init.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/cli/configs.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/cli/deploy.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/cli/embedder.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/cli/merge_texts.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/cli/run_proxy.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/cli/sun_rich.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/cli/swagger.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/components/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/components/llm.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/components/retriever.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/components/vectorstore.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/alloydb.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/alloydb_client.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/database.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/lancedb.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/sql/sb/create_function.sql +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/sql/sb/create_function_time.sql +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/sql/sb/create_table.sql +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/sql/sb/delete_source_row.sql +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/sql/sb/return_sources.sql +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/sql/sb/setup.sql +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/static_dbs.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/database/uuid.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/discovery_engine/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/discovery_engine/chunker_handler.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/discovery_engine/create_new.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/embedder/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/embedder/embed_chunk.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/gcs/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/gcs/add_file.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/gcs/download_url.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/gcs/metadata.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/langfuse/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/langfuse/callback.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/langfuse/prompts.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/llamaindex/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/llamaindex/generate.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/llamaindex/get_files.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/llamaindex/import_files.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/logging.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/lookup/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/lookup/model_lookup.yaml +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/patches/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/patches/langchain/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/patches/langchain/lancedb.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/patches/langchain/vertexai.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/pubsub/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/pubsub/process_pubsub.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/pubsub/pubsub_manager.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/qna/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/qna/parsers.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/qna/retry.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/streaming/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/streaming/content_buffer.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/streaming/langserve.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/streaming/stream_lookup.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/streaming/streaming.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/summarise/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/summarise/summarise.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/utils/__init__.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/utils/api_key.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/utils/big_context.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/utils/config.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/utils/config_schema.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/utils/gcp.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/utils/gcp_project.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/utils/parsers.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/utils/timedelta.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/utils/user_ids.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/utils/version.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/vertex/extensions.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/vertex/extensions_class.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo/vertex/init.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo.egg-info/dependency_links.txt +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo.egg-info/entry_points.txt +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo.egg-info/requires.txt +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/sunholo.egg-info/top_level.txt +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/tests/test_chat_history.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/tests/test_chunker.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/tests/test_config.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/tests/test_dispatch_to_qa.py +0 -0
- {sunholo-0.71.10 → sunholo-0.71.12}/tests/test_swagger.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.71.
|
|
3
|
+
Version: 0.71.12
|
|
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.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.71.12.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
|
|
@@ -87,8 +87,14 @@ class DiscoveryEngineClient:
|
|
|
87
87
|
location=self.location,
|
|
88
88
|
collection=collection,
|
|
89
89
|
)
|
|
90
|
+
|
|
91
|
+
def create_data_store(self, type="chunk", chunk_size: int = 500, collection: str = "default_collection"):
|
|
92
|
+
if type == "chunk":
|
|
93
|
+
return self.create_data_store_chunk(chunk_size, collection)
|
|
94
|
+
else:
|
|
95
|
+
raise NotImplementedError("Not done yet - non-chunk data stores.")
|
|
90
96
|
|
|
91
|
-
def
|
|
97
|
+
def create_data_store_chunk(
|
|
92
98
|
self, chunk_size: int = 500,
|
|
93
99
|
collection: str = "default_collection"
|
|
94
100
|
) -> str:
|
|
@@ -166,7 +172,6 @@ class DiscoveryEngineClient:
|
|
|
166
172
|
num_next_chunks: int = 3,
|
|
167
173
|
page_size: int = 10,
|
|
168
174
|
parse_chunks_to_string: bool = True,
|
|
169
|
-
doc_or_chunks: str = "CHUNKS", # or DOCUMENTS
|
|
170
175
|
serving_config: str = "default_serving_config",
|
|
171
176
|
):
|
|
172
177
|
"""Retrieves chunks or documents based on a query.
|
|
@@ -178,6 +183,7 @@ class DiscoveryEngineClient:
|
|
|
178
183
|
num_next_chunks (int, optional): Number of next chunks to return for context (default is 3).
|
|
179
184
|
page_size (int, optional): The maximum number of results to return per page (default is 10).
|
|
180
185
|
parse_chunks_to_string: If True will put chunks in one big string, False will return object
|
|
186
|
+
serving_config: The resource name of the Search serving config
|
|
181
187
|
|
|
182
188
|
Returns:
|
|
183
189
|
discoveryengine.SearchResponse: The search response object containing the search results.
|
|
@@ -198,12 +204,13 @@ class DiscoveryEngineClient:
|
|
|
198
204
|
serving_config
|
|
199
205
|
)
|
|
200
206
|
|
|
207
|
+
|
|
201
208
|
search_request = discoveryengine.SearchRequest(
|
|
202
209
|
serving_config=serving_config_path,
|
|
203
210
|
query=query,
|
|
204
211
|
page_size=page_size,
|
|
205
212
|
content_search_spec=discoveryengine.SearchRequest.ContentSearchSpec(
|
|
206
|
-
search_result_mode=
|
|
213
|
+
search_result_mode="CHUNKS",
|
|
207
214
|
chunk_spec=discoveryengine.SearchRequest.ContentSearchSpec.ChunkSpec(
|
|
208
215
|
num_previous_chunks=num_previous_chunks,
|
|
209
216
|
num_next_chunks=num_next_chunks,
|
|
@@ -270,7 +277,9 @@ class DiscoveryEngineClient:
|
|
|
270
277
|
search_tier=None,
|
|
271
278
|
search_add_ons=None,
|
|
272
279
|
) -> str:
|
|
273
|
-
|
|
280
|
+
"""
|
|
281
|
+
You only need this if calling Data Store via Vertex Tools.
|
|
282
|
+
"""
|
|
274
283
|
# The full resource name of the collection
|
|
275
284
|
# e.g. projects/{project}/locations/{location}/collections/default_collection
|
|
276
285
|
parent = self.data_store_path()
|
|
@@ -300,8 +309,13 @@ class DiscoveryEngineClient:
|
|
|
300
309
|
)
|
|
301
310
|
|
|
302
311
|
# Make the request
|
|
303
|
-
|
|
312
|
+
try:
|
|
313
|
+
operation = self.engine_client.create_engine(request=request)
|
|
314
|
+
except AlreadyExists as err:
|
|
315
|
+
log.info(f"Engine already exists: - {str(err)}")
|
|
304
316
|
|
|
317
|
+
return engine_id
|
|
318
|
+
|
|
305
319
|
log.info(f"Waiting for create vertex ai search operation to complete: {operation.operation.name}")
|
|
306
320
|
response = operation.result()
|
|
307
321
|
|
|
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
|
+
|
|
@@ -88,6 +88,10 @@ def get_vertex_memories(vector_name):
|
|
|
88
88
|
|
|
89
89
|
try:
|
|
90
90
|
project_id = value.get('project_id') or get_gcp_project()
|
|
91
|
+
if value.get('chunks'):
|
|
92
|
+
log.warning("Data stores for chunks do not work with Tools yet, call data store directly instead")
|
|
93
|
+
continue
|
|
94
|
+
|
|
91
95
|
de = DiscoveryEngineClient(vector_name, project_id=project_id)
|
|
92
96
|
log.info(f"Found vectorstore {vectorstore}")
|
|
93
97
|
|
|
@@ -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.
|
|
3
|
+
Version: 0.71.12
|
|
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.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.71.12.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
|
|
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
|