sunholo 0.119.18__tar.gz → 0.120.1__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 (186) hide show
  1. {sunholo-0.119.18/src/sunholo.egg-info → sunholo-0.120.1}/PKG-INFO +5 -3
  2. {sunholo-0.119.18 → sunholo-0.120.1}/pyproject.toml +5 -3
  3. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/auth/refresh.py +3 -2
  4. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/genai/file_handling.py +130 -65
  5. {sunholo-0.119.18 → sunholo-0.120.1/src/sunholo.egg-info}/PKG-INFO +5 -3
  6. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo.egg-info/requires.txt +4 -2
  7. {sunholo-0.119.18 → sunholo-0.120.1}/LICENSE.txt +0 -0
  8. {sunholo-0.119.18 → sunholo-0.120.1}/MANIFEST.in +0 -0
  9. {sunholo-0.119.18 → sunholo-0.120.1}/README.md +0 -0
  10. {sunholo-0.119.18 → sunholo-0.120.1}/setup.cfg +0 -0
  11. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/__init__.py +0 -0
  12. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/__init__.py +0 -0
  13. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/chat_history.py +0 -0
  14. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/dispatch_to_qa.py +0 -0
  15. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/fastapi/__init__.py +0 -0
  16. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/fastapi/base.py +0 -0
  17. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/fastapi/qna_routes.py +0 -0
  18. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/flask/__init__.py +0 -0
  19. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/flask/base.py +0 -0
  20. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/flask/qna_routes.py +0 -0
  21. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/flask/vac_routes.py +0 -0
  22. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/langserve.py +0 -0
  23. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/pubsub.py +0 -0
  24. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/route.py +0 -0
  25. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/special_commands.py +0 -0
  26. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/agents/swagger.py +0 -0
  27. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/archive/__init__.py +0 -0
  28. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/archive/archive.py +0 -0
  29. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/auth/__init__.py +0 -0
  30. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/auth/gcloud.py +0 -0
  31. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/auth/run.py +0 -0
  32. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/azure/__init__.py +0 -0
  33. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/azure/auth.py +0 -0
  34. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/azure/blobs.py +0 -0
  35. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/azure/event_grid.py +0 -0
  36. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/bots/__init__.py +0 -0
  37. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/bots/discord.py +0 -0
  38. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/bots/github_webhook.py +0 -0
  39. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/bots/webapp.py +0 -0
  40. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/chunker/__init__.py +0 -0
  41. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/chunker/azure.py +0 -0
  42. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/chunker/doc_handling.py +0 -0
  43. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/chunker/encode_metadata.py +0 -0
  44. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/chunker/images.py +0 -0
  45. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/chunker/loaders.py +0 -0
  46. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/chunker/message_data.py +0 -0
  47. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/chunker/pdfs.py +0 -0
  48. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/chunker/process_chunker_data.py +0 -0
  49. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/chunker/publish.py +0 -0
  50. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/chunker/pubsub.py +0 -0
  51. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/chunker/splitter.py +0 -0
  52. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/cli/__init__.py +0 -0
  53. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/cli/chat_vac.py +0 -0
  54. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/cli/cli.py +0 -0
  55. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/cli/cli_init.py +0 -0
  56. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/cli/configs.py +0 -0
  57. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/cli/deploy.py +0 -0
  58. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/cli/embedder.py +0 -0
  59. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/cli/merge_texts.py +0 -0
  60. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/cli/run_proxy.py +0 -0
  61. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/cli/sun_rich.py +0 -0
  62. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/cli/swagger.py +0 -0
  63. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/cli/vertex.py +0 -0
  64. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/components/__init__.py +0 -0
  65. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/components/llm.py +0 -0
  66. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/components/retriever.py +0 -0
  67. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/components/vectorstore.py +0 -0
  68. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/custom_logging.py +0 -0
  69. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/__init__.py +0 -0
  70. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/alloydb.py +0 -0
  71. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/alloydb_client.py +0 -0
  72. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/database.py +0 -0
  73. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/lancedb.py +0 -0
  74. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/sql/sb/create_function.sql +0 -0
  75. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/sql/sb/create_function_time.sql +0 -0
  76. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/sql/sb/create_table.sql +0 -0
  77. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/sql/sb/delete_source_row.sql +0 -0
  78. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/sql/sb/return_sources.sql +0 -0
  79. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/sql/sb/setup.sql +0 -0
  80. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/static_dbs.py +0 -0
  81. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/database/uuid.py +0 -0
  82. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/discovery_engine/__init__.py +0 -0
  83. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/discovery_engine/chunker_handler.py +0 -0
  84. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/discovery_engine/cli.py +0 -0
  85. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/discovery_engine/create_new.py +0 -0
  86. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/discovery_engine/discovery_engine_client.py +0 -0
  87. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/discovery_engine/get_ai_search_chunks.py +0 -0
  88. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/embedder/__init__.py +0 -0
  89. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/embedder/embed_chunk.py +0 -0
  90. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/embedder/embed_metadata.py +0 -0
  91. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/excel/__init__.py +0 -0
  92. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/excel/plugin.py +0 -0
  93. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/gcs/__init__.py +0 -0
  94. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/gcs/add_file.py +0 -0
  95. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/gcs/download_folder.py +0 -0
  96. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/gcs/download_url.py +0 -0
  97. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/gcs/extract_and_sign.py +0 -0
  98. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/gcs/metadata.py +0 -0
  99. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/genai/__init__.py +0 -0
  100. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/genai/genaiv2.py +0 -0
  101. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/genai/images.py +0 -0
  102. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/genai/init.py +0 -0
  103. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/genai/process_funcs_cls.py +0 -0
  104. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/genai/safety.py +0 -0
  105. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/invoke/__init__.py +0 -0
  106. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/invoke/async_class.py +0 -0
  107. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/invoke/direct_vac_func.py +0 -0
  108. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/invoke/invoke_vac_utils.py +0 -0
  109. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/langchain_types.py +0 -0
  110. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/langfuse/__init__.py +0 -0
  111. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/langfuse/callback.py +0 -0
  112. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/langfuse/evals.py +0 -0
  113. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/langfuse/prompts.py +0 -0
  114. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/llamaindex/__init__.py +0 -0
  115. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/llamaindex/get_files.py +0 -0
  116. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/llamaindex/import_files.py +0 -0
  117. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/llamaindex/llamaindex_class.py +0 -0
  118. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/llamaindex/user_history.py +0 -0
  119. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/lookup/__init__.py +0 -0
  120. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/lookup/model_lookup.yaml +0 -0
  121. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/mcp/__init__.py +0 -0
  122. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/mcp/cli.py +0 -0
  123. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/pubsub/__init__.py +0 -0
  124. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/pubsub/process_pubsub.py +0 -0
  125. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/pubsub/pubsub_manager.py +0 -0
  126. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/qna/__init__.py +0 -0
  127. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/qna/parsers.py +0 -0
  128. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/qna/retry.py +0 -0
  129. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/senses/__init__.py +0 -0
  130. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/senses/stream_voice.py +0 -0
  131. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/streaming/__init__.py +0 -0
  132. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/streaming/content_buffer.py +0 -0
  133. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/streaming/langserve.py +0 -0
  134. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/streaming/stream_lookup.py +0 -0
  135. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/streaming/streaming.py +0 -0
  136. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/summarise/__init__.py +0 -0
  137. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/summarise/summarise.py +0 -0
  138. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/agent/__init__.py +0 -0
  139. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/agent/agent_service.py +0 -0
  140. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/agent/app.py +0 -0
  141. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/agent/my_log.py +0 -0
  142. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/agent/tools/__init__.py +0 -0
  143. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/agent/tools/your_agent.py +0 -0
  144. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/agent/vac_service.py +0 -0
  145. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/project/__init__.py +0 -0
  146. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/project/app.py +0 -0
  147. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/project/my_log.py +0 -0
  148. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/project/vac_service.py +0 -0
  149. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/system_services/__init__.py +0 -0
  150. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/system_services/app.py +0 -0
  151. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/templates/system_services/my_log.py +0 -0
  152. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/terraform/__init__.py +0 -0
  153. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/terraform/tfvars_editor.py +0 -0
  154. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/tools/__init__.py +0 -0
  155. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/tools/web_browser.py +0 -0
  156. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/__init__.py +0 -0
  157. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/api_key.py +0 -0
  158. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/big_context.py +0 -0
  159. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/config.py +0 -0
  160. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/config_class.py +0 -0
  161. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/config_schema.py +0 -0
  162. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/gcp.py +0 -0
  163. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/gcp_project.py +0 -0
  164. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/mime.py +0 -0
  165. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/parsers.py +0 -0
  166. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/timedelta.py +0 -0
  167. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/user_ids.py +0 -0
  168. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/utils/version.py +0 -0
  169. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/vertex/__init__.py +0 -0
  170. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/vertex/extensions_call.py +0 -0
  171. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/vertex/extensions_class.py +0 -0
  172. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/vertex/genai_functions.py +0 -0
  173. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/vertex/init.py +0 -0
  174. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/vertex/memory_tools.py +0 -0
  175. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/vertex/safety.py +0 -0
  176. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo/vertex/type_dict_to_json.py +0 -0
  177. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo.egg-info/SOURCES.txt +0 -0
  178. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo.egg-info/dependency_links.txt +0 -0
  179. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo.egg-info/entry_points.txt +0 -0
  180. {sunholo-0.119.18 → sunholo-0.120.1}/src/sunholo.egg-info/top_level.txt +0 -0
  181. {sunholo-0.119.18 → sunholo-0.120.1}/tests/test_async.py +0 -0
  182. {sunholo-0.119.18 → sunholo-0.120.1}/tests/test_async_genai2.py +0 -0
  183. {sunholo-0.119.18 → sunholo-0.120.1}/tests/test_chat_history.py +0 -0
  184. {sunholo-0.119.18 → sunholo-0.120.1}/tests/test_config.py +0 -0
  185. {sunholo-0.119.18 → sunholo-0.120.1}/tests/test_genai2.py +0 -0
  186. {sunholo-0.119.18 → sunholo-0.120.1}/tests/test_unstructured.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: sunholo
