sunholo 0.71.15__py3-none-any.whl → 0.71.17__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/flask/qna_routes.py +1 -2
- sunholo/cli/chat_vac.py +33 -2
- sunholo/gcs/add_file.py +46 -18
- sunholo/vertex/extensions_class.py +36 -3
- {sunholo-0.71.15.dist-info → sunholo-0.71.17.dist-info}/METADATA +2 -2
- {sunholo-0.71.15.dist-info → sunholo-0.71.17.dist-info}/RECORD +10 -10
- {sunholo-0.71.15.dist-info → sunholo-0.71.17.dist-info}/LICENSE.txt +0 -0
- {sunholo-0.71.15.dist-info → sunholo-0.71.17.dist-info}/WHEEL +0 -0
- {sunholo-0.71.15.dist-info → sunholo-0.71.17.dist-info}/entry_points.txt +0 -0
- {sunholo-0.71.15.dist-info → sunholo-0.71.17.dist-info}/top_level.txt +0 -0
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
|
|
16
16
|
import json
|
|
17
17
|
import traceback
|
|
18
|
-
import datetime
|
|
19
18
|
import uuid
|
|
20
19
|
|
|
21
20
|
from ...agents import extract_chat_history, handle_special_commands
|
|
@@ -142,7 +141,7 @@ def register_qna_routes(app, stream_interpreter, vac_interpreter):
|
|
|
142
141
|
name="start_streaming_chat",
|
|
143
142
|
metadata=vac_config,
|
|
144
143
|
input = all_input,
|
|
145
|
-
completion_start_time=datetime.
|
|
144
|
+
completion_start_time=datetime.now(),
|
|
146
145
|
model=vac_config.get("model") or vac_config.get("llm")
|
|
147
146
|
)
|
|
148
147
|
|
sunholo/cli/chat_vac.py
CHANGED
|
@@ -5,9 +5,11 @@ from ..utils.config import load_config_key
|
|
|
5
5
|
from ..utils.api_key import has_multivac_api_key
|
|
6
6
|
from ..logging import log
|
|
7
7
|
from ..qna.parsers import parse_output
|
|
8
|
+
from ..gcs.add_file import add_file_to_gcs
|
|
8
9
|
from .run_proxy import clean_proxy_list, start_proxy, stop_proxy
|
|
9
10
|
|
|
10
11
|
import uuid
|
|
12
|
+
import os
|
|
11
13
|
import sys
|
|
12
14
|
import subprocess
|
|
13
15
|
import json
|
|
@@ -44,6 +46,17 @@ def get_service_url(vac_name, project, region, no_config=False):
|
|
|
44
46
|
|
|
45
47
|
return url
|
|
46
48
|
|
|
49
|
+
def handle_file_upload(file, vector_name):
|
|
50
|
+
if not Path(file).is_file():
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
file_url = add_file_to_gcs(file,
|
|
54
|
+
vector_name=vector_name,
|
|
55
|
+
metadata={"type": "cli"},
|
|
56
|
+
bucket_filepath=f"{vector_name}/uploads/{os.path.basename(file)}")
|
|
57
|
+
|
|
58
|
+
return file_url
|
|
59
|
+
|
|
47
60
|
def stream_chat_session(service_url, service_name, stream=True):
|
|
48
61
|
|
|
49
62
|
user_id = generate_user_id()
|
|
@@ -62,7 +75,25 @@ def stream_chat_session(service_url, service_name, stream=True):
|
|
|
62
75
|
|
|
63
76
|
if special_reply:
|
|
64
77
|
console.print(f"[bold yellow]{service_name}:[/bold yellow] {special_reply}", end='\n')
|
|
65
|
-
continue
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
if user_input.lower().startswith("upload"):
|
|
81
|
+
file_path = user_input.split(" ", 1)[1] if " " in user_input else None
|
|
82
|
+
if not file_path:
|
|
83
|
+
console.print("[bold red]Please provide a valid file path.[/bold red]")
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
file_reply = handle_file_upload(file_path, vector_name=service_name)
|
|
88
|
+
if not file_reply:
|
|
89
|
+
console.print("[bold red]Invalid file upload[/bold red]")
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
console.print(f"[bold yellow]{service_name}:[/bold yellow] Uploaded {file_path} to {file_reply}", end='\n')
|
|
93
|
+
|
|
94
|
+
except FileNotFoundError:
|
|
95
|
+
console.print("[bold red]File not found. Please check the path and try again.[/bold red]")
|
|
96
|
+
continue
|
|
66
97
|
|
|
67
98
|
if not stream:
|
|
68
99
|
vac_response = send_to_qa(user_input,
|
|
@@ -70,7 +101,7 @@ def stream_chat_session(service_url, service_name, stream=True):
|
|
|
70
101
|
chat_history=chat_history,
|
|
71
102
|
message_author=user_id,
|
|
72
103
|
#TODO: populate these
|
|
73
|
-
image_url=
|
|
104
|
+
image_url=file_reply,
|
|
74
105
|
source_filters=None,
|
|
75
106
|
search_kwargs=None,
|
|
76
107
|
private_docs=None,
|
sunholo/gcs/add_file.py
CHANGED
|
@@ -25,25 +25,63 @@ from ..logging import log
|
|
|
25
25
|
from ..utils.config import load_config_key
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def handle_base64_image(base64_data, vector_name):
|
|
28
|
+
def handle_base64_image(base64_data: str, vector_name: str, extension: str):
|
|
29
|
+
"""
|
|
30
|
+
Handle base64 image data, decode it, save it as a file, upload it to GCS, and return the image URI and MIME type.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
base64_data (str): The base64 encoded image data.
|
|
34
|
+
vector_name (str): The vector name for the GCS path.
|
|
35
|
+
extension (str): The file extension of the image (e.g., ".jpg", ".png").
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Tuple[str, str]: The URI of the uploaded image and the MIME type.
|
|
39
|
+
"""
|
|
29
40
|
model = load_config_key("llm", vector_name, "vacConfig")
|
|
30
|
-
if model.startswith("openai"):
|
|
31
|
-
return base64_data, base64_data.split(",",1)
|
|
32
|
-
|
|
41
|
+
if model.startswith("openai"): # pass it to gpt directly
|
|
42
|
+
return base64_data, base64_data.split(",", 1)
|
|
43
|
+
|
|
33
44
|
try:
|
|
34
45
|
header, encoded = base64_data.split(",", 1)
|
|
35
46
|
data = base64.b64decode(encoded)
|
|
36
47
|
|
|
37
|
-
filename = f"{uuid.uuid4()}
|
|
48
|
+
filename = f"{uuid.uuid4()}{extension}"
|
|
38
49
|
with open(filename, "wb") as f:
|
|
39
50
|
f.write(data)
|
|
40
51
|
|
|
41
52
|
image_uri = add_file_to_gcs(filename, vector_name)
|
|
42
53
|
os.remove(filename) # Clean up the saved file
|
|
43
|
-
|
|
54
|
+
|
|
55
|
+
# Determine MIME type based on extension
|
|
56
|
+
mime_type = {
|
|
57
|
+
".jpg": "image/jpeg",
|
|
58
|
+
".jpeg": "image/jpeg",
|
|
59
|
+
".png": "image/png",
|
|
60
|
+
".gif": "image/gif",
|
|
61
|
+
".bmp": "image/bmp",
|
|
62
|
+
".tiff": "image/tiff"
|
|
63
|
+
}.get(extension.lower(), "application/octet-stream") # Default MIME type if unknown
|
|
64
|
+
|
|
65
|
+
return image_uri, mime_type
|
|
44
66
|
except Exception as e:
|
|
45
67
|
raise Exception(f'Base64 image upload failed: {str(e)}')
|
|
46
68
|
|
|
69
|
+
|
|
70
|
+
def resolve_bucket(vector_name):
|
|
71
|
+
bucket_config = load_config_key("upload", vector_name, "vacConfig")
|
|
72
|
+
if bucket_config:
|
|
73
|
+
if bucket_config.get("buckets"):
|
|
74
|
+
bucket_name = bucket_config.get("buckets").get("all")
|
|
75
|
+
else:
|
|
76
|
+
bucket_name = os.getenv('GCS_BUCKET')
|
|
77
|
+
if not bucket_name:
|
|
78
|
+
raise ValueError("No bucket found to upload to: GCS_BUCKET returned None")
|
|
79
|
+
|
|
80
|
+
if bucket_name.startswith("gs://"):
|
|
81
|
+
bucket_name = bucket_name.removeprefix("gs://")
|
|
82
|
+
|
|
83
|
+
return bucket_name
|
|
84
|
+
|
|
47
85
|
def add_file_to_gcs(filename: str, vector_name:str, bucket_name: str=None, metadata:dict=None, bucket_filepath:str=None):
|
|
48
86
|
|
|
49
87
|
if not storage:
|
|
@@ -55,18 +93,8 @@ def add_file_to_gcs(filename: str, vector_name:str, bucket_name: str=None, metad
|
|
|
55
93
|
log.error(f"Error creating storage client: {str(err)}")
|
|
56
94
|
return None
|
|
57
95
|
|
|
58
|
-
if bucket_name
|
|
59
|
-
|
|
60
|
-
if bucket_config:
|
|
61
|
-
if bucket_config.get("buckets"):
|
|
62
|
-
bucket_name = bucket_config.get("buckets").get("all")
|
|
63
|
-
|
|
64
|
-
bucket_name = bucket_name if bucket_name else os.getenv('GCS_BUCKET', None)
|
|
65
|
-
if bucket_name is None:
|
|
66
|
-
raise ValueError("No bucket found to upload to: GCS_BUCKET returned None")
|
|
67
|
-
|
|
68
|
-
if bucket_name.startswith("gs://"):
|
|
69
|
-
bucket_name = bucket_name.removeprefix("gs://")
|
|
96
|
+
if not bucket_name:
|
|
97
|
+
bucket_name = resolve_bucket(vector_name)
|
|
70
98
|
|
|
71
99
|
bucket = storage_client.get_bucket(bucket_name)
|
|
72
100
|
now = datetime.datetime.now()
|
|
@@ -136,7 +136,11 @@ class VertexAIExtensions:
|
|
|
136
136
|
|
|
137
137
|
return response
|
|
138
138
|
|
|
139
|
-
def execute_code_extension(self,
|
|
139
|
+
def execute_code_extension(self,
|
|
140
|
+
query: str,
|
|
141
|
+
filenames: list[str] = None,
|
|
142
|
+
gcs_files: list[str] = None,
|
|
143
|
+
bucket_name: str = None):
|
|
140
144
|
if filenames and gcs_files:
|
|
141
145
|
raise ValueError("Can't specify both filenames and gcs_files")
|
|
142
146
|
|
|
@@ -149,7 +153,19 @@ class VertexAIExtensions:
|
|
|
149
153
|
break
|
|
150
154
|
|
|
151
155
|
if not code_interpreter_exists:
|
|
152
|
-
|
|
156
|
+
if bucket_name:
|
|
157
|
+
runtime_config = {
|
|
158
|
+
"codeInterpreterRuntimeConfig": {
|
|
159
|
+
"fileInputGcsBucket": f"{bucket_name}/extensions/input/",
|
|
160
|
+
"fileOutputGcsBucket": f"{bucket_name}/extensions/output/",
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else:
|
|
164
|
+
runtime_config = {}
|
|
165
|
+
|
|
166
|
+
extension_code_interpreter = extensions.Extension.from_hub("code_interpreter", runtime_config=runtime_config)
|
|
167
|
+
|
|
168
|
+
# This field is only applicable when `file_output_gcs_bucket` is specified in `Extension.CodeInterpreterRuntimeConfig`.
|
|
153
169
|
|
|
154
170
|
operation_params = {"query": query}
|
|
155
171
|
|
|
@@ -166,7 +182,7 @@ class VertexAIExtensions:
|
|
|
166
182
|
|
|
167
183
|
if gcs_files:
|
|
168
184
|
operation_params["file_gcs_uris"] = gcs_files
|
|
169
|
-
|
|
185
|
+
log.info("Executing code interpreter")
|
|
170
186
|
response = extension_code_interpreter.execute(
|
|
171
187
|
operation_id="generate_and_execute",
|
|
172
188
|
operation_params=operation_params)
|
|
@@ -175,7 +191,24 @@ class VertexAIExtensions:
|
|
|
175
191
|
[item['name'] for item in response['output_files']])
|
|
176
192
|
|
|
177
193
|
if response.get('execution_error'):
|
|
194
|
+
#TODO: setup iteration many times with a timeout
|
|
178
195
|
log.error(f"Code Execution Response failed with: {response.get('execution_error')} - maybe retry?")
|
|
196
|
+
new_query = f"""
|
|
197
|
+
<original_query>{query}</original_query>
|
|
198
|
+
<original_output>{response.get('generated_code')}</original_output>
|
|
199
|
+
The code above failed with this error:
|
|
200
|
+
<code_error>{response.get('execution_error')}</code_error>
|
|
201
|
+
Please try again again to satisfy the original query.
|
|
202
|
+
"""
|
|
203
|
+
operation_params = {"query": new_query}
|
|
204
|
+
response = extension_code_interpreter.execute(
|
|
205
|
+
operation_id="generate_and_execute",
|
|
206
|
+
operation_params=operation_params)
|
|
207
|
+
|
|
208
|
+
self.CODE_INTERPRETER_WRITTEN_FILES.extend([item['name'] for item in response['output_files']])
|
|
209
|
+
if response.get('execution_error'):
|
|
210
|
+
log.error(f"Code Execution Response failed twice: {response.get('execution_error')}")
|
|
211
|
+
|
|
179
212
|
|
|
180
213
|
return response
|
|
181
214
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.71.
|
|
3
|
+
Version: 0.71.17
|
|
4
4
|
Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
|
|
5
5
|
Home-page: https://github.com/sunholo-data/sunholo-py
|
|
6
|
-
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.71.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.71.17.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -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=FgSaCODyoTtlstJtsqlLPScdgRUtv9_plxftdzHdVFo,809
|
|
16
|
-
sunholo/agents/flask/qna_routes.py,sha256=
|
|
16
|
+
sunholo/agents/flask/qna_routes.py,sha256=TUsVn1MGcJ-8JFET5aR9mjGPd4z6K__SjTN8JOOsiTU,21814
|
|
17
17
|
sunholo/agents/flask/vac_routes.py,sha256=l2-w7x437F0Uu3QvwNueEYPtnKuIee6bHJ7LUMt_tkY,19520
|
|
18
18
|
sunholo/archive/__init__.py,sha256=qNHWm5rGPVOlxZBZCpA1wTYPbalizRT7f8X4rs2t290,31
|
|
19
19
|
sunholo/archive/archive.py,sha256=C-UhG5x-XtZ8VheQp92IYJqgD0V3NFQjniqlit94t18,1197
|
|
@@ -33,7 +33,7 @@ sunholo/chunker/pdfs.py,sha256=daCZ1xjn1YvxlifIyxskWNpLJLe-Q9D_Jq12MWx3tZo,2473
|
|
|
33
33
|
sunholo/chunker/publish.py,sha256=tiO615A2uo_ZjzdFDzNH1PL_1kJeLMUQwLJ4w67rNIc,2932
|
|
34
34
|
sunholo/chunker/splitter.py,sha256=jtGfi_ZdhVdyFhfw0e4ynEpmwIyrxQtV63OituYWy6o,6729
|
|
35
35
|
sunholo/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
-
sunholo/cli/chat_vac.py,sha256=
|
|
36
|
+
sunholo/cli/chat_vac.py,sha256=3XnfJEJXr-BaNsmvoOPlZsLXqcen4NKs5LP6THNoCzM,20069
|
|
37
37
|
sunholo/cli/cli.py,sha256=8e00HBN6eYIUJ8cnvKteBJNn7aZPRMk4b82jwcGg9D4,3741
|
|
38
38
|
sunholo/cli/cli_init.py,sha256=JMZ9AX2cPDZ-_mv3adiv2ToFVNyRPtjk9Biszl1kiR0,2358
|
|
39
39
|
sunholo/cli/configs.py,sha256=QUM9DvKOdZmEQRM5uI3Nh887T0YDiSMr7O240zTLqws,4546
|
|
@@ -67,7 +67,7 @@ sunholo/discovery_engine/discovery_engine_client.py,sha256=YYsFeaW41l8jmWCruQnYx
|
|
|
67
67
|
sunholo/embedder/__init__.py,sha256=sI4N_CqgEVcrMDxXgxKp1FsfsB4FpjoXgPGkl4N_u4I,44
|
|
68
68
|
sunholo/embedder/embed_chunk.py,sha256=d_dIzeNF630Q0Ar-u1hxos60s0tLIImJccAvuo_LTIw,6814
|
|
69
69
|
sunholo/gcs/__init__.py,sha256=DtVw_AZwQn-IguR5BJuIi2XJeF_FQXizhJikzRNrXiE,50
|
|
70
|
-
sunholo/gcs/add_file.py,sha256=
|
|
70
|
+
sunholo/gcs/add_file.py,sha256=W23NI_UhcgeqhuVLNyCjAnSluhM0xhGDZoMAP7yRNwI,6618
|
|
71
71
|
sunholo/gcs/download_url.py,sha256=8XSEf8byfubqs5CMQeF_tn9wxqwUTq3n9mo5mLNIUTA,4801
|
|
72
72
|
sunholo/gcs/metadata.py,sha256=C9sMPsHsq1ETetdQCqB3EBs3Kws8b8QHS9L7ei_v5aw,891
|
|
73
73
|
sunholo/langfuse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -111,13 +111,13 @@ sunholo/utils/user_ids.py,sha256=SQd5_H7FE7vcTZp9AQuQDWBXd4FEEd7TeVMQe1H4Ny8,292
|
|
|
111
111
|
sunholo/utils/version.py,sha256=P1QAJQdZfT2cMqdTSmXmcxrD2PssMPEGM-WI6083Fck,237
|
|
112
112
|
sunholo/vertex/__init__.py,sha256=XH7FUKxdIgN9H2iDcWxL3sRnVHC3297G24RqEn4Ob0Y,240
|
|
113
113
|
sunholo/vertex/extensions.py,sha256=d-Ikt9gHFf-jUMPmyU-xHwYe22QtEyr90Ua1LDKgTws,11026
|
|
114
|
-
sunholo/vertex/extensions_class.py,sha256=
|
|
114
|
+
sunholo/vertex/extensions_class.py,sha256=jYWaOF7zNwcdZgI8CNolRvNa7BA2McgjBcpwZspKCWk,11964
|
|
115
115
|
sunholo/vertex/init.py,sha256=-w7b9GKsyJnAJpYHYz6_zBUtmeJeLXlEkgOfwoe4DEI,2715
|
|
116
116
|
sunholo/vertex/memory_tools.py,sha256=FLTbNX_YbpxxUxZHAsXEihlUgLELfLOfxsdEkwDm_GI,6546
|
|
117
117
|
sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
|
|
118
|
-
sunholo-0.71.
|
|
119
|
-
sunholo-0.71.
|
|
120
|
-
sunholo-0.71.
|
|
121
|
-
sunholo-0.71.
|
|
122
|
-
sunholo-0.71.
|
|
123
|
-
sunholo-0.71.
|
|
118
|
+
sunholo-0.71.17.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
|
|
119
|
+
sunholo-0.71.17.dist-info/METADATA,sha256=a1SosPFBZUDdFgpnGGNdg6D9j24uEpN1J37vkwq2Sxw,6767
|
|
120
|
+
sunholo-0.71.17.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
|
|
121
|
+
sunholo-0.71.17.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
|
|
122
|
+
sunholo-0.71.17.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
|
|
123
|
+
sunholo-0.71.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|