sunholo 0.101.3__py3-none-any.whl → 0.101.5__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.
@@ -8,10 +8,11 @@ from tenacity import AsyncRetrying, retry_if_exception_type, wait_random_exponen
8
8
  log = setup_logging("sunholo_AsyncTaskRunner")
9
9
 
10
10
  class AsyncTaskRunner:
11
- def __init__(self, retry_enabled=False, retry_kwargs=None):
11
+ def __init__(self, retry_enabled=False, retry_kwargs=None, timeout=120):
12
12
  self.tasks = []
13
13
  self.retry_enabled = retry_enabled
14
14
  self.retry_kwargs = retry_kwargs or {}
15
+ self.timeout = timeout
15
16
 
16
17
  def add_task(self, func: Callable[..., Any], *args: Any, **kwargs: Any):
17
18
  """
@@ -23,7 +24,6 @@ class AsyncTaskRunner:
23
24
  **kwargs: Keyword arguments for the function.
24
25
  """
25
26
  log.info(f"Adding task: {func.__name__} with args: {args}, kwargs: {kwargs}")
26
- # Store the function name, function itself, args, and kwargs
27
27
  self.tasks.append((func.__name__, func, args, kwargs))
28
28
 
29
29
  async def run_async_as_completed(self) -> AsyncGenerator[Dict[str, Any], None]:
@@ -32,54 +32,36 @@ class AsyncTaskRunner:
32
32
  while periodically sending heartbeat messages.
