sunholo 0.95.0__tar.gz → 0.95.2__tar.gz
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-0.95.0 → sunholo-0.95.2}/PKG-INFO +2 -2
- {sunholo-0.95.0 → sunholo-0.95.2}/setup.py +1 -1
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/flask/vac_routes.py +55 -58
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/invoke/async_class.py +8 -9
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/streaming/content_buffer.py +81 -1
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/vertex/memory_tools.py +25 -23
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo.egg-info/PKG-INFO +2 -2
- {sunholo-0.95.0 → sunholo-0.95.2}/LICENSE.txt +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/MANIFEST.in +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/README.md +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/setup.cfg +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/chat_history.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/dispatch_to_qa.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/fastapi/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/fastapi/base.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/fastapi/qna_routes.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/flask/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/flask/base.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/flask/qna_routes.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/langserve.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/pubsub.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/route.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/special_commands.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/agents/swagger.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/archive/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/archive/archive.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/auth/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/auth/gcloud.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/auth/refresh.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/auth/run.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/azure/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/azure/auth.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/azure/blobs.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/azure/event_grid.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/bots/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/bots/discord.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/bots/github_webhook.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/bots/webapp.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/chunker/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/chunker/azure.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/chunker/doc_handling.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/chunker/encode_metadata.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/chunker/images.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/chunker/loaders.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/chunker/message_data.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/chunker/pdfs.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/chunker/process_chunker_data.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/chunker/publish.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/chunker/pubsub.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/chunker/splitter.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/cli/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/cli/chat_vac.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/cli/cli.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/cli/cli_init.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/cli/configs.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/cli/deploy.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/cli/embedder.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/cli/merge_texts.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/cli/run_proxy.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/cli/sun_rich.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/cli/swagger.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/cli/vertex.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/components/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/components/llm.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/components/retriever.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/components/vectorstore.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/custom_logging.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/alloydb.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/alloydb_client.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/database.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/lancedb.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/sql/sb/create_function.sql +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/sql/sb/create_function_time.sql +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/sql/sb/create_table.sql +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/sql/sb/delete_source_row.sql +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/sql/sb/return_sources.sql +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/sql/sb/setup.sql +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/static_dbs.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/database/uuid.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/discovery_engine/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/discovery_engine/chunker_handler.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/discovery_engine/create_new.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/discovery_engine/discovery_engine_client.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/discovery_engine/get_ai_search_chunks.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/embedder/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/embedder/embed_chunk.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/excel/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/excel/plugin.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/gcs/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/gcs/add_file.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/gcs/download_folder.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/gcs/download_url.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/gcs/metadata.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/genai/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/genai/init.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/genai/process_funcs_cls.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/genai/safety.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/invoke/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/invoke/direct_vac_func.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/invoke/invoke_vac_utils.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/langfuse/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/langfuse/callback.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/langfuse/evals.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/langfuse/prompts.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/llamaindex/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/llamaindex/get_files.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/llamaindex/import_files.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/llamaindex/llamaindex_class.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/llamaindex/user_history.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/lookup/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/lookup/model_lookup.yaml +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/patches/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/patches/langchain/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/patches/langchain/lancedb.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/patches/langchain/vertexai.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/pubsub/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/pubsub/process_pubsub.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/pubsub/pubsub_manager.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/qna/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/qna/parsers.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/qna/retry.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/streaming/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/streaming/langserve.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/streaming/stream_lookup.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/streaming/streaming.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/summarise/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/summarise/summarise.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/terraform/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/terraform/tfvars_editor.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/tools/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/tools/web_browser.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/utils/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/utils/api_key.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/utils/big_context.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/utils/config.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/utils/config_class.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/utils/config_schema.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/utils/gcp.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/utils/gcp_project.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/utils/parsers.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/utils/timedelta.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/utils/user_ids.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/utils/version.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/vertex/__init__.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/vertex/extensions_call.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/vertex/extensions_class.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/vertex/genai_functions.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/vertex/init.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/vertex/safety.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo/vertex/type_dict_to_json.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo.egg-info/SOURCES.txt +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo.egg-info/dependency_links.txt +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo.egg-info/entry_points.txt +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo.egg-info/requires.txt +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/sunholo.egg-info/top_level.txt +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/tests/test_chat_history.py +0 -0
- {sunholo-0.95.0 → sunholo-0.95.2}/tests/test_config.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.95.
|
|
3
|
+
Version: 0.95.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.95.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.95.2.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -185,11 +185,9 @@ if __name__ == "__main__":
|
|
|
185
185
|
|
|
186
186
|
log.info(f"OpenAI response: {openai_response}")
|
|
187
187
|
return jsonify(openai_response)
|
|
188
|
-
|
|
188
|
+
|
|
189
189
|
def handle_stream_vac(self, vector_name):
|
|
190
190
|
observed_stream_interpreter = self.stream_interpreter
|
|
191
|
-
|
|
192
|
-
# Determine if the stream interpreter is async or sync
|
|
193
191
|
is_async = inspect.iscoroutinefunction(self.stream_interpreter)
|
|
194
192
|
|
|
195
193
|
if is_async:
|
|
@@ -219,52 +217,53 @@ if __name__ == "__main__":
|
|
|
219
217
|
def generate_response_content():
|
|
220
218
|
try:
|
|
221
219
|
if is_async:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
220
|
+
from queue import Queue, Empty
|
|
221
|
+
result_queue = Queue()
|
|
222
|
+
import threading
|
|
223
|
+
|
|
224
|
+
def run_async():
|
|
225
|
+
async def process_async():
|
|
226
|
+
try:
|
|
227
|
+
async_gen = start_streaming_chat_async(
|
|
228
|
+
question=all_input["user_input"],
|
|
229
|
+
vector_name=vector_name,
|
|
230
|
+
qna_func_async=observed_stream_interpreter,
|
|
231
|
+
chat_history=all_input["chat_history"],
|
|
232
|
+
wait_time=all_input["stream_wait_time"],
|
|
233
|
+
timeout=all_input["stream_timeout"],
|
|
234
|
+
trace_id=trace.id if trace else None,
|
|
235
|
+
**all_input["kwargs"]
|
|
236
|
+
)
|
|
237
|
+
log.info(f"{async_gen=}")
|
|
238
|
+
async for chunk in async_gen:
|
|
239
|
+
if isinstance(chunk, dict) and 'answer' in chunk:
|
|
240
|
+
if trace:
|
|
241
|
+
chunk["trace_id"] = trace.id
|
|
242
|
+
chunk["trace_url"] = trace.get_trace_url()
|
|
243
|
+
generation.end(output=json.dumps(chunk))
|
|
244
|
+
span.end(output=json.dumps(chunk))
|
|
245
|
+
trace.update(output=json.dumps(chunk))
|
|
246
|
+
archive_qa(chunk, vector_name)
|
|
247
|
+
result_queue.put(json.dumps(chunk))
|
|
248
|
+
else:
|
|
249
|
+
result_queue.put(chunk)
|
|
250
|
+
except Exception as e:
|
|
251
|
+
result_queue.put(f"Streaming Error: {str(e)} {traceback.format_exc()}")
|
|
252
|
+
finally:
|
|
253
|
+
result_queue.put(None) # Sentinel
|
|
254
|
+
asyncio.run(process_async())
|
|
255
|
+
|
|
256
|
+
thread = threading.Thread(target=run_async)
|
|
257
|
+
thread.start()
|
|
258
|
+
|
|
259
|
+
# Read from the queue and yield results
|
|
260
|
+
while True:
|
|
261
|
+
chunk = result_queue.get()
|
|
262
|
+
if chunk is None:
|
|
263
|
+
break
|
|
264
|
+
yield chunk
|
|
265
|
+
|
|
266
|
+
thread.join()
|
|
268
267
|
else:
|
|
269
268
|
log.info("sync streaming response")
|
|
270
269
|
for chunk in start_streaming_chat(
|
|
@@ -286,16 +285,16 @@ if __name__ == "__main__":
|
|
|
286
285
|
generation.end(output=json.dumps(chunk))
|
|
287
286
|
span.end(output=json.dumps(chunk))
|
|
288
287
|
trace.update(output=json.dumps(chunk))
|
|
289
|
-
|
|
288
|
+
yield json.dumps(chunk)
|
|
290
289
|
else:
|
|
291
290
|
yield chunk
|
|
292
291
|
|
|
293
292
|
except Exception as e:
|
|
294
293
|
yield f"Streaming Error: {str(e)} {traceback.format_exc()}"
|
|
295
|
-
|
|
294
|
+
|
|
296
295
|
# Here, the generator function will handle streaming the content to the client.
|
|
297
296
|
response = Response(generate_response_content(), content_type='text/plain; charset=utf-8')
|
|
298
|
-
response.headers['Transfer-Encoding'] = 'chunked'
|
|
297
|
+
response.headers['Transfer-Encoding'] = 'chunked'
|
|
299
298
|
|
|
300
299
|
log.debug(f"streaming response: {response}")
|
|
301
300
|
if trace:
|
|
@@ -307,12 +306,10 @@ if __name__ == "__main__":
|
|
|
307
306
|
return response
|
|
308
307
|
|
|
309
308
|
@staticmethod
|
|
310
|
-
async def
|
|
311
|
-
"""Helper function to
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
result.append(item)
|
|
315
|
-
return result
|
|
309
|
+
async def _async_generator_to_stream(async_gen_func):
|
|
310
|
+
"""Helper function to stream the async generator's values to the client."""
|
|
311
|
+
async for item in async_gen_func():
|
|
312
|
+
yield item
|
|
316
313
|
|
|
317
314
|
def langfuse_eval_response(self, trace_id, eval_percent=0.01):
|
|
318
315
|
"""
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import
|
|
2
|
+
from ..custom_logging import log
|
|
3
3
|
import traceback
|
|
4
4
|
from typing import Callable, List, Any, AsyncGenerator, Dict
|
|
5
5
|
from tenacity import AsyncRetrying, retry_if_exception_type, wait_random_exponential, stop_after_attempt
|
|
6
6
|
|
|
7
|
-
logger = logging.getLogger(__name__)
|
|
8
|
-
|
|
9
7
|
class AsyncTaskRunner:
|
|
10
8
|
def __init__(self, retry_enabled=False, retry_kwargs=None):
|
|
11
9
|
self.tasks = []
|
|
@@ -14,18 +12,19 @@ class AsyncTaskRunner:
|
|
|
14
12
|
|
|
15
13
|
def add_task(self, func: Callable[..., Any], *args: Any):
|
|
16
14
|
"""Adds a task to the list of tasks to be executed."""
|
|
17
|
-
|
|
15
|
+
log.info(f"Adding task: {func.__name__} with args: {args}")
|
|
18
16
|
self.tasks.append((func.__name__, func, args))
|
|
19
17
|
|
|
20
18
|
async def run_async_as_completed(self) -> AsyncGenerator[Dict[str, Any], None]:
|
|
21
19
|
"""Runs all tasks concurrently and yields results as they complete."""
|
|
22
|
-
|
|
20
|
+
log.info("Running tasks asynchronously and yielding results as they complete")
|
|
23
21
|
tasks = {}
|
|
24
22
|
for name, func, args in self.tasks:
|
|
25
23
|
coro = self._task_wrapper(name, func, args)
|
|
26
24
|
task = asyncio.create_task(coro)
|
|
27
25
|
tasks[task] = name
|
|
28
26
|
|
|
27
|
+
log.info(f"Start async run with {len(self.tasks)} runners")
|
|
29
28
|
while tasks:
|
|
30
29
|
done, _ = await asyncio.wait(tasks.keys(), return_when=asyncio.FIRST_COMPLETED)
|
|
31
30
|
for task in done:
|
|
@@ -34,7 +33,7 @@ class AsyncTaskRunner:
|
|
|
34
33
|
result = await task
|
|
35
34
|
yield {name: result}
|
|
36
35
|
except Exception as e:
|
|
37
|
-
|
|
36
|
+
log.error(f"Task {name} resulted in an error: {e}\n{traceback.format_exc()}")
|
|
38
37
|
yield {name: e}
|
|
39
38
|
|
|
40
39
|
async def _task_wrapper(self, name: str, func: Callable[..., Any], args: Any) -> Any:
|
|
@@ -65,13 +64,13 @@ class AsyncTaskRunner:
|
|
|
65
64
|
try:
|
|
66
65
|
return await run_func()
|
|
67
66
|
except Exception as e:
|
|
68
|
-
|
|
67
|
+
log.error(f"Error in task {name}: {e}\n{traceback.format_exc()}")
|
|
69
68
|
raise
|
|
70
69
|
|
|
71
70
|
def _log_results(self, results: List[Any]):
|
|
72
71
|
"""Logs the results of the task executions."""
|
|
73
72
|
for result in results:
|
|
74
73
|
if isinstance(result, Exception):
|
|
75
|
-
|
|
74
|
+
log.error(f"Task resulted in an error: {result}")
|
|
76
75
|
else:
|
|
77
|
-
|
|
76
|
+
log.info(f"Task completed successfully: {result}")
|
|
@@ -51,6 +51,17 @@ class ContentBuffer:
|
|
|
51
51
|
Adds the given text to the existing content of the buffer.
|
|
52
52
|
"""
|
|
53
53
|
self.content += text
|
|
54
|
+
|
|
55
|
+
async def async_write(self, text: str):
|
|
56
|
+
"""
|
|
57
|
+
Asynchronously writes text to the content buffer.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
text (str): The text to be added to the buffer.
|
|
61
|
+
|
|
62
|
+
Adds the given text to the existing content of the buffer.
|
|
63
|
+
"""
|
|
64
|
+
self.content += text
|
|
54
65
|
|
|
55
66
|
def read(self) -> str:
|
|
56
67
|
"""
|
|
@@ -63,6 +74,15 @@ class ContentBuffer:
|
|
|
63
74
|
"""
|
|
64
75
|
return self.content
|
|
65
76
|
|
|
77
|
+
async def async_read(self) -> str:
|
|
78
|
+
"""
|
|
79
|
+
Asynchronously reads the entire content from the buffer.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
str: The content of the buffer.
|
|
83
|
+
"""
|
|
84
|
+
return self.content
|
|
85
|
+
|
|
66
86
|
def clear(self):
|
|
67
87
|
"""
|
|
68
88
|
Clears the content buffer.
|
|
@@ -71,7 +91,15 @@ class ContentBuffer:
|
|
|
71
91
|
"""
|
|
72
92
|
self.content = ""
|
|
73
93
|
|
|
74
|
-
|
|
94
|
+
async def async_clear(self):
|
|
95
|
+
"""
|
|
96
|
+
Asynchronously clears the content buffer.
|
|
97
|
+
|
|
98
|
+
Empties the buffer content, resetting it to an empty string.
|
|
99
|
+
"""
|
|
100
|
+
self.content = ""
|
|
101
|
+
|
|
102
|
+
|
|
75
103
|
class BufferStreamingStdOutCallbackHandler(StreamingStdOutCallbackHandler):
|
|
76
104
|
"""
|
|
77
105
|
A callback handler for streaming LLM output to a content buffer.
|
|
@@ -136,6 +164,24 @@ class BufferStreamingStdOutCallbackHandler(StreamingStdOutCallbackHandler):
|
|
|
136
164
|
if not self.in_code_block:
|
|
137
165
|
self._process_buffer()
|
|
138
166
|
|
|
167
|
+
async def async_on_llm_new_token(self, token: str, **kwargs: Any) -> None:
|
|
168
|
+
"""
|
|
169
|
+
Asynchronously processes a new token from the LLM output.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
token (str): The new token generated by the LLM.
|
|
173
|
+
**kwargs: Additional keyword arguments.
|
|
174
|
+
"""
|
|
175
|
+
log.debug(f"async_on_llm_new_token: {token}")
|
|
176
|
+
|
|
177
|
+
self.buffer += token
|
|
178
|
+
|
|
179
|
+
if '```' in token:
|
|
180
|
+
self.in_code_block = not self.in_code_block
|
|
181
|
+
|
|
182
|
+
if not self.in_code_block:
|
|
183
|
+
await self._async_process_buffer()
|
|
184
|
+
|
|
139
185
|
def _process_buffer(self):
|
|
140
186
|
"""
|
|
141
187
|
Processes the buffer content and writes to the content buffer.
|
|
@@ -154,6 +200,24 @@ class BufferStreamingStdOutCallbackHandler(StreamingStdOutCallbackHandler):
|
|
|
154
200
|
self.content_buffer.write(self.buffer)
|
|
155
201
|
self.buffer = ""
|
|
156
202
|
|
|
203
|
+
async def _async_process_buffer(self):
|
|
204
|
+
"""
|
|
205
|
+
Asynchronously processes the buffer content and writes to the content buffer.
|
|
206
|
+
|
|
207
|
+
If the buffer ends with a numbered list pattern or specified tokens, the buffer is flushed
|
|
208
|
+
to the content buffer. Otherwise, the buffer is left intact for further accumulation.
|
|
209
|
+
"""
|
|
210
|
+
matches = list(re.finditer(r'\n(\d+\.\s)', self.buffer))
|
|
211
|
+
if matches:
|
|
212
|
+
last_match = matches[-1]
|
|
213
|
+
start_of_last_match = last_match.start() + 1
|
|
214
|
+
await self.content_buffer.async_write(self.buffer[:start_of_last_match])
|
|
215
|
+
self.buffer = self.buffer[start_of_last_match:]
|
|
216
|
+
else:
|
|
217
|
+
if any(self.buffer.endswith(t) for t in self.tokens):
|
|
218
|
+
await self.content_buffer.async_write(self.buffer)
|
|
219
|
+
self.buffer = ""
|
|
220
|
+
|
|
157
221
|
def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
|
|
158
222
|
"""
|
|
159
223
|
Handles the end of LLM streaming.
|
|
@@ -172,3 +236,19 @@ class BufferStreamingStdOutCallbackHandler(StreamingStdOutCallbackHandler):
|
|
|
172
236
|
|
|
173
237
|
self.stream_finished.set()
|
|
174
238
|
log.info("Streaming LLM response ended successfully")
|
|
239
|
+
|
|
240
|
+
async def async_on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
|
|
241
|
+
"""
|
|
242
|
+
Asynchronously handles the end of LLM streaming.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
response (LLMResult): The result returned by the LLM.
|
|
246
|
+
**kwargs: Additional keyword arguments.
|
|
247
|
+
"""
|
|
248
|
+
if self.buffer:
|
|
249
|
+
await self.content_buffer.async_write(self.buffer)
|
|
250
|
+
self.buffer = ""
|
|
251
|
+
log.info("Flushing remaining LLM response buffer")
|
|
252
|
+
|
|
253
|
+
self.stream_finished.set()
|
|
254
|
+
log.info("Streaming LLM response ended successfully")
|
|
@@ -153,42 +153,44 @@ def print_grounding_response(response):
|
|
|
153
153
|
prev_index = 0
|
|
154
154
|
markdown_text = ""
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if not context:
|
|
161
|
-
log.info(f"Skipping Grounding Attribution {attribution}")
|
|
162
|
-
continue
|
|
163
|
-
|
|
164
|
-
title = context.title
|
|
165
|
-
uri = context.uri
|
|
166
|
-
end_index = int(attribution.segment.end_index)
|
|
156
|
+
for grounding_support in grounding_metadata.grounding_supports:
|
|
157
|
+
text_segment = text_bytes[
|
|
158
|
+
prev_index : grounding_support.segment.end_index
|
|
159
|
+
].decode(ENCODING)
|
|
167
160
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
161
|
+
footnotes_text = ""
|
|
162
|
+
for grounding_chunk_index in grounding_support.grounding_chunk_indices:
|
|
163
|
+
footnotes_text += f"[{grounding_chunk_index + 1}]"
|
|
171
164
|
|
|
172
|
-
text_segment
|
|
173
|
-
|
|
174
|
-
prev_index = end_index
|
|
165
|
+
markdown_text += f"{text_segment} {footnotes_text}\n"
|
|
166
|
+
prev_index = grounding_support.segment.end_index
|
|
175
167
|
|
|
176
168
|
if prev_index < len(text_bytes):
|
|
177
169
|
markdown_text += str(text_bytes[prev_index:], encoding=ENCODING)
|
|
178
170
|
|
|
179
|
-
markdown_text += "\n## Grounding Sources\n"
|
|
171
|
+
markdown_text += "\n----\n## Grounding Sources\n"
|
|
180
172
|
|
|
181
173
|
if grounding_metadata.web_search_queries:
|
|
182
174
|
markdown_text += (
|
|
183
175
|
f"\n**Web Search Queries:** {grounding_metadata.web_search_queries}\n"
|
|
184
176
|
)
|
|
177
|
+
if grounding_metadata.search_entry_point:
|
|
178
|
+
markdown_text += f"\n**Search Entry Point:**\n {grounding_metadata.search_entry_point.rendered_content}\n"
|
|
185
179
|
elif grounding_metadata.retrieval_queries:
|
|
186
180
|
markdown_text += (
|
|
187
181
|
f"\n**Retrieval Queries:** {grounding_metadata.retrieval_queries}\n"
|
|
188
182
|
)
|
|
189
183
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
184
|
+
markdown_text += "### Grounding Chunks\n"
|
|
185
|
+
|
|
186
|
+
for index, grounding_chunk in enumerate(
|
|
187
|
+
grounding_metadata.grounding_chunks, start=1
|
|
188
|
+
):
|
|
189
|
+
context = grounding_chunk.web or grounding_chunk.retrieved_context
|
|
190
|
+
if not context:
|
|
191
|
+
print(f"Skipping Grounding Chunk {grounding_chunk}")
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
markdown_text += f"{index}. [{context.title}]({context.uri})\n"
|
|
195
|
+
|
|
196
|
+
return markdown_text
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.95.
|
|
3
|
+
Version: 0.95.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.95.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.95.2.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|