sunholo 0.139.1__tar.gz → 0.140.4__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.
Files changed (189) hide show
  1. {sunholo-0.139.1/src/sunholo.egg-info → sunholo-0.140.4}/PKG-INFO +1 -1
  2. {sunholo-0.139.1 → sunholo-0.140.4}/pyproject.toml +1 -1
  3. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/__init__.py +1 -1
  4. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/chat_history.py +63 -0
  5. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/flask/__init__.py +0 -1
  6. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/flask/vac_routes.py +118 -7
  7. {sunholo-0.139.1 → sunholo-0.140.4/src/sunholo.egg-info}/PKG-INFO +1 -1
  8. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo.egg-info/SOURCES.txt +0 -1
  9. sunholo-0.139.1/src/sunholo/agents/flask/qna_routes.py +0 -604
  10. {sunholo-0.139.1 → sunholo-0.140.4}/LICENSE.txt +0 -0
  11. {sunholo-0.139.1 → sunholo-0.140.4}/MANIFEST.in +0 -0
  12. {sunholo-0.139.1 → sunholo-0.140.4}/README.md +0 -0
  13. {sunholo-0.139.1 → sunholo-0.140.4}/setup.cfg +0 -0
  14. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/__init__.py +0 -0
  15. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/dispatch_to_qa.py +0 -0
  16. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/fastapi/__init__.py +0 -0
  17. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/fastapi/base.py +0 -0
  18. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/fastapi/qna_routes.py +0 -0
  19. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/flask/base.py +0 -0
  20. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/langserve.py +0 -0
  21. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/pubsub.py +0 -0
  22. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/route.py +0 -0
  23. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/special_commands.py +0 -0
  24. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/agents/swagger.py +0 -0
  25. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/archive/__init__.py +0 -0
  26. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/archive/archive.py +0 -0
  27. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/auth/__init__.py +0 -0
  28. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/auth/gcloud.py +0 -0
  29. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/auth/refresh.py +0 -0
  30. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/auth/run.py +0 -0
  31. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/azure/__init__.py +0 -0
  32. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/azure/auth.py +0 -0
  33. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/azure/blobs.py +0 -0
  34. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/azure/event_grid.py +0 -0
  35. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/bots/__init__.py +0 -0
  36. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/bots/discord.py +0 -0
  37. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/bots/github_webhook.py +0 -0
  38. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/bots/webapp.py +0 -0
  39. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/chunker/__init__.py +0 -0
  40. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/chunker/azure.py +0 -0
  41. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/chunker/doc_handling.py +0 -0
  42. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/chunker/encode_metadata.py +0 -0
  43. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/chunker/images.py +0 -0
  44. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/chunker/loaders.py +0 -0
  45. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/chunker/message_data.py +0 -0
  46. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/chunker/pdfs.py +0 -0
  47. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/chunker/process_chunker_data.py +0 -0
  48. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/chunker/publish.py +0 -0
  49. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/chunker/pubsub.py +0 -0
  50. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/chunker/splitter.py +0 -0
  51. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/cli/__init__.py +0 -0
  52. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/cli/chat_vac.py +0 -0
  53. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/cli/cli.py +0 -0
  54. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/cli/cli_init.py +0 -0
  55. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/cli/configs.py +0 -0
  56. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/cli/deploy.py +0 -0
  57. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/cli/embedder.py +0 -0
  58. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/cli/merge_texts.py +0 -0
  59. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/cli/run_proxy.py +0 -0
  60. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/cli/sun_rich.py +0 -0
  61. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/cli/swagger.py +0 -0
  62. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/cli/vertex.py +0 -0
  63. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/components/__init__.py +0 -0
  64. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/components/llm.py +0 -0
  65. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/components/retriever.py +0 -0
  66. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/components/vectorstore.py +0 -0
  67. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/custom_logging.py +0 -0
  68. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/__init__.py +0 -0
  69. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/alloydb.py +0 -0
  70. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/alloydb_client.py +0 -0
  71. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/database.py +0 -0
  72. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/lancedb.py +0 -0
  73. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/sql/sb/create_function.sql +0 -0
  74. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/sql/sb/create_function_time.sql +0 -0
  75. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/sql/sb/create_table.sql +0 -0
  76. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/sql/sb/delete_source_row.sql +0 -0
  77. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/sql/sb/return_sources.sql +0 -0
  78. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/sql/sb/setup.sql +0 -0
  79. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/static_dbs.py +0 -0
  80. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/database/uuid.py +0 -0
  81. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/discovery_engine/__init__.py +0 -0
  82. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/discovery_engine/chunker_handler.py +0 -0
  83. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/discovery_engine/cli.py +0 -0
  84. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/discovery_engine/create_new.py +0 -0
  85. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/discovery_engine/discovery_engine_client.py +0 -0
  86. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/discovery_engine/get_ai_search_chunks.py +0 -0
  87. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/embedder/__init__.py +0 -0
  88. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/embedder/embed_chunk.py +0 -0
  89. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/embedder/embed_metadata.py +0 -0
  90. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/excel/__init__.py +0 -0
  91. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/excel/plugin.py +0 -0
  92. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/gcs/__init__.py +0 -0
  93. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/gcs/add_file.py +0 -0
  94. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/gcs/download_folder.py +0 -0
  95. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/gcs/download_gcs_text.py +0 -0
  96. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/gcs/download_url.py +0 -0
  97. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/gcs/extract_and_sign.py +0 -0
  98. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/gcs/metadata.py +0 -0
  99. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/genai/__init__.py +0 -0
  100. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/genai/file_handling.py +0 -0
  101. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/genai/genaiv2.py +0 -0
  102. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/genai/images.py +0 -0
  103. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/genai/init.py +0 -0
  104. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/genai/process_funcs_cls.py +0 -0
  105. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/genai/safety.py +0 -0
  106. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/invoke/__init__.py +0 -0
  107. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/invoke/async_class.py +0 -0
  108. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/invoke/direct_vac_func.py +0 -0
  109. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/invoke/invoke_vac_utils.py +0 -0
  110. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/langchain_types.py +0 -0
  111. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/langfuse/__init__.py +0 -0
  112. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/langfuse/callback.py +0 -0
  113. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/langfuse/evals.py +0 -0
  114. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/langfuse/prompts.py +0 -0
  115. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/llamaindex/__init__.py +0 -0
  116. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/llamaindex/get_files.py +0 -0
  117. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/llamaindex/import_files.py +0 -0
  118. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/llamaindex/llamaindex_class.py +0 -0
  119. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/llamaindex/user_history.py +0 -0
  120. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/lookup/__init__.py +0 -0
  121. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/lookup/model_lookup.yaml +0 -0
  122. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/mcp/__init__.py +0 -0
  123. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/mcp/cli.py +0 -0
  124. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/ollama/__init__.py +0 -0
  125. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/ollama/ollama_images.py +0 -0
  126. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/pubsub/__init__.py +0 -0
  127. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/pubsub/process_pubsub.py +0 -0
  128. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/pubsub/pubsub_manager.py +0 -0
  129. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/qna/__init__.py +0 -0
  130. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/qna/parsers.py +0 -0
  131. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/qna/retry.py +0 -0
  132. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/senses/__init__.py +0 -0
  133. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/senses/stream_voice.py +0 -0
  134. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/streaming/__init__.py +0 -0
  135. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/streaming/content_buffer.py +0 -0
  136. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/streaming/langserve.py +0 -0
  137. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/streaming/stream_lookup.py +0 -0
  138. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/streaming/streaming.py +0 -0
  139. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/summarise/__init__.py +0 -0
  140. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/summarise/summarise.py +0 -0
  141. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/agent/__init__.py +0 -0
  142. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/agent/agent_service.py +0 -0
  143. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/agent/app.py +0 -0
  144. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/agent/my_log.py +0 -0
  145. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/agent/tools/__init__.py +0 -0
  146. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/agent/tools/your_agent.py +0 -0
  147. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/agent/vac_service.py +0 -0
  148. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/project/__init__.py +0 -0
  149. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/project/app.py +0 -0
  150. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/project/my_log.py +0 -0
  151. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/project/vac_service.py +0 -0
  152. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/system_services/__init__.py +0 -0
  153. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/system_services/app.py +0 -0
  154. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/templates/system_services/my_log.py +0 -0
  155. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/terraform/__init__.py +0 -0
  156. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/terraform/tfvars_editor.py +0 -0
  157. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/tools/__init__.py +0 -0
  158. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/tools/web_browser.py +0 -0
  159. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/__init__.py +0 -0
  160. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/api_key.py +0 -0
  161. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/big_context.py +0 -0
  162. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/config.py +0 -0
  163. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/config_class.py +0 -0
  164. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/config_schema.py +0 -0
  165. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/gcp.py +0 -0
  166. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/gcp_project.py +0 -0
  167. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/mime.py +0 -0
  168. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/parsers.py +0 -0
  169. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/timedelta.py +0 -0
  170. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/user_ids.py +0 -0
  171. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/utils/version.py +0 -0
  172. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/vertex/__init__.py +0 -0
  173. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/vertex/extensions_call.py +0 -0
  174. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/vertex/extensions_class.py +0 -0
  175. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/vertex/genai_functions.py +0 -0
  176. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/vertex/init.py +0 -0
  177. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/vertex/memory_tools.py +0 -0
  178. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/vertex/safety.py +0 -0
  179. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo/vertex/type_dict_to_json.py +0 -0
  180. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo.egg-info/dependency_links.txt +0 -0
  181. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo.egg-info/entry_points.txt +0 -0
  182. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo.egg-info/requires.txt +0 -0
  183. {sunholo-0.139.1 → sunholo-0.140.4}/src/sunholo.egg-info/top_level.txt +0 -0
  184. {sunholo-0.139.1 → sunholo-0.140.4}/tests/test_async.py +0 -0
  185. {sunholo-0.139.1 → sunholo-0.140.4}/tests/test_async_genai2.py +0 -0
  186. {sunholo-0.139.1 → sunholo-0.140.4}/tests/test_chat_history.py +0 -0
  187. {sunholo-0.139.1 → sunholo-0.140.4}/tests/test_config.py +0 -0
  188. {sunholo-0.139.1 → sunholo-0.140.4}/tests/test_genai2.py +0 -0
  189. {sunholo-0.139.1 → sunholo-0.140.4}/tests/test_unstructured.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sunholo