3
- Version: 0.119.18
3
+ Version: 0.120.1
4
4
  Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
5
5
  Author-email: Holosun ApS <multivac@sunholo.com>
6
6
  License: Apache License, Version 2.0
@@ -28,6 +28,7 @@ Provides-Extra: test
28
28
  Requires-Dist: pytest; extra == "test"
29
29
  Requires-Dist: pytest-cov; extra == "test"
30
30
  Provides-Extra: all
31
+ Requires-Dist: aiofiles; extra == "all"
31
32
  Requires-Dist: aiohttp; extra == "all"
32
33
  Requires-Dist: anthropic[vertex]; extra == "all"
33
34
  Requires-Dist: asyncpg; extra == "all"
@@ -50,7 +51,7 @@ Requires-Dist: google-cloud-pubsub; extra == "all"
50
51
  Requires-Dist: google-cloud-discoveryengine>=0.13.4; extra == "all"
51
52
  Requires-Dist: google-cloud-texttospeech; extra == "all"
52
53
  Requires-Dist: google-generativeai>=0.7.1; extra == "all"
53
- Requires-Dist: google-genai; extra == "all"
54
+ Requires-Dist: google-genai>=0.2.2; extra == "all"
54
55
  Requires-Dist: gunicorn; extra == "all"
55
56
  Requires-Dist: httpcore; extra == "all"