33
33
  """
34
34
  log.info("Running tasks asynchronously and yielding results as they complete")
35
-
36
- # Create a queue for inter-coroutine communication
37
35
  queue = asyncio.Queue()
38
-
39
- # List to keep track of all running tasks and their heartbeats
40
36
  task_infos = []
41
37
 
42
- # Start all tasks and their corresponding heartbeats
43
38
  for name, func, args, kwargs in self.tasks:
44
- # Create an event to signal task completion to the heartbeat
45
39
  log.info(f"Executing task: {name=}, {func=} with args: {args}, kwargs: {kwargs}")
46
40
  completion_event = asyncio.Event()
47
-
48
- # Start the main task with retries
49
- task_coro = self._run_with_retries(name, func, args, kwargs, queue, completion_event)
41
+ task_coro = self._run_with_retries_and_timeout(name, func, args, kwargs, queue, completion_event)
50
42
  task = asyncio.create_task(task_coro)
51
-
52
- # Start the heartbeat coroutine
53
43
  heartbeat_coro = self._send_heartbeat(name, completion_event, queue)
54
44
  heartbeat_task = asyncio.create_task(heartbeat_coro)
55
-
56
- # Store task information for management
57
45
  task_infos.append({
58
46
  'name': name,
59
47
  'task': task,
60
48
  'heartbeat_task': heartbeat_task,
61
49
  'completion_event': completion_event
62
50
  })
63
-
64
51
  log.info(f"Started task '{name}' and its heartbeat")
65
52
 
66
53
  log.info(f"Started async run with {len(self.tasks)} tasks and heartbeats")
67
-
68
- # Create a monitor task to detect when all tasks and heartbeats are done
69
54
  monitor = asyncio.create_task(self._monitor_tasks(task_infos, queue))
70
55
 
71
- # Continuously yield messages from the queue until sentinel is received
72
56
  while True:
73
57
  message = await queue.get()
74
58
  if message is None:
75
59
  log.info("Received sentinel. Exiting message loop.")
76
- break # Sentinel received, all tasks and heartbeats are done
60
+ break
77
61
  log.info(f"Received message from queue: {message}")
78
62
  yield message
79
63
 
80
- # Wait for the monitor to finish
81
64
  await monitor
82
-
83
65
  log.info("All tasks and heartbeats have completed")
84
66
 
85
67
  async def _monitor_tasks(self, task_infos, queue):
@@ -101,22 +83,18 @@ class AsyncTaskRunner:
101
83
  pass
102
84
  log.info(f"Monitor: Heartbeat for task '{info['name']}' has been canceled")
103
85
 
104
- # Send a sentinel to indicate completion
105
86
  await queue.put(None)
106
87
  log.info("Monitor: Sent sentinel to queue")
107
88
 
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:
115
- """
116
- Executes a task with optional retries and sends completion or error messages to the queue.
117
- """
89
+ async def _run_with_retries_and_timeout(self,
90
+ name: str,
91
+ func: Callable[..., Any],
92
+ args: tuple,
93
+ kwargs: dict,
94
+ queue: asyncio.Queue,
95
+ completion_event: asyncio.Event) -> None:
118
96
  try:
119
- log.info(f"run_with_retries: {name=}, {func=} with args: {args}, kwargs: {kwargs}")
97
+ log.info(f"run_with_retries_and_timeout: {name=}, {func=} with args: {args}, kwargs: {kwargs}")
120
98
  if self.retry_enabled:
121
99
  retry_kwargs = {
122
100
  'wait': wait_random_exponential(multiplier=1, max=60),
@@ -127,19 +105,21 @@ class AsyncTaskRunner:
127
105
  async for attempt in AsyncRetrying(**retry_kwargs):
128
106
  with attempt:
129
107
  log.info(f"Starting task '{name}' with retry")
130
- result = await self._execute_task(func, *args, **kwargs)
108
+ result = await asyncio.wait_for(self._execute_task(func, *args, **kwargs), timeout=self.timeout)
131
109
  await queue.put({'type': 'task_complete', 'func_name': name, 'result': result})
132
110
  log.info(f"Sent 'task_complete' message for task '{name}'")
133
111
  return
134
112
  else:
135
113
  log.info(f"Starting task '{name}' with no retry")
136
- result = await self._execute_task(func, *args, **kwargs)
114
+ result = await asyncio.wait_for(self._execute_task(func, *args, **kwargs), timeout=self.timeout)
137
115
  await queue.put({'type': 'task_complete', 'func_name': name, 'result': result})
138
116
  log.info(f"Sent 'task_complete' message for task '{name}'")
117
+ except asyncio.TimeoutError:
118
+ log.error(f"Task '{name}' timed out after {self.timeout} seconds")
119
+ await queue.put({'type': 'task_error', 'func_name': name, 'error': f'Task timed out after {self.timeout} seconds'})
139
120
  except Exception as e:
140
121
  log.error(f"Error in task '{name}': {e}\n{traceback.format_exc()}")
141
122
  await queue.put({'type': 'task_error', 'func_name': name, 'error': f'{e}\n{traceback.format_exc()}'})
142
- log.info(f"Sent 'task_error' message for task '{name}'")
143
123
  finally:
144
124
  log.info(f"Task '{name}' completed.")
145
125
  completion_event.set()
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.101.3
3
+ Version: 0.101.5
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.3.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.101.5.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -91,7 +91,7 @@ sunholo/genai/init.py,sha256=yG8E67TduFCTQPELo83OJuWfjwTnGZsyACospahyEaY,687
91
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=DViVOHsuir7Zrfa3tVKgvz5wLCfXba15XryWGuMjKYI,8540
94
+ sunholo/invoke/async_class.py,sha256=G8vD2H94fpBc37mSJSQODEKJ67P2mPQEHabtDaLOvxE,8033
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
@@ -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.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,,
150
+ sunholo-0.101.5.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
151
+ sunholo-0.101.5.dist-info/METADATA,sha256=29r2NF8uW_rC_tcj2FqzKRZi55czwnq3kGheIdpNw2c,8312
152
+ sunholo-0.101.5.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
153
+ sunholo-0.101.5.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
154
+ sunholo-0.101.5.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
155
+ sunholo-0.101.5.dist-info/RECORD,,