sunholo 0.60.2__py3-none-any.whl → 0.60.4__py3-none-any.whl
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/__init__.py +3 -0
- sunholo/cli/chat_vac.py +74 -35
- sunholo/cli/cli.py +21 -5
- sunholo/cli/run_proxy.py +104 -12
- sunholo/cli/sun_rich.py +3 -0
- sunholo/database/alloydb.py +50 -0
- sunholo/gcs/download_url.py +1 -1
- sunholo/logging.py +0 -3
- sunholo/streaming/langserve.py +2 -2
- sunholo/utils/config.py +15 -10
- {sunholo-0.60.2.dist-info → sunholo-0.60.4.dist-info}/METADATA +10 -6
- {sunholo-0.60.2.dist-info → sunholo-0.60.4.dist-info}/RECORD +16 -15
- {sunholo-0.60.2.dist-info → sunholo-0.60.4.dist-info}/LICENSE.txt +0 -0
- {sunholo-0.60.2.dist-info → sunholo-0.60.4.dist-info}/WHEEL +0 -0
- {sunholo-0.60.2.dist-info → sunholo-0.60.4.dist-info}/entry_points.txt +0 -0
- {sunholo-0.60.2.dist-info → sunholo-0.60.4.dist-info}/top_level.txt +0 -0
sunholo/__init__.py
CHANGED
sunholo/cli/chat_vac.py
CHANGED
|
@@ -1,34 +1,44 @@
|
|
|
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
|
-
from .run_proxy import clean_proxy_list, start_proxy
|
|
6
|
+
from .run_proxy import clean_proxy_list, start_proxy, stop_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(vac_name, project, region):
|
|
19
|
+
agent_name = load_config_key("agent", vac_name, kind="vacConfig")
|
|
10
20
|
proxies = clean_proxy_list()
|
|
11
|
-
if
|
|
12
|
-
port = proxies[
|
|
13
|
-
|
|
21
|
+
if agent_name in proxies:
|
|
22
|
+
port = proxies[agent_name]['port']
|
|
23
|
+
url = f"http://127.0.0.1:{port}"
|
|
14
24
|
else:
|
|
15
|
-
print(f"No proxy found running for service: {
|
|
16
|
-
|
|
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)
|
|
17
27
|
|
|
18
|
-
|
|
28
|
+
return url
|
|
19
29
|
|
|
20
|
-
|
|
30
|
+
def stream_chat_session(service_name, project, region):
|
|
31
|
+
|
|
32
|
+
service_url = get_service_url(service_name, project, region)
|
|
21
33
|
user_id = generate_user_id()
|
|
22
34
|
chat_history = []
|
|
23
35
|
while True:
|
|
24
36
|
session_id = str(uuid.uuid4())
|
|
25
|
-
user_input =
|
|
37
|
+
user_input = Prompt.ask("[bold cyan]You[/bold cyan]")
|
|
26
38
|
if user_input.lower() in ["exit", "quit"]:
|
|
27
|
-
print("Exiting chat session.")
|
|
39
|
+
console.print("[bold red]Exiting chat session.[/bold red]")
|
|
28
40
|
break
|
|
29
41
|
|
|
30
|
-
chat_history.append({"role": "Human", "content": user_input})
|
|
31
|
-
|
|
32
42
|
def stream_response():
|
|
33
43
|
generate = generate_proxy_stream(
|
|
34
44
|
send_to_qa,
|
|
@@ -60,24 +70,30 @@ def stream_chat_session(service_name):
|
|
|
60
70
|
|
|
61
71
|
response_started = False
|
|
62
72
|
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
|
-
|
|
68
|
-
if isinstance(token, bytes):
|
|
69
|
-
token = token.decode('utf-8')
|
|
70
|
-
print(token, end='', flush=True)
|
|
71
|
-
vac_response += token
|
|
72
73
|
|
|
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})
|
|
74
89
|
response_started = False
|
|
75
|
-
print()
|
|
90
|
+
console.print()
|
|
91
|
+
console.rule()
|
|
76
92
|
|
|
77
|
-
def headless_mode(service_name, user_input, chat_history=None):
|
|
93
|
+
def headless_mode(service_name, user_input, project, region, chat_history=None):
|
|
78
94
|
chat_history = chat_history or []
|
|
79
|
-
|
|
80
|
-
service_url = get_service_url(service_name)
|
|
95
|
+
|
|
96
|
+
service_url = get_service_url(service_name, project, region)
|
|
81
97
|
user_id = generate_user_id()
|
|
82
98
|
session_id = str(uuid.uuid4())
|
|
83
99
|
|
|
@@ -110,13 +126,17 @@ def headless_mode(service_name, user_input, chat_history=None):
|
|
|
110
126
|
for part in generate():
|
|
111
127
|
yield part
|
|
112
128
|
|
|
113
|
-
|
|
129
|
+
vac_response = ""
|
|
130
|
+
|
|
114
131
|
for token in stream_response():
|
|
115
132
|
if isinstance(token, bytes):
|
|
116
133
|
token = token.decode('utf-8')
|
|
117
134
|
print(token, end='', flush=True)
|
|
135
|
+
vac_response += token
|
|
118
136
|
|
|
119
|
-
|
|
137
|
+
if vac_response:
|
|
138
|
+
chat_history.append({"name": "Human", "content": user_input})
|
|
139
|
+
chat_history.append({"name": "AI", "content": vac_response})
|
|
120
140
|
print() # For new line after streaming ends
|
|
121
141
|
|
|
122
142
|
return chat_history
|
|
@@ -124,15 +144,34 @@ def headless_mode(service_name, user_input, chat_history=None):
|
|
|
124
144
|
|
|
125
145
|
def vac_command(args):
|
|
126
146
|
try:
|
|
127
|
-
service_url = get_service_url(args.
|
|
147
|
+
service_url = get_service_url(args.vac_name, args.project, args.region)
|
|
128
148
|
except ValueError as e:
|
|
129
|
-
print(f"ERROR: Could not start {args.
|
|
149
|
+
console.print(f"[bold red]ERROR: Could not start {args.vac_name} proxy URL: {str(e)}[/bold red]")
|
|
130
150
|
return
|
|
131
|
-
|
|
151
|
+
|
|
152
|
+
agent_name = load_config_key("agent", args.vac_name, kind="vacConfig")
|
|
153
|
+
|
|
132
154
|
if args.headless:
|
|
133
|
-
headless_mode(args.
|
|
155
|
+
headless_mode(args.vac_name, args.user_input, args.project, args.region, args.chat_history)
|
|
156
|
+
stop_proxy(agent_name)
|
|
134
157
|
else:
|
|
135
|
-
|
|
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
|
+
|
|
136
175
|
|
|
137
176
|
def setup_vac_subparser(subparsers):
|
|
138
177
|
"""
|
|
@@ -142,7 +181,7 @@ def setup_vac_subparser(subparsers):
|
|
|
142
181
|
subparsers: The subparsers object from argparse.ArgumentParser().
|
|
143
182
|
"""
|
|
144
183
|
vac_parser = subparsers.add_parser('vac', help='Interact with deployed VAC services.')
|
|
145
|
-
vac_parser.add_argument('
|
|
184
|
+
vac_parser.add_argument('vac_name', help='Name of the VAC service.')
|
|
146
185
|
vac_parser.add_argument('user_input', help='User input for the VAC service when in headless mode.', nargs='?', default=None)
|
|
147
186
|
vac_parser.add_argument('--headless', action='store_true', help='Run in headless mode.')
|
|
148
187
|
vac_parser.add_argument('--chat_history', help='Chat history for headless mode (as JSON string).', default=None)
|
sunholo/cli/cli.py
CHANGED
|
@@ -7,22 +7,38 @@ 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
|
+
|
|
15
|
+
|
|
16
|
+
def load_default_gcp_config():
|
|
17
|
+
gcp_config = load_config_key('gcp_config', 'global', kind="vacConfig")
|
|
18
|
+
if gcp_config:
|
|
19
|
+
return gcp_config.get('project_id', ''), gcp_config.get('location', 'europe-west1')
|
|
20
|
+
else:
|
|
21
|
+
return '', 'europe-west1'
|
|
22
|
+
|
|
13
23
|
def main(args=None):
|
|
24
|
+
|
|
25
|
+
|
|
14
26
|
"""
|
|
15
27
|
Entry point for the sunholo console script. This function parses command line arguments
|
|
16
28
|
and invokes the appropriate functionality based on the user input.
|
|
17
29
|
|
|
18
|
-
|
|
30
|
+
Get started:
|
|
19
31
|
```bash
|
|
20
|
-
sunholo
|
|
32
|
+
sunholo --help
|
|
21
33
|
```
|
|
22
34
|
"""
|
|
35
|
+
default_project, default_region = load_default_gcp_config()
|
|
36
|
+
|
|
23
37
|
parser = argparse.ArgumentParser(description="sunholo CLI tool for deploying GenAI VACs")
|
|
24
38
|
parser.add_argument('--debug', action='store_true', help='Enable debug output')
|
|
25
|
-
|
|
39
|
+
parser.add_argument('--project', default=default_project, help='GCP project to list Cloud Run services from.')
|
|
40
|
+
parser.add_argument('--region', default=default_region, help='Region to list Cloud Run services from.')
|
|
41
|
+
|
|
26
42
|
subparsers = parser.add_subparsers(title='commands',
|
|
27
43
|
description='Valid commands',
|
|
28
44
|
help='Commands',
|
|
@@ -45,8 +61,8 @@ def main(args=None):
|
|
|
45
61
|
args = parser.parse_args(args)
|
|
46
62
|
|
|
47
63
|
if args.debug:
|
|
48
|
-
log.setLevel(logging.
|
|
49
|
-
logging.getLogger().setLevel(logging.
|
|
64
|
+
log.setLevel(logging.DEBUG)
|
|
65
|
+
logging.getLogger().setLevel(logging.DEBUG)
|
|
50
66
|
else:
|
|
51
67
|
log.setLevel(logging.WARNING)
|
|
52
68
|
logging.getLogger().setLevel(logging.WARNING)
|
sunholo/cli/run_proxy.py
CHANGED
|
@@ -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
|
-
print("ERROR: gcloud is not installed or not found in PATH.")
|
|
55
|
+
print("[bold red]ERROR: gcloud is not installed or not found in PATH.[/bold red]")
|
|
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,100 @@ 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
|
"""
|
|
162
|
-
|
|
242
|
+
with console.status("[bold orange]Listing Proxies[/bold orange]", spinner="star"):
|
|
243
|
+
proxies = clean_proxy_list()
|
|
244
|
+
|
|
163
245
|
if not proxies:
|
|
164
246
|
print("No proxies currently running.")
|
|
165
247
|
else:
|
|
166
|
-
|
|
248
|
+
table = Table(title="VAC Proxies")
|
|
249
|
+
table.add_column("VAC")
|
|
250
|
+
table.add_column("Port")
|
|
251
|
+
table.add_column("PID")
|
|
252
|
+
table.add_column("URL")
|
|
253
|
+
|
|
167
254
|
for service_name, info in proxies.items():
|
|
168
255
|
url = f"http://127.0.0.1:{info['port']}"
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
256
|
+
table.add_row(service_name, str(info['port']), str(info['pid']), url)
|
|
257
|
+
|
|
258
|
+
console.print(table)
|
|
172
259
|
|
|
173
260
|
def setup_proxy_subparser(subparsers):
|
|
174
261
|
"""
|
|
@@ -177,13 +264,12 @@ def setup_proxy_subparser(subparsers):
|
|
|
177
264
|
Args:
|
|
178
265
|
subparsers: The subparsers object from argparse.ArgumentParser().
|
|
179
266
|
"""
|
|
180
|
-
|
|
267
|
+
|
|
268
|
+
proxy_parser = subparsers.add_parser('proxy', help='Set up or stop a proxy to the VAC Cloud Run services')
|
|
181
269
|
proxy_subparsers = proxy_parser.add_subparsers(dest='proxy_command', required=True)
|
|
182
270
|
|
|
183
|
-
start_parser = proxy_subparsers.add_parser('start', help='Start the proxy to the Cloud Run service
|
|
271
|
+
start_parser = proxy_subparsers.add_parser('start', help='Start the proxy to the VAC Cloud Run service')
|
|
184
272
|
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
273
|
start_parser.add_argument('--port', type=int, help='Port to run the proxy on. Auto-assigns if not provided.')
|
|
188
274
|
start_parser.set_defaults(func=lambda args: start_proxy(args.service_name, args.region, args.project, args.port))
|
|
189
275
|
|
|
@@ -194,4 +280,10 @@ def setup_proxy_subparser(subparsers):
|
|
|
194
280
|
list_parser = proxy_subparsers.add_parser('list', help='List all running proxies.')
|
|
195
281
|
list_parser.set_defaults(func=lambda args: list_proxies())
|
|
196
282
|
|
|
283
|
+
stop_all_parser = proxy_subparsers.add_parser('stop-all', help='Stop all running proxies.')
|
|
284
|
+
stop_all_parser.set_defaults(func=lambda args: stop_all_proxies())
|
|
285
|
+
|
|
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
|
+
|
|
197
289
|
|
sunholo/cli/sun_rich.py
ADDED
sunholo/database/alloydb.py
CHANGED
|
@@ -171,6 +171,56 @@ class AlloyDBClient:
|
|
|
171
171
|
|
|
172
172
|
return self.execute_sql(query)
|
|
173
173
|
|
|
174
|
+
def create_database(self, database_name):
|
|
175
|
+
self.execute_sql(f'CREATE DATABASE "{database_name}"')
|
|
176
|
+
|
|
177
|
+
def fetch_owners(self):
|
|
178
|
+
owners = self.execute_sql('SELECT table_schema, table_name, privilege_type FROM information_schema.table_privileges')
|
|
179
|
+
for row in owners:
|
|
180
|
+
print(f"Schema: {row[0]}, Table: {row[1]}, Privilege: {row[2]}")
|
|
181
|
+
return owners
|
|
182
|
+
|
|
183
|
+
def create_schema(self, schema_name="public"):
|
|
184
|
+
self.execute_sql(f'CREATE SCHEMA IF NOT EXISTS {schema_name};')
|
|
185
|
+
|
|
186
|
+
def grant_permissions(self, schema_name, users):
|
|
187
|
+
for user in users:
|
|
188
|
+
self.execute_sql(f'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA {schema_name} TO "{user}";')
|
|
189
|
+
self.execute_sql(f'GRANT USAGE, CREATE ON SCHEMA {schema_name} TO "{user}";')
|
|
190
|
+
self.execute_sql(f'ALTER DEFAULT PRIVILEGES IN SCHEMA {schema_name} GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "{user}";')
|
|
191
|
+
self.execute_sql(f'GRANT USAGE ON SCHEMA information_schema TO "{user}";')
|
|
192
|
+
self.execute_sql(f'GRANT SELECT ON information_schema.columns TO "{user}";')
|
|
193
|
+
|
|
194
|
+
def create_docstore_tables(self, vector_names, users):
|
|
195
|
+
for vector_name in vector_names:
|
|
196
|
+
table_name = f"{vector_name}_docstore"
|
|
197
|
+
sql = f'''
|
|
198
|
+
CREATE TABLE IF NOT EXISTS "{table_name}"
|
|
199
|
+
(page_content TEXT, doc_id UUID, source TEXT, images_gsurls JSONB, chunk_metadata JSONB, langchain_metadata JSONB)
|
|
200
|
+
'''
|
|
201
|
+
self.execute_sql(sql)
|
|
202
|
+
|
|
203
|
+
for user in users:
|
|
204
|
+
self.execute_sql(f'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE "{table_name}" TO "{user}";')
|
|
205
|
+
|
|
206
|
+
vectorstore_id = f"{vector_name}_vectorstore_1536"
|
|
207
|
+
sql = f'''
|
|
208
|
+
CREATE TABLE IF NOT EXISTS "{vectorstore_id}" (
|
|
209
|
+
langchain_id UUID NOT NULL,
|
|
210
|
+
content TEXT NOT NULL,
|
|
211
|
+
embedding vector NOT NULL,
|
|
212
|
+
source TEXT,
|
|
213
|
+
langchain_metadata JSONB,
|
|
214
|
+
docstore_doc_id UUID,
|
|
215
|
+
eventTime TIMESTAMPTZ
|
|
216
|
+
);
|
|
217
|
+
'''
|
|
218
|
+
self.execute_sql(sql)
|
|
219
|
+
|
|
220
|
+
for user in users:
|
|
221
|
+
self.execute_sql(f'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE {vectorstore_id} TO "{user}";')
|
|
222
|
+
|
|
223
|
+
|
|
174
224
|
alloydb_table_cache = {} # Our cache, initially empty # noqa: F841
|
|
175
225
|
def create_alloydb_table(vector_name, engine, type = "vectorstore", alloydb_config=None, username=None):
|
|
176
226
|
global alloydb_table_cache
|
sunholo/gcs/download_url.py
CHANGED
|
@@ -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:
|
sunholo/logging.py
CHANGED
|
@@ -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
|
|
sunholo/streaming/langserve.py
CHANGED
|
@@ -157,8 +157,8 @@ def parse_json_data(json_data: dict):
|
|
|
157
157
|
"""
|
|
158
158
|
try:
|
|
159
159
|
if isinstance(json_data, dict):
|
|
160
|
-
content = json_data.get('content')
|
|
161
|
-
if content:
|
|
160
|
+
content = json_data.get('content', None)
|
|
161
|
+
if content is not None: # content can be '' empty string
|
|
162
162
|
#log.debug(f'Yield content: {content}')
|
|
163
163
|
yield content
|
|
164
164
|
else:
|
sunholo/utils/config.py
CHANGED
|
@@ -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.4
|
|
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.4.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,12 +56,15 @@ 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
|
-
Requires-Dist: python-socketio ; extra == 'all'
|
|
62
63
|
Provides-Extra: anthropic
|
|
63
64
|
Requires-Dist: langchain-anthropic ; extra == 'anthropic'
|
|
65
|
+
Provides-Extra: cli
|
|
66
|
+
Requires-Dist: jsonschema ; extra == 'cli'
|
|
67
|
+
Requires-Dist: rich ; extra == 'cli'
|
|
64
68
|
Provides-Extra: database
|
|
65
69
|
Requires-Dist: asyncpg ; extra == 'database'
|
|
66
70
|
Requires-Dist: supabase ; extra == 'database'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
sunholo/__init__.py,sha256=
|
|
2
|
-
sunholo/logging.py,sha256=
|
|
1
|
+
sunholo/__init__.py,sha256=0CdpufyRKWyZe7J7UKigL6j_qOorM-p0OjHIAuf9M38,864
|
|
2
|
+
sunholo/logging.py,sha256=1jzfy4q9h5DNG4MjwtbTiM8keZxymlrZ0gDQEtGLMHY,11400
|
|
3
3
|
sunholo/agents/__init__.py,sha256=CnlbVohPt-Doth9PyROSlN3P8xMV9j9yS19YE-wCS90,341
|
|
4
4
|
sunholo/agents/chat_history.py,sha256=PbwYmw1TwzI8H-cwQIGgHZ6UIr2Qb-JWow0RG3ayLM8,5195
|
|
5
5
|
sunholo/agents/dispatch_to_qa.py,sha256=h5qbcPqJ5JGa21T8Z5is7jbn4eG3P4xULLj_X25q3WM,8208
|
|
@@ -32,20 +32,21 @@ sunholo/chunker/pdfs.py,sha256=daCZ1xjn1YvxlifIyxskWNpLJLe-Q9D_Jq12MWx3tZo,2473
|
|
|
32
32
|
sunholo/chunker/publish.py,sha256=PoT8q3XJeFCg10WrLkYhuaaXIrGVkvUD3-R9IfoWoH4,2703
|
|
33
33
|
sunholo/chunker/splitter.py,sha256=FLkDhkePkg_zGQpFBK13Cznw575D-Rf9pcaCpc1HUxY,6726
|
|
34
34
|
sunholo/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
-
sunholo/cli/chat_vac.py,sha256=
|
|
36
|
-
sunholo/cli/cli.py,sha256=
|
|
35
|
+
sunholo/cli/chat_vac.py,sha256=038AkvGDEK5cBWqQTRrTQYCsa1FDWzZGHaHOyrS2lFE,6841
|
|
36
|
+
sunholo/cli/cli.py,sha256=LWA5OveHx3ocy9KD1XwAwosBFrTUwifArIbltYgpul4,2420
|
|
37
37
|
sunholo/cli/cli_init.py,sha256=JMZ9AX2cPDZ-_mv3adiv2ToFVNyRPtjk9Biszl1kiR0,2358
|
|
38
38
|
sunholo/cli/configs.py,sha256=QUM9DvKOdZmEQRM5uI3Nh887T0YDiSMr7O240zTLqws,4546
|
|
39
39
|
sunholo/cli/deploy.py,sha256=zxdwUsRTRMC8U5vyRv0JiKBLFn84Ug_Tc88-_h9hJSs,1609
|
|
40
40
|
sunholo/cli/merge_texts.py,sha256=U9vdMwKmcPoc6iPOWX5MKSxn49dNGbNzVLw8ui5PhEU,1823
|
|
41
|
-
sunholo/cli/run_proxy.py,sha256=
|
|
41
|
+
sunholo/cli/run_proxy.py,sha256=kYi3LnMdi27IqzA-k37WyT8AnFifn5ePP7jNK-8UmhE,9694
|
|
42
|
+
sunholo/cli/sun_rich.py,sha256=UpMqeJ0C8i0pkue1AHnnyyX0bFJ9zZeJ7HBR6yhuA8A,54
|
|
42
43
|
sunholo/components/__init__.py,sha256=RJGNEihwvRIiDScKis04RHJv4yZGI1UpXlOmuCptNZI,208
|
|
43
44
|
sunholo/components/llm.py,sha256=T4we3tGmqUj4tPwxQr9M6AXv_BALqZV_dRSvINan-oU,10374
|
|
44
45
|
sunholo/components/prompt.py,sha256=eZSghXkIlRzXiSrzgkG7e5ytUYq6R6LV-qjHU8jStig,6353
|
|
45
46
|
sunholo/components/retriever.py,sha256=_Lyt9RIgb2PD-rhV6oKAadiUs3ukT5uAYGW197tEskw,3755
|
|
46
47
|
sunholo/components/vectorstore.py,sha256=dzspqOBtuxSjCFxem5_50sqwUUjbZ4oBYERtCwxZR6E,5619
|
|
47
48
|
sunholo/database/__init__.py,sha256=Zz0Shcq-CtStf9rJGIYB_Ybzb8rY_Q9mfSj-nviM490,241
|
|
48
|
-
sunholo/database/alloydb.py,sha256=
|
|
49
|
+
sunholo/database/alloydb.py,sha256=0zRLyeC9nACzj3v36ET9NqLeuzdwBJ2bE09CzgVTTFM,17098
|
|
49
50
|
sunholo/database/database.py,sha256=doY05kG8BZBLL-arh4hq5ef1ouWOtGHqdsDc6M2YHgk,7345
|
|
50
51
|
sunholo/database/lancedb.py,sha256=WSrbY5mgyeXx6i7UBiz4YQ_i5UIYVYFo-vPGO72bQKY,707
|
|
51
52
|
sunholo/database/static_dbs.py,sha256=aOyU3AJ-Dzz3qSNjbuN2293cfYw5PhkcQuQxdwPMJ4w,435
|
|
@@ -60,7 +61,7 @@ sunholo/embedder/__init__.py,sha256=sI4N_CqgEVcrMDxXgxKp1FsfsB4FpjoXgPGkl4N_u4I,
|
|
|
60
61
|
sunholo/embedder/embed_chunk.py,sha256=XV1kdDUWw2QO-am5_Yl7GrYP9V_4i1XRNNFPhqUSnZQ,5851
|
|
61
62
|
sunholo/gcs/__init__.py,sha256=DtVw_AZwQn-IguR5BJuIi2XJeF_FQXizhJikzRNrXiE,50
|
|
62
63
|
sunholo/gcs/add_file.py,sha256=JmJIuz5Z1h7-eJ6s2eE3wc8Y4IAv3Jridq1xfQbD9_E,4711
|
|
63
|
-
sunholo/gcs/download_url.py,sha256=
|
|
64
|
+
sunholo/gcs/download_url.py,sha256=8XSEf8byfubqs5CMQeF_tn9wxqwUTq3n9mo5mLNIUTA,4801
|
|
64
65
|
sunholo/gcs/metadata.py,sha256=C9sMPsHsq1ETetdQCqB3EBs3Kws8b8QHS9L7ei_v5aw,891
|
|
65
66
|
sunholo/langfuse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
67
|
sunholo/langfuse/callback.py,sha256=G9xcZHpLvyzolU57ycItLaooMCtRuM37QJSWjiwQEd0,1776
|
|
@@ -82,22 +83,22 @@ sunholo/qna/parsers.py,sha256=mH_SIgN2yXzvcoQZt9ITkdJSw3jgZGuu0p8q_H-kdSM,2140
|
|
|
82
83
|
sunholo/qna/retry.py,sha256=gFgOf9AxrZMIO9OwOYu1EW7rhNhyfnw_o4XAsNLBOVQ,2021
|
|
83
84
|
sunholo/streaming/__init__.py,sha256=k8dBqhzyS1Oi6NfADtRtWfnPtU1FU2kQz-YxH9yrNeQ,197
|
|
84
85
|
sunholo/streaming/content_buffer.py,sha256=fWcg0oTf470M3U40VAChfmHmWRFgRD8VaT90jNfBCH0,6455
|
|
85
|
-
sunholo/streaming/langserve.py,sha256=
|
|
86
|
+
sunholo/streaming/langserve.py,sha256=4AeNt4FPv461s20_5q17Nx83cHjK7Dl3gVOcWAxMgOk,6350
|
|
86
87
|
sunholo/streaming/streaming.py,sha256=9z6pXINEopuL_Z1RnmgXAoZJum9dzyuOxqYtEYnjf8w,16405
|
|
87
88
|
sunholo/summarise/__init__.py,sha256=MZk3dblUMODcPb1crq4v-Z508NrFIpkSWNf9FIO8BcU,38
|
|
88
89
|
sunholo/summarise/summarise.py,sha256=C3HhjepTjUhUC8FLk4jMQIBvq1BcORniwuTFHjPVhVo,3784
|
|
89
90
|
sunholo/utils/__init__.py,sha256=G11nN_6ATjxpuMfG_BvcUr9UU8onPIgkpTK6CjOcbr8,48
|
|
90
91
|
sunholo/utils/big_context.py,sha256=gJIP7_ZL-YSLhOMq8jmFTMqH1wq8eB1NK7oKPeZAq2s,5578
|
|
91
|
-
sunholo/utils/config.py,sha256=
|
|
92
|
+
sunholo/utils/config.py,sha256=3GSDFGbHugB3qlkHadKi3Z1CEr6EZhCOQ3TiNt03nNU,8594
|
|
92
93
|
sunholo/utils/config_schema.py,sha256=Wv-ncitzljOhgbDaq9qnFqH5LCuxNv59dTGDWgd1qdk,4189
|
|
93
94
|
sunholo/utils/gcp.py,sha256=B2G1YKjeD7X9dqO86Jrp2vPuFwZ223Xl5Tg09Ndw-oc,5760
|
|
94
95
|
sunholo/utils/parsers.py,sha256=OrHmASqIbI45atVOhiGodgLvnfrzkvVzyHnSvAXD89I,3841
|
|
95
96
|
sunholo/utils/user_ids.py,sha256=SQd5_H7FE7vcTZp9AQuQDWBXd4FEEd7TeVMQe1H4Ny8,292
|
|
96
97
|
sunholo/vertex/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
97
98
|
sunholo/vertex/init_vertex.py,sha256=JDMUaBRdednzbKF-5p33qqLit2LMsvgvWW-NRz0AqO0,1801
|
|
98
|
-
sunholo-0.60.
|
|
99
|
-
sunholo-0.60.
|
|
100
|
-
sunholo-0.60.
|
|
101
|
-
sunholo-0.60.
|
|
102
|
-
sunholo-0.60.
|
|
103
|
-
sunholo-0.60.
|
|
99
|
+
sunholo-0.60.4.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
|
|
100
|
+
sunholo-0.60.4.dist-info/METADATA,sha256=nHPHNqcRw2WAX2guIAM0QXFVcQLx3FzlB024g-jfK4k,8057
|
|
101
|
+
sunholo-0.60.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
102
|
+
sunholo-0.60.4.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
|
|
103
|
+
sunholo-0.60.4.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
|
|
104
|
+
sunholo-0.60.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|