56
57
  Requires-Dist: httpx; extra == "all"
@@ -122,6 +123,7 @@ Requires-Dist: pytesseract; extra == "pipeline"
122
123
  Requires-Dist: tabulate; extra == "pipeline"
123
124
  Requires-Dist: unstructured[all-docs,local-inference]; extra == "pipeline"
124
125
  Provides-Extra: gcp
126
+ Requires-Dist: aiofiles; extra == "gcp"
125
127
  Requires-Dist: anthropic[vertex]; extra == "gcp"
126
128
  Requires-Dist: google-api-python-client; extra == "gcp"
127
129
  Requires-Dist: google-auth-httplib2; extra == "gcp"
@@ -136,7 +138,7 @@ Requires-Dist: google-cloud-logging; extra == "gcp"
136
138
  Requires-Dist: google-cloud-pubsub; extra == "gcp"
137
139
  Requires-Dist: google-cloud-discoveryengine>=0.13.4; extra == "gcp"
138
140
  Requires-Dist: google-cloud-texttospeech; extra == "gcp"
139
- Requires-Dist: google-genai; extra == "gcp"
141
+ Requires-Dist: google-genai>=0.2.2; extra == "gcp"
140
142
  Requires-Dist: google-generativeai>=0.8.3; extra == "gcp"
