sunholo 0.101.0__py3-none-any.whl → 0.101.3__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/genai/process_funcs_cls.py +27 -2
- sunholo/invoke/async_class.py +29 -16
- sunholo/langfuse/prompts.py +36 -4
- {sunholo-0.101.0.dist-info → sunholo-0.101.3.dist-info}/METADATA +2 -2
- {sunholo-0.101.0.dist-info → sunholo-0.101.3.dist-info}/RECORD +9 -9
- {sunholo-0.101.0.dist-info → sunholo-0.101.3.dist-info}/LICENSE.txt +0 -0
- {sunholo-0.101.0.dist-info → sunholo-0.101.3.dist-info}/WHEEL +0 -0
- {sunholo-0.101.0.dist-info → sunholo-0.101.3.dist-info}/entry_points.txt +0 -0
- {sunholo-0.101.0.dist-info → sunholo-0.101.3.dist-info}/top_level.txt +0 -0
|
@@ -14,13 +14,16 @@ try:
|
|
|
14
14
|
import proto
|
|
15
15
|
from google.generativeai.types import RequestOptions
|
|
16
16
|
from google.api_core import retry
|
|
17
|
+
from google.generativeai import ChatSession
|
|
17
18
|
except ImportError:
|
|
18
19
|
genai = None
|
|
20
|
+
ChatSession = None
|
|
19
21
|
|
|
20
22
|
from .images import extract_gs_images_and_genai_upload
|
|
21
23
|
|
|
22
24
|
if TYPE_CHECKING:
|
|
23
25
|
from google.generativeai.protos import Part
|
|
26
|
+
from google.generativeai import ChatSession
|
|
24
27
|
|
|
25
28
|
|
|
26
29
|
|
|
@@ -369,8 +372,28 @@ class GenAIFunctionProcessor:
|
|
|
369
372
|
log.info(f"Cleaning:\n{string}\n > to >\n{clean}")
|
|
370
373
|
|
|
371
374
|
return clean
|
|
375
|
+
|
|
376
|
+
def convert_composite_to_native(self, value):
|
|
377
|
+
"""
|
|
378
|
+
Recursively converts a proto MapComposite or RepeatedComposite object to native Python types.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
value: The proto object, which could be a MapComposite, RepeatedComposite, or a primitive.
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
The equivalent Python dictionary, list, or primitive type.
|
|
385
|
+
"""
|
|
386
|
+
if isinstance(value, proto.marshal.collections.maps.MapComposite):
|
|
387
|
+
# Convert MapComposite to a dictionary, recursively processing its values
|
|
388
|
+
return {key: self.convert_composite_to_native(val) for key, val in value.items()}
|
|
389
|
+
elif isinstance(value, proto.marshal.collections.repeated.RepeatedComposite):
|
|
390
|
+
# Convert RepeatedComposite to a list, recursively processing its elements
|
|
391
|
+
return [self.convert_composite_to_native(item) for item in value]
|
|
392
|
+
else:
|
|
393
|
+
# If it's a primitive value, return it as is
|
|
394
|
+
return value
|
|
372
395
|
|
|
373
|
-
def run_agent_loop(self, chat, content, callback=None, guardrail_max=10, loop_return=3):
|
|
396
|
+
def run_agent_loop(self, chat:ChatSession, content:list, callback=None, guardrail_max=10, loop_return=3):
|
|
374
397
|
"""
|
|
375
398
|
Runs the agent loop, sending messages to the orchestrator, processing responses, and executing functions.
|
|
376
399
|
|
|
@@ -488,7 +511,9 @@ class GenAIFunctionProcessor:
|
|
|
488
511
|
fn_result_json = None
|
|
489
512
|
# Convert MapComposite to a standard Python dictionary
|
|
490
513
|
if isinstance(fn_result, proto.marshal.collections.maps.MapComposite):
|
|
491
|
-
fn_result_json =
|
|
514
|
+
fn_result_json = self.convert_composite_to_native(fn_result)
|
|
515
|
+
elif isinstance(fn_result, proto.marshal.collections.repeated.RepeatedComposite):
|
|
516
|
+
fn_result = self.convert_composite_to_native(fn_result)
|
|
492
517
|
elif isinstance(fn_result, dict):
|
|
493
518
|
fn_result_json = fn_result
|
|
494
519
|
elif isinstance(fn_result, str):
|
sunholo/invoke/async_class.py
CHANGED
|
@@ -13,12 +13,18 @@ class AsyncTaskRunner:
|
|
|
13
13
|
self.retry_enabled = retry_enabled
|
|
14
14
|
self.retry_kwargs = retry_kwargs or {}
|
|
15
15
|
|
|
16
|
-
def add_task(self, func: Callable[..., Any], *args: Any):
|
|
16
|
+
def add_task(self, func: Callable[..., Any], *args: Any, **kwargs: Any):
|
|
17
17
|
"""
|
|
18
|
-
Adds a task to the list of tasks to be executed.
|
|
18
|
+
Adds a task to the list of tasks to be executed, supporting both positional and keyword arguments.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
func: The function to be executed.
|
|
22
|
+
*args: Positional arguments for the function.
|
|
23
|
+
**kwargs: Keyword arguments for the function.
|
|
19
24
|
"""
|
|
20
|
-
log.info(f"Adding task: {func.__name__} with args: {args}")
|
|
21
|
-
|
|
25
|
+
log.info(f"Adding task: {func.__name__} with args: {args}, kwargs: {kwargs}")
|
|
26
|
+
# Store the function name, function itself, args, and kwargs
|
|
27
|
+
self.tasks.append((func.__name__, func, args, kwargs))
|
|
22
28
|
|
|
23
29
|
async def run_async_as_completed(self) -> AsyncGenerator[Dict[str, Any], None]:
|
|
24
30
|
"""
|
|
@@ -34,12 +40,13 @@ class AsyncTaskRunner:
|
|
|
34
40
|
task_infos = []
|
|
35
41
|
|
|
36
42
|
# Start all tasks and their corresponding heartbeats
|
|
37
|
-
for name, func, args in self.tasks:
|
|
43
|
+
for name, func, args, kwargs in self.tasks:
|
|
38
44
|
# Create an event to signal task completion to the heartbeat
|
|
45
|
+
log.info(f"Executing task: {name=}, {func=} with args: {args}, kwargs: {kwargs}")
|
|
39
46
|
completion_event = asyncio.Event()
|
|
40
47
|
|
|
41
48
|
# Start the main task with retries
|
|
42
|
-
task_coro = self._run_with_retries(name, func,
|
|
49
|
+
task_coro = self._run_with_retries(name, func, args, kwargs, queue, completion_event)
|
|
43
50
|
task = asyncio.create_task(task_coro)
|
|
44
51
|
|
|
45
52
|
# Start the heartbeat coroutine
|
|
@@ -98,11 +105,18 @@ class AsyncTaskRunner:
|
|
|
98
105
|
await queue.put(None)
|
|
99
106
|
log.info("Monitor: Sent sentinel to queue")
|
|
100
107
|
|
|
101
|
-
async def _run_with_retries(self,
|
|
108
|
+
async def _run_with_retries(self,
|
|
109
|
+
name: str,
|
|
110
|
+
func: Callable[..., Any],
|
|
111
|
+
args: tuple,
|
|
112
|
+
kwargs: dict,
|
|
113
|
+
queue: asyncio.Queue,
|
|
114
|
+
completion_event: asyncio.Event) -> None:
|
|
102
115
|
"""
|
|
103
116
|
Executes a task with optional retries and sends completion or error messages to the queue.
|
|
104
117
|
"""
|
|
105
118
|
try:
|
|
119
|
+
log.info(f"run_with_retries: {name=}, {func=} with args: {args}, kwargs: {kwargs}")
|
|
106
120
|
if self.retry_enabled:
|
|
107
121
|
retry_kwargs = {
|
|
108
122
|
'wait': wait_random_exponential(multiplier=1, max=60),
|
|
@@ -113,13 +127,13 @@ class AsyncTaskRunner:
|
|
|
113
127
|
async for attempt in AsyncRetrying(**retry_kwargs):
|
|
114
128
|
with attempt:
|
|
115
129
|
log.info(f"Starting task '{name}' with retry")
|
|
116
|
-
result = await self._execute_task(func, *args)
|
|
130
|
+
result = await self._execute_task(func, *args, **kwargs)
|
|
117
131
|
await queue.put({'type': 'task_complete', 'func_name': name, 'result': result})
|
|
118
132
|
log.info(f"Sent 'task_complete' message for task '{name}'")
|
|
119
133
|
return
|
|
120
134
|
else:
|
|
121
135
|
log.info(f"Starting task '{name}' with no retry")
|
|
122
|
-
result = await self._execute_task(func, *args)
|
|
136
|
+
result = await self._execute_task(func, *args, **kwargs)
|
|
123
137
|
await queue.put({'type': 'task_complete', 'func_name': name, 'result': result})
|
|
124
138
|
log.info(f"Sent 'task_complete' message for task '{name}'")
|
|
125
139
|
except Exception as e:
|
|
@@ -128,24 +142,24 @@ class AsyncTaskRunner:
|
|
|
128
142
|
log.info(f"Sent 'task_error' message for task '{name}'")
|
|
129
143
|
finally:
|
|
130
144
|
log.info(f"Task '{name}' completed.")
|
|
131
|
-
# Set the completion event after sending the message
|
|
132
145
|
completion_event.set()
|
|
133
146
|
|
|
134
|
-
async def _execute_task(self, func: Callable[..., Any], *args: Any) -> Any:
|
|
147
|
+
async def _execute_task(self, func: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
|
|
135
148
|
"""
|
|
136
149
|
Executes the given task function and returns its result.
|
|
137
150
|
|
|
138
151
|
Args:
|
|
139
152
|
func (Callable): The callable to execute.
|
|
140
|
-
*args:
|
|
153
|
+
*args: Positional arguments to pass to the callable.
|
|
154
|
+
**kwargs: Keyword arguments to pass to the callable.
|
|
141
155
|
|
|
142
156
|
Returns:
|
|
143
157
|
Any: The result of the task.
|
|
144
158
|
"""
|
|
145
159
|
if asyncio.iscoroutinefunction(func):
|
|
146
|
-
return await func(*args)
|
|
160
|
+
return await func(*args, **kwargs)
|
|
147
161
|
else:
|
|
148
|
-
return await asyncio.to_thread(func, *args)
|
|
162
|
+
return await asyncio.to_thread(func, *args, **kwargs)
|
|
149
163
|
|
|
150
164
|
async def _send_heartbeat(self, func_name: str, completion_event: asyncio.Event, queue: asyncio.Queue, interval: int = 2):
|
|
151
165
|
"""
|
|
@@ -174,5 +188,4 @@ class AsyncTaskRunner:
|
|
|
174
188
|
except asyncio.CancelledError:
|
|
175
189
|
log.info(f"Heartbeat for task '{func_name}' has been canceled")
|
|
176
190
|
finally:
|
|
177
|
-
log.info(f"Heartbeat for task '{func_name}' stopped")
|
|
178
|
-
|
|
191
|
+
log.info(f"Heartbeat for task '{func_name}' stopped")
|
sunholo/langfuse/prompts.py
CHANGED
|
@@ -6,17 +6,49 @@ def load_prompt_from_yaml(key, prefix="sunholo", load_from_file=False, f_string=
|
|
|
6
6
|
"""
|
|
7
7
|
Returns a string you can use with prompts.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
If load_from_file=False, by default it will try to load from Langfuse, if fails (which is laggy so not ideal) then load from file.
|
|
10
|
+
|
|
11
|
+
Prompts on Langfuse should be specified with a name with {prefix}-{key} e.g. "sunholo-hello"
|
|
12
|
+
|
|
13
|
+
Prompts in files will use yaml:
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
kind: promptConfig
|
|
17
|
+
apiVersion: v1
|
|
18
|
+
prompts:
|
|
19
|
+
sunholo:
|
|
20
|
+
hello: |
|
|
21
|
+
Say hello to {name}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
And load via utils.ConfigManager:
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
# equivalent to load_prompt_from_yaml("hello", load_from_file=True)
|
|
28
|
+
config = ConfigManager("sunholo")
|
|
29
|
+
config.promptConfig("hello")
|
|
30
|
+
```
|
|
10
31
|
|
|
11
32
|
If f_string is True will be in a Langchain style prompt e.g. { one brace }
|
|
12
|
-
If f_string is False will be Langfuse style prompt e.g. {{ two braces }}
|
|
33
|
+
If f_string is False will be Langfuse style prompt e.g. {{ two braces }} - see https://langfuse.com/docs/prompts/get-started
|
|
13
34
|
|
|
14
35
|
Example:
|
|
15
36
|
|
|
16
37
|
```python
|
|
17
38
|
from sunholo.langfuse.prompts import load_prompt_from_yaml
|
|
18
|
-
|
|
19
|
-
|
|
39
|
+
# f_string
|
|
40
|
+
hello_template = load_prompt_from_yaml("hello")
|
|
41
|
+
hello_template.format(name="Bob")
|
|
42
|
+
|
|
43
|
+
#langfuse style
|
|
44
|
+
hello_template = load_prompt_from_yaml("hello", f_string=False)
|
|
45
|
+
hello_template.compile(name="Bob")
|
|
46
|
+
|
|
47
|
+
# if prompt not available on langfuse, will attempt to load from local promptConfig file
|
|
48
|
+
hello_template = load_prompt_from_yaml("hello", load_from_file=True)
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
|
|
20
52
|
"""
|
|
21
53
|
config = ConfigManager(prefix)
|
|
22
54
|
if load_from_file:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.101.
|
|
3
|
+
Version: 0.101.3
|
|
4
4
|
Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
|
|
5
5
|
Home-page: https://github.com/sunholo-data/sunholo-py
|
|
6
|
-
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.101.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.101.3.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -88,16 +88,16 @@ sunholo/gcs/metadata.py,sha256=oQLcXi4brsZ74aegWyC1JZmhlaEV270HS5_UWtAYYWE,898
|
|
|
88
88
|
sunholo/genai/__init__.py,sha256=dBl6IA3-Fx6-Vx81r0XqxHlUq6WeW1iDX188dpChu8s,115
|
|
89
89
|
sunholo/genai/images.py,sha256=WaeO705mK0BOz1k7OwsGslw7qF1WiyE_zsueYt07vV8,1191
|
|
90
90
|
sunholo/genai/init.py,sha256=yG8E67TduFCTQPELo83OJuWfjwTnGZsyACospahyEaY,687
|
|
91
|
-
sunholo/genai/process_funcs_cls.py,sha256=
|
|
91
|
+
sunholo/genai/process_funcs_cls.py,sha256=krjhwruW-7OUR0gEOPC5Ew9KOoAoTWDMW5HfvpvuPNM,28755
|
|
92
92
|
sunholo/genai/safety.py,sha256=mkFDO_BeEgiKjQd9o2I4UxB6XI7a9U-oOFjZ8LGRUC4,1238
|
|
93
93
|
sunholo/invoke/__init__.py,sha256=o1RhwBGOtVK0MIdD55fAIMCkJsxTksi8GD5uoqVKI-8,184
|
|
94
|
-
sunholo/invoke/async_class.py,sha256=
|
|
94
|
+
sunholo/invoke/async_class.py,sha256=DViVOHsuir7Zrfa3tVKgvz5wLCfXba15XryWGuMjKYI,8540
|
|
95
95
|
sunholo/invoke/direct_vac_func.py,sha256=GXSCMkC6vOWGUtQjxy-ZpTrMvJa3CgcW-y9mDpJwWC8,9533
|
|
96
96
|
sunholo/invoke/invoke_vac_utils.py,sha256=sJc1edHTHMzMGXjji1N67c3iUaP7BmAL5nj82Qof63M,2053
|
|
97
97
|
sunholo/langfuse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
98
|
sunholo/langfuse/callback.py,sha256=jl0SZsFS53uMW9DGeM9SOL_EsRZsba0wwFGLqKzu9_U,1684
|
|
99
99
|
sunholo/langfuse/evals.py,sha256=fQBaC0dBTYfgCzyfv9QBRvUfc9f42lbwQAeZmynaHO8,3841
|
|
100
|
-
sunholo/langfuse/prompts.py,sha256=
|
|
100
|
+
sunholo/langfuse/prompts.py,sha256=9TkmT2A-P4YRlk7sWz_EYk6yASxPKU8jhM7jAg9ND_Q,2330
|
|
101
101
|
sunholo/llamaindex/__init__.py,sha256=DlY_cHWCsVEV1C5WBgDdHRgOMlJc8pDoCRukUJ8PT9w,88
|
|
102
102
|
sunholo/llamaindex/get_files.py,sha256=6rhXCDqQ_lrIapISQ_OYQDjiSATXvS_9m3qq53-oIl0,781
|
|
103
103
|
sunholo/llamaindex/import_files.py,sha256=Bnic5wz8c61af9Kwq8KSrNBbc4imYnzMtBCb2jzSImI,6224
|
|
@@ -147,9 +147,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
|
|
|
147
147
|
sunholo/vertex/memory_tools.py,sha256=tBZxqVZ4InTmdBvLlOYwoSEWu4-kGquc-gxDwZCC4FA,7667
|
|
148
148
|
sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
|
|
149
149
|
sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
|
|
150
|
-
sunholo-0.101.
|
|
151
|
-
sunholo-0.101.
|
|
152
|
-
sunholo-0.101.
|
|
153
|
-
sunholo-0.101.
|
|
154
|
-
sunholo-0.101.
|
|
155
|
-
sunholo-0.101.
|
|
150
|
+
sunholo-0.101.3.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
|
|
151
|
+
sunholo-0.101.3.dist-info/METADATA,sha256=X_RjEUyzY8ejvVlwkm7Pf1GIkAxybLMR0v2HpKentr4,8312
|
|
152
|
+
sunholo-0.101.3.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
153
|
+
sunholo-0.101.3.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
|
|
154
|
+
sunholo-0.101.3.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
|
|
155
|
+
sunholo-0.101.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|