sunholo 0.60.4__tar.gz → 0.60.7__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.4 → sunholo-0.60.7}/PKG-INFO +2 -2
- {sunholo-0.60.4 → sunholo-0.60.7}/setup.py +1 -1
- sunholo-0.60.7/sunholo/cli/chat_vac.py +301 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/cli/run_proxy.py +7 -62
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo.egg-info/PKG-INFO +2 -2
- sunholo-0.60.4/sunholo/cli/chat_vac.py +0 -188
- {sunholo-0.60.4 → sunholo-0.60.7}/LICENSE.txt +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/MANIFEST.in +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/README.md +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/setup.cfg +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/chat_history.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/dispatch_to_qa.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/fastapi/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/fastapi/base.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/fastapi/qna_routes.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/flask/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/flask/base.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/flask/qna_routes.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/langserve.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/pubsub.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/route.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/special_commands.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/agents/test_chat_history.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/archive/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/archive/archive.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/auth/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/auth/run.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/bots/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/bots/discord.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/bots/github_webhook.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/bots/webapp.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/chunker/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/chunker/data_to_embed_pubsub.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/chunker/doc_handling.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/chunker/images.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/chunker/loaders.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/chunker/message_data.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/chunker/pdfs.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/chunker/publish.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/chunker/splitter.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/cli/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/cli/cli.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/cli/cli_init.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/cli/configs.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/cli/deploy.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/cli/merge_texts.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/cli/sun_rich.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/components/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/components/llm.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/components/prompt.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/components/retriever.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/components/vectorstore.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/database/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/database/alloydb.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/database/database.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/database/lancedb.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/database/sql/sb/create_function.sql +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/database/sql/sb/create_function_time.sql +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/database/sql/sb/create_table.sql +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/database/sql/sb/delete_source_row.sql +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/database/sql/sb/return_sources.sql +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/database/sql/sb/setup.sql +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/database/static_dbs.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/database/uuid.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/embedder/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/embedder/embed_chunk.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/gcs/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/gcs/add_file.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/gcs/download_url.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/gcs/metadata.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/langfuse/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/langfuse/callback.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/langfuse/prompts.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/llamaindex/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/llamaindex/generate.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/llamaindex/import_files.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/logging.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/lookup/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/lookup/model_lookup.yaml +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/patches/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/patches/langchain/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/patches/langchain/lancedb.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/patches/langchain/vertexai.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/pubsub/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/pubsub/process_pubsub.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/pubsub/pubsub_manager.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/qna/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/qna/parsers.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/qna/retry.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/streaming/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/streaming/content_buffer.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/streaming/langserve.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/streaming/streaming.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/summarise/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/summarise/summarise.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/utils/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/utils/big_context.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/utils/config.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/utils/config_schema.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/utils/gcp.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/utils/parsers.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/utils/user_ids.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/vertex/__init__.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo/vertex/init_vertex.py +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo.egg-info/SOURCES.txt +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo.egg-info/dependency_links.txt +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo.egg-info/entry_points.txt +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo.egg-info/requires.txt +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/sunholo.egg-info/top_level.txt +0 -0
- {sunholo-0.60.4 → sunholo-0.60.7}/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.7
|
|
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.7.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
from ..agents import send_to_qa
|
|
2
|
+
from ..streaming import generate_proxy_stream
|
|
3
|
+
from ..utils.user_ids import generate_user_id
|
|
4
|
+
from ..utils.config import load_config_key
|
|
5
|
+
|
|
6
|
+
from .run_proxy import clean_proxy_list, start_proxy, stop_proxy
|
|
7
|
+
|
|
8
|
+
import uuid
|
|
9
|
+
import sys
|
|
10
|
+
import subprocess
|
|
11
|
+
import json
|
|
12
|
+
|
|
13
|
+
from rich import print
|
|
14
|
+
from .sun_rich import console
|
|
15
|
+
|
|
16
|
+
from rich.prompt import Prompt
|
|
17
|
+
from rich.panel import Panel
|
|
18
|
+
from rich.text import Text
|
|
19
|
+
from rich.table import Table
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_service_url(vac_name, project, region):
|
|
23
|
+
agent_name = load_config_key("agent", vac_name, kind="vacConfig")
|
|
24
|
+
proxies = clean_proxy_list()
|
|
25
|
+
if agent_name in proxies:
|
|
26
|
+
port = proxies[agent_name]['port']
|
|
27
|
+
url = f"http://127.0.0.1:{port}"
|
|
28
|
+
else:
|
|
29
|
+
print(f"No proxy found running for service: {agent_name} required for {vac_name} - attempting to connect")
|
|
30
|
+
url = start_proxy(agent_name, region, project)
|
|
31
|
+
|
|
32
|
+
return url
|
|
33
|
+
|
|
34
|
+
def stream_chat_session(service_url, service_name):
|
|
35
|
+
|
|
36
|
+
user_id = generate_user_id()
|
|
37
|
+
chat_history = []
|
|
38
|
+
while True:
|
|
39
|
+
session_id = str(uuid.uuid4())
|
|
40
|
+
user_input = Prompt.ask("[bold cyan]You[/bold cyan]")
|
|
41
|
+
if user_input.lower() in ["exit", "quit"]:
|
|
42
|
+
console.print("[bold red]Exiting chat session.[/bold red]")
|
|
43
|
+
break
|
|
44
|
+
|
|
45
|
+
def stream_response():
|
|
46
|
+
generate = generate_proxy_stream(
|
|
47
|
+
send_to_qa,
|
|
48
|
+
user_input,
|
|
49
|
+
vector_name=service_name,
|
|
50
|
+
chat_history=chat_history,
|
|
51
|
+
generate_f_output=lambda x: x, # Replace with actual processing function
|
|
52
|
+
stream_wait_time=0.5,
|
|
53
|
+
stream_timeout=120,
|
|
54
|
+
message_author=user_id,
|
|
55
|
+
#TODO: populate these
|
|
56
|
+
image_url=None,
|
|
57
|
+
source_filters=None,
|
|
58
|
+
search_kwargs=None,
|
|
59
|
+
private_docs=None,
|
|
60
|
+
whole_document=False,
|
|
61
|
+
source_filters_and_or=False,
|
|
62
|
+
# system kwargs
|
|
63
|
+
configurable={
|
|
64
|
+
"vector_name": service_name,
|
|
65
|
+
},
|
|
66
|
+
user_id=user_id,
|
|
67
|
+
session_id=session_id,
|
|
68
|
+
message_source="cli",
|
|
69
|
+
override_endpoint=service_url
|
|
70
|
+
)
|
|
71
|
+
for part in generate():
|
|
72
|
+
yield part
|
|
73
|
+
|
|
74
|
+
response_started = False
|
|
75
|
+
vac_response = ""
|
|
76
|
+
|
|
77
|
+
# point or star?
|
|
78
|
+
with console.status("[bold orange]Thinking...[/bold orange]", spinner="star") as status:
|
|
79
|
+
for token in stream_response():
|
|
80
|
+
if not response_started:
|
|
81
|
+
status.stop()
|
|
82
|
+
console.print(f"[bold yellow]{service_name}:[/bold yellow] ", end='')
|
|
83
|
+
response_started = True
|
|
84
|
+
|
|
85
|
+
if isinstance(token, bytes):
|
|
86
|
+
token = token.decode('utf-8')
|
|
87
|
+
console.print(token, end='')
|
|
88
|
+
vac_response += token
|
|
89
|
+
|
|
90
|
+
chat_history.append({"name": "Human", "content": user_input})
|
|
91
|
+
chat_history.append({"name": "AI", "content": vac_response})
|
|
92
|
+
response_started = False
|
|
93
|
+
console.print()
|
|
94
|
+
console.rule()
|
|
95
|
+
|
|
96
|
+
def headless_mode(service_url, service_name, user_input, chat_history=None):
|
|
97
|
+
chat_history = chat_history or []
|
|
98
|
+
|
|
99
|
+
user_id = generate_user_id()
|
|
100
|
+
session_id = str(uuid.uuid4())
|
|
101
|
+
|
|
102
|
+
def stream_response():
|
|
103
|
+
generate = generate_proxy_stream(
|
|
104
|
+
send_to_qa,
|
|
105
|
+
user_input,
|
|
106
|
+
vector_name=service_name,
|
|
107
|
+
chat_history=chat_history,
|
|
108
|
+
generate_f_output=lambda x: x, # Replace with actual processing function
|
|
109
|
+
stream_wait_time=0.5,
|
|
110
|
+
stream_timeout=120,
|
|
111
|
+
message_author=user_id,
|
|
112
|
+
#TODO: populate these
|
|
113
|
+
image_url=None,
|
|
114
|
+
source_filters=None,
|
|
115
|
+
search_kwargs=None,
|
|
116
|
+
private_docs=None,
|
|
117
|
+
whole_document=False,
|
|
118
|
+
source_filters_and_or=False,
|
|
119
|
+
# system kwargs
|
|
120
|
+
configurable={
|
|
121
|
+
"vector_name": service_name,
|
|
122
|
+
},
|
|
123
|
+
user_id=user_id,
|
|
124
|
+
session_id=session_id,
|
|
125
|
+
message_source="cli",
|
|
126
|
+
override_endpoint=service_url
|
|
127
|
+
)
|
|
128
|
+
for part in generate():
|
|
129
|
+
yield part
|
|
130
|
+
|
|
131
|
+
vac_response = ""
|
|
132
|
+
|
|
133
|
+
for token in stream_response():
|
|
134
|
+
if isinstance(token, bytes):
|
|
135
|
+
token = token.decode('utf-8')
|
|
136
|
+
print(token, end='', flush=True)
|
|
137
|
+
vac_response += token
|
|
138
|
+
|
|
139
|
+
if vac_response:
|
|
140
|
+
chat_history.append({"name": "Human", "content": user_input})
|
|
141
|
+
chat_history.append({"name": "AI", "content": vac_response})
|
|
142
|
+
print() # For new line after streaming ends
|
|
143
|
+
|
|
144
|
+
return chat_history
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def vac_command(args):
|
|
148
|
+
if args.action == 'list':
|
|
149
|
+
list_cloud_run_services(args.project, args.region)
|
|
150
|
+
return
|
|
151
|
+
elif args.action == 'get-url':
|
|
152
|
+
service_url = get_cloud_run_service_url(args.project, args.region, args.vac_name)
|
|
153
|
+
if service_url:
|
|
154
|
+
console.print(service_url)
|
|
155
|
+
return
|
|
156
|
+
elif args.action == 'chat':
|
|
157
|
+
|
|
158
|
+
if not args.no_proxy:
|
|
159
|
+
try:
|
|
160
|
+
service_url = get_service_url(args.vac_name, args.project, args.region)
|
|
161
|
+
except ValueError as e:
|
|
162
|
+
console.print(f"[bold red]ERROR: Could not start {args.vac_name} proxy URL: {str(e)}[/bold red]")
|
|
163
|
+
sys.exit(1)
|
|
164
|
+
else:
|
|
165
|
+
service_url = get_cloud_run_service_url(args.project, args.region, args.vac_name)
|
|
166
|
+
console.print(f"Not using a proxy, connecting directly to {service_url}")
|
|
167
|
+
|
|
168
|
+
agent_name = load_config_key("agent", args.vac_name, kind="vacConfig")
|
|
169
|
+
|
|
170
|
+
if args.headless:
|
|
171
|
+
headless_mode(service_url, args.vac_name, args.user_input, args.chat_history)
|
|
172
|
+
else:
|
|
173
|
+
display_name = load_config_key("display_name", vector_name=args.vac_name, kind="vacConfig")
|
|
174
|
+
description = load_config_key("description", vector_name=args.vac_name, kind="vacConfig")
|
|
175
|
+
|
|
176
|
+
if agent_name == "langserve":
|
|
177
|
+
subtitle = f"{service_url}/{args.vac_name}/playground/"
|
|
178
|
+
else:
|
|
179
|
+
subtitle = f"{agent_name} - {service_url}/vac/{args.vac_name}"
|
|
180
|
+
|
|
181
|
+
print(
|
|
182
|
+
Panel(description or "Starting VAC chat session",
|
|
183
|
+
title=display_name or args.vac_name,
|
|
184
|
+
subtitle=subtitle)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
stream_chat_session(service_url, args.vac_name)
|
|
188
|
+
|
|
189
|
+
stop_proxy(agent_name)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def list_cloud_run_services(project, region):
|
|
193
|
+
"""
|
|
194
|
+
Lists all Cloud Run services the user has access to in a specific project and region.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
project (str): The GCP project ID.
|
|
198
|
+
region (str): The region of the Cloud Run services.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
# point or star?
|
|
202
|
+
with console.status("[bold orange]Listing Cloud Run Services[/bold orange]", spinner="star") as status:
|
|
203
|
+
try:
|
|
204
|
+
result = subprocess.run(
|
|
205
|
+
["gcloud", "run", "services", "list", "--project", project, "--region", region, "--format=json"],
|
|
206
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30
|
|
207
|
+
)
|
|
208
|
+
if result.returncode != 0:
|
|
209
|
+
status.stop()
|
|
210
|
+
console.print(f"[bold red]ERROR: Unable to list Cloud Run services: {result.stderr.decode()}[/bold red]")
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
services = json.loads(result.stdout.decode())
|
|
214
|
+
if not services:
|
|
215
|
+
status.stop()
|
|
216
|
+
console.print("[bold red]No Cloud Run services found.[/bold red]")
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
proxies = clean_proxy_list()
|
|
220
|
+
status.stop()
|
|
221
|
+
|
|
222
|
+
table = Table(title="VAC Cloud Run Services")
|
|
223
|
+
table.add_column("Service Name")
|
|
224
|
+
table.add_column("Region")
|
|
225
|
+
table.add_column("URL")
|
|
226
|
+
table.add_column("Proxied")
|
|
227
|
+
table.add_column("Port")
|
|
228
|
+
|
|
229
|
+
for service in services:
|
|
230
|
+
service_name = service['metadata']['name']
|
|
231
|
+
service_url = service['status']['url']
|
|
232
|
+
if service_name in proxies:
|
|
233
|
+
proxied = "Yes"
|
|
234
|
+
proxy_port = proxies[service_name]['port']
|
|
235
|
+
else:
|
|
236
|
+
proxied = "No"
|
|
237
|
+
proxy_port = "-"
|
|
238
|
+
table.add_row(service_name, region, service_url, proxied, str(proxy_port))
|
|
239
|
+
|
|
240
|
+
console.print(table)
|
|
241
|
+
except Exception as e:
|
|
242
|
+
status.stop()
|
|
243
|
+
console.print(f"[bold red]ERROR: An unexpected error occurred: {e}[/bold red]")
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def get_cloud_run_service_url(project, region, service_name):
|
|
247
|
+
"""
|
|
248
|
+
Retrieves the URL of a specific Cloud Run service in a given project and region.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
project (str): The GCP project ID.
|
|
252
|
+
region (str): The region of the Cloud Run service.
|
|
253
|
+
service_name (str): The name of the Cloud Run service.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
str: The URL of the Cloud Run service, or an error message if not found.
|
|
257
|
+
"""
|
|
258
|
+
try:
|
|
259
|
+
result = subprocess.run(
|
|
260
|
+
["gcloud", "run", "services", "describe", service_name, "--project", project, "--region", region, "--format=json"],
|
|
261
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30
|
|
262
|
+
)
|
|
263
|
+
if result.returncode != 0:
|
|
264
|
+
console.print(f"[bold red]ERROR: Unable to get Cloud Run service URL: {result.stderr.decode()}[/bold red]")
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
service = json.loads(result.stdout.decode())
|
|
268
|
+
service_url = service['status']['url']
|
|
269
|
+
return service_url
|
|
270
|
+
except Exception as e:
|
|
271
|
+
console.print(f"[bold red]ERROR: An unexpected error occurred: {e}[/bold red]")
|
|
272
|
+
return None
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def setup_vac_subparser(subparsers):
|
|
277
|
+
"""
|
|
278
|
+
Sets up an argparse subparser for the 'vac' command.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
subparsers: The subparsers object from argparse.ArgumentParser().
|
|
282
|
+
"""
|
|
283
|
+
vac_parser = subparsers.add_parser('vac', help='Interact with deployed VAC services.')
|
|
284
|
+
vac_subparsers = vac_parser.add_subparsers(dest='action', help='VAC subcommands')
|
|
285
|
+
|
|
286
|
+
# Subcommand for listing VAC services
|
|
287
|
+
list_parser = vac_subparsers.add_parser('list', help='List all VAC services.')
|
|
288
|
+
|
|
289
|
+
# Subcommand for getting the URL of a specific VAC service
|
|
290
|
+
get_url_parser = vac_subparsers.add_parser('get-url', help='Get the URL of a specific VAC service.')
|
|
291
|
+
get_url_parser.add_argument('vac_name', help='Name of the VAC service.')
|
|
292
|
+
|
|
293
|
+
# Subcommand for interacting with a VAC service
|
|
294
|
+
chat_parser = vac_subparsers.add_parser('chat', help='Interact with a VAC service.')
|
|
295
|
+
chat_parser.add_argument('vac_name', help='Name of the VAC service.')
|
|
296
|
+
chat_parser.add_argument('user_input', help='User input for the VAC service when in headless mode.', nargs='?', default=None)
|
|
297
|
+
chat_parser.add_argument('--headless', action='store_true', help='Run in headless mode.')
|
|
298
|
+
chat_parser.add_argument('--chat_history', help='Chat history for headless mode (as JSON string).', default=None)
|
|
299
|
+
chat_parser.add_argument('--no_proxy', action='store_true', help='Do not use the proxy and connect directly to the VAC service.')
|
|
300
|
+
|
|
301
|
+
vac_parser.set_defaults(func=vac_command)
|
|
@@ -109,7 +109,7 @@ def start_proxy(service_name, region, project, port=None):
|
|
|
109
109
|
proxies = clean_proxy_list()
|
|
110
110
|
|
|
111
111
|
if service_name in proxies:
|
|
112
|
-
print(f"Proxy for service {service_name} is already running on port {proxies[service_name]['port']}.")
|
|
112
|
+
console.print(f"Proxy for service [bold orange]'{service_name}'[/bold orange] is already running on port {proxies[service_name]['port']}.")
|
|
113
113
|
return
|
|
114
114
|
|
|
115
115
|
if not port:
|
|
@@ -130,7 +130,7 @@ def start_proxy(service_name, region, project, port=None):
|
|
|
130
130
|
}
|
|
131
131
|
save_proxies(proxies)
|
|
132
132
|
|
|
133
|
-
print(f"Proxy for {service_name} setup complete on port {port}")
|
|
133
|
+
console.print(f"Proxy for [bold orange]'{service_name}'[/bold orange] setup complete on port {port}")
|
|
134
134
|
list_proxies()
|
|
135
135
|
|
|
136
136
|
return f"http://127.0.0.1:{port}"
|
|
@@ -154,11 +154,11 @@ def stop_proxy(service_name):
|
|
|
154
154
|
os.kill(pid, signal.SIGTERM)
|
|
155
155
|
del proxies[service_name]
|
|
156
156
|
save_proxies(proxies)
|
|
157
|
-
print(f"Proxy for {service_name} stopped.")
|
|
157
|
+
console.print(f"Proxy for [bold orange]'{service_name}'[bold orange] stopped.")
|
|
158
158
|
except ProcessLookupError:
|
|
159
|
-
print(f"No process found with PID: {pid}")
|
|
159
|
+
console.print(f"No process found with PID: {pid}")
|
|
160
160
|
except Exception as e:
|
|
161
|
-
print(f"Error stopping proxy for {service_name}: {e}")
|
|
161
|
+
console.print(f"[bold red]Error stopping proxy for {service_name}: {e}[/bold red]")
|
|
162
162
|
|
|
163
163
|
list_proxies()
|
|
164
164
|
|
|
@@ -172,69 +172,16 @@ def stop_all_proxies():
|
|
|
172
172
|
pid = info["pid"]
|
|
173
173
|
try:
|
|
174
174
|
os.kill(pid, signal.SIGTERM)
|
|
175
|
-
print(f"Proxy for {service_name} stopped.")
|
|
175
|
+
print(f"Proxy for [bold orange]'{service_name}'[/bold orange] stopped.")
|
|
176
176
|
except ProcessLookupError:
|
|
177
177
|
print(f"No process found with PID: {pid}")
|
|
178
178
|
except Exception as e:
|
|
179
|
-
print(f"Error stopping proxy for {service_name}: {e}")
|
|
179
|
+
print(f"Error stopping proxy for [bold orange]'{service_name}'[/bold orange]: {e}")
|
|
180
180
|
|
|
181
181
|
save_proxies({})
|
|
182
182
|
|
|
183
183
|
list_proxies()
|
|
184
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
|
-
|
|
238
185
|
def list_proxies():
|
|
239
186
|
"""
|
|
240
187
|
Lists all running proxies.
|
|
@@ -283,7 +230,5 @@ def setup_proxy_subparser(subparsers):
|
|
|
283
230
|
stop_all_parser = proxy_subparsers.add_parser('stop-all', help='Stop all running proxies.')
|
|
284
231
|
stop_all_parser.set_defaults(func=lambda args: stop_all_proxies())
|
|
285
232
|
|
|
286
|
-
list_services_parser = proxy_subparsers.add_parser('list-vacs', help='List all Cloud Run VAC services.')
|
|
287
|
-
list_services_parser.set_defaults(func=lambda args: list_cloud_run_services(args.project, args.region))
|
|
288
233
|
|
|
289
234
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.60.
|
|
3
|
+
Version: 0.60.7
|
|
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.7.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
from ..agents import send_to_qa
|
|
2
|
-
from ..streaming import generate_proxy_stream
|
|
3
|
-
from ..utils.user_ids import generate_user_id
|
|
4
|
-
from ..utils.config import load_config_key
|
|
5
|
-
|
|
6
|
-
from .run_proxy import clean_proxy_list, start_proxy, stop_proxy
|
|
7
|
-
|
|
8
|
-
import uuid
|
|
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(vac_name, project, region):
|
|
19
|
-
agent_name = load_config_key("agent", vac_name, kind="vacConfig")
|
|
20
|
-
proxies = clean_proxy_list()
|
|
21
|
-
if agent_name in proxies:
|
|
22
|
-
port = proxies[agent_name]['port']
|
|
23
|
-
url = f"http://127.0.0.1:{port}"
|
|
24
|
-
else:
|
|
25
|
-
print(f"No proxy found running for service: {agent_name} required for {vac_name} - attempting to connect")
|
|
26
|
-
url = start_proxy(agent_name, region, project)
|
|
27
|
-
|
|
28
|
-
return url
|
|
29
|
-
|
|
30
|
-
def stream_chat_session(service_name, project, region):
|
|
31
|
-
|
|
32
|
-
service_url = get_service_url(service_name, project, region)
|
|
33
|
-
user_id = generate_user_id()
|
|
34
|
-
chat_history = []
|
|
35
|
-
while True:
|
|
36
|
-
session_id = str(uuid.uuid4())
|
|
37
|
-
user_input = Prompt.ask("[bold cyan]You[/bold cyan]")
|
|
38
|
-
if user_input.lower() in ["exit", "quit"]:
|
|
39
|
-
console.print("[bold red]Exiting chat session.[/bold red]")
|
|
40
|
-
break
|
|
41
|
-
|
|
42
|
-
def stream_response():
|
|
43
|
-
generate = generate_proxy_stream(
|
|
44
|
-
send_to_qa,
|
|
45
|
-
user_input,
|
|
46
|
-
vector_name=service_name,
|
|
47
|
-
chat_history=chat_history,
|
|
48
|
-
generate_f_output=lambda x: x, # Replace with actual processing function
|
|
49
|
-
stream_wait_time=0.5,
|
|
50
|
-
stream_timeout=120,
|
|
51
|
-
message_author=user_id,
|
|
52
|
-
#TODO: populate these
|
|
53
|
-
image_url=None,
|
|
54
|
-
source_filters=None,
|
|
55
|
-
search_kwargs=None,
|
|
56
|
-
private_docs=None,
|
|
57
|
-
whole_document=False,
|
|
58
|
-
source_filters_and_or=False,
|
|
59
|
-
# system kwargs
|
|
60
|
-
configurable={
|
|
61
|
-
"vector_name": service_name,
|
|
62
|
-
},
|
|
63
|
-
user_id=user_id,
|
|
64
|
-
session_id=session_id,
|
|
65
|
-
message_source="cli",
|
|
66
|
-
override_endpoint=service_url
|
|
67
|
-
)
|
|
68
|
-
for part in generate():
|
|
69
|
-
yield part
|
|
70
|
-
|
|
71
|
-
response_started = False
|
|
72
|
-
vac_response = ""
|
|
73
|
-
|
|
74
|
-
# point or star?
|
|
75
|
-
with console.status("[bold orange]Thinking...[/bold orange]", spinner="star") as status:
|
|
76
|
-
for token in stream_response():
|
|
77
|
-
if not response_started:
|
|
78
|
-
status.stop()
|
|
79
|
-
console.print(f"[bold yellow]{service_name}:[/bold yellow] ", end='')
|
|
80
|
-
response_started = True
|
|
81
|
-
|
|
82
|
-
if isinstance(token, bytes):
|
|
83
|
-
token = token.decode('utf-8')
|
|
84
|
-
console.print(token, end='')
|
|
85
|
-
vac_response += token
|
|
86
|
-
|
|
87
|
-
chat_history.append({"name": "Human", "content": user_input})
|
|
88
|
-
chat_history.append({"name": "AI", "content": vac_response})
|
|
89
|
-
response_started = False
|
|
90
|
-
console.print()
|
|
91
|
-
console.rule()
|
|
92
|
-
|
|
93
|
-
def headless_mode(service_name, user_input, project, region, chat_history=None):
|
|
94
|
-
chat_history = chat_history or []
|
|
95
|
-
|
|
96
|
-
service_url = get_service_url(service_name, project, region)
|
|
97
|
-
user_id = generate_user_id()
|
|
98
|
-
session_id = str(uuid.uuid4())
|
|
99
|
-
|
|
100
|
-
def stream_response():
|
|
101
|
-
generate = generate_proxy_stream(
|
|
102
|
-
send_to_qa,
|
|
103
|
-
user_input,
|
|
104
|
-
vector_name=service_name,
|
|
105
|
-
chat_history=chat_history,
|
|
106
|
-
generate_f_output=lambda x: x, # Replace with actual processing function
|
|
107
|
-
stream_wait_time=0.5,
|
|
108
|
-
stream_timeout=120,
|
|
109
|
-
message_author=user_id,
|
|
110
|
-
#TODO: populate these
|
|
111
|
-
image_url=None,
|
|
112
|
-
source_filters=None,
|
|
113
|
-
search_kwargs=None,
|
|
114
|
-
private_docs=None,
|
|
115
|
-
whole_document=False,
|
|
116
|
-
source_filters_and_or=False,
|
|
117
|
-
# system kwargs
|
|
118
|
-
configurable={
|
|
119
|
-
"vector_name": service_name,
|
|
120
|
-
},
|
|
121
|
-
user_id=user_id,
|
|
122
|
-
session_id=session_id,
|
|
123
|
-
message_source="cli",
|
|
124
|
-
override_endpoint=service_url
|
|
125
|
-
)
|
|
126
|
-
for part in generate():
|
|
127
|
-
yield part
|
|
128
|
-
|
|
129
|
-
vac_response = ""
|
|
130
|
-
|
|
131
|
-
for token in stream_response():
|
|
132
|
-
if isinstance(token, bytes):
|
|
133
|
-
token = token.decode('utf-8')
|
|
134
|
-
print(token, end='', flush=True)
|
|
135
|
-
vac_response += token
|
|
136
|
-
|
|
137
|
-
if vac_response:
|
|
138
|
-
chat_history.append({"name": "Human", "content": user_input})
|
|
139
|
-
chat_history.append({"name": "AI", "content": vac_response})
|
|
140
|
-
print() # For new line after streaming ends
|
|
141
|
-
|
|
142
|
-
return chat_history
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def vac_command(args):
|
|
146
|
-
try:
|
|
147
|
-
service_url = get_service_url(args.vac_name, args.project, args.region)
|
|
148
|
-
except ValueError as e:
|
|
149
|
-
console.print(f"[bold red]ERROR: Could not start {args.vac_name} proxy URL: {str(e)}[/bold red]")
|
|
150
|
-
return
|
|
151
|
-
|
|
152
|
-
agent_name = load_config_key("agent", args.vac_name, kind="vacConfig")
|
|
153
|
-
|
|
154
|
-
if args.headless:
|
|
155
|
-
headless_mode(args.vac_name, args.user_input, args.project, args.region, args.chat_history)
|
|
156
|
-
stop_proxy(agent_name)
|
|
157
|
-
else:
|
|
158
|
-
display_name = load_config_key("display_name", vector_name=args.vac_name, kind="vacConfig")
|
|
159
|
-
description = load_config_key("description", vector_name=args.vac_name, kind="vacConfig")
|
|
160
|
-
|
|
161
|
-
if agent_name == "langserve":
|
|
162
|
-
subtitle = f"{service_url}/{args.vac_name}/playground/"
|
|
163
|
-
else:
|
|
164
|
-
subtitle = f"{agent_name} - {service_url}/vac/{args.vac_name}"
|
|
165
|
-
|
|
166
|
-
print(
|
|
167
|
-
Panel(description or "Starting VAC chat session",
|
|
168
|
-
title=display_name or args.vac_name,
|
|
169
|
-
subtitle=subtitle)
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
stream_chat_session(args.vac_name, args.project, args.region)
|
|
173
|
-
stop_proxy(agent_name)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
def setup_vac_subparser(subparsers):
|
|
177
|
-
"""
|
|
178
|
-
Sets up an argparse subparser for the 'vac' command.
|
|
179
|
-
|
|
180
|
-
Args:
|
|
181
|
-
subparsers: The subparsers object from argparse.ArgumentParser().
|
|
182
|
-
"""
|
|
183
|
-
vac_parser = subparsers.add_parser('vac', help='Interact with deployed VAC services.')
|
|
184
|
-
vac_parser.add_argument('vac_name', help='Name of the VAC service.')
|
|
185
|
-
vac_parser.add_argument('user_input', help='User input for the VAC service when in headless mode.', nargs='?', default=None)
|
|
186
|
-
vac_parser.add_argument('--headless', action='store_true', help='Run in headless mode.')
|
|
187
|
-
vac_parser.add_argument('--chat_history', help='Chat history for headless mode (as JSON string).', default=None)
|
|
188
|
-
vac_parser.set_defaults(func=vac_command)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|