141
143
  Requires-Dist: langchain-google-genai>=2.0.0; extra == "gcp"
142
144
  Requires-Dist: langchain_google_alloydb_pg>=0.2.2; extra == "gcp"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sunholo"
7
- version = "0.119.18"
7
+ version = "0.120.1"
8
8
  description = "Large Language Model DevOps - a package to help deploy LLMs to the Cloud."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -59,6 +59,7 @@ test = [
59
59
  # any other test dependencies
60
60
  ]
61
61
  all = [
62
+ "aiofiles",
62
63
  "aiohttp",
63
64
  "anthropic[vertex]",
64
65
  "asyncpg",
@@ -81,7 +82,7 @@ all = [
81
82
  "google-cloud-discoveryengine>=0.13.4",
82
83
  "google-cloud-texttospeech",
83
84
  "google-generativeai>=0.7.1",
84
- "google-genai",
85
+ "google-genai>=0.2.2",
85
86
  "gunicorn",
86
87
  "httpcore",
87
88
  "httpx",
@@ -159,6 +160,7 @@ pipeline = [
159
160
  "unstructured[local-inference,all-docs]"
160
161
  ]
161
162
  gcp = [
163
+ "aiofiles",
162
164
  "anthropic[vertex]",
163
165
  "google-api-python-client",
164
166
  "google-auth-httplib2",
@@ -173,7 +175,7 @@ gcp = [
173
175
  "google-cloud-pubsub",
174
176
  "google-cloud-discoveryengine>=0.13.4",
175
177
  "google-cloud-texttospeech",
176
- "google-genai",
178
+ "google-genai>=0.2.2",
177
179
  "google-generativeai>=0.8.3",
178
180
  "langchain-google-genai>=2.0.0",
179
181
  "langchain_google_alloydb_pg>=0.2.2",
@@ -9,8 +9,9 @@ from ..custom_logging import log
9
9
  def get_default_email():
10
10
 
11
11
  # https://stackoverflow.com/questions/64234214/how-to-generate-a-blob-signed-url-in-google-cloud-run
12
-
13
- gcs_credentials, project_id = refresh_credentials()
12
+ gcs_credentials, project_id = get_default_creds()
13
+ if gcs_credentials is None:
14
+ gcs_credentials, project_id = refresh_credentials()
14
15
 
15
16
  if gcs_credentials is None:
16
17
  log.error("Could not refresh the credentials properly.")
@@ -3,6 +3,7 @@ from ..gcs import get_bytes_from_gcs
3
3
 
4
4
  from functools import partial
5
5
  import mimetypes
6
+ import uuid
6
7
  import asyncio
7
8
  import tempfile
8
9
  import re
@@ -11,6 +12,7 @@ import traceback
11
12
  try:
12
13
  import google.generativeai as genai
13
14
  from google import genai as genaiv2
15
+
14
16
  except ImportError:
15
17
  genai = None
16
18
  genaiv2 = None
@@ -77,32 +79,44 @@ ALLOWED_MIME_TYPES = set(AUDIO_MIMES + VIDEO_MIMES + IMAGE_MIMES + DOCUMENT_MIME
77
79
  # ]
78
80
 
79
81
  def sanitize_file(filename):
82
+ """
83
+ Sanitize filename to conform to Gemini API requirements:
84
+ - Only lowercase alphanumeric characters and dashes
85
+ - Cannot begin or end with a dash
86
+ """
80
87
  # Split the filename into name and extension
81
88
  name, extension = os.path.splitext(filename)
82
89
 
83
- # Sanitize the name by removing invalid characters and converting to lowercase
84
- sanitized_name = re.sub(r'[^a-z0-9-]', '', name.lower())
85
- sanitized_name = re.sub(r'^-+|-+$', '', sanitized_name) # Remove leading or trailing dashes
90
+ # Convert to lowercase
91
+ name = name.lower()
92
+
93
+ # Replace any non-alphanumeric characters (including underscores) with dashes
94
+ sanitized_name = re.sub(r'[^a-z0-9]', '-', name)
95
+
96
+ # Remove consecutive dashes
97
+ sanitized_name = re.sub(r'-+', '-', sanitized_name)
86
98
 
87
- # Reattach the original extension
99
+ # Remove leading or trailing dashes
100
+ sanitized_name = sanitized_name.strip('-')
101
+
102
+ # If the name is empty after sanitization, use a default
103
+ if not sanitized_name:
104
+ sanitized_name = 'file'
105
+
106
+ # Limit the length
88
107
  return sanitized_name[:40]
89
108
 
90
- async def construct_file_content(gs_list, bucket:str, genai_lib=False):
109
+ async def construct_file_content(gs_list, bucket:str, genai_lib=False, timeout=60):
91
110
  """
92
111
  Args:
93
112
  - gs_list: a list of dicts representing files in a bucket
94
- - contentType: The content type of the file on GCS
95
- - storagePath: The path in the bucket
96
- - name: The name of the file
97
- - url: The URL of the file that can be used to display the contents
98
113
  - bucket: The bucket the files are in
99
- - genai: whether its using the genai SDK
100
-
114
+ - genai_lib: whether its using the genai SDK
115
+ - timeout: timeout in seconds for the gather operation
101
116
  """
102
117
 
103
118
  file_list = []
104
119
  for element in gs_list:
105
-
106
120
  the_mime_type = element.get('contentType')
107
121
  if the_mime_type is None:
108
122
  continue
@@ -114,7 +128,7 @@ async def construct_file_content(gs_list, bucket:str, genai_lib=False):
114
128
  log.warning(f'{the_mime_type} is not in allowed MIME types for {element.get("name")}')
115
129
 
116
130
  if not file_list:
117
- return {"role": "user", "parts": [{"text": "No eligible contentTypes were found"}]}
131
+ return [{"role": "user", "parts": [{"text": "No eligible contentTypes were found"}]}]
118
132
 
119
133
  content = []
120
134
 
@@ -124,34 +138,59 @@ async def construct_file_content(gs_list, bucket:str, genai_lib=False):
124
138
  img_url = f"gs://{bucket}/{file_info['storagePath']}"
125
139
  display_url = file_info.get('url')
126
140
  mime_type = file_info['contentType']
127
- name = sanitize_file(file_info['name'])
141
+ # Generate a unique name for each file
142
+ import uuid
143
+ original_name = sanitize_file(file_info['name'])
144
+ unique_id = str(uuid.uuid4())[:8]
145
+ unique_name = sanitize_file(f"{original_name}-{unique_id}")
146
+
128
147
  display_name = file_info['name']
129
- log.info(f"Processing {name=} {display_name=}")
148
+ log.info(f"Processing {unique_name=} {display_name=}")
149
+
130
150
  try:
131
151
  if not genai_lib:
132
- myfile = genai.get_file(name)
152
+ myfile = genai.get_file(unique_name)
133
153
  else:
134
154
  client = genaiv2.Client()
135
- myfile = client.files.get(name=name)
155
+ myfile = client.files.get(name=unique_name)
136
156
  content.append(myfile)
137
157
  content.append(f"You have been given the ability to work with file {display_name=} with {mime_type=} {display_url=}")
138
- log.info(f"Found existing genai.get_file {name=}")
158
+ log.info(f"Found existing genai.get_file {unique_name=}")
139
159
  except Exception as e:
140
- log.info(f"Not found checking genai.get_file: '{name}' {str(e)}")
160
+ log.info(f"Not found checking genai.get_file: '{unique_name}' {str(e)}")
141
161
  tasks.append(
142
162
  download_gcs_upload_genai(img_url,
143
- mime_type=mime_type,
144
- name=name,
145
- display_url=display_url,
146
- display_name=display_name,
147
- genai_lib=genai_lib)
148
- )
163
+ mime_type=mime_type,
164
+ name=unique_name,
165
+ display_url=display_url,
166
+ display_name=display_name,
167
+ genai_lib=genai_lib)
168
+ )
149
169
 
150
- # Run all tasks in parallel
170
+ # Process files with timeout
151
171
  if tasks:
152
- task_content = await asyncio.gather(*tasks)
153
- content.extend(task_content)
154
-
172
+ try:
173
+ # Add timeout to prevent hanging
174
+ task_results = await asyncio.wait_for(asyncio.gather(*tasks, return_exceptions=True), timeout=timeout)
175
+
176
+ # Process results, handle any exceptions
177
+ for result in task_results:
178
+ if isinstance(result, Exception):
179
+ log.error(f"Task failed with error: {str(result)}")
180
+ # Add error message to content
181
+ content.append({"role": "user", "parts": [{"text": f"Error processing file: {str(result)}"}]})
182
+ else:
183
+ # Normalize the result structure based on genai_lib
184
+ if genai_lib and isinstance(result, list):
185
+ content.append(result[0]) # The file object
186
+ content.append({"role": "user", "parts": [{"text": result[1]}]}) # The message
187
+ else:
188
+ content.append(result)
189
+ except asyncio.TimeoutError:
190
+ log.error(f"Timeout occurred after {timeout} seconds while processing files")
191
+ content.append({"role": "user", "parts": [{"text": f"Some files could not be processed within the time limit ({timeout} seconds)"}]})
192
+
193
+ log.info(f"construct_file_content completed with {len(content)} items")
155
194
  return content
156
195
 
157
196
  # Helper function to handle each file download with error handling
@@ -169,10 +208,9 @@ async def download_gcs_upload_genai(img_url,
169
208
  display_url=None,
170
209
  display_name=None,
171
210
  retries=3, delay=2, genai_lib=False):
172
- import aiofiles
173
- from google.generativeai.types import file_types
174
211
  """
175
212
  Downloads and uploads a file with retries in case of failure.
213
+ Thread-safe implementation using unique file paths.
176
214
 
177
215
  Args:
178
216
  - img_url: str The URL of the file to download.
@@ -184,9 +222,10 @@ async def download_gcs_upload_genai(img_url,
184
222
  Returns:
185
223
  - downloaded_content: The result of the file upload if successful.
186
224
  """
225
+ import aiofiles
187
226
  for attempt in range(retries):
188
227
  try:
189
- log.info(f"Upload {attempt} for {img_url=}")
228
+ log.info(f"Upload attempt [{attempt}] for {img_url=}")
190
229
  # Download the file bytes asynchronously
191
230
  file_bytes = await asyncio.to_thread(get_bytes_from_gcs, img_url)
192
231
  if not file_bytes:
@@ -200,50 +239,72 @@ async def download_gcs_upload_genai(img_url,
200
239
 
201
240
  if file_size > 19434343:
202
241
  log.warning(f"File size for {img_url}: {file_size} is too big.")
203
- msg = f"The file for {img_url} is too large ({file_size} bytes) to be used directly. Use RAG instead or {display_url=}"
242
+ msg = f"The file for {img_url} is too large ({file_size} bytes) to be used directly. Use RAG instead or {display_url=}"
204
243
  return {"role": "user", "parts": [{"text": msg}]}
205
244
 
206
245
  extension = mimetypes.guess_extension(mime_type)
207
246
 
208
- # Use aiofiles for asynchronous file operations
209
- temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=extension)
210
- downloaded_file = temp_file.name
211
-
212
- sanitized_file = sanitize_file(downloaded_file)
213
-
214
- log.info(f"Writing file {sanitized_file}")
215
- async with aiofiles.open(sanitized_file, 'wb') as f:
247
+ # Create a unique directory for this upload task
248
+ unique_id = str(uuid.uuid4())
249
+ temp_dir = os.path.join(tempfile.gettempdir(), f"upload_{unique_id}")
250
+ os.makedirs(temp_dir, exist_ok=True)
251
+
252
+ # Create a file with unique path
253
+ file_path = os.path.join(temp_dir, f"file_{unique_id}{extension}")
254
+
255
+ log.info(f"Writing file {file_path}")
256
+ async with aiofiles.open(file_path, 'wb') as f:
216
257
  await f.write(file_bytes)
217
258
 
218
- # Upload the file and get its content reference
219
- if not genai_lib:
220
- try:
221
- downloaded_content: file_types.File = await asyncio.to_thread(
259
+ try:
260
+ if not genai_lib:
261
+ downloaded_content = await asyncio.to_thread(
222
262
  partial(genai.upload_file, name=name, mime_type=mime_type, display_name=display_name),
223
- sanitized_file
224
- )
263
+ file_path
264
+ )
265
+
266
+ # Clean up after successful upload
267
+ try:
268
+ os.remove(file_path)
269
+ os.rmdir(temp_dir)
270
+ except OSError as e:
271
+ log.warning(f"Cleanup error (non-critical): {str(e)}")
272
+
225
273
  return {"role": "user", "parts": [{"file_data": downloaded_content},
226
- {"text": f"You have been given the ability to read and work with filename '{display_name=}' with {mime_type=} {display_url=}"}
227
- ]}
228
- except Exception as err:
229
- msg = f"Could not upload {sanitized_file} to genai.upload_file: {str(err)} {traceback.format_exc()} {display_url=}"
230
- log.error(msg)
231
- return {"role": "user", "parts": [{"text": msg}]}
232
- else:
233
- try:
274
+ {"text": f"You have been given the ability to read and work with filename '{display_name}' with {mime_type=} {display_url=}"}
275
+ ]}
276
+ else:
234
277
  client = genaiv2.Client()
235
- downloaded_content = await asyncio.to_thread(
236
- client.files.upload,
237
- file=sanitized_file,
238
- config=dict(mime_type=mime_type, display_name=display_name)
239
- )
278
+
279
+ # Use semaphore to limit concurrent uploads
280
+ async with upload_semaphore:
281
+ downloaded_content = await asyncio.to_thread(
282
+ client.files.upload,
283
+ file=file_path,
284
+ config=dict(mime_type=mime_type, display_name=display_name)
285
+ )
286
+
287
+ # Clean up after successful upload
288
+ try:
289
+ os.remove(file_path)
290
+ os.rmdir(temp_dir)
291
+ except OSError as e:
292
+ log.warning(f"Cleanup error (non-critical): {str(e)}")
293
+
240
294
  return [downloaded_content,
241
- f"You have been given the ability to read and work with filename '{display_name=}' with {mime_type=} {display_url=}"]
295
+ f"You have been given the ability to read and work with filename '{display_name}' with {mime_type=} {display_url=}"]
242
296
 