3
- Version: 0.139.1
3
+ Version: 0.140.4
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sunholo"
7
- version = "0.139.1"
7
+ version = "0.140.4"
8
8
  description = "AI DevOps - a package to help deploy GenAI to the Cloud."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -2,6 +2,6 @@ from .chat_history import extract_chat_history
2
2
  from .dispatch_to_qa import send_to_qa, send_to_qa_async
3
3
  from .pubsub import process_pubsub
4
4
  from .special_commands import handle_special_commands, app_to_store, handle_files
5
- from .flask import register_qna_routes, create_app, VACRoutes
5
+ from .flask import create_app, VACRoutes
6
6
  from .fastapi import register_qna_fastapi_routes, create_fastapi_app
7
7
  from .swagger import config_to_swagger
@@ -1,6 +1,69 @@
1
1
  import json
2
2
  from ..custom_logging import log
3
3
 
4
+
5
+ async def extract_chat_history_async(chat_history=None):
6
+ """
7
+ Extracts paired chat history between human and AI messages.
8
+
9
+ For this lightweight processing, we use a simpler approach that minimizes overhead.
10
+
11
+ Args:
12
+ chat_history (list): List of chat messages.
13
+
14
+ Returns:
15
+ list: List of tuples with paired human and AI messages.
16
+ """
17
+ if not chat_history:
18
+ log.info("No chat history found")
19
+ return []
20
+
21
+ log.info(f"Extracting chat history: {chat_history}")
22
+ paired_messages = []
23
+
24
+ # Handle special case of initial bot message
25
+ if chat_history and is_bot(chat_history[0]):
26
+ first_message = chat_history[0]
27
+ log.info(f"Extracting first_message: {first_message}")
28
+ blank_human_message = {"name": "Human", "content": "", "embeds": []}
29
+
30
+ # Since create_message_element is so lightweight, we don't need async here
31
+ blank_element = create_message_element(blank_human_message)
32
+ bot_element = create_message_element(first_message)
33
+
34
+ paired_messages.append((blank_element, bot_element))
35
+ chat_history = chat_history[1:]
36
+
37
+ # Pre-process all messages in one batch (more efficient than one-by-one)
38
+ message_types = []
39
+ message_contents = []
40
+
41
+ for message in chat_history:
42
+ is_human_msg = is_human(message)
43
+ is_bot_msg = is_bot(message)
44
+
45
+ # Extract content for all messages at once
46
+ content = create_message_element(message)
47
+
48
+ message_types.append((is_human_msg, is_bot_msg))
49
+ message_contents.append(content)
50
+
51
+ # Pair messages efficiently
52
+ last_human_message = ""
53
+ for i, ((is_human_msg, is_bot_msg), content) in enumerate(zip(message_types, message_contents)):
54
+ if is_human_msg:
55
+ last_human_message = content
56
+ log.info(f"Extracted human message: {last_human_message}")
57
+ elif is_bot_msg:
58
+ ai_message = content
59
+ log.info(f"Extracted AI message: {ai_message}")
60
+ paired_messages.append((last_human_message, ai_message))
61
+ last_human_message = ""
62
+
63
+ log.info(f"Paired messages: {paired_messages}")
64
+ return paired_messages
65
+
66
+
4
67
  def extract_chat_history(chat_history=None):
