sunholo 0.60.2__tar.gz → 0.60.3__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.60.2 → sunholo-0.60.3}/PKG-INFO +10 -6
- {sunholo-0.60.2 → sunholo-0.60.3}/setup.py +11 -5
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/__init__.py +3 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/cli/chat_vac.py +45 -22
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/cli/cli.py +25 -3
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/cli/run_proxy.py +99 -8
- sunholo-0.60.3/sunholo/cli/sun_rich.py +3 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/gcs/download_url.py +1 -1
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/logging.py +0 -3
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/utils/config.py +15 -10
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo.egg-info/PKG-INFO +10 -6
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo.egg-info/SOURCES.txt +1 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo.egg-info/requires.txt +9 -4
- {sunholo-0.60.2 → sunholo-0.60.3}/LICENSE.txt +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/MANIFEST.in +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/README.md +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/setup.cfg +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/chat_history.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/dispatch_to_qa.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/fastapi/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/fastapi/base.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/fastapi/qna_routes.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/flask/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/flask/base.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/flask/qna_routes.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/langserve.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/pubsub.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/route.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/special_commands.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/agents/test_chat_history.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/archive/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/archive/archive.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/auth/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/auth/run.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/bots/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/bots/discord.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/bots/github_webhook.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/bots/webapp.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/chunker/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/chunker/data_to_embed_pubsub.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/chunker/doc_handling.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/chunker/images.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/chunker/loaders.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/chunker/message_data.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/chunker/pdfs.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/chunker/publish.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/chunker/splitter.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/cli/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/cli/cli_init.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/cli/configs.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/cli/deploy.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/cli/merge_texts.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/components/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/components/llm.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/components/prompt.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/components/retriever.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/components/vectorstore.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/database/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/database/alloydb.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/database/database.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/database/lancedb.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/database/sql/sb/create_function.sql +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/database/sql/sb/create_function_time.sql +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/database/sql/sb/create_table.sql +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/database/sql/sb/delete_source_row.sql +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/database/sql/sb/return_sources.sql +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/database/sql/sb/setup.sql +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/database/static_dbs.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/database/uuid.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/embedder/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/embedder/embed_chunk.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/gcs/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/gcs/add_file.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/gcs/metadata.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/langfuse/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/langfuse/callback.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/langfuse/prompts.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/llamaindex/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/llamaindex/generate.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/llamaindex/import_files.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/lookup/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/lookup/model_lookup.yaml +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/patches/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/patches/langchain/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/patches/langchain/lancedb.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/patches/langchain/vertexai.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/pubsub/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/pubsub/process_pubsub.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/pubsub/pubsub_manager.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/qna/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/qna/parsers.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/qna/retry.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/streaming/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/streaming/content_buffer.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/streaming/langserve.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/streaming/streaming.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/summarise/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/summarise/summarise.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/utils/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/utils/big_context.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/utils/config_schema.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/utils/gcp.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/utils/parsers.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/utils/user_ids.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/vertex/__init__.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo/vertex/init_vertex.py +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo.egg-info/dependency_links.txt +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo.egg-info/entry_points.txt +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/sunholo.egg-info/top_level.txt +0 -0
- {sunholo-0.60.2 → sunholo-0.60.3}/test/test_dispatch_to_qa.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.60.
|
|
3
|
+
Version: 0.60.3
|
|
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.60.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.60.3.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -18,13 +18,13 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE.txt
|
|
21
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: google-auth
|
|
22
22
|
Requires-Dist: langchain
|
|
23
23
|
Requires-Dist: langchain_experimental
|
|
24
24
|
Requires-Dist: langchain-community
|
|
25
|
-
Requires-Dist: google-auth
|
|
26
25
|
Provides-Extra: all
|
|
27
26
|
Requires-Dist: asyncpg; extra == "all"
|
|
27
|
+
Requires-Dist: fastapi; extra == "all"
|
|
28
28
|
Requires-Dist: flask; extra == "all"
|
|
29
29
|
Requires-Dist: google-auth; extra == "all"
|
|
30
30
|
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
@@ -42,6 +42,7 @@ Requires-Dist: google-generativeai; extra == "all"
|
|
|
42
42
|
Requires-Dist: gunicorn; extra == "all"
|
|
43
43
|
Requires-Dist: httpcore; extra == "all"
|
|
44
44
|
Requires-Dist: httpx; extra == "all"
|
|
45
|
+
Requires-Dist: jsonschema; extra == "all"
|
|
45
46
|
Requires-Dist: lancedb; extra == "all"
|
|
46
47
|
Requires-Dist: langchain; extra == "all"
|
|
47
48
|
Requires-Dist: langchain_experimental; extra == "all"
|
|
@@ -55,10 +56,13 @@ Requires-Dist: pg8000; extra == "all"
|
|
|
55
56
|
Requires-Dist: pgvector; extra == "all"
|
|
56
57
|
Requires-Dist: psycopg2-binary; extra == "all"
|
|
57
58
|
Requires-Dist: pypdf; extra == "all"
|
|
58
|
-
Requires-Dist:
|
|
59
|
+
Requires-Dist: python-socketio; extra == "all"
|
|
60
|
+
Requires-Dist: rich; extra == "all"
|
|
59
61
|
Requires-Dist: supabase; extra == "all"
|
|
60
62
|
Requires-Dist: tiktoken; extra == "all"
|
|
61
|
-
|
|
63
|
+
Provides-Extra: cli
|
|
64
|
+
Requires-Dist: jsonschema; extra == "cli"
|
|
65
|
+
Requires-Dist: rich; extra == "cli"
|
|
62
66
|
Provides-Extra: database
|
|
63
67
|
Requires-Dist: asyncpg; extra == "database"
|
|
64
68
|
Requires-Dist: supabase; extra == "database"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from setuptools import setup, find_packages
|
|
2
2
|
|
|
3
3
|
# Define your base version
|
|
4
|
-
version = '0.60.
|
|
4
|
+
version = '0.60.3'
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name='sunholo',
|
|
@@ -28,17 +28,17 @@ setup(
|
|
|
28
28
|
},
|
|
29
29
|
install_requires=[
|
|
30
30
|
# Base dependencies
|
|
31
|
-
"
|
|
31
|
+
"google-auth", # to check if on gcp
|
|
32
32
|
"langchain",
|
|
33
33
|
"langchain_experimental",
|
|
34
34
|
"langchain-community",
|
|
35
|
-
"google-auth" # to check if on gcp
|
|
36
35
|
# Add the minimal dependencies that your package requires here
|
|
37
36
|
],
|
|
38
37
|
extras_require={
|
|
39
38
|
# Define optional dependencies with feature names
|
|
40
39
|
'all': [
|
|
41
40
|
"asyncpg",
|
|
41
|
+
"fastapi",
|
|
42
42
|
"flask",
|
|
43
43
|
"google-auth",
|
|
44
44
|
"google-auth-httplib2",
|
|
@@ -56,6 +56,7 @@ setup(
|
|
|
56
56
|
"gunicorn",
|
|
57
57
|
"httpcore",
|
|
58
58
|
"httpx",
|
|
59
|
+
"jsonschema",
|
|
59
60
|
"lancedb",
|
|
60
61
|
"langchain",
|
|
61
62
|
"langchain_experimental",
|
|
@@ -69,10 +70,15 @@ setup(
|
|
|
69
70
|
"pgvector",
|
|
70
71
|
"psycopg2-binary",
|
|
71
72
|
"pypdf",
|
|
72
|
-
"
|
|
73
|
+
"python-socketio",
|
|
74
|
+
"rich",
|
|
73
75
|
"supabase",
|
|
74
76
|
"tiktoken",
|
|
75
|
-
|
|
77
|
+
|
|
78
|
+
],
|
|
79
|
+
'cli': [
|
|
80
|
+
"jsonschema",
|
|
81
|
+
"rich"
|
|
76
82
|
],
|
|
77
83
|
'database': [
|
|
78
84
|
"asyncpg",
|
|
@@ -1,30 +1,39 @@
|
|
|
1
1
|
from ..agents import send_to_qa
|
|
2
2
|
from ..streaming import generate_proxy_stream
|
|
3
3
|
from ..utils.user_ids import generate_user_id
|
|
4
|
+
from ..utils.config import load_config_key
|
|
4
5
|
|
|
5
6
|
from .run_proxy import clean_proxy_list, start_proxy
|
|
6
7
|
|
|
7
8
|
import uuid
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
from rich import print
|
|
11
|
+
from .sun_rich import console
|
|
12
|
+
|
|
13
|
+
from rich.prompt import Prompt
|
|
14
|
+
from rich.panel import Panel
|
|
15
|
+
from rich.text import Text
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_service_url(service_name, project, region):
|
|
10
19
|
proxies = clean_proxy_list()
|
|
11
20
|
if service_name in proxies:
|
|
12
21
|
port = proxies[service_name]['port']
|
|
13
22
|
return f"http://127.0.0.1:{port}"
|
|
14
23
|
else:
|
|
15
24
|
print(f"No proxy found running for service: {service_name} - attempting to connect")
|
|
16
|
-
return start_proxy(service_name)
|
|
25
|
+
return start_proxy(service_name, region, project)
|
|
17
26
|
|
|
18
|
-
def stream_chat_session(service_name):
|
|
27
|
+
def stream_chat_session(service_name, project, region):
|
|
19
28
|
|
|
20
|
-
service_url = get_service_url(service_name)
|
|
29
|
+
service_url = get_service_url(service_name, project, region)
|
|
21
30
|
user_id = generate_user_id()
|
|
22
31
|
chat_history = []
|
|
23
32
|
while True:
|
|
24
33
|
session_id = str(uuid.uuid4())
|
|
25
|
-
user_input =
|
|
34
|
+
user_input = Prompt.ask("[bold cyan]You[/bold cyan]")
|
|
26
35
|
if user_input.lower() in ["exit", "quit"]:
|
|
27
|
-
print("Exiting chat session.")
|
|
36
|
+
console.print("[bold red]Exiting chat session.[/bold red]")
|
|
28
37
|
break
|
|
29
38
|
|
|
30
39
|
chat_history.append({"role": "Human", "content": user_input})
|
|
@@ -60,24 +69,29 @@ def stream_chat_session(service_name):
|
|
|
60
69
|
|
|
61
70
|
response_started = False
|
|
62
71
|
vac_response = ""
|
|
63
|
-
for token in stream_response():
|
|
64
|
-
if not response_started:
|
|
65
|
-
print(f"VAC {service_name}: ", end='', flush=True)
|
|
66
|
-
response_started = True
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
# point or star?
|
|
74
|
+
with console.status("[bold orange]Thinking...[/bold orange]", spinner="star") as status:
|
|
75
|
+
for token in stream_response():
|
|
76
|
+
if not response_started:
|
|
77
|
+
status.stop()
|
|
78
|
+
console.print(f"[bold yellow]{service_name}:[/bold yellow] ", end='')
|
|
79
|
+
response_started = True
|
|
80
|
+
|
|
81
|
+
if isinstance(token, bytes):
|
|
82
|
+
token = token.decode('utf-8')
|
|
83
|
+
console.print(token, end='')
|
|
84
|
+
vac_response += token
|
|
72
85
|
|
|
73
86
|
chat_history.append({"role": "AI", "content": vac_response})
|
|
74
87
|
response_started = False
|
|
75
|
-
print()
|
|
88
|
+
console.print()
|
|
89
|
+
console.rule()
|
|
76
90
|
|
|
77
|
-
def headless_mode(service_name, user_input, chat_history=None):
|
|
91
|
+
def headless_mode(service_name, user_input, project, region, chat_history=None):
|
|
78
92
|
chat_history = chat_history or []
|
|
79
93
|
chat_history.append({"role": "Human", "content": user_input})
|
|
80
|
-
service_url = get_service_url(
|
|
94
|
+
service_url = get_service_url(project, region)
|
|
81
95
|
user_id = generate_user_id()
|
|
82
96
|
session_id = str(uuid.uuid4())
|
|
83
97
|
|
|
@@ -124,15 +138,24 @@ def headless_mode(service_name, user_input, chat_history=None):
|
|
|
124
138
|
|
|
125
139
|
def vac_command(args):
|
|
126
140
|
try:
|
|
127
|
-
service_url = get_service_url(args.service_name)
|
|
141
|
+
service_url = get_service_url(args.service_name, args.project, args.region)
|
|
128
142
|
except ValueError as e:
|
|
129
|
-
print(f"ERROR: Could not start {args.service_name} proxy URL: {str(e)}")
|
|
143
|
+
console.print(f"[bold red]ERROR: Could not start {args.service_name} proxy URL: {str(e)}[/bold red]")
|
|
130
144
|
return
|
|
131
|
-
|
|
145
|
+
|
|
146
|
+
display_name = load_config_key("display_name", vector_name=args.service_name, kind="vacConfig")
|
|
147
|
+
description = load_config_key("description", vector_name=args.service_name, kind="vacConfig")
|
|
148
|
+
|
|
149
|
+
print(
|
|
150
|
+
Panel(description or "Starting VAC chat session",
|
|
151
|
+
title=display_name or args.service_name,
|
|
152
|
+
subtitle=service_url)
|
|
153
|
+
)
|
|
154
|
+
|
|
132
155
|
if args.headless:
|
|
133
|
-
headless_mode(args.service_name, args.user_input, args.chat_history)
|
|
156
|
+
headless_mode(args.service_name, args.user_input, args.project, args.region, args.chat_history)
|
|
134
157
|
else:
|
|
135
|
-
stream_chat_session(args.service_name)
|
|
158
|
+
stream_chat_session(args.service_name, args.project, args.region)
|
|
136
159
|
|
|
137
160
|
def setup_vac_subparser(subparsers):
|
|
138
161
|
"""
|
|
@@ -7,22 +7,44 @@ from .cli_init import setup_init_subparser
|
|
|
7
7
|
from .merge_texts import setup_merge_text_subparser
|
|
8
8
|
from .run_proxy import setup_proxy_subparser
|
|
9
9
|
from .chat_vac import setup_vac_subparser
|
|
10
|
+
from ..utils.config import load_config_key
|
|
10
11
|
|
|
11
12
|
from ..logging import log
|
|
12
13
|
|
|
14
|
+
from rich import print
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
from rich.prompt import Prompt
|
|
17
|
+
from rich.spinner import Spinner
|
|
18
|
+
from rich.panel import Panel
|
|
19
|
+
from rich.text import Text
|
|
20
|
+
console = Console()
|
|
21
|
+
|
|
22
|
+
def load_default_gcp_config():
|
|
23
|
+
gcp_config = load_config_key('gcp_config', 'global', kind="vacConfig")
|
|
24
|
+
if gcp_config:
|
|
25
|
+
return gcp_config.get('project_id', ''), gcp_config.get('location', 'europe-west1')
|
|
26
|
+
else:
|
|
27
|
+
return '', 'europe-west1'
|
|
28
|
+
|
|
13
29
|
def main(args=None):
|
|
30
|
+
|
|
31
|
+
|
|
14
32
|
"""
|
|
15
33
|
Entry point for the sunholo console script. This function parses command line arguments
|
|
16
34
|
and invokes the appropriate functionality based on the user input.
|
|
17
35
|
|
|
18
|
-
|
|
36
|
+
Get started:
|
|
19
37
|
```bash
|
|
20
|
-
sunholo
|
|
38
|
+
sunholo --help
|
|
21
39
|
```
|
|
22
40
|
"""
|
|
41
|
+
default_project, default_region = load_default_gcp_config()
|
|
42
|
+
|
|
23
43
|
parser = argparse.ArgumentParser(description="sunholo CLI tool for deploying GenAI VACs")
|
|
24
44
|
parser.add_argument('--debug', action='store_true', help='Enable debug output')
|
|
25
|
-
|
|
45
|
+
parser.add_argument('--project', default=default_project, help='GCP project to list Cloud Run services from.')
|
|
46
|
+
parser.add_argument('--region', default=default_region, help='Region to list Cloud Run services from.')
|
|
47
|
+
|
|
26
48
|
subparsers = parser.add_subparsers(title='commands',
|
|
27
49
|
description='Valid commands',
|
|
28
50
|
help='Commands',
|
|
@@ -3,6 +3,10 @@ import os
|
|
|
3
3
|
import signal
|
|
4
4
|
import json
|
|
5
5
|
|
|
6
|
+
from .sun_rich import console
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
from rich import print
|
|
9
|
+
|
|
6
10
|
PROXY_TRACKER_FILE = '.vac_proxy_tracker.json'
|
|
7
11
|
DEFAULT_PORT = 8080
|
|
8
12
|
|
|
@@ -46,13 +50,13 @@ def check_gcloud():
|
|
|
46
50
|
"""
|
|
47
51
|
try:
|
|
48
52
|
# Check if gcloud is installed
|
|
49
|
-
result = subprocess.run(["gcloud", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
53
|
+
result = subprocess.run(["gcloud", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30)
|
|
50
54
|
if result.returncode != 0:
|
|
51
55
|
print("ERROR: gcloud is not installed or not found in PATH.")
|
|
52
56
|
return False
|
|
53
57
|
|
|
54
58
|
# Check if gcloud is authenticated
|
|
55
|
-
result = subprocess.run(["gcloud", "auth", "list"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
59
|
+
result = subprocess.run(["gcloud", "auth", "list"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30)
|
|
56
60
|
if result.returncode != 0 or "ACTIVE" not in result.stdout.decode():
|
|
57
61
|
print("ERROR: gcloud is not authenticated. Please run 'gcloud auth login'.")
|
|
58
62
|
return False
|
|
@@ -89,6 +93,9 @@ def save_proxies(proxies):
|
|
|
89
93
|
with open(PROXY_TRACKER_FILE, 'w') as file:
|
|
90
94
|
json.dump(proxies, file, indent=4)
|
|
91
95
|
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
92
99
|
def start_proxy(service_name, region, project, port=None):
|
|
93
100
|
"""
|
|
94
101
|
Starts the gcloud proxy to the Cloud Run service and stores the PID.
|
|
@@ -155,20 +162,99 @@ def stop_proxy(service_name):
|
|
|
155
162
|
|
|
156
163
|
list_proxies()
|
|
157
164
|
|
|
165
|
+
def stop_all_proxies():
|
|
166
|
+
"""
|
|
167
|
+
Stops all running gcloud proxies.
|
|
168
|
+
"""
|
|
169
|
+
proxies = clean_proxy_list()
|
|
170
|
+
|
|
171
|
+
for service_name, info in proxies.items():
|
|
172
|
+
pid = info["pid"]
|
|
173
|
+
try:
|
|
174
|
+
os.kill(pid, signal.SIGTERM)
|
|
175
|
+
print(f"Proxy for {service_name} stopped.")
|
|
176
|
+
except ProcessLookupError:
|
|
177
|
+
print(f"No process found with PID: {pid}")
|
|
178
|
+
except Exception as e:
|
|
179
|
+
print(f"Error stopping proxy for {service_name}: {e}")
|
|
180
|
+
|
|
181
|
+
save_proxies({})
|
|
182
|
+
|
|
183
|
+
list_proxies()
|
|
184
|
+
|
|
185
|
+
def list_cloud_run_services(project, region):
|
|
186
|
+
"""
|
|
187
|
+
Lists all Cloud Run services the user has access to in a specific project and region.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
project (str): The GCP project ID.
|
|
191
|
+
region (str): The region of the Cloud Run services.
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
# point or star?
|
|
195
|
+
with console.status("[bold orange]Listing Cloud Run Services[/bold orange]", spinner="star") as status:
|
|
196
|
+
try:
|
|
197
|
+
result = subprocess.run(
|
|
198
|
+
["gcloud", "run", "services", "list", "--project", project, "--region", region, "--format=json"],
|
|
199
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30
|
|
200
|
+
)
|
|
201
|
+
if result.returncode != 0:
|
|
202
|
+
status.stop()
|
|
203
|
+
console.print(f"[bold red]ERROR: Unable to list Cloud Run services: {result.stderr.decode()}[/bold red]")
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
services = json.loads(result.stdout.decode())
|
|
207
|
+
if not services:
|
|
208
|
+
status.stop()
|
|
209
|
+
console.print("[bold red]No Cloud Run services found.[/bold red]")
|
|
210
|
+
return
|
|
211
|
+
|
|
212
|
+
proxies = clean_proxy_list()
|
|
213
|
+
status.stop()
|
|
214
|
+
|
|
215
|
+
table = Table(title="VAC Cloud Run Services")
|
|
216
|
+
table.add_column("Service Name")
|
|
217
|
+
table.add_column("Region")
|
|
218
|
+
table.add_column("URL")
|
|
219
|
+
table.add_column("Proxied")
|
|
220
|
+
table.add_column("Port")
|
|
221
|
+
|
|
222
|
+
for service in services:
|
|
223
|
+
service_name = service['metadata']['name']
|
|
224
|
+
service_url = service['status']['url']
|
|
225
|
+
if service_name in proxies:
|
|
226
|
+
proxied = "Yes"
|
|
227
|
+
proxy_port = proxies[service_name]['port']
|
|
228
|
+
else:
|
|
229
|
+
proxied = "No"
|
|
230
|
+
proxy_port = "-"
|
|
231
|
+
table.add_row(service_name, region, service_url, proxied, str(proxy_port))
|
|
232
|
+
|
|
233
|
+
console.print(table)
|
|
234
|
+
except Exception as e:
|
|
235
|
+
status.stop()
|
|
236
|
+
console.print(f"[bold red]ERROR: An unexpected error occurred: {e}[/bold red]")
|
|
237
|
+
|
|
158
238
|
def list_proxies():
|
|
159
239
|
"""
|
|
160
240
|
Lists all running proxies.
|
|
161
241
|
"""
|
|
242
|
+
print("Listing Proxies...")
|
|
162
243
|
proxies = clean_proxy_list()
|
|
163
244
|
if not proxies:
|
|
164
245
|
print("No proxies currently running.")
|
|
165
246
|
else:
|
|
166
|
-
|
|
247
|
+
table = Table(title="VAC Proxies")
|
|
248
|
+
table.add_column("VAC")
|
|
249
|
+
table.add_column("Port")
|
|
250
|
+
table.add_column("PID")
|
|
251
|
+
table.add_column("URL")
|
|
252
|
+
|
|
167
253
|
for service_name, info in proxies.items():
|
|
168
254
|
url = f"http://127.0.0.1:{info['port']}"
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
255
|
+
table.add_row(service_name, str(info['port']), str(info['pid']), url)
|
|
256
|
+
|
|
257
|
+
console.print(table)
|
|
172
258
|
|
|
173
259
|
def setup_proxy_subparser(subparsers):
|
|
174
260
|
"""
|
|
@@ -177,13 +263,12 @@ def setup_proxy_subparser(subparsers):
|
|
|
177
263
|
Args:
|
|
178
264
|
subparsers: The subparsers object from argparse.ArgumentParser().
|
|
179
265
|
"""
|
|
266
|
+
|
|
180
267
|
proxy_parser = subparsers.add_parser('proxy', help='Set up or stop a proxy to the Cloud Run service.')
|
|
181
268
|
proxy_subparsers = proxy_parser.add_subparsers(dest='proxy_command', required=True)
|
|
182
269
|
|
|
183
270
|
start_parser = proxy_subparsers.add_parser('start', help='Start the proxy to the Cloud Run service.')
|
|
184
271
|
start_parser.add_argument('service_name', help='Name of the Cloud Run service.')
|
|
185
|
-
start_parser.add_argument('--region', default='europe-west1', help='Region of the Cloud Run service.')
|
|
186
|
-
start_parser.add_argument('--project', default='multivac-internal-dev', help='GCP project of the Cloud Run service.')
|
|
187
272
|
start_parser.add_argument('--port', type=int, help='Port to run the proxy on. Auto-assigns if not provided.')
|
|
188
273
|
start_parser.set_defaults(func=lambda args: start_proxy(args.service_name, args.region, args.project, args.port))
|
|
189
274
|
|
|
@@ -194,4 +279,10 @@ def setup_proxy_subparser(subparsers):
|
|
|
194
279
|
list_parser = proxy_subparsers.add_parser('list', help='List all running proxies.')
|
|
195
280
|
list_parser.set_defaults(func=lambda args: list_proxies())
|
|
196
281
|
|
|
282
|
+
stop_all_parser = proxy_subparsers.add_parser('stop-all', help='Stop all running proxies.')
|
|
283
|
+
stop_all_parser.set_defaults(func=lambda args: stop_all_proxies())
|
|
284
|
+
|
|
285
|
+
list_services_parser = proxy_subparsers.add_parser('list-vacs', help='List all Cloud Run VAC services.')
|
|
286
|
+
list_services_parser.set_defaults(func=lambda args: list_cloud_run_services(args.project, args.region))
|
|
287
|
+
|
|
197
288
|
|
|
@@ -29,7 +29,7 @@ if is_running_on_gcp():
|
|
|
29
29
|
|
|
30
30
|
def refresh_credentials():
|
|
31
31
|
if not is_running_on_gcp():
|
|
32
|
-
log.
|
|
32
|
+
log.debug("Not running on Google Cloud so no credentials available for GCS.")
|
|
33
33
|
return False
|
|
34
34
|
if not gcs_credentials.token or gcs_credentials.expired or not gcs_credentials.valid:
|
|
35
35
|
try:
|
|
@@ -217,9 +217,6 @@ def setup_logging(logger_name=None, log_level=logging.INFO, project_id=None):
|
|
|
217
217
|
if logger_name is None:
|
|
218
218
|
logger_name = "sunholo"
|
|
219
219
|
|
|
220
|
-
if Client and os.environ.get('GOOGLE_CLOUD_LOGGING') != "1":
|
|
221
|
-
print("GOOGLE_CLOUD_LOGGING != 1 but authentication with Google Cloud Logging enabled - missing env var setting?")
|
|
222
|
-
|
|
223
220
|
if not Client and os.environ.get('GOOGLE_CLOUD_LOGGING') == "1":
|
|
224
221
|
print("Found GOOGLE_CLOUD_LOGGING=1 but no GCP Client available, install via `pip install sunholo[gcp]` and/or authenticate")
|
|
225
222
|
|
|
@@ -59,12 +59,12 @@ def load_all_configs():
|
|
|
59
59
|
config_folder = os.getenv("_CONFIG_FOLDER", os.getcwd())
|
|
60
60
|
config_folder = os.path.join(config_folder, "config")
|
|
61
61
|
|
|
62
|
-
log.
|
|
62
|
+
log.debug(f"Loading all configs from folder: {config_folder}")
|
|
63
63
|
current_time = datetime.now()
|
|
64
64
|
|
|
65
65
|
configs_by_kind = defaultdict(dict)
|
|
66
66
|
for filename in os.listdir(config_folder):
|
|
67
|
-
log.
|
|
67
|
+
log.debug(f"config file: {filename}")
|
|
68
68
|
if filename in ["cloudbuild.yaml", "cloud_run_urls.json"]:
|
|
69
69
|
# skip these
|
|
70
70
|
continue
|
|
@@ -193,7 +193,7 @@ def load_config_key(key: str, vector_name: str, kind: str=None):
|
|
|
193
193
|
configs_by_kind = load_all_configs()
|
|
194
194
|
|
|
195
195
|
if kind:
|
|
196
|
-
log.
|
|
196
|
+
log.debug(f"Got kind: {kind} - applying to configs")
|
|
197
197
|
|
|
198
198
|
if not configs_by_kind:
|
|
199
199
|
log.warning("Did not load configs via folder")
|
|
@@ -204,18 +204,23 @@ def load_config_key(key: str, vector_name: str, kind: str=None):
|
|
|
204
204
|
else:
|
|
205
205
|
config, filename = load_config(filename)
|
|
206
206
|
|
|
207
|
-
log.
|
|
207
|
+
log.debug(f"Fetching '{key}' for '{vector_name}'")
|
|
208
208
|
apiVersion = config.get('apiVersion')
|
|
209
209
|
kind = config.get('kind')
|
|
210
210
|
vac = config.get('vac')
|
|
211
211
|
|
|
212
212
|
if not apiVersion or not kind:
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
log.info(f"Loaded config file {kind}/{apiVersion}")
|
|
213
|
+
raise ValueError("Deprecated config file, move to config with `apiVersion` and `kind` set")
|
|
214
|
+
|
|
215
|
+
log.debug(f"Loaded config file {kind}/{apiVersion}")
|
|
217
216
|
|
|
218
217
|
if kind == 'vacConfig':
|
|
218
|
+
if vector_name == 'global':
|
|
219
|
+
key_value = config.get(key)
|
|
220
|
+
log.debug(f'vac_config global value for {key}: {key_value}')
|
|
221
|
+
|
|
222
|
+
return key_value
|
|
223
|
+
|
|
219
224
|
vac = config.get('vac')
|
|
220
225
|
if not vac:
|
|
221
226
|
raise ValueError("Deprecated config file, move to config with `vac:` at top level for `vector_name`")
|
|
@@ -223,7 +228,7 @@ def load_config_key(key: str, vector_name: str, kind: str=None):
|
|
|
223
228
|
if not vac_config:
|
|
224
229
|
raise ValueError(f"No config array was found for {vector_name} in {filename}")
|
|
225
230
|
|
|
226
|
-
log.
|
|
231
|
+
log.debug(f'vac_config: {vac_config} for {vector_name} - fetching "{key}"')
|
|
227
232
|
key_value = vac_config.get(key)
|
|
228
233
|
|
|
229
234
|
return key_value
|
|
@@ -236,7 +241,7 @@ def load_config_key(key: str, vector_name: str, kind: str=None):
|
|
|
236
241
|
if not prompt_for_vector_name:
|
|
237
242
|
raise ValueError(f"Could not find prompt for vector_name {vector_name}")
|
|
238
243
|
|
|
239
|
-
log.
|
|
244
|
+
log.debug(f'prompts: {prompt_for_vector_name} for {vector_name} - fetching "{key}"')
|
|
240
245
|
key_value = prompt_for_vector_name.get(key)
|
|
241
246
|
|
|
242
247
|
return key_value
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.60.
|
|
3
|
+
Version: 0.60.3
|
|
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.60.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.60.3.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -18,13 +18,13 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE.txt
|
|
21
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: google-auth
|
|
22
22
|
Requires-Dist: langchain
|
|
23
23
|
Requires-Dist: langchain_experimental
|
|
24
24
|
Requires-Dist: langchain-community
|
|
25
|
-
Requires-Dist: google-auth
|
|
26
25
|
Provides-Extra: all
|
|
27
26
|
Requires-Dist: asyncpg; extra == "all"
|
|
27
|
+
Requires-Dist: fastapi; extra == "all"
|
|
28
28
|
Requires-Dist: flask; extra == "all"
|
|
29
29
|
Requires-Dist: google-auth; extra == "all"
|
|
30
30
|
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
@@ -42,6 +42,7 @@ Requires-Dist: google-generativeai; extra == "all"
|
|
|
42
42
|
Requires-Dist: gunicorn; extra == "all"
|
|
43
43
|
Requires-Dist: httpcore; extra == "all"
|
|
44
44
|
Requires-Dist: httpx; extra == "all"
|
|
45
|
+
Requires-Dist: jsonschema; extra == "all"
|
|
45
46
|
Requires-Dist: lancedb; extra == "all"
|
|
46
47
|
Requires-Dist: langchain; extra == "all"
|
|
47
48
|
Requires-Dist: langchain_experimental; extra == "all"
|
|
@@ -55,10 +56,13 @@ Requires-Dist: pg8000; extra == "all"
|
|
|
55
56
|
Requires-Dist: pgvector; extra == "all"
|
|
56
57
|
Requires-Dist: psycopg2-binary; extra == "all"
|
|
57
58
|
Requires-Dist: pypdf; extra == "all"
|
|
58
|
-
Requires-Dist:
|
|
59
|
+
Requires-Dist: python-socketio; extra == "all"
|
|
60
|
+
Requires-Dist: rich; extra == "all"
|
|
59
61
|
Requires-Dist: supabase; extra == "all"
|
|
60
62
|
Requires-Dist: tiktoken; extra == "all"
|
|
61
|
-
|
|
63
|
+
Provides-Extra: cli
|
|
64
|
+
Requires-Dist: jsonschema; extra == "cli"
|
|
65
|
+
Requires-Dist: rich; extra == "cli"
|
|
62
66
|
Provides-Extra: database
|
|
63
67
|
Requires-Dist: asyncpg; extra == "database"
|
|
64
68
|
Requires-Dist: supabase; extra == "database"
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
google-auth
|
|
2
2
|
langchain
|
|
3
3
|
langchain_experimental
|
|
4
4
|
langchain-community
|
|
5
|
-
google-auth
|
|
6
5
|
|
|
7
6
|
[all]
|
|
8
7
|
asyncpg
|
|
8
|
+
fastapi
|
|
9
9
|
flask
|
|
10
10
|
google-auth
|
|
11
11
|
google-auth-httplib2
|
|
@@ -23,6 +23,7 @@ google-generativeai
|
|
|
23
23
|
gunicorn
|
|
24
24
|
httpcore
|
|
25
25
|
httpx
|
|
26
|
+
jsonschema
|
|
26
27
|
lancedb
|
|
27
28
|
langchain
|
|
28
29
|
langchain_experimental
|
|
@@ -36,14 +37,18 @@ pg8000
|
|
|
36
37
|
pgvector
|
|
37
38
|
psycopg2-binary
|
|
38
39
|
pypdf
|
|
39
|
-
|
|
40
|
+
python-socketio
|
|
41
|
+
rich
|
|
40
42
|
supabase
|
|
41
43
|
tiktoken
|
|
42
|
-
python-socketio
|
|
43
44
|
|
|
44
45
|
[anthropic]
|
|
45
46
|
langchain-anthropic
|
|
46
47
|
|
|
48
|
+
[cli]
|
|
49
|
+
jsonschema
|
|
50
|
+
rich
|
|
51
|
+
|
|
47
52
|
[database]
|
|
48
53
|
asyncpg
|
|
49
54
|
supabase
|
|
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
|