sunholo 0.100.0__py3-none-any.whl → 0.100.1__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.
@@ -21,8 +21,9 @@ class AsyncTaskRunner:
21
21
  """
22
22
  log.info("Running tasks asynchronously and yielding results and heartbeats as they occur")
23
23
  queue = asyncio.Queue()
24
-
25
24
  tasks = {}
25
+ completed_tasks = set()
26
+
26
27
  for name, func, args in self.tasks:
27
28
  coro = self._task_wrapper(name, func, args, queue)
28
29
  task = asyncio.create_task(coro)
@@ -32,9 +33,11 @@ class AsyncTaskRunner:
32
33
  if not queue.empty():
33
34
  message = await queue.get()
34
35
  log.info(f"Found queue message: {message}")
36
+ # Ignore heartbeats from completed tasks
37
+ if message['type'] == 'heartbeat' and message['func_name'] in completed_tasks:
38
+ continue
35
39
  yield message
36
40
  else:
37
- # Wait for either a message in the queue or a task to complete
38
41
  done, _ = await asyncio.wait(
39
42
  list(tasks.keys()),
40
43
  timeout=0.1,
@@ -42,6 +45,7 @@ class AsyncTaskRunner:
42
45
  )
43
46
  for task in done:
44
47
  name = tasks.pop(task)
48
+ completed_tasks.add(name)
45
49
  try:
46
50
  result = await task
47
51
  await queue.put({'type': 'task_complete', 'func_name': name, 'result': result})
@@ -49,19 +53,20 @@ class AsyncTaskRunner:
49
53
  log.error(f"Task {name} resulted in an error: {e}\n{traceback.format_exc()}")
50
54
  await queue.put({'type': 'task_error', 'func_name': name, 'error': e})
51
55
 
52
- # After all tasks have completed, process any remaining messages in the queue
56
+ # Process any remaining messages in the queue
53
57
  while not queue.empty():
54
58
  message = await queue.get()
59
+ log.info(f"Found queue message: {message}")
60
+ if message['type'] == 'heartbeat' and message['func_name'] in completed_tasks:
61
+ continue
55
62
  yield message
56
63
 
57
64
  async def _task_wrapper(self, name: str, func: Callable[..., Any], args: Any, queue: asyncio.Queue) -> Any:
58
65
  """Wraps the task function to process its output and handle retries, while managing heartbeat updates."""
59
66
  async def run_func():
60
67
  if asyncio.iscoroutinefunction(func):
61
- # If the function is async, await it
62
68
  return await func(*args)
63
69
  else:
64
- # If the function is sync, run it in a thread to prevent blocking
65
70
  return await asyncio.to_thread(func, *args)
66
71
 
67
72
  # Start the heartbeat task
@@ -87,18 +92,12 @@ class AsyncTaskRunner:
87
92
  finally:
88
93
  # Stop the heartbeat task
89
94
  heartbeat_task.cancel()
90
- # Let the heartbeat_task finish in the background without awaiting it
95
+ # Wait for the heartbeat task to finish
91
96
  try:
92
- await asyncio.shield(heartbeat_task) # Ensure cancellation is handled cleanly
97
+ await heartbeat_task
93
98
  except asyncio.CancelledError:
94
99
  pass
95
100
 
96
- # Send a message indicating task completion to update the spinner's state
97
- completion_html = (
98
- f'<div style="display: none;" data-complete-id="{name}-spinner"></div>'
99
- )
100
- await queue.put({'type': 'heartbeat', 'func_name': name, 'token': completion_html})
101
-
102
101
  async def _send_heartbeat(self, queue: asyncio.Queue, func_name: str, interval=2):
103
102
  """
104
103
  Sends a periodic heartbeat to keep the task alive and update the spinner with elapsed time.
@@ -117,9 +116,8 @@ class AsyncTaskRunner:
117
116
  # Keep track of elapsed time
118
117
  elapsed_time = 0
119
118
 
120
- # Keep sending heartbeats until task completes
121
- while True:
122
- try:
119
+ try:
120
+ while True:
123
121
  await asyncio.sleep(interval) # Sleep for the interval