5
68
  """
6
69
  Extracts paired chat history between human and AI messages.
@@ -1,3 +1,2 @@
1
- from .qna_routes import register_qna_routes
2
1
  from .base import create_app
3
2
  from .vac_routes import VACRoutes
@@ -8,6 +8,7 @@ import inspect
8
8
  import asyncio
9
9
 
10
10
  from ...agents import extract_chat_history, handle_special_commands
11
+ from ..chat_history import extract_chat_history_async
11
12
  from ...qna.parsers import parse_output
12
13
  from ...streaming import start_streaming_chat, start_streaming_chat_async
13
14
  from ...archive import archive_qa
@@ -57,11 +58,12 @@ if __name__ == "__main__":
57
58
  ```
58
59
 
59
60
  """
60
- def __init__(self, app, stream_interpreter, vac_interpreter=None, additional_routes=None):
61
+ def __init__(self, app, stream_interpreter: callable, vac_interpreter:callable=None, additional_routes:dict=None, async_stream:bool=False):
61
62
  self.app = app
62
63
  self.stream_interpreter = stream_interpreter
63
64
  self.vac_interpreter = vac_interpreter or partial(self.vac_interpreter_default)
64
65
  self.additional_routes = additional_routes if additional_routes is not None else []
66
+ self.async_stream = async_stream
65
67
  self.register_routes()
