sunholo 0.60.0__py3-none-any.whl → 0.60.2__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/agents/dispatch_to_qa.py +5 -1
- sunholo/agents/flask/qna_routes.py +4 -0
- sunholo/agents/route.py +2 -2
- sunholo/cli/chat_vac.py +149 -0
- sunholo/cli/cli.py +17 -0
- sunholo/cli/cli_init.py +3 -23
- sunholo/cli/run_proxy.py +197 -0
- sunholo/components/retriever.py +2 -1
- sunholo/components/vectorstore.py +27 -19
- sunholo/database/alloydb.py +1 -1
- sunholo/logging.py +8 -3
- sunholo/utils/config.py +0 -1
- sunholo/utils/user_ids.py +10 -0
- {sunholo-0.60.0.dist-info → sunholo-0.60.2.dist-info}/METADATA +2 -2
- {sunholo-0.60.0.dist-info → sunholo-0.60.2.dist-info}/RECORD +19 -16
- {sunholo-0.60.0.dist-info → sunholo-0.60.2.dist-info}/LICENSE.txt +0 -0
- {sunholo-0.60.0.dist-info → sunholo-0.60.2.dist-info}/WHEEL +0 -0
- {sunholo-0.60.0.dist-info → sunholo-0.60.2.dist-info}/entry_points.txt +0 -0
- {sunholo-0.60.0.dist-info → sunholo-0.60.2.dist-info}/top_level.txt +0 -0
sunholo/agents/dispatch_to_qa.py
CHANGED
|
@@ -52,8 +52,12 @@ def prep_request_payload(user_input, chat_history, vector_name, stream, **kwargs
|
|
|
52
52
|
agent = load_config_key("agent", vector_name=vector_name, kind="vacConfig")
|
|
53
53
|
agent_type = load_config_key("agent_type", vector_name=vector_name, kind="vacConfig")
|
|
54
54
|
|
|
55
|
+
override_endpoint = kwargs.get("override_endpoint")
|
|
56
|
+
if override_endpoint:
|
|
57
|
+
log.info(f"Overriding endpoint with {override_endpoint}")
|
|
58
|
+
|
|
55
59
|
# {'stream': '', 'invoke': ''}
|
|
56
|
-
endpoints = route_endpoint(vector_name)
|
|
60
|
+
endpoints = route_endpoint(vector_name, override_endpoint=override_endpoint)
|
|
57
61
|
|
|
58
62
|
qna_endpoint = endpoints["stream"] if stream else endpoints["invoke"]
|
|
59
63
|
|
|
@@ -37,6 +37,10 @@ except ImportError as err:
|
|
|
37
37
|
|
|
38
38
|
def register_qna_routes(app, stream_interpreter, vac_interpreter):
|
|
39
39
|
|
|
40
|
+
@app.route("/")
|
|
41
|
+
def home():
|
|
42
|
+
return jsonify("OK")
|
|
43
|
+
|
|
40
44
|
@app.route('/vac/streaming/<vector_name>', methods=['POST'])
|
|
41
45
|
@observe()
|
|
42
46
|
def stream_qa(vector_name):
|
sunholo/agents/route.py
CHANGED
|
@@ -35,13 +35,13 @@ def route_qna(vector_name):
|
|
|
35
35
|
log.info(f'agent_url: {agent_url}')
|
|
36
36
|
return agent_url
|
|
37
37
|
|
|
38
|
-
def route_endpoint(vector_name):
|
|
38
|
+
def route_endpoint(vector_name, override_endpoint=None):
|
|
39
39
|
|
|
40
40
|
agent_type = load_config_key('agent_type', vector_name, kind="vacConfig")
|
|
41
41
|
if not agent_type:
|
|
42
42
|
agent_type = load_config_key('agent', vector_name, kind="vacConfig")
|
|
43
43
|
|
|
44
|
-
stem = route_qna(vector_name)
|
|
44
|
+
stem = route_qna(vector_name) if not override_endpoint else override_endpoint
|
|
45
45
|
|
|
46
46
|
agent_config, _ = load_config('config/agent_config.yaml')
|
|
47
47
|
|
sunholo/cli/chat_vac.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from ..agents import send_to_qa
|
|
2
|
+
from ..streaming import generate_proxy_stream
|
|
3
|
+
from ..utils.user_ids import generate_user_id
|
|
4
|
+
|
|
5
|
+
from .run_proxy import clean_proxy_list, start_proxy
|
|
6
|
+
|
|
7
|
+
import uuid
|
|
8
|
+
|
|
9
|
+
def get_service_url(service_name):
|
|
10
|
+
proxies = clean_proxy_list()
|
|
11
|
+
if service_name in proxies:
|
|
12
|
+
port = proxies[service_name]['port']
|
|
13
|
+
return f"http://127.0.0.1:{port}"
|
|
14
|
+
else:
|
|
15
|
+
print(f"No proxy found running for service: {service_name} - attempting to connect")
|
|
16
|
+
return start_proxy(service_name)
|
|
17
|
+
|
|
18
|
+
def stream_chat_session(service_name):
|
|
19
|
+
|
|
20
|
+
service_url = get_service_url(service_name)
|
|
21
|
+
user_id = generate_user_id()
|
|
22
|
+
chat_history = []
|
|
23
|
+
while True:
|
|
24
|
+
session_id = str(uuid.uuid4())
|
|
25
|
+
user_input = input("You: ")
|
|
26
|
+
if user_input.lower() in ["exit", "quit"]:
|
|
27
|
+
print("Exiting chat session.")
|
|
28
|
+
break
|
|
29
|
+
|
|
30
|
+
chat_history.append({"role": "Human", "content": user_input})
|
|
31
|
+
|
|
32
|
+
def stream_response():
|
|
33
|
+
generate = generate_proxy_stream(
|
|
34
|
+
send_to_qa,
|
|
35
|
+
user_input,
|
|
36
|
+
vector_name=service_name,
|
|
37
|
+
chat_history=chat_history,
|
|
38
|
+
generate_f_output=lambda x: x, # Replace with actual processing function
|
|
39
|
+
stream_wait_time=0.5,
|
|
40
|
+
stream_timeout=120,
|
|
41
|
+
message_author=user_id,
|
|
42
|
+
#TODO: populate these
|
|
43
|
+
image_url=None,
|
|
44
|
+
source_filters=None,
|
|
45
|
+
search_kwargs=None,
|
|
46
|
+
private_docs=None,
|
|
47
|
+
whole_document=False,
|
|
48
|
+
source_filters_and_or=False,
|
|
49
|
+
# system kwargs
|
|
50
|
+
configurable={
|
|
51
|
+
"vector_name": service_name,
|
|
52
|
+
},
|
|
53
|
+
user_id=user_id,
|
|
54
|
+
session_id=session_id,
|
|
55
|
+
message_source="cli",
|
|
56
|
+
override_endpoint=service_url
|
|
57
|
+
)
|
|
58
|
+
for part in generate():
|
|
59
|
+
yield part
|
|
60
|
+
|
|
61
|
+
response_started = False
|
|
62
|
+
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
|
+
chat_history.append({"role": "AI", "content": vac_response})
|
|
74
|
+
response_started = False
|
|
75
|
+
print() # For new line after streaming ends
|
|
76
|
+
|
|
77
|
+
def headless_mode(service_name, user_input, chat_history=None):
|
|
78
|
+
chat_history = chat_history or []
|
|
79
|
+
chat_history.append({"role": "Human", "content": user_input})
|
|
80
|
+
service_url = get_service_url(service_name)
|
|
81
|
+
user_id = generate_user_id()
|
|
82
|
+
session_id = str(uuid.uuid4())
|
|
83
|
+
|
|
84
|
+
def stream_response():
|
|
85
|
+
generate = generate_proxy_stream(
|
|
86
|
+
send_to_qa,
|
|
87
|
+
user_input,
|
|
88
|
+
vector_name=service_name,
|
|
89
|
+
chat_history=chat_history,
|
|
90
|
+
generate_f_output=lambda x: x, # Replace with actual processing function
|
|
91
|
+
stream_wait_time=0.5,
|
|
92
|
+
stream_timeout=120,
|
|
93
|
+
message_author=user_id,
|
|
94
|
+
#TODO: populate these
|
|
95
|
+
image_url=None,
|
|
96
|
+
source_filters=None,
|
|
97
|
+
search_kwargs=None,
|
|
98
|
+
private_docs=None,
|
|
99
|
+
whole_document=False,
|
|
100
|
+
source_filters_and_or=False,
|
|
101
|
+
# system kwargs
|
|
102
|
+
configurable={
|
|
103
|
+
"vector_name": service_name,
|
|
104
|
+
},
|
|
105
|
+
user_id=user_id,
|
|
106
|
+
session_id=session_id,
|
|
107
|
+
message_source="cli",
|
|
108
|
+
override_endpoint=service_url
|
|
109
|
+
)
|
|
110
|
+
for part in generate():
|
|
111
|
+
yield part
|
|
112
|
+
|
|
113
|
+
print(f"VAC {service_name}: ", end='', flush=True)
|
|
114
|
+
for token in stream_response():
|
|
115
|
+
if isinstance(token, bytes):
|
|
116
|
+
token = token.decode('utf-8')
|
|
117
|
+
print(token, end='', flush=True)
|
|
118
|
+
|
|
119
|
+
chat_history.append({"role": "AI", "content": token})
|
|
120
|
+
print() # For new line after streaming ends
|
|
121
|
+
|
|
122
|
+
return chat_history
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def vac_command(args):
|
|
126
|
+
try:
|
|
127
|
+
service_url = get_service_url(args.service_name)
|
|
128
|
+
except ValueError as e:
|
|
129
|
+
print(f"ERROR: Could not start {args.service_name} proxy URL: {str(e)}")
|
|
130
|
+
return
|
|
131
|
+
print(f"== Starting VAC chat session with {args.service_name} via proxy URL: {service_url}")
|
|
132
|
+
if args.headless:
|
|
133
|
+
headless_mode(args.service_name, args.user_input, args.chat_history)
|
|
134
|
+
else:
|
|
135
|
+
stream_chat_session(args.service_name)
|
|
136
|
+
|
|
137
|
+
def setup_vac_subparser(subparsers):
|
|
138
|
+
"""
|
|
139
|
+
Sets up an argparse subparser for the 'vac' command.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
subparsers: The subparsers object from argparse.ArgumentParser().
|
|
143
|
+
"""
|
|
144
|
+
vac_parser = subparsers.add_parser('vac', help='Interact with deployed VAC services.')
|
|
145
|
+
vac_parser.add_argument('service_name', help='Name of the VAC service.')
|
|
146
|
+
vac_parser.add_argument('user_input', help='User input for the VAC service when in headless mode.', nargs='?', default=None)
|
|
147
|
+
vac_parser.add_argument('--headless', action='store_true', help='Run in headless mode.')
|
|
148
|
+
vac_parser.add_argument('--chat_history', help='Chat history for headless mode (as JSON string).', default=None)
|
|
149
|
+
vac_parser.set_defaults(func=vac_command)
|
sunholo/cli/cli.py
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import argparse
|
|
2
|
+
import logging
|
|
2
3
|
|
|
3
4
|
from .configs import setup_list_configs_subparser
|
|
4
5
|
from .deploy import setup_deploy_subparser
|
|
5
6
|
from .cli_init import setup_init_subparser
|
|
6
7
|
from .merge_texts import setup_merge_text_subparser
|
|
8
|
+
from .run_proxy import setup_proxy_subparser
|
|
9
|
+
from .chat_vac import setup_vac_subparser
|
|
7
10
|
|
|
11
|
+
from ..logging import log
|
|
8
12
|
|
|
9
13
|
def main(args=None):
|
|
10
14
|
"""
|
|
@@ -17,6 +21,8 @@ def main(args=None):
|
|
|
17
21
|
```
|
|
18
22
|
"""
|
|
19
23
|
parser = argparse.ArgumentParser(description="sunholo CLI tool for deploying GenAI VACs")
|
|
24
|
+
parser.add_argument('--debug', action='store_true', help='Enable debug output')
|
|
25
|
+
|
|
20
26
|
subparsers = parser.add_subparsers(title='commands',
|
|
21
27
|
description='Valid commands',
|
|
22
28
|
help='Commands',
|
|
@@ -31,9 +37,20 @@ def main(args=None):
|
|
|
31
37
|
setup_init_subparser(subparsers)
|
|
32
38
|
# merge-text command
|
|
33
39
|
setup_merge_text_subparser(subparsers)
|
|
40
|
+
# proxy command
|
|
41
|
+
setup_proxy_subparser(subparsers)
|
|
42
|
+
# vac command
|
|
43
|
+
setup_vac_subparser(subparsers)
|
|
34
44
|
|
|
35
45
|
args = parser.parse_args(args)
|
|
36
46
|
|
|
47
|
+
if args.debug:
|
|
48
|
+
log.setLevel(logging.INFO)
|
|
49
|
+
logging.getLogger().setLevel(logging.INFO)
|
|
50
|
+
else:
|
|
51
|
+
log.setLevel(logging.WARNING)
|
|
52
|
+
logging.getLogger().setLevel(logging.WARNING)
|
|
53
|
+
|
|
37
54
|
if hasattr(args, 'func'):
|
|
38
55
|
args.func(args)
|
|
39
56
|
else:
|
sunholo/cli/cli_init.py
CHANGED
|
@@ -6,28 +6,9 @@ def init_project(args):
|
|
|
6
6
|
"""
|
|
7
7
|
Initializes a new sunholo project with a basic configuration file and directory structure.
|
|
8
8
|
|
|
9
|
-
**Explanation:**
|
|
10
|
-
|
|
11
|
-
1. **Import Necessary Modules:**
|
|
12
|
-
- `os` for file system operations.
|
|
13
|
-
- `shutil` for copying files and directories.
|
|
14
|
-
- `log` from `sunholo.logging` for logging messages.
|
|
15
|
-
- `get_module_filepath` from `sunholo.utils.config` to get the absolute path of template files.
|
|
16
|
-
|
|
17
|
-
2. **`init_project` Function:**
|
|
18
|
-
- Takes an `args` object from argparse, containing the `project_name`.
|
|
19
|
-
- Creates the project directory using `os.makedirs`.
|
|
20
|
-
- Copies template files from the `templates/project` directory to the new project directory using `shutil.copy` and `shutil.copytree`.
|
|
21
|
-
- Logs informative messages about the initialization process.
|
|
22
|
-
|
|
23
|
-
3. **`setup_init_subparser` Function:**
|
|
24
|
-
- Sets up the `init` subcommand for the `sunholo` CLI.
|
|
25
|
-
- Adds an argument `project_name` to specify the name of the new project.
|
|
26
|
-
- Sets the `func` attribute to `init_project`, so the parser knows which function to call when the `init` command is used.
|
|
27
|
-
|
|
28
9
|
**Template Files (`templates/project`):**
|
|
29
10
|
|
|
30
|
-
|
|
11
|
+
A `templates/project` directory is within the `sunholo` package with the following template files in it:
|
|
31
12
|
|
|
32
13
|
* **`config/llm_config.yaml`:** A basic configuration file with placeholders for LLM settings, vector stores, etc.
|
|
33
14
|
* **`config/cloud_run_urls.json`:** A template for Cloud Run URLs.
|
|
@@ -37,14 +18,13 @@ You'll need to create a `templates/project` directory within your `sunholo` pack
|
|
|
37
18
|
|
|
38
19
|
**Usage:**
|
|
39
20
|
|
|
40
|
-
|
|
21
|
+
Users can initialize a new project using the following command:
|
|
41
22
|
|
|
42
23
|
```bash
|
|
43
24
|
sunholo init my_genai_project
|
|
44
25
|
```
|
|
45
26
|
|
|
46
27
|
This will create a new directory named `my_genai_project` with the template files, allowing users to start building their GenAI application.
|
|
47
|
-
|
|
48
28
|
"""
|
|
49
29
|
project_name = args.project_name
|
|
50
30
|
project_dir = os.path.join(os.getcwd(), project_name)
|
|
@@ -75,6 +55,6 @@ def setup_init_subparser(subparsers):
|
|
|
75
55
|
"""
|
|
76
56
|
Sets up an argparse subparser for the 'init' command.
|
|
77
57
|
"""
|
|
78
|
-
init_parser = subparsers.add_parser('init', help='Initializes a new
|
|
58
|
+
init_parser = subparsers.add_parser('init', help='Initializes a new Multivac project.')
|
|
79
59
|
init_parser.add_argument('project_name', help='The name of the new project.')
|
|
80
60
|
init_parser.set_defaults(func=init_project)
|
sunholo/cli/run_proxy.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import os
|
|
3
|
+
import signal
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
PROXY_TRACKER_FILE = '.vac_proxy_tracker.json'
|
|
7
|
+
DEFAULT_PORT = 8080
|
|
8
|
+
|
|
9
|
+
def create_hyperlink(url, text):
|
|
10
|
+
"""
|
|
11
|
+
Creates a hyperlink for the console.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
url (str): The URL for the hyperlink.
|
|
15
|
+
text (str): The text to display for the hyperlink.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
str: The formatted hyperlink.
|
|
19
|
+
"""
|
|
20
|
+
return f"\033]8;;{url}\033\\{text}\033]8;;\033\\"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_next_available_port(proxies, default_port):
|
|
24
|
+
"""
|
|
25
|
+
Get the next available port starting from the default port.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
proxies (dict): Current proxies with their assigned ports.
|
|
29
|
+
default_port (int): Default starting port.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
int: The next available port.
|
|
33
|
+
"""
|
|
34
|
+
used_ports = {info["port"] for info in proxies.values()}
|
|
35
|
+
port = default_port
|
|
36
|
+
while port in used_ports:
|
|
37
|
+
port += 1
|
|
38
|
+
return port
|
|
39
|
+
|
|
40
|
+
def check_gcloud():
|
|
41
|
+
"""
|
|
42
|
+
Checks if gcloud is installed and authenticated.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
bool: True if gcloud is installed and authenticated, False otherwise.
|
|
46
|
+
"""
|
|
47
|
+
try:
|
|
48
|
+
# Check if gcloud is installed
|
|
49
|
+
result = subprocess.run(["gcloud", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
50
|
+
if result.returncode != 0:
|
|
51
|
+
print("ERROR: gcloud is not installed or not found in PATH.")
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
# Check if gcloud is authenticated
|
|
55
|
+
result = subprocess.run(["gcloud", "auth", "list"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
56
|
+
if result.returncode != 0 or "ACTIVE" not in result.stdout.decode():
|
|
57
|
+
print("ERROR: gcloud is not authenticated. Please run 'gcloud auth login'.")
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
print("gcloud is installed and authenticated.")
|
|
61
|
+
return True
|
|
62
|
+
except Exception as e:
|
|
63
|
+
print(f"ERROR: An unexpected error occurred: {e}")
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
def is_process_running(pid):
|
|
67
|
+
try:
|
|
68
|
+
os.kill(pid, 0)
|
|
69
|
+
return True
|
|
70
|
+
except OSError:
|
|
71
|
+
print("WARNING: VAC Proxy lost connection")
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
def load_proxies():
|
|
75
|
+
if os.path.exists(PROXY_TRACKER_FILE):
|
|
76
|
+
with open(PROXY_TRACKER_FILE, 'r') as file:
|
|
77
|
+
return json.load(file)
|
|
78
|
+
return {}
|
|
79
|
+
|
|
80
|
+
def clean_proxy_list():
|
|
81
|
+
proxies = load_proxies()
|
|
82
|
+
updated_proxies = {k: v for k, v in proxies.items() if is_process_running(v["pid"])}
|
|
83
|
+
if len(proxies) != len(updated_proxies):
|
|
84
|
+
save_proxies(updated_proxies)
|
|
85
|
+
|
|
86
|
+
return updated_proxies
|
|
87
|
+
|
|
88
|
+
def save_proxies(proxies):
|
|
89
|
+
with open(PROXY_TRACKER_FILE, 'w') as file:
|
|
90
|
+
json.dump(proxies, file, indent=4)
|
|
91
|
+
|
|
92
|
+
def start_proxy(service_name, region, project, port=None):
|
|
93
|
+
"""
|
|
94
|
+
Starts the gcloud proxy to the Cloud Run service and stores the PID.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
service_name (str): Name of the Cloud Run service.
|
|
98
|
+
region (str): Region of the Cloud Run service.
|
|
99
|
+
project (str): GCP project of the Cloud Run service.
|
|
100
|
+
port (int, optional): Port to run the proxy on. If not provided, auto-assigns the next available port.
|
|
101
|
+
"""
|
|
102
|
+
proxies = clean_proxy_list()
|
|
103
|
+
|
|
104
|
+
if service_name in proxies:
|
|
105
|
+
print(f"Proxy for service {service_name} is already running on port {proxies[service_name]['port']}.")
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
if not port:
|
|
109
|
+
port = get_next_available_port(proxies, DEFAULT_PORT)
|
|
110
|
+
|
|
111
|
+
command = [
|
|
112
|
+
"gcloud", "run", "services", "proxy", service_name,
|
|
113
|
+
"--region", region,
|
|
114
|
+
"--project", project,
|
|
115
|
+
"--port", str(port)
|
|
116
|
+
]
|
|
117
|
+
with open(os.devnull, 'w') as devnull:
|
|
118
|
+
process = subprocess.Popen(command, stdout=devnull, stderr=devnull, preexec_fn=os.setpgrp)
|
|
119
|
+
|
|
120
|
+
proxies[service_name] = {
|
|
121
|
+
"pid": process.pid,
|
|
122
|
+
"port": port
|
|
123
|
+
}
|
|
124
|
+
save_proxies(proxies)
|
|
125
|
+
|
|
126
|
+
print(f"Proxy for {service_name} setup complete on port {port}")
|
|
127
|
+
list_proxies()
|
|
128
|
+
|
|
129
|
+
return f"http://127.0.0.1:{port}"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def stop_proxy(service_name):
|
|
133
|
+
"""
|
|
134
|
+
Stops the gcloud proxy to the Cloud Run service using the stored PID.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
service_name (str): Name of the Cloud Run service.
|
|
138
|
+
"""
|
|
139
|
+
proxies = clean_proxy_list()
|
|
140
|
+
|
|
141
|
+
if service_name not in proxies:
|
|
142
|
+
print(f"No proxy found for service: {service_name}")
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
pid = proxies[service_name]["pid"]
|
|
146
|
+
try:
|
|
147
|
+
os.kill(pid, signal.SIGTERM)
|
|
148
|
+
del proxies[service_name]
|
|
149
|
+
save_proxies(proxies)
|
|
150
|
+
print(f"Proxy for {service_name} stopped.")
|
|
151
|
+
except ProcessLookupError:
|
|
152
|
+
print(f"No process found with PID: {pid}")
|
|
153
|
+
except Exception as e:
|
|
154
|
+
print(f"Error stopping proxy for {service_name}: {e}")
|
|
155
|
+
|
|
156
|
+
list_proxies()
|
|
157
|
+
|
|
158
|
+
def list_proxies():
|
|
159
|
+
"""
|
|
160
|
+
Lists all running proxies.
|
|
161
|
+
"""
|
|
162
|
+
proxies = clean_proxy_list()
|
|
163
|
+
if not proxies:
|
|
164
|
+
print("No proxies currently running.")
|
|
165
|
+
else:
|
|
166
|
+
print("=== VAC Proxies Running ===")
|
|
167
|
+
for service_name, info in proxies.items():
|
|
168
|
+
url = f"http://127.0.0.1:{info['port']}"
|
|
169
|
+
hyperlink = create_hyperlink(url, url)
|
|
170
|
+
print(f"- VAC: {service_name} Port: {info['port']} PID: {info['pid']} URL: {hyperlink}")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def setup_proxy_subparser(subparsers):
|
|
174
|
+
"""
|
|
175
|
+
Sets up an argparse subparser for the 'proxy' command.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
subparsers: The subparsers object from argparse.ArgumentParser().
|
|
179
|
+
"""
|
|
180
|
+
proxy_parser = subparsers.add_parser('proxy', help='Set up or stop a proxy to the Cloud Run service.')
|
|
181
|
+
proxy_subparsers = proxy_parser.add_subparsers(dest='proxy_command', required=True)
|
|
182
|
+
|
|
183
|
+
start_parser = proxy_subparsers.add_parser('start', help='Start the proxy to the Cloud Run service.')
|
|
184
|
+
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
|
+
start_parser.add_argument('--port', type=int, help='Port to run the proxy on. Auto-assigns if not provided.')
|
|
188
|
+
start_parser.set_defaults(func=lambda args: start_proxy(args.service_name, args.region, args.project, args.port))
|
|
189
|
+
|
|
190
|
+
stop_parser = proxy_subparsers.add_parser('stop', help='Stop the proxy to the Cloud Run service.')
|
|
191
|
+
stop_parser.add_argument('service_name', help='Name of the Cloud Run service.')
|
|
192
|
+
stop_parser.set_defaults(func=lambda args: stop_proxy(args.service_name))
|
|
193
|
+
|
|
194
|
+
list_parser = proxy_subparsers.add_parser('list', help='List all running proxies.')
|
|
195
|
+
list_parser.set_defaults(func=lambda args: list_proxies())
|
|
196
|
+
|
|
197
|
+
|
sunholo/components/retriever.py
CHANGED
|
@@ -48,7 +48,8 @@ def pick_retriever(vector_name, embeddings=None):
|
|
|
48
48
|
log.info(f"Found vectorstore {vectorstore}")
|
|
49
49
|
if embeddings is None:
|
|
50
50
|
embeddings = get_embeddings(vector_name)
|
|
51
|
-
|
|
51
|
+
read_only = value.get('readonly')
|
|
52
|
+
vectorstore = pick_vectorstore(vectorstore, vector_name=vector_name, embeddings=embeddings, read_only=read_only)
|
|
52
53
|
k_override = value.get('k', 3)
|
|
53
54
|
vs_retriever = vectorstore.as_retriever(search_kwargs=dict(k=k_override))
|
|
54
55
|
retriever_list.append(vs_retriever)
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import os
|
|
15
15
|
from ..logging import log
|
|
16
16
|
|
|
17
|
-
def pick_vectorstore(vs_str, vector_name, embeddings):
|
|
17
|
+
def pick_vectorstore(vs_str, vector_name, embeddings, read_only=None):
|
|
18
18
|
log.debug('Picking vectorstore')
|
|
19
19
|
|
|
20
20
|
if vs_str == 'supabase':
|
|
@@ -23,8 +23,9 @@ def pick_vectorstore(vs_str, vector_name, embeddings):
|
|
|
23
23
|
|
|
24
24
|
from ..database.database import setup_supabase
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
if not read_only:
|
|
27
|
+
log.debug(f"Initiating Supabase store: {vector_name}")
|
|
28
|
+
setup_supabase(vector_name)
|
|
28
29
|
|
|
29
30
|
# init embedding and vector store
|
|
30
31
|
supabase_url = os.getenv('SUPABASE_URL')
|
|
@@ -70,10 +71,14 @@ def pick_vectorstore(vs_str, vector_name, embeddings):
|
|
|
70
71
|
elif vs_str == 'alloydb':
|
|
71
72
|
from langchain_google_alloydb_pg import AlloyDBVectorStore
|
|
72
73
|
from ..database.alloydb import create_alloydb_table, create_alloydb_engine
|
|
74
|
+
from ..database.database import get_vector_size
|
|
73
75
|
|
|
76
|
+
vector_size = get_vector_size(vector_name)
|
|
74
77
|
engine = create_alloydb_engine(vector_name)
|
|
75
78
|
|
|
76
|
-
table_name =
|
|
79
|
+
table_name = f"{vector_name}_vectorstore_{vector_size}"
|
|
80
|
+
if not read_only:
|
|
81
|
+
table_name = create_alloydb_table(vector_name, engine)
|
|
77
82
|
|
|
78
83
|
log.info(f"Chose AlloyDB with table name {table_name}")
|
|
79
84
|
vectorstore = AlloyDBVectorStore.create_sync(
|
|
@@ -106,21 +111,24 @@ def pick_vectorstore(vs_str, vector_name, embeddings):
|
|
|
106
111
|
try:
|
|
107
112
|
table = db.open_table(vector_name)
|
|
108
113
|
except FileNotFoundError as err:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
114
|
+
if not read_only:
|
|
115
|
+
log.info(f"{err} - Could not open table for {vector_name} - creating new table")
|
|
116
|
+
init = f"Creating new table for {vector_name}"
|
|
117
|
+
table = db.create_table(
|
|
118
|
+
vector_name,
|
|
119
|
+
data=[
|
|
120
|
+
{
|
|
121
|
+
"vector": embeddings.embed_query(init),
|
|
122
|
+
"text": init,
|
|
123
|
+
"id": "1",
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
mode="overwrite",
|
|
127
|
+
)
|
|
128
|
+
else:
|
|
129
|
+
log.info(f"{err} - Could not create table for {vector_name} as read_only=True")
|
|
130
|
+
|
|
131
|
+
log.info(f"Initiating LanceDB object for {vector_name} using {LANCEDB_BUCKET}")
|
|
124
132
|
vectorstore = LanceDB(
|
|
125
133
|
connection=table,
|
|
126
134
|
embedding=embeddings,
|
sunholo/database/alloydb.py
CHANGED
|
@@ -178,7 +178,7 @@ def create_alloydb_table(vector_name, engine, type = "vectorstore", alloydb_conf
|
|
|
178
178
|
try:
|
|
179
179
|
if type == "vectorstore":
|
|
180
180
|
from .database import get_vector_size
|
|
181
|
-
vector_size = get_vector_size(vector_name
|
|
181
|
+
vector_size = get_vector_size(vector_name)
|
|
182
182
|
table_name = f"{vector_name}_{type}_{vector_size}"
|
|
183
183
|
if table_name in alloydb_table_cache:
|
|
184
184
|
log.info(f"AlloyDB Table '{table_name}' exists in cache, skipping creation.")
|
sunholo/logging.py
CHANGED
|
@@ -228,11 +228,16 @@ def setup_logging(logger_name=None, log_level=logging.INFO, project_id=None):
|
|
|
228
228
|
project_id = get_gcp_project()
|
|
229
229
|
# Instantiate the GoogleCloudLogging class
|
|
230
230
|
gc_logger = GoogleCloudLogging(project_id, log_level=log_level, logger_name=logger_name)
|
|
231
|
+
# Setup logging and return the logger instance
|
|
232
|
+
return gc_logger.setup_logging()
|
|
231
233
|
else:
|
|
232
|
-
|
|
234
|
+
if not logging.getLogger().hasHandlers():
|
|
235
|
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
236
|
+
log = logging.getLogger(logger_name)
|
|
237
|
+
|
|
238
|
+
return log
|
|
233
239
|
|
|
234
|
-
|
|
235
|
-
return gc_logger.setup_logging()
|
|
240
|
+
|
|
236
241
|
|
|
237
242
|
|
|
238
243
|
|
sunholo/utils/config.py
CHANGED
|
@@ -191,7 +191,6 @@ def load_config_key(key: str, vector_name: str, kind: str=None):
|
|
|
191
191
|
assert isinstance(vector_name, str), f"vector_name must be a string, got a {type(vector_name)}"
|
|
192
192
|
|
|
193
193
|
configs_by_kind = load_all_configs()
|
|
194
|
-
log.info(f"configs by kind: {configs_by_kind}")
|
|
195
194
|
|
|
196
195
|
if kind:
|
|
197
196
|
log.info(f"Got kind: {kind} - applying to configs")
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
import hashlib
|
|
3
|
+
import platform
|
|
4
|
+
import socket
|
|
5
|
+
|
|
6
|
+
def generate_user_id():
|
|
7
|
+
data = f"{socket.gethostname()}-{platform.platform()}-{platform.processor()}"
|
|
8
|
+
hashed_id = hashlib.sha256(data.encode('utf-8')).hexdigest()
|
|
9
|
+
|
|
10
|
+
return str(uuid.uuid5(uuid.NAMESPACE_DNS, hashed_id))
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.60.
|
|
3
|
+
Version: 0.60.2
|
|
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.2.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
sunholo/__init__.py,sha256=KfqJV0T-2WCIlabNxeX3CvLS-bBGCl9n5aWy091lr2M,841
|
|
2
|
-
sunholo/logging.py,sha256=
|
|
2
|
+
sunholo/logging.py,sha256=bwiNlbYNzC7FdsrbFOvnvmgfKaRjhA5T73gmYJaUd8I,11589
|
|
3
3
|
sunholo/agents/__init__.py,sha256=CnlbVohPt-Doth9PyROSlN3P8xMV9j9yS19YE-wCS90,341
|
|
4
4
|
sunholo/agents/chat_history.py,sha256=PbwYmw1TwzI8H-cwQIGgHZ6UIr2Qb-JWow0RG3ayLM8,5195
|
|
5
|
-
sunholo/agents/dispatch_to_qa.py,sha256=
|
|
5
|
+
sunholo/agents/dispatch_to_qa.py,sha256=h5qbcPqJ5JGa21T8Z5is7jbn4eG3P4xULLj_X25q3WM,8208
|
|
6
6
|
sunholo/agents/langserve.py,sha256=FdhQjorAY2bMn2rpuabNT6bU3uqSKWrl8DjpH3L_V7k,4375
|
|
7
7
|
sunholo/agents/pubsub.py,sha256=5hbbhbBGyVWRpt2sAGC5FEheYH1mCCwVUhZEB1S7vGg,1337
|
|
8
|
-
sunholo/agents/route.py,sha256=
|
|
8
|
+
sunholo/agents/route.py,sha256=0klBifx-QtMGsjq8HB04s9Bytm0nFXPYaWKeyt-S9S4,2356
|
|
9
9
|
sunholo/agents/special_commands.py,sha256=PI4ADgFQvPDCeCpOeWIrD4bD432NYFeVcBBnkqTBWi8,6457
|
|
10
10
|
sunholo/agents/test_chat_history.py,sha256=vPbPu0xREEs4J4X_zJKBY1f19Vy5yV05_CKfUUQqfFg,3923
|
|
11
11
|
sunholo/agents/fastapi/__init__.py,sha256=S_pj4_bTUmDGoq_exaREHlOKThi0zTuGT0VZY0YfODQ,88
|
|
@@ -13,7 +13,7 @@ sunholo/agents/fastapi/base.py,sha256=clk76cHbUAvU0OYJrRfCWX_5f0ACbhDsIzYBhI3wyo
|
|
|
13
13
|
sunholo/agents/fastapi/qna_routes.py,sha256=DgK4Btu5XriOC1JaRQ4G_nWEjJfnQ0J5pyLanF6eF1g,3857
|
|
14
14
|
sunholo/agents/flask/__init__.py,sha256=uqfHNw2Ru3EJ4dJEcbp86h_lkquBQPMxZbjhV_xe3rs,72
|
|
15
15
|
sunholo/agents/flask/base.py,sha256=RUGWBYWeV60FatYF5sMRrxD-INU97Vodsi6JaB6i93s,763
|
|
16
|
-
sunholo/agents/flask/qna_routes.py,sha256=
|
|
16
|
+
sunholo/agents/flask/qna_routes.py,sha256=Pr2dxM7GKhXNKv6t7_J578BN6oMETAYFe6ydEm4D90g,8620
|
|
17
17
|
sunholo/archive/__init__.py,sha256=qNHWm5rGPVOlxZBZCpA1wTYPbalizRT7f8X4rs2t290,31
|
|
18
18
|
sunholo/archive/archive.py,sha256=C-UhG5x-XtZ8VheQp92IYJqgD0V3NFQjniqlit94t18,1197
|
|
19
19
|
sunholo/auth/__init__.py,sha256=4owDjSaWYkbTlPK47UHTOC0gCWbZsqn4ZIEw5NWZTlg,28
|
|
@@ -32,18 +32,20 @@ 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/
|
|
36
|
-
sunholo/cli/
|
|
35
|
+
sunholo/cli/chat_vac.py,sha256=8UHiEhnnApZBPfWdb9_BQoGuKsRCN84p4dKsPKzF_sU,5433
|
|
36
|
+
sunholo/cli/cli.py,sha256=2sPbnHsAhvmga-gWUXoVl0nbH1qaYj85kwPwxG0DfGo,1904
|
|
37
|
+
sunholo/cli/cli_init.py,sha256=JMZ9AX2cPDZ-_mv3adiv2ToFVNyRPtjk9Biszl1kiR0,2358
|
|
37
38
|
sunholo/cli/configs.py,sha256=QUM9DvKOdZmEQRM5uI3Nh887T0YDiSMr7O240zTLqws,4546
|
|
38
39
|
sunholo/cli/deploy.py,sha256=zxdwUsRTRMC8U5vyRv0JiKBLFn84Ug_Tc88-_h9hJSs,1609
|
|
39
40
|
sunholo/cli/merge_texts.py,sha256=U9vdMwKmcPoc6iPOWX5MKSxn49dNGbNzVLw8ui5PhEU,1823
|
|
41
|
+
sunholo/cli/run_proxy.py,sha256=JOLenOmsd7VLePofeYqZUyJyQ9Mlfq4nnVa5cbEDXEY,6578
|
|
40
42
|
sunholo/components/__init__.py,sha256=RJGNEihwvRIiDScKis04RHJv4yZGI1UpXlOmuCptNZI,208
|
|
41
43
|
sunholo/components/llm.py,sha256=T4we3tGmqUj4tPwxQr9M6AXv_BALqZV_dRSvINan-oU,10374
|
|
42
44
|
sunholo/components/prompt.py,sha256=eZSghXkIlRzXiSrzgkG7e5ytUYq6R6LV-qjHU8jStig,6353
|
|
43
|
-
sunholo/components/retriever.py,sha256=
|
|
44
|
-
sunholo/components/vectorstore.py,sha256=
|
|
45
|
+
sunholo/components/retriever.py,sha256=_Lyt9RIgb2PD-rhV6oKAadiUs3ukT5uAYGW197tEskw,3755
|
|
46
|
+
sunholo/components/vectorstore.py,sha256=dzspqOBtuxSjCFxem5_50sqwUUjbZ4oBYERtCwxZR6E,5619
|
|
45
47
|
sunholo/database/__init__.py,sha256=Zz0Shcq-CtStf9rJGIYB_Ybzb8rY_Q9mfSj-nviM490,241
|
|
46
|
-
sunholo/database/alloydb.py,sha256=
|
|
48
|
+
sunholo/database/alloydb.py,sha256=J4VzrW2ChIYyqtccUBCtoN-vClfn-iipEDJpxN7GJkY,14820
|
|
47
49
|
sunholo/database/database.py,sha256=doY05kG8BZBLL-arh4hq5ef1ouWOtGHqdsDc6M2YHgk,7345
|
|
48
50
|
sunholo/database/lancedb.py,sha256=WSrbY5mgyeXx6i7UBiz4YQ_i5UIYVYFo-vPGO72bQKY,707
|
|
49
51
|
sunholo/database/static_dbs.py,sha256=aOyU3AJ-Dzz3qSNjbuN2293cfYw5PhkcQuQxdwPMJ4w,435
|
|
@@ -86,15 +88,16 @@ sunholo/summarise/__init__.py,sha256=MZk3dblUMODcPb1crq4v-Z508NrFIpkSWNf9FIO8BcU
|
|
|
86
88
|
sunholo/summarise/summarise.py,sha256=C3HhjepTjUhUC8FLk4jMQIBvq1BcORniwuTFHjPVhVo,3784
|
|
87
89
|
sunholo/utils/__init__.py,sha256=G11nN_6ATjxpuMfG_BvcUr9UU8onPIgkpTK6CjOcbr8,48
|
|
88
90
|
sunholo/utils/big_context.py,sha256=gJIP7_ZL-YSLhOMq8jmFTMqH1wq8eB1NK7oKPeZAq2s,5578
|
|
89
|
-
sunholo/utils/config.py,sha256=
|
|
91
|
+
sunholo/utils/config.py,sha256=6n7foIi7kk6EKaS9MOAf0911oI7grkkgiTCya9k3LmI,8448
|
|
90
92
|
sunholo/utils/config_schema.py,sha256=Wv-ncitzljOhgbDaq9qnFqH5LCuxNv59dTGDWgd1qdk,4189
|
|
91
93
|
sunholo/utils/gcp.py,sha256=B2G1YKjeD7X9dqO86Jrp2vPuFwZ223Xl5Tg09Ndw-oc,5760
|
|
92
94
|
sunholo/utils/parsers.py,sha256=OrHmASqIbI45atVOhiGodgLvnfrzkvVzyHnSvAXD89I,3841
|
|
95
|
+
sunholo/utils/user_ids.py,sha256=SQd5_H7FE7vcTZp9AQuQDWBXd4FEEd7TeVMQe1H4Ny8,292
|
|
93
96
|
sunholo/vertex/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
97
|
sunholo/vertex/init_vertex.py,sha256=JDMUaBRdednzbKF-5p33qqLit2LMsvgvWW-NRz0AqO0,1801
|
|
95
|
-
sunholo-0.60.
|
|
96
|
-
sunholo-0.60.
|
|
97
|
-
sunholo-0.60.
|
|
98
|
-
sunholo-0.60.
|
|
99
|
-
sunholo-0.60.
|
|
100
|
-
sunholo-0.60.
|
|
98
|
+
sunholo-0.60.2.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
|
|
99
|
+
sunholo-0.60.2.dist-info/METADATA,sha256=7tTs86ZrZTMa9ZJ-K1X18LX6ixQDsvMwqpjXe4gwcuE,7903
|
|
100
|
+
sunholo-0.60.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
101
|
+
sunholo-0.60.2.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
|
|
102
|
+
sunholo-0.60.2.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
|
|
103
|
+
sunholo-0.60.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|