sunholo 0.140.13__tar.gz → 0.142.0__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.140.13/src/sunholo.egg-info → sunholo-0.142.0}/PKG-INFO +18 -12
- {sunholo-0.140.13 → sunholo-0.142.0}/README.md +17 -11
- {sunholo-0.140.13 → sunholo-0.142.0}/pyproject.toml +1 -1
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/discovery_engine/discovery_engine_client.py +1 -1
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/invoke/async_class.py +98 -8
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/mime.py +14 -2
- sunholo-0.142.0/src/sunholo/utils/proto_convert.py +25 -0
- {sunholo-0.140.13 → sunholo-0.142.0/src/sunholo.egg-info}/PKG-INFO +18 -12
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo.egg-info/SOURCES.txt +1 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/LICENSE.txt +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/MANIFEST.in +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/setup.cfg +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/chat_history.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/dispatch_to_qa.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/fastapi/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/fastapi/base.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/fastapi/qna_routes.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/flask/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/flask/base.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/flask/vac_routes.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/langserve.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/pubsub.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/route.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/special_commands.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/agents/swagger.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/archive/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/archive/archive.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/auth/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/auth/gcloud.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/auth/refresh.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/auth/run.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/azure/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/azure/auth.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/azure/blobs.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/azure/event_grid.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/bots/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/bots/discord.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/bots/github_webhook.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/bots/webapp.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/chunker/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/chunker/azure.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/chunker/doc_handling.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/chunker/encode_metadata.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/chunker/images.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/chunker/loaders.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/chunker/message_data.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/chunker/pdfs.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/chunker/process_chunker_data.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/chunker/publish.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/chunker/pubsub.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/chunker/splitter.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/cli/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/cli/chat_vac.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/cli/cli.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/cli/cli_init.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/cli/configs.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/cli/deploy.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/cli/embedder.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/cli/merge_texts.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/cli/run_proxy.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/cli/sun_rich.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/cli/swagger.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/cli/vertex.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/components/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/components/llm.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/components/retriever.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/components/vectorstore.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/custom_logging.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/alloydb.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/alloydb_client.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/database.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/lancedb.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/sql/sb/create_function.sql +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/sql/sb/create_function_time.sql +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/sql/sb/create_table.sql +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/sql/sb/delete_source_row.sql +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/sql/sb/return_sources.sql +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/sql/sb/setup.sql +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/static_dbs.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/database/uuid.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/discovery_engine/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/discovery_engine/chunker_handler.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/discovery_engine/cli.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/discovery_engine/create_new.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/discovery_engine/get_ai_search_chunks.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/embedder/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/embedder/embed_chunk.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/embedder/embed_metadata.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/excel/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/excel/plugin.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/gcs/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/gcs/add_file.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/gcs/download_folder.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/gcs/download_gcs_text.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/gcs/download_url.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/gcs/extract_and_sign.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/gcs/metadata.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/genai/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/genai/file_handling.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/genai/genaiv2.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/genai/images.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/genai/init.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/genai/process_funcs_cls.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/genai/safety.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/invoke/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/invoke/direct_vac_func.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/invoke/invoke_vac_utils.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/langchain_types.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/langfuse/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/langfuse/callback.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/langfuse/evals.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/langfuse/prompts.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/llamaindex/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/llamaindex/get_files.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/llamaindex/import_files.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/llamaindex/llamaindex_class.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/llamaindex/user_history.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/lookup/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/lookup/model_lookup.yaml +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/mcp/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/mcp/cli.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/ollama/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/ollama/ollama_images.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/pubsub/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/pubsub/process_pubsub.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/pubsub/pubsub_manager.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/qna/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/qna/parsers.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/qna/retry.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/senses/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/senses/stream_voice.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/streaming/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/streaming/content_buffer.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/streaming/langserve.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/streaming/stream_lookup.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/streaming/streaming.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/summarise/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/summarise/summarise.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/agent/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/agent/agent_service.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/agent/app.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/agent/my_log.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/agent/tools/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/agent/tools/your_agent.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/agent/vac_service.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/project/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/project/app.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/project/my_log.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/project/vac_service.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/system_services/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/system_services/app.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/templates/system_services/my_log.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/terraform/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/terraform/tfvars_editor.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/tools/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/tools/web_browser.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/api_key.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/big_context.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/config.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/config_class.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/config_schema.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/gcp.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/gcp_project.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/parsers.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/timedelta.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/user_ids.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/utils/version.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/vertex/__init__.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/vertex/extensions_call.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/vertex/extensions_class.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/vertex/genai_functions.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/vertex/init.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/vertex/memory_tools.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/vertex/safety.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/vertex/type_dict_to_json.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo.egg-info/dependency_links.txt +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo.egg-info/entry_points.txt +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo.egg-info/requires.txt +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo.egg-info/top_level.txt +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/tests/test_async.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/tests/test_async_genai2.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/tests/test_chat_history.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/tests/test_config.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/tests/test_genai2.py +0 -0
- {sunholo-0.140.13 → sunholo-0.142.0}/tests/test_unstructured.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: sunholo
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.142.0
|
4
4
|
Summary: AI DevOps - a package to help deploy GenAI to the Cloud.
|
5
5
|
Author-email: Holosun ApS <multivac@sunholo.com>
|
6
6
|
License: Apache License, Version 2.0
|
@@ -255,7 +255,7 @@ vac:
|
|
255
255
|
sunholo vac chat my-agent
|
256
256
|
```
|
257
257
|
|
258
|
-
4. **
|
258
|
+
4. **Run your agent as a local Flask app:**
|
259
259
|
```bash
|
260
260
|
sunholo deploy my-agent
|
261
261
|
```
|
@@ -369,26 +369,32 @@ vac:
|
|
369
369
|
|
370
370
|
```bash
|
371
371
|
# Project Management
|
372
|
-
sunholo init <project-name> # Create new project
|
372
|
+
sunholo init <project-name> # Create new project from template
|
373
373
|
sunholo list-configs # List all configurations
|
374
|
-
sunholo list-configs --validate # Validate
|
374
|
+
sunholo list-configs --validate # Validate configurations
|
375
375
|
|
376
376
|
# Development
|
377
377
|
sunholo vac chat <vac-name> # Chat with a VAC locally
|
378
|
-
sunholo vac list # List available VACs
|
378
|
+
sunholo vac list # List available VACs
|
379
|
+
sunholo vac get-url <vac-name> # Get Cloud Run URL for a VAC
|
379
380
|
sunholo proxy start <service> # Start local proxy to cloud service
|
380
|
-
|
381
|
-
#
|
382
|
-
sunholo deploy <vac-name> # Deploy to Cloud Run
|
383
|
-
sunholo deploy <vac-name> --dev # Deploy to dev environment
|
381
|
+
sunholo proxy list # List running proxies
|
382
|
+
sunholo deploy <vac-name> # Run Flask app locally
|
384
383
|
|
385
384
|
# Document Processing
|
386
|
-
sunholo embed <vac-name> #
|
385
|
+
sunholo embed <vac-name> # Process and embed documents
|
387
386
|
sunholo merge-text <folder> <output> # Merge files for context
|
388
387
|
|
389
388
|
# Cloud Services
|
390
|
-
sunholo discovery-engine create <name> # Create Discovery Engine
|
391
|
-
sunholo
|
389
|
+
sunholo discovery-engine create <name> # Create Discovery Engine instance
|
390
|
+
sunholo vertex list-extensions # List Vertex AI extensions
|
391
|
+
sunholo swagger <vac-name> # Generate OpenAPI spec
|
392
|
+
|
393
|
+
# Integration Tools
|
394
|
+
sunholo excel-init # Initialize Excel plugin
|
395
|
+
sunholo llamaindex <query> # Query with LlamaIndex
|
396
|
+
sunholo mcp list-tools # List MCP tools
|
397
|
+
sunholo tts <text> # Text-to-speech synthesis
|
392
398
|
```
|
393
399
|
|
394
400
|
## 📝 Examples
|
@@ -69,7 +69,7 @@ vac:
|
|
69
69
|
sunholo vac chat my-agent
|
70
70
|
```
|
71
71
|
|
72
|
-
4. **
|
72
|
+
4. **Run your agent as a local Flask app:**
|
73
73
|
```bash
|
74
74
|
sunholo deploy my-agent
|
75
75
|
```
|
@@ -183,26 +183,32 @@ vac:
|
|
183
183
|
|
184
184
|
```bash
|
185
185
|
# Project Management
|
186
|
-
sunholo init <project-name> # Create new project
|
186
|
+
sunholo init <project-name> # Create new project from template
|
187
187
|
sunholo list-configs # List all configurations
|
188
|
-
sunholo list-configs --validate # Validate
|
188
|
+
sunholo list-configs --validate # Validate configurations
|
189
189
|
|
190
190
|
# Development
|
191
191
|
sunholo vac chat <vac-name> # Chat with a VAC locally
|
192
|
-
sunholo vac list # List available VACs
|
192
|
+
sunholo vac list # List available VACs
|
193
|
+
sunholo vac get-url <vac-name> # Get Cloud Run URL for a VAC
|
193
194
|
sunholo proxy start <service> # Start local proxy to cloud service
|
194
|
-
|
195
|
-
#
|
196
|
-
sunholo deploy <vac-name> # Deploy to Cloud Run
|
197
|
-
sunholo deploy <vac-name> --dev # Deploy to dev environment
|
195
|
+
sunholo proxy list # List running proxies
|
196
|
+
sunholo deploy <vac-name> # Run Flask app locally
|
198
197
|
|
199
198
|
# Document Processing
|
200
|
-
sunholo embed <vac-name> #
|
199
|
+
sunholo embed <vac-name> # Process and embed documents
|
201
200
|
sunholo merge-text <folder> <output> # Merge files for context
|
202
201
|
|
203
202
|
# Cloud Services
|
204
|
-
sunholo discovery-engine create <name> # Create Discovery Engine
|
205
|
-
sunholo
|
203
|
+
sunholo discovery-engine create <name> # Create Discovery Engine instance
|
204
|
+
sunholo vertex list-extensions # List Vertex AI extensions
|
205
|
+
sunholo swagger <vac-name> # Generate OpenAPI spec
|
206
|
+
|
207
|
+
# Integration Tools
|
208
|
+
sunholo excel-init # Initialize Excel plugin
|
209
|
+
sunholo llamaindex <query> # Query with LlamaIndex
|
210
|
+
sunholo mcp list-tools # List MCP tools
|
211
|
+
sunholo tts <text> # Text-to-speech synthesis
|
206
212
|
```
|
207
213
|
|
208
214
|
## 📝 Examples
|
{sunholo-0.140.13 → sunholo-0.142.0}/src/sunholo/discovery_engine/discovery_engine_client.py
RENAMED
@@ -280,7 +280,7 @@ class DiscoveryEngineClient:
|
|
280
280
|
# Use async_search_with_filters with filter_str=None to perform a regular search
|
281
281
|
return await self.async_search_with_filters(
|
282
282
|
query=query,
|
283
|
-
filter_str=
|
283
|
+
filter_str=filter_str,
|
284
284
|
num_previous_chunks=num_previous_chunks,
|
285
285
|
num_next_chunks=num_next_chunks,
|
286
286
|
page_size=page_size,
|
@@ -8,12 +8,39 @@ 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,
|
11
|
+
def __init__(self,
|
12
|
+
retry_enabled: bool = False,
|
13
|
+
retry_kwargs: dict = None,
|
14
|
+
timeout: int = 120,
|
15
|
+
max_concurrency: int = 20,
|
16
|
+
heartbeat_extends_timeout: bool = False,
|
17
|
+
hard_timeout: int = None):
|
18
|
+
"""
|
19
|
+
Initialize AsyncTaskRunner with configurable timeout behavior.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
retry_enabled: Whether to enable retries
|
23
|
+
retry_kwargs: Retry configuration
|
24
|
+
timeout: Base timeout for tasks (seconds)
|
25
|
+
max_concurrency: Maximum concurrent tasks
|
26
|
+
heartbeat_extends_timeout: If True, heartbeats reset the timeout timer
|
27
|
+
hard_timeout: Maximum absolute timeout regardless of heartbeats (seconds).
|
28
|
+
If None, defaults to timeout * 5 when heartbeat_extends_timeout=True
|
29
|
+
"""
|
12
30
|
self.tasks = []
|
13
31
|
self.retry_enabled = retry_enabled
|
14
32
|
self.retry_kwargs = retry_kwargs or {}
|
15
33
|
self.timeout = timeout
|
16
34
|
self.semaphore = asyncio.Semaphore(max_concurrency)
|
35
|
+
self.heartbeat_extends_timeout = heartbeat_extends_timeout
|
36
|
+
|
37
|
+
# Set hard timeout
|
38
|
+
if hard_timeout is not None:
|
39
|
+
self.hard_timeout = hard_timeout
|
40
|
+
elif heartbeat_extends_timeout:
|
41
|
+
self.hard_timeout = timeout * 5 # Default to 5x base timeout
|
42
|
+
else:
|
43
|
+
self.hard_timeout = timeout # Same as regular timeout
|
17
44
|
|
18
45
|
def add_task(self, func: Callable[..., Any], *args: Any, **kwargs: Any):
|
19
46
|
"""
|
@@ -39,9 +66,11 @@ class AsyncTaskRunner:
|
|
39
66
|
for name, func, args, kwargs in self.tasks:
|
40
67
|
log.info(f"Executing task: {name=}, {func=} with args: {args}, kwargs: {kwargs}")
|
41
68
|
completion_event = asyncio.Event()
|
42
|
-
|
69
|
+
last_heartbeat = {'time': time.time()} # Shared mutable object for heartbeat tracking
|
70
|
+
|
71
|
+
task_coro = self._run_with_retries_and_timeout(name, func, args, kwargs, queue, completion_event, last_heartbeat)
|
43
72
|
task = asyncio.create_task(task_coro)
|
44
|
-
heartbeat_coro = self._send_heartbeat(name, completion_event, queue)
|
73
|
+
heartbeat_coro = self._send_heartbeat(name, completion_event, queue, last_heartbeat)
|
45
74
|
heartbeat_task = asyncio.create_task(heartbeat_coro)
|
46
75
|
task_infos.append({
|
47
76
|
'name': name,
|
@@ -93,9 +122,12 @@ class AsyncTaskRunner:
|
|
93
122
|
args: tuple,
|
94
123
|
kwargs: dict,
|
95
124
|
queue: asyncio.Queue,
|
96
|
-
completion_event: asyncio.Event
|
125
|
+
completion_event: asyncio.Event,
|
126
|
+
last_heartbeat: dict) -> None:
|
97
127
|
try:
|
98
128
|
log.info(f"run_with_retries_and_timeout: {name=}, {func=} with args: {args}, kwargs: {kwargs}")
|
129
|
+
log.info(f"Timeout mode: heartbeat_extends_timeout={self.heartbeat_extends_timeout}, timeout={self.timeout}s, hard_timeout={self.hard_timeout}s")
|
130
|
+
|
99
131
|
if self.retry_enabled:
|
100
132
|
retry_kwargs = {
|
101
133
|
'wait': wait_random_exponential(multiplier=1, max=60),
|
@@ -106,13 +138,13 @@ class AsyncTaskRunner:
|
|
106
138
|
async for attempt in AsyncRetrying(**retry_kwargs):
|
107
139
|
with attempt:
|
108
140
|
log.info(f"Starting task '{name}' with retry")
|
109
|
-
result = await
|
141
|
+
result = await self._execute_task_with_timeout(func, name, last_heartbeat, *args, **kwargs)
|
110
142
|
await queue.put({'type': 'task_complete', 'func_name': name, 'result': result})
|
111
143
|
log.info(f"Sent 'task_complete' message for task '{name}'")
|
112
144
|
return
|
113
145
|
else:
|
114
146
|
log.info(f"Starting task '{name}' with no retry")
|
115
|
-
result = await
|
147
|
+
result = await self._execute_task_with_timeout(func, name, last_heartbeat, *args, **kwargs)
|
116
148
|
await queue.put({'type': 'task_complete', 'func_name': name, 'result': result})
|
117
149
|
log.info(f"Sent 'task_complete' message for task '{name}'")
|
118
150
|
except asyncio.TimeoutError:
|
@@ -125,6 +157,55 @@ class AsyncTaskRunner:
|
|
125
157
|
log.info(f"Task '{name}' completed.")
|
126
158
|
completion_event.set()
|
127
159
|
|
160
|
+
async def _execute_task_with_timeout(self, func: Callable[..., Any], name: str, last_heartbeat: dict, *args: Any, **kwargs: Any) -> Any:
|
161
|
+
"""
|
162
|
+
Execute task with either fixed timeout or heartbeat-extendable timeout.
|
163
|
+
"""
|
164
|
+
if not self.heartbeat_extends_timeout:
|
165
|
+
# Original behavior - fixed timeout
|
166
|
+
return await asyncio.wait_for(self._execute_task(func, *args, **kwargs), timeout=self.timeout)
|
167
|
+
else:
|
168
|
+
# New behavior - heartbeat extends timeout
|
169
|
+
return await self._execute_task_with_heartbeat_timeout(func, name, last_heartbeat, *args, **kwargs)
|
170
|
+
|
171
|
+
async def _execute_task_with_heartbeat_timeout(self, func: Callable[..., Any], name: str, last_heartbeat: dict, *args: Any, **kwargs: Any) -> Any:
|
172
|
+
"""
|
173
|
+
Execute task with heartbeat-extendable timeout and hard timeout limit.
|
174
|
+
"""
|
175
|
+
start_time = time.time()
|
176
|
+
task = asyncio.create_task(self._execute_task(func, *args, **kwargs))
|
177
|
+
|
178
|
+
while not task.done():
|
179
|
+
current_time = time.time()
|
180
|
+
|
181
|
+
# Check hard timeout first (absolute limit)
|
182
|
+
if current_time - start_time > self.hard_timeout:
|
183
|
+
task.cancel()
|
184
|
+
try:
|
185
|
+
await task
|
186
|
+
except asyncio.CancelledError:
|
187
|
+
pass
|
188
|
+
raise asyncio.TimeoutError(f"Hard timeout exceeded ({self.hard_timeout}s)")
|
189
|
+
|
190
|
+
# Check soft timeout (extends with heartbeats)
|
191
|
+
time_since_heartbeat = current_time - last_heartbeat['time']
|
192
|
+
if time_since_heartbeat > self.timeout:
|
193
|
+
task.cancel()
|
194
|
+
try:
|
195
|
+
await task
|
196
|
+
except asyncio.CancelledError:
|
197
|
+
pass
|
198
|
+
raise asyncio.TimeoutError(f"Timeout exceeded - no heartbeat for {self.timeout}s")
|
199
|
+
|
200
|
+
# Wait a bit before checking again
|
201
|
+
try:
|
202
|
+
await asyncio.wait_for(asyncio.shield(task), timeout=1.0)
|
203
|
+
break # Task completed
|
204
|
+
except asyncio.TimeoutError:
|
205
|
+
continue # Check timeouts again
|
206
|
+
|
207
|
+
return await task
|
208
|
+
|
128
209
|
async def _execute_task(self, func: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
|
129
210
|
"""
|
130
211
|
Executes the given task function and returns its result.
|
@@ -143,14 +224,16 @@ class AsyncTaskRunner:
|
|
143
224
|
else:
|
144
225
|
return await asyncio.to_thread(func, *args, **kwargs)
|
145
226
|
|
146
|
-
async def _send_heartbeat(self, func_name: str, completion_event: asyncio.Event, queue: asyncio.Queue, interval: int = 2):
|
227
|
+
async def _send_heartbeat(self, func_name: str, completion_event: asyncio.Event, queue: asyncio.Queue, last_heartbeat: dict, interval: int = 2):
|
147
228
|
"""
|
148
229
|
Sends periodic heartbeat updates to indicate the task is still in progress.
|
230
|
+
Updates last_heartbeat time if heartbeat_extends_timeout is enabled.
|
149
231
|
|
150
232
|
Args:
|
151
233
|
func_name (str): The name of the task function.
|
152
234
|
completion_event (asyncio.Event): Event to signal when the task is completed.
|
153
235
|
queue (asyncio.Queue): The queue to send heartbeat messages to.
|
236
|
+
last_heartbeat (dict): Mutable dict containing the last heartbeat time.
|
154
237
|
interval (int): How frequently to send heartbeat messages (in seconds).
|
155
238
|
"""
|
156
239
|
start_time = time.time()
|
@@ -158,7 +241,14 @@ class AsyncTaskRunner:
|
|
158
241
|
try:
|
159
242
|
while not completion_event.is_set():
|
160
243
|
await asyncio.sleep(interval)
|
161
|
-
|
244
|
+
current_time = time.time()
|
245
|
+
elapsed_time = int(current_time - start_time)
|
246
|
+
|
247
|
+
# Update last heartbeat time if heartbeat extends timeout
|
248
|
+
if self.heartbeat_extends_timeout:
|
249
|
+
last_heartbeat['time'] = current_time
|
250
|
+
log.debug(f"Updated heartbeat time for task '{func_name}' at {current_time}")
|
251
|
+
|
162
252
|
heartbeat_message = {
|
163
253
|
'type': 'heartbeat',
|
164
254
|
'name': func_name,
|
@@ -83,7 +83,6 @@ def get_mime_type_gemini(file_path:str) -> str:
|
|
83
83
|
|
84
84
|
# Define the mapping of extensions to MIME types
|
85
85
|
mime_types = {
|
86
|
-
|
87
86
|
# Images
|
88
87
|
'png': 'image/png',
|
89
88
|
'jpg': 'image/jpeg',
|
@@ -111,7 +110,20 @@ def get_mime_type_gemini(file_path:str) -> str:
|
|
111
110
|
'rtf': 'text/rtf',
|
112
111
|
|
113
112
|
# Special case: JSON files are treated as plain text
|
114
|
-
'json': 'text/plain'
|
113
|
+
'json': 'text/plain',
|
114
|
+
|
115
|
+
# Audio
|
116
|
+
'mp3': 'audio/mp3',
|
117
|
+
'mpeg': 'audio/mpeg',
|
118
|
+
'wav': 'audio/wav',
|
119
|
+
|
120
|
+
# Video
|
121
|
+
'mov': 'video/mov',
|
122
|
+
'mp4': 'video/mp4',
|
123
|
+
'mpg': 'video/mpeg',
|
124
|
+
'avi': 'video/avi',
|
125
|
+
'wmv': 'video/wmv',
|
126
|
+
'flv': 'video/flv'
|
115
127
|
}
|
116
128
|
|
117
129
|
# Return the appropriate MIME type, defaulting to None if unknown
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
def convert_composite_to_native(value):
|
4
|
+
"""
|
5
|
+
Recursively converts a proto MapComposite or RepeatedComposite object to native Python types.
|
6
|
+
|
7
|
+
Args:
|
8
|
+
value: The proto object, which could be a MapComposite, RepeatedComposite, or a primitive.
|
9
|
+
|
10
|
+
Returns:
|
11
|
+
The equivalent Python dictionary, list, or primitive type.
|
12
|
+
"""
|
13
|
+
import proto
|
14
|
+
|
15
|
+
if isinstance(value, proto.marshal.collections.maps.MapComposite):
|
16
|
+
# Convert MapComposite to a dictionary, recursively processing its values
|
17
|
+
return {key: convert_composite_to_native(val) for key, val in value.items()}
|
18
|
+
elif isinstance(value, proto.marshal.collections.repeated.RepeatedComposite):
|
19
|
+
# Convert RepeatedComposite to a list, recursively processing its elements
|
20
|
+
return [convert_composite_to_native(item) for item in value]
|
21
|
+
else:
|
22
|
+
# If it's a primitive value, return it as is
|
23
|
+
return value
|
24
|
+
|
25
|
+
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: sunholo
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.142.0
|
4
4
|
Summary: AI DevOps - a package to help deploy GenAI to the Cloud.
|
5
5
|
Author-email: Holosun ApS <multivac@sunholo.com>
|
6
6
|
License: Apache License, Version 2.0
|
@@ -255,7 +255,7 @@ vac:
|
|
255
255
|
sunholo vac chat my-agent
|
256
256
|
```
|
257
257
|
|
258
|
-
4. **
|
258
|
+
4. **Run your agent as a local Flask app:**
|
259
259
|
```bash
|
260
260
|
sunholo deploy my-agent
|
261
261
|
```
|
@@ -369,26 +369,32 @@ vac:
|
|
369
369
|
|
370
370
|
```bash
|
371
371
|
# Project Management
|
372
|
-
sunholo init <project-name> # Create new project
|
372
|
+
sunholo init <project-name> # Create new project from template
|
373
373
|
sunholo list-configs # List all configurations
|
374
|
-
sunholo list-configs --validate # Validate
|
374
|
+
sunholo list-configs --validate # Validate configurations
|
375
375
|
|
376
376
|
# Development
|
377
377
|
sunholo vac chat <vac-name> # Chat with a VAC locally
|
378
|
-
sunholo vac list # List available VACs
|
378
|
+
sunholo vac list # List available VACs
|
379
|
+
sunholo vac get-url <vac-name> # Get Cloud Run URL for a VAC
|
379
380
|
sunholo proxy start <service> # Start local proxy to cloud service
|
380
|
-
|
381
|
-
#
|
382
|
-
sunholo deploy <vac-name> # Deploy to Cloud Run
|
383
|
-
sunholo deploy <vac-name> --dev # Deploy to dev environment
|
381
|
+
sunholo proxy list # List running proxies
|
382
|
+
sunholo deploy <vac-name> # Run Flask app locally
|
384
383
|
|
385
384
|
# Document Processing
|
386
|
-
sunholo embed <vac-name> #
|
385
|
+
sunholo embed <vac-name> # Process and embed documents
|
387
386
|
sunholo merge-text <folder> <output> # Merge files for context
|
388
387
|
|
389
388
|
# Cloud Services
|
390
|
-
sunholo discovery-engine create <name> # Create Discovery Engine
|
391
|
-
sunholo
|
389
|
+
sunholo discovery-engine create <name> # Create Discovery Engine instance
|
390
|
+
sunholo vertex list-extensions # List Vertex AI extensions
|
391
|
+
sunholo swagger <vac-name> # Generate OpenAPI spec
|
392
|
+
|
393
|
+
# Integration Tools
|
394
|
+
sunholo excel-init # Initialize Excel plugin
|
395
|
+
sunholo llamaindex <query> # Query with LlamaIndex
|
396
|
+
sunholo mcp list-tools # List MCP tools
|
397
|
+
sunholo tts <text> # Text-to-speech synthesis
|
392
398
|
```
|
393
399
|
|
394
400
|
## 📝 Examples
|
@@ -167,6 +167,7 @@ src/sunholo/utils/gcp.py
|
|
167
167
|
src/sunholo/utils/gcp_project.py
|
168
168
|
src/sunholo/utils/mime.py
|
169
169
|
src/sunholo/utils/parsers.py
|
170
|
+
src/sunholo/utils/proto_convert.py
|
170
171
|
src/sunholo/utils/timedelta.py
|
171
172
|
src/sunholo/utils/user_ids.py
|
172
173
|
src/sunholo/utils/version.py
|
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
|
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
|