66
68
 
67
69
 
@@ -94,12 +96,16 @@ if __name__ == "__main__":
94
96
  # Basic routes
95
97
  self.app.route("/", methods=['GET'])(self.home)
96
98
  self.app.route("/health", methods=['GET'])(self.health)
97
-
98
- # Streaming VAC
99
- self.app.route('/vac/streaming/<vector_name>',
100
- methods=['POST'],
101
- provide_automatic_options=False)(self.handle_stream_vac)
102
-
99
+
100
+ if self.async_stream: # Use async treatment
101
+ log.info("async_stream enabled")
102
+ self.app.route('/vac/streaming/<vector_name>',
103
+ methods=['POST'],
104
+ provide_automatic_options=False)(self.handle_stream_vac_async)
105
+ else:
106
+ self.app.route('/vac/streaming/<vector_name>',
107
+ methods=['POST'],
108
+ provide_automatic_options=False)(self.handle_stream_vac)
103
109
  # Static VAC
104
110
  self.app.route('/vac/<vector_name>',
105
111
  methods=['POST'],
@@ -332,6 +338,51 @@ if __name__ == "__main__":
332
338
 
333
339
  return response
334
340
 
341
+ async def handle_stream_vac_async(self, vector_name):
342
+ observed_stream_interpreter = self.stream_interpreter
343
+ is_async = inspect.iscoroutinefunction(self.stream_interpreter)
344
+
345
+ if not is_async:
346
+ raise ValueError(f"Stream interpreter must be async: {observed_stream_interpreter}")
347
+
348
+ # Use the async version of prep_vac
349
+ prep = await self.prep_vac_async(request, vector_name)
350
+ log.info(f"Processing async prep: {prep}")
351
+ all_input = prep["all_input"]
352
+
353
+ log.info(f'Streaming async data with: {all_input}')
354
+
355
+ async def generate_response_content():
356
+ try:
357
+ # Direct async handling without the queue/thread approach
358
+ async_gen = start_streaming_chat_async(
359
+ question=all_input["user_input"],
360
+ vector_name=vector_name,
361
+ qna_func_async=observed_stream_interpreter,
362
+ chat_history=all_input["chat_history"],
363
+ wait_time=all_input["stream_wait_time"],
364
+ timeout=all_input["stream_timeout"],
365
+ **all_input["kwargs"]
366
+ )
367
+
368
+ log.info(f"{async_gen=}")
369
+ async for chunk in async_gen:
370
+ if isinstance(chunk, dict) and 'answer' in chunk:
371
+ await archive_qa(chunk, vector_name)
372
+ yield json.dumps(chunk)
373
+ else:
374
+ yield chunk
375
+
376
+ except Exception as e:
377
+ yield f"Streaming async Error: {str(e)} {traceback.format_exc()}"
378
+
379
+ response = Response(generate_response_content(), content_type='text/plain; charset=utf-8')
380
+ response.headers['Transfer-Encoding'] = 'chunked'
381
+
382
+ log.debug(f"streaming async response: {response}")
383
+
384
+ return response
385
+
335
386
  @staticmethod
336
387
  async def _async_generator_to_stream(async_gen_func):
337
388
  """Helper function to stream the async generator's values to the client."""
@@ -699,6 +750,66 @@ if __name__ == "__main__":
699
750
  "vac_config": vac_config
700
751
  }