124
122
  elapsed_time += interval # Increment elapsed time
125
123
  log.info(f"Sending heartbeat for {func_name}: {elapsed_time}s elapsed")
@@ -130,5 +128,11 @@ class AsyncTaskRunner:
130
128
  f'</div>'
131
129
  )
132
130
  await queue.put({'type': 'heartbeat', 'func_name': func_name, 'token': update_html})
133
- except asyncio.CancelledError:
134
- break # Exit the loop if the heartbeat task is canceled
131
+ except asyncio.CancelledError:
132
+ log.info(f"Heartbeat task for {func_name} has been cancelled.")
133
+ finally:
134
+ # Send a message indicating task completion to update the spinner's state
135
+ completion_html = (
136
+ f'<div style="display: none;" data-complete-id="{func_name}-spinner"></div>'
137
+ )
138
+ await queue.put({'type': 'heartbeat', 'func_name': func_name, 'token': completion_html})
@@ -173,13 +173,13 @@ class BufferStreamingStdOutCallbackHandler(StreamingStdOutCallbackHandler):
173
173
  The buffer content is written to the content buffer when appropriate tokens or
174
174
  patterns are detected.
175
175
  """
176
- log.debug(f"on_llm_new_token: {token}")
176
+ #log.debug(f"on_llm_new_token: {token}")
177
177
 
178
178
  # Check if the token is a heartbeat message
179
179
  if self._is_heartbeat_token(token):
180
180
  # Strip the [[HEARTBEAT]] markers and write immediately
181
181
  heartbeat_content = self._strip_heartbeat_markers(token)
182
- log.info(f"Heartbeat token detected, writing immediately: {heartbeat_content}")
182
+ #log.info(f"Heartbeat token detected, writing immediately: {heartbeat_content}")
183
183
  self.content_buffer.write(heartbeat_content)
184
184
  else:
185
185
  self.buffer += token
@@ -268,13 +268,13 @@ class BufferStreamingStdOutCallbackHandlerAsync(StreamingStdOutCallbackHandler):
268
268
  log.info("Starting to stream LLM")
269
269
 
270
270
  async def async_on_llm_new_token(self, token: str, **kwargs: Any) -> None:
271
- log.debug(f"async_on_llm_new_token: {token}")
271
+ #log.debug(f"async_on_llm_new_token: {token}")
272
272
 
273
273
  # Check if the token is a heartbeat message
274
274
  if self._is_heartbeat_token(token):
275
275
  # Strip the [[HEARTBEAT]] markers and write immediately
276
276
  heartbeat_content = self._strip_heartbeat_markers(token)
277
- log.info(f"Heartbeat token detected, writing immediately: {heartbeat_content}")
277
+ #log.info(f"Heartbeat token detected, writing immediately: {heartbeat_content}")
278
278
  await self.content_buffer.async_write(heartbeat_content)
279
279
  else:
280
280
  self.buffer += token
@@ -194,7 +194,7 @@ async def start_streaming_chat_async(question, vector_name, qna_func_async, chat
194
194
 
195
195
  content_to_send = await content_buffer.async_read()
196
196
  if content_to_send:
197
- log.info(f"Content to send: {content_to_send}")
197
+ log.info(f"==Async\n{content_to_send}")
198
198
  yield content_to_send
199
199
  await content_buffer.async_clear()
200
200
  else:
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.100.0
3
+ Version: 0.100.1
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.100.0.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.100.1.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -89,7 +89,7 @@ sunholo/genai/init.py,sha256=yG8E67TduFCTQPELo83OJuWfjwTnGZsyACospahyEaY,687
89
89
  sunholo/genai/process_funcs_cls.py,sha256=MF3wm-N-zoYvme4I8ffXM9I7cog8OFyBnLu1e3A6eVc,26695
90
90
  sunholo/genai/safety.py,sha256=mkFDO_BeEgiKjQd9o2I4UxB6XI7a9U-oOFjZ8LGRUC4,1238
91
91
  sunholo/invoke/__init__.py,sha256=o1RhwBGOtVK0MIdD55fAIMCkJsxTksi8GD5uoqVKI-8,184
92
- sunholo/invoke/async_class.py,sha256=6AQ3d1-8F0lG9y3pXli1IsJW7eDqGLxoIFxYnmjbmn0,6067
92
+ sunholo/invoke/async_class.py,sha256=TbYzdS2RDu1WhbYcq6wt7GTJzrtk-7Y3I-2AbJAD1Ik,6134
93
93
  sunholo/invoke/direct_vac_func.py,sha256=GXSCMkC6vOWGUtQjxy-ZpTrMvJa3CgcW-y9mDpJwWC8,9533
94
94
  sunholo/invoke/invoke_vac_utils.py,sha256=sJc1edHTHMzMGXjji1N67c3iUaP7BmAL5nj82Qof63M,2053
95
95
  sunholo/langfuse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -114,10 +114,10 @@ sunholo/qna/__init__.py,sha256=F8q1uR_HreoSX0IfmKY1qoSwIgXhO2Q8kuDSxh9_-EE,28
114
114
  sunholo/qna/parsers.py,sha256=YpOaK5S_LxJ6FbliSYDc3AVOJ62RVduayoNnzi_p8CM,2494
115
115
  sunholo/qna/retry.py,sha256=yMw7RTkw-RXCzfENPJOt8c32mXlpvOR589EGkvK-6yI,2028
116
116
  sunholo/streaming/__init__.py,sha256=MpbydI2UYo_adttPQFkxNM33b-QRyNEbrKJx0C2AGPc,241
117
- sunholo/streaming/content_buffer.py,sha256=0oNltM8jBU_3-728--DGdXfS278C2s1xwdcbWthGFks,12810
117
+ sunholo/streaming/content_buffer.py,sha256=dGzzajY_DEnIxMERiApCKUSzDI5mXKByW9L4dmYRqSw,12814
118
118
  sunholo/streaming/langserve.py,sha256=hi7q8WY8DPKrALl9m_dOMxWOdE-iEuk7YW05SVDFIX8,6514
119
119
  sunholo/streaming/stream_lookup.py,sha256=hYg1DbdSE_QNJ8ZB-ynXJlWgvFjrGvwoUsGJu_E0pRQ,360
120
- sunholo/streaming/streaming.py,sha256=FUdcotNbLQg2VV_mHnHloLJNL2gDQYAYsaNiHjzaT2I,16467
120
+ sunholo/streaming/streaming.py,sha256=S4GzVNol3J_8tkzXodvkcR-THiTGnIKYj40NtrBhtL4,16459
121
121
  sunholo/summarise/__init__.py,sha256=MZk3dblUMODcPb1crq4v-Z508NrFIpkSWNf9FIO8BcU,38
122
122
  sunholo/summarise/summarise.py,sha256=95A-6PXFGanjona8DvZPnnIHLbzZ2ip5hO0wOAJQhfw,3791
123
123
  sunholo/terraform/__init__.py,sha256=yixxEltc3n9UpZaVi05GlgS-YRq_DVGjUc37I9ajeP4,76
@@ -144,9 +144,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
144
144
  sunholo/vertex/memory_tools.py,sha256=tBZxqVZ4InTmdBvLlOYwoSEWu4-kGquc-gxDwZCC4FA,7667
145
145
  sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
146
146
  sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
147
- sunholo-0.100.0.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
148
- sunholo-0.100.0.dist-info/METADATA,sha256=3E-fkTmyUzOkUNGCshJmLpnirzeSiKHaRk8oYec-xQQ,8312
149
- sunholo-0.100.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
150
- sunholo-0.100.0.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
151
- sunholo-0.100.0.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
152
- sunholo-0.100.0.dist-info/RECORD,,
147
+ sunholo-0.100.1.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
148
+ sunholo-0.100.1.dist-info/METADATA,sha256=dFU8BBZlnOGZpfnkvOiQ-_yO3Hs12mNi0BFWnVQh9QQ,8312
149
+ sunholo-0.100.1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
150
+ sunholo-0.100.1.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
151
+ sunholo-0.100.1.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
152
+ sunholo-0.100.1.dist-info/RECORD,,