243
- except Exception as err:
244
- msg = f"Could not upload {sanitized_file} to genaiv2.client.files.upload: {str(err)} {traceback.format_exc()} {display_url=}"
245
- log.error(msg)
246
- return {"role": "user", "parts": [{"text": msg}]}
297
+ except Exception as err:
298
+ # Clean up on error
299
+ try:
300
+ os.remove(file_path)
301
+ os.rmdir(temp_dir)
302
+ except OSError:
303
+ pass
304
+
305
+ msg = f"Could not upload {file_path} to {'genai.upload_file' if not genai_lib else 'genaiv2.client.files.upload'}: {str(err)} {traceback.format_exc()} {display_url=}"
306
+ log.error(msg)
307
+ return {"role": "user", "parts": [{"text": msg}]}
247
308
 
248
309
  except Exception as err:
249
310
  log.error(f"Error processing file {img_url} {mime_type=} on attempt {attempt + 1}/{retries}: {str(err)}")
@@ -255,3 +316,7 @@ async def download_gcs_upload_genai(img_url,
255
316
  else:
256
317
  raise err # Raise the error after max retries
257
318
 
319
+ # Add this at the module level
320
+ # Create a semaphore to limit concurrent uploads
321
+ upload_semaphore = asyncio.Semaphore(5) # Adjust the value based on your needs
322
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: sunholo
3
- Version: 0.119.18
3
+ Version: 0.120.1
4
4
  Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
5
5
  Author-email: Holosun ApS <multivac@sunholo.com>
6
6
  License: Apache License, Version 2.0
@@ -28,6 +28,7 @@ Provides-Extra: test
28
28
  Requires-Dist: pytest; extra == "test"
29
29
  Requires-Dist: pytest-cov; extra == "test"
30
30
  Provides-Extra: all
31
+ Requires-Dist: aiofiles; extra == "all"
31
32
  Requires-Dist: aiohttp; extra == "all"
32
33
  Requires-Dist: anthropic[vertex]; extra == "all"
33
34
  Requires-Dist: asyncpg; extra == "all"
@@ -50,7 +51,7 @@ Requires-Dist: google-cloud-pubsub; extra == "all"
50
51
  Requires-Dist: google-cloud-discoveryengine>=0.13.4; extra == "all"
51
52
  Requires-Dist: google-cloud-texttospeech; extra == "all"
52
53
  Requires-Dist: google-generativeai>=0.7.1; extra == "all"
53
- Requires-Dist: google-genai; extra == "all"
54
+ Requires-Dist: google-genai>=0.2.2; extra == "all"
54
55
  Requires-Dist: gunicorn; extra == "all"
55
56
  Requires-Dist: httpcore; extra == "all"
56
57
  Requires-Dist: httpx; extra == "all"
@@ -122,6 +123,7 @@ Requires-Dist: pytesseract; extra == "pipeline"
122
123
  Requires-Dist: tabulate; extra == "pipeline"
123
124
  Requires-Dist: unstructured[all-docs,local-inference]; extra == "pipeline"
124
125
  Provides-Extra: gcp
126
+ Requires-Dist: aiofiles; extra == "gcp"
125
127
  Requires-Dist: anthropic[vertex]; extra == "gcp"
126
128
  Requires-Dist: google-api-python-client; extra == "gcp"
127
129
  Requires-Dist: google-auth-httplib2; extra == "gcp"
@@ -136,7 +138,7 @@ Requires-Dist: google-cloud-logging; extra == "gcp"
136
138
  Requires-Dist: google-cloud-pubsub; extra == "gcp"
137
139
  Requires-Dist: google-cloud-discoveryengine>=0.13.4; extra == "gcp"
138
140
  Requires-Dist: google-cloud-texttospeech; extra == "gcp"
139
- Requires-Dist: google-genai; extra == "gcp"
141
+ Requires-Dist: google-genai>=0.2.2; extra == "gcp"
140
142
  Requires-Dist: google-generativeai>=0.8.3; extra == "gcp"
141
143
  Requires-Dist: langchain-google-genai>=2.0.0; extra == "gcp"
142
144
  Requires-Dist: langchain_google_alloydb_pg>=0.2.2; extra == "gcp"
@@ -6,6 +6,7 @@ ruamel.yaml
6
6
  tenacity
7
7
 
8
8
  [all]
9
+ aiofiles
9
10
  aiohttp
10
11
  anthropic[vertex]
11
12
  asyncpg
@@ -28,7 +29,7 @@ google-cloud-pubsub
28
29
  google-cloud-discoveryengine>=0.13.4
29
30
  google-cloud-texttospeech
30
31
  google-generativeai>=0.7.1
31
- google-genai
32
+ google-genai>=0.2.2
32
33
  gunicorn
33
34
  httpcore
34
35
  httpx
@@ -97,6 +98,7 @@ requests
97
98
  rich
98
99
 
99
100
  [gcp]
101
+ aiofiles
100
102
  anthropic[vertex]
101
103
  google-api-python-client
102
104
  google-auth-httplib2
@@ -111,7 +113,7 @@ google-cloud-logging
111
113
  google-cloud-pubsub
112
114
  google-cloud-discoveryengine>=0.13.4
113
115
  google-cloud-texttospeech
114
- google-genai
116
+ google-genai>=0.2.2
115
117
  google-generativeai>=0.8.3
116
118
  langchain-google-genai>=2.0.0
117
119
  langchain_google_alloydb_pg>=0.2.2
File without changes
File without changes
File without changes
File without changes