701
752
 
753
+ async def prep_vac_async(self, request, vector_name):
754
+ """Async version of prep_vac."""
755
+ # Parse request data
756
+ if request.content_type.startswith('application/json'):
757
+ data = request.get_json()
758
+ elif request.content_type.startswith('multipart/form-data'):
759
+ data = request.form.to_dict()
760
+ if 'file' in request.files:
761
+ file = request.files['file']
762
+ if file.filename != '':
763
+ log.info(f"Found file: {file.filename} to upload to GCS")
764
+ try:
765
+ # Make file upload async if possible
766
+ image_uri, mime_type = await self.handle_file_upload_async(file, vector_name)
767
+ data["image_uri"] = image_uri
768
+ data["mime"] = mime_type
769
+ except Exception as e:
770
+ log.error(traceback.format_exc())
771
+ return jsonify({'error': str(e), 'traceback': traceback.format_exc()}), 500
772
+ else:
773
+ log.error("No file selected")
774
+ return jsonify({"error": "No file selected"}), 400
775
+ else:
776
+ return jsonify({"error": "Unsupported content type"}), 400
777
+
778
+ log.info(f"vac/{vector_name} got data: {data}")
779
+
780
+ # Run these operations concurrently
781
+ tasks = []
782
+
783
+ # Extract other data while configs load
784
+ user_input = data.pop('user_input').strip()
785
+ stream_wait_time = data.pop('stream_wait_time', 7)
786
+ stream_timeout = data.pop('stream_timeout', 120)
787
+ chat_history = data.pop('chat_history', None)
788
+ vector_name_param = data.pop('vector_name', vector_name)
789
+ data.pop('trace_id', None) # to ensure not in kwargs
790
+
791
+ # Task 3: Process chat history
792
+ chat_history_task = asyncio.create_task(extract_chat_history_async(chat_history))
793
+ tasks.append(chat_history_task)
794
+
795
+ # Await all tasks concurrently
796
+ results = await asyncio.gather(*tasks, return_exceptions=True)
797
+
798
+ paired_messages = results[0] if not isinstance(results[0], Exception) else []
799
+
800
+ # Only create span after we have trace
801
+ all_input = {
802
+ 'user_input': user_input,
803
+ 'vector_name': vector_name_param,
804
+ 'chat_history': paired_messages,
805
+ 'stream_wait_time': stream_wait_time,
806
+ 'stream_timeout': stream_timeout,
807
+ 'kwargs': data
808
+ }
809
+
810
+ return {
811
+ "all_input": all_input
812
+ }
702
813
 
703
814
  def handle_file_upload(self, file, vector_name):
704
815
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sunholo
3
- Version: 0.139.1
3
+ Version: 0.140.4
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
@@ -24,7 +24,6 @@ src/sunholo/agents/fastapi/base.py
24
24
  src/sunholo/agents/fastapi/qna_routes.py
25
25
  src/sunholo/agents/flask/__init__.py
26
26
  src/sunholo/agents/flask/base.py
27
- src/sunholo/agents/flask/qna_routes.py
28
27
  src/sunholo/agents/flask/vac_routes.py
29
28
  src/sunholo/archive/__init__.py
30
29
  src/sunholo/archive/archive.py