sunholo 0.143.3__tar.gz → 0.143.7__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 (196) hide show
  1. {sunholo-0.143.3/src/sunholo.egg-info → sunholo-0.143.7}/PKG-INFO +1 -1
  2. {sunholo-0.143.3 → sunholo-0.143.7}/pyproject.toml +1 -1
  3. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/__init__.py +4 -1
  4. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/flask/vac_routes.py +9 -17
  5. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/mcp/__init__.py +11 -2
  6. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/mcp/mcp_manager.py +66 -28
  7. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/mcp/vac_mcp_server.py +3 -9
  8. {sunholo-0.143.3 → sunholo-0.143.7/src/sunholo.egg-info}/PKG-INFO +1 -1
  9. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo.egg-info/SOURCES.txt +1 -2
  10. sunholo-0.143.3/tests/test_vac_routes_mcp.py +0 -339
  11. {sunholo-0.143.3 → sunholo-0.143.7}/LICENSE.txt +0 -0
  12. {sunholo-0.143.3 → sunholo-0.143.7}/MANIFEST.in +0 -0
  13. {sunholo-0.143.3 → sunholo-0.143.7}/README.md +0 -0
  14. {sunholo-0.143.3 → sunholo-0.143.7}/setup.cfg +0 -0
  15. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/a2a/__init__.py +0 -0
  16. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/a2a/agent_card.py +0 -0
  17. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/a2a/task_manager.py +0 -0
  18. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/a2a/vac_a2a_agent.py +0 -0
  19. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/__init__.py +0 -0
  20. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/chat_history.py +0 -0
  21. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/dispatch_to_qa.py +0 -0
  22. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/fastapi/__init__.py +0 -0
  23. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/fastapi/base.py +0 -0
  24. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/fastapi/qna_routes.py +0 -0
  25. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/flask/__init__.py +0 -0
  26. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/flask/base.py +0 -0
  27. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/langserve.py +0 -0
  28. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/pubsub.py +0 -0
  29. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/route.py +0 -0
  30. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/special_commands.py +0 -0
  31. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/agents/swagger.py +0 -0
  32. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/archive/__init__.py +0 -0
  33. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/archive/archive.py +0 -0
  34. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/auth/__init__.py +0 -0
  35. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/auth/gcloud.py +0 -0
  36. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/auth/refresh.py +0 -0
  37. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/auth/run.py +0 -0
  38. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/azure/__init__.py +0 -0
  39. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/azure/auth.py +0 -0
  40. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/azure/blobs.py +0 -0
  41. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/azure/event_grid.py +0 -0
  42. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/bots/__init__.py +0 -0
  43. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/bots/discord.py +0 -0
  44. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/bots/github_webhook.py +0 -0
  45. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/bots/webapp.py +0 -0
  46. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/chunker/__init__.py +0 -0
  47. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/chunker/azure.py +0 -0
  48. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/chunker/doc_handling.py +0 -0
  49. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/chunker/encode_metadata.py +0 -0
  50. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/chunker/images.py +0 -0
  51. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/chunker/loaders.py +0 -0
  52. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/chunker/message_data.py +0 -0
  53. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/chunker/pdfs.py +0 -0
  54. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/chunker/process_chunker_data.py +0 -0
  55. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/chunker/publish.py +0 -0
  56. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/chunker/pubsub.py +0 -0
  57. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/chunker/splitter.py +0 -0
  58. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/cli/__init__.py +0 -0
  59. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/cli/chat_vac.py +0 -0
  60. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/cli/cli.py +0 -0
  61. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/cli/cli_init.py +0 -0
  62. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/cli/configs.py +0 -0
  63. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/cli/deploy.py +0 -0
  64. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/cli/embedder.py +0 -0
  65. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/cli/merge_texts.py +0 -0
  66. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/cli/run_proxy.py +0 -0
  67. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/cli/sun_rich.py +0 -0
  68. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/cli/swagger.py +0 -0
  69. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/cli/vertex.py +0 -0
  70. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/components/__init__.py +0 -0
  71. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/components/llm.py +0 -0
  72. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/components/retriever.py +0 -0
  73. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/components/vectorstore.py +0 -0
  74. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/custom_logging.py +0 -0
  75. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/__init__.py +0 -0
  76. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/alloydb.py +0 -0
  77. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/alloydb_client.py +0 -0
  78. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/database.py +0 -0
  79. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/lancedb.py +0 -0
  80. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/sql/sb/create_function.sql +0 -0
  81. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/sql/sb/create_function_time.sql +0 -0
  82. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/sql/sb/create_table.sql +0 -0
  83. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/sql/sb/delete_source_row.sql +0 -0
  84. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/sql/sb/return_sources.sql +0 -0
  85. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/sql/sb/setup.sql +0 -0
  86. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/static_dbs.py +0 -0
  87. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/database/uuid.py +0 -0
  88. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/discovery_engine/__init__.py +0 -0
  89. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/discovery_engine/chunker_handler.py +0 -0
  90. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/discovery_engine/cli.py +0 -0
  91. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/discovery_engine/create_new.py +0 -0
  92. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/discovery_engine/discovery_engine_client.py +0 -0
  93. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/discovery_engine/get_ai_search_chunks.py +0 -0
  94. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/embedder/__init__.py +0 -0
  95. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/embedder/embed_chunk.py +0 -0
  96. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/embedder/embed_metadata.py +0 -0
  97. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/excel/__init__.py +0 -0
  98. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/excel/plugin.py +0 -0
  99. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/gcs/__init__.py +0 -0
  100. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/gcs/add_file.py +0 -0
  101. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/gcs/download_folder.py +0 -0
  102. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/gcs/download_gcs_text.py +0 -0
  103. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/gcs/download_url.py +0 -0
  104. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/gcs/extract_and_sign.py +0 -0
  105. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/gcs/metadata.py +0 -0
  106. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/genai/__init__.py +0 -0
  107. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/genai/file_handling.py +0 -0
  108. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/genai/genaiv2.py +0 -0
  109. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/genai/images.py +0 -0
  110. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/genai/init.py +0 -0
  111. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/genai/process_funcs_cls.py +0 -0
  112. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/genai/safety.py +0 -0
  113. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/invoke/__init__.py +0 -0
  114. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/invoke/async_class.py +0 -0
  115. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/invoke/direct_vac_func.py +0 -0
  116. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/invoke/invoke_vac_utils.py +0 -0
  117. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/langchain_types.py +0 -0
  118. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/langfuse/__init__.py +0 -0
  119. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/langfuse/callback.py +0 -0
  120. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/langfuse/evals.py +0 -0
  121. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/langfuse/prompts.py +0 -0
  122. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/llamaindex/__init__.py +0 -0
  123. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/llamaindex/get_files.py +0 -0
  124. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/llamaindex/import_files.py +0 -0
  125. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/llamaindex/llamaindex_class.py +0 -0
  126. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/llamaindex/user_history.py +0 -0
  127. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/lookup/__init__.py +0 -0
  128. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/lookup/model_lookup.yaml +0 -0
  129. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/mcp/cli.py +0 -0
  130. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/ollama/__init__.py +0 -0
  131. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/ollama/ollama_images.py +0 -0
  132. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/pubsub/__init__.py +0 -0
  133. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/pubsub/process_pubsub.py +0 -0
  134. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/pubsub/pubsub_manager.py +0 -0
  135. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/qna/__init__.py +0 -0
  136. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/qna/parsers.py +0 -0
  137. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/qna/retry.py +0 -0
  138. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/senses/__init__.py +0 -0
  139. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/senses/stream_voice.py +0 -0
  140. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/streaming/__init__.py +0 -0
  141. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/streaming/content_buffer.py +0 -0
  142. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/streaming/langserve.py +0 -0
  143. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/streaming/stream_lookup.py +0 -0
  144. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/streaming/streaming.py +0 -0
  145. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/summarise/__init__.py +0 -0
  146. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/summarise/summarise.py +0 -0
  147. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/agent/__init__.py +0 -0
  148. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/agent/agent_service.py +0 -0
  149. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/agent/app.py +0 -0
  150. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/agent/my_log.py +0 -0
  151. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/agent/tools/__init__.py +0 -0
  152. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/agent/tools/your_agent.py +0 -0
  153. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/agent/vac_service.py +0 -0
  154. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/project/__init__.py +0 -0
  155. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/project/app.py +0 -0
  156. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/project/my_log.py +0 -0
  157. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/project/vac_service.py +0 -0
  158. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/system_services/__init__.py +0 -0
  159. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/system_services/app.py +0 -0
  160. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/templates/system_services/my_log.py +0 -0
  161. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/terraform/__init__.py +0 -0
  162. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/terraform/tfvars_editor.py +0 -0
  163. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/tools/__init__.py +0 -0
  164. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/tools/web_browser.py +0 -0
  165. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/__init__.py +0 -0
  166. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/api_key.py +0 -0
  167. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/big_context.py +0 -0
  168. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/config.py +0 -0
  169. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/config_class.py +0 -0
  170. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/config_schema.py +0 -0
  171. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/gcp.py +0 -0
  172. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/gcp_project.py +0 -0
  173. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/mime.py +0 -0
  174. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/parsers.py +0 -0
  175. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/proto_convert.py +0 -0
  176. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/timedelta.py +0 -0
  177. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/user_ids.py +0 -0
  178. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/utils/version.py +0 -0
  179. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/vertex/__init__.py +0 -0
  180. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/vertex/extensions_call.py +0 -0
  181. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/vertex/extensions_class.py +0 -0
  182. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/vertex/genai_functions.py +0 -0
  183. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/vertex/init.py +0 -0
  184. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/vertex/memory_tools.py +0 -0
  185. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/vertex/safety.py +0 -0
  186. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo/vertex/type_dict_to_json.py +0 -0
  187. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo.egg-info/dependency_links.txt +0 -0
  188. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo.egg-info/entry_points.txt +0 -0
  189. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo.egg-info/requires.txt +0 -0
  190. {sunholo-0.143.3 → sunholo-0.143.7}/src/sunholo.egg-info/top_level.txt +0 -0
  191. {sunholo-0.143.3 → sunholo-0.143.7}/tests/test_async.py +0 -0
  192. {sunholo-0.143.3 → sunholo-0.143.7}/tests/test_async_genai2.py +0 -0
  193. {sunholo-0.143.3 → sunholo-0.143.7}/tests/test_chat_history.py +0 -0
  194. {sunholo-0.143.3 → sunholo-0.143.7}/tests/test_config.py +0 -0
  195. {sunholo-0.143.3 → sunholo-0.143.7}/tests/test_genai2.py +0 -0
  196. {sunholo-0.143.3 → sunholo-0.143.7}/tests/test_unstructured.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sunholo
3
- Version: 0.143.3
3
+ Version: 0.143.7
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.143.3"
7
+ version = "0.143.7"
8
8
  description = "AI DevOps - a package to help deploy GenAI to the Cloud."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -15,7 +15,10 @@ from . import invoke
15
15
  from . import langfuse
16
16
  from . import llamaindex
17
17
  from . import lookup
18
- from . import mcp
18
+ try:
19
+ from . import mcp
20
+ except ImportError:
21
+ mcp = None
19
22
  from . import ollama
20
23
  from . import pubsub
21
24
  from . import qna
@@ -38,12 +38,14 @@ except ImportError:
38
38
 
39
39
  try:
40
40
  from ...mcp.vac_mcp_server import VACMCPServer
41
- from mcp.server.models import InitializationOptions
42
- from mcp import JSONRPCMessage, ErrorData, INTERNAL_ERROR
41
+ from mcp.server import Server
42
+ from mcp.types import JSONRPCMessage, ErrorData, INTERNAL_ERROR
43
43
  except ImportError:
44
44
  VACMCPServer = None
45
- InitializationOptions = None
45
+ Server = None
46
46
  JSONRPCMessage = None
47
+ ErrorData = None
48
+ INTERNAL_ERROR = None
47
49
 
48
50
  try:
49
51
  from ...a2a.vac_a2a_agent import VACA2AAgent
@@ -1062,11 +1064,8 @@ if __name__ == "__main__":
1062
1064
 
1063
1065
  try:
1064
1066
  # Process the request through the server
1065
- await server.run(
1066
- read_messages(),
1067
- write_queue,
1068
- InitializationOptions() if InitializationOptions else None
1069
- )
1067
+ # Use the server's run method with HTTP transport
1068
+ await server.run()
1070
1069
  except Exception as e:
1071
1070
  log.error(f"Error processing MCP request: {e}")
1072
1071
  await write_queue.put(None)
@@ -1084,15 +1083,8 @@ if __name__ == "__main__":
1084
1083
  loop = asyncio.new_event_loop()
1085
1084
  asyncio.set_event_loop(loop)
1086
1085
  try:
1087
- responses = loop.run_until_complete(process_request())
1088
-
1089
- # Parse and return the response
1090
- if responses:
1091
- # The response should be a single JSON-RPC response
1092
- response_data = json_module.loads(responses[0])
1093
- return jsonify(response_data)
1094
- else:
1095
- return jsonify({"error": "No response from MCP server"}), 500
1086
+ response_data = loop.run_until_complete(process_request())
1087
+ return jsonify(response_data)
1096
1088
 
1097
1089
  except Exception as e:
1098
1090
  log.error(f"MCP server error: {str(e)}")
@@ -14,7 +14,16 @@
14
14
 
15
15
  """MCP (Model Context Protocol) integration for Sunholo."""
16
16
 
17
- from .mcp_manager import MCPClientManager
18
- from .vac_mcp_server import VACMCPServer
17
+ try:
18
+ from .mcp_manager import MCPClientManager
19
+ except ImportError as e:
20
+ print(f"Warning: MCPClientManager not available - {e}")
21
+ MCPClientManager = None
22
+
23
+ try:
24
+ from .vac_mcp_server import VACMCPServer
25
+ except ImportError as e:
26
+ print(f"Warning: VACMCPServer not available - {e}")
27
+ VACMCPServer = None
19
28
 
20
29
  __all__ = ['MCPClientManager', 'VACMCPServer']
@@ -4,11 +4,34 @@ This shows how to integrate MCP servers with your Flask/VACRoutes application.
4
4
  """
5
5
 
6
6
  from typing import Dict, Any, List, Optional
7
+ import asyncio
7
8
 
8
- # Official MCP imports
9
- from mcp import StdioServerParameters, ClientSession
10
- from mcp.client.stdio import stdio_client
11
- from mcp.types import Tool, Resource, TextContent, CallToolResult
9
+ # MCP SDK imports - try different import paths
10
+ try:
11
+ from mcp.client.stdio import StdioClientTransport
12
+ from mcp.client.session import ClientSession
13
+ except ImportError:
14
+ try:
15
+ # Alternative import paths
16
+ from mcp.client import StdioClientTransport, ClientSession
17
+ except ImportError:
18
+ try:
19
+ # Another alternative
20
+ from mcp import StdioClientTransport, ClientSession
21
+ except ImportError:
22
+ StdioClientTransport = None
23
+ ClientSession = None
24
+
25
+ try:
26
+ from mcp.types import Tool, Resource, TextContent, CallToolResult
27
+ except ImportError:
28
+ try:
29
+ from mcp import Tool, Resource, TextContent, CallToolResult
30
+ except ImportError:
31
+ Tool = None
32
+ Resource = None
33
+ TextContent = None
34
+ CallToolResult = None
12
35
 
13
36
 
14
37
  class MCPClientManager:
@@ -23,41 +46,42 @@ class MCPClientManager:
23
46
  if server_name in self.sessions:
24
47
  return self.sessions[server_name]
25
48
 
26
- # Create server parameters
27
- server_params = StdioServerParameters(
49
+ if not StdioClientTransport or not ClientSession:
50
+ raise ImportError("MCP client dependencies not available")
51
+
52
+ # Create transport and session
53
+ transport = StdioClientTransport(
28
54
  command=command,
29
55
  args=args or []
30
56
  )
57
+ session = ClientSession(transport)
58
+ await session.initialize()
31
59
 
32
- # Connect to the server
33
- async with stdio_client(server_params) as (read, write):
34
- # Create and initialize client session directly
35
- session = ClientSession(read, write)
36
- await session.initialize()
37
- self.sessions[server_name] = session
38
- self.server_configs[server_name] = {
39
- "command": command,
40
- "args": args
41
- }
42
- return session
60
+ self.sessions[server_name] = session
61
+ self.server_configs[server_name] = {
62
+ "command": command,
63
+ "args": args
64
+ }
65
+ return session
43
66
 
44
67
  async def list_tools(self, server_name: Optional[str] = None) -> List[Tool]:
45
68
  """List available tools from one or all connected servers."""
46
69
  if server_name:
47
70
  session = self.sessions.get(server_name)
48
71
  if session:
49
- return await session.list_tools()
72
+ result = await session.list_tools()
73
+ return result.tools
50
74
  return []
51
75
 
52
76
  # List from all servers
53
77
  all_tools = []
54
78
  for name, session in self.sessions.items():
55
- tools = await session.list_tools()
79
+ result = await session.list_tools()
56
80
  # Add server name to tool metadata
57
- for tool in tools:
81
+ for tool in result.tools:
58
82
  tool.metadata = tool.metadata or {}
59
83
  tool.metadata["server"] = name
60
- all_tools.extend(tools)
84
+ all_tools.extend(result.tools)
61
85
  return all_tools
62
86
 
63
87
  async def call_tool(self, server_name: str, tool_name: str, arguments: Dict[str, Any]) -> CallToolResult:
@@ -67,7 +91,13 @@ class MCPClientManager:
67
91
  raise ValueError(f"Not connected to server: {server_name}")
68
92
 
69
93
  # Call the tool
70
- result = await session.call_tool(tool_name, arguments)
94
+ try:
95
+ from mcp.types import CallToolRequest
96
+ request = CallToolRequest(name=tool_name, arguments=arguments)
97
+ result = await session.call_tool(request)
98
+ except ImportError:
99
+ # Try direct call if Request types not available
100
+ result = await session.call_tool(tool_name, arguments)
71
101
  return result
72
102
 
73
103
  async def list_resources(self, server_name: Optional[str] = None) -> List[Resource]:
@@ -75,17 +105,18 @@ class MCPClientManager:
75
105
  if server_name:
76
106
  session = self.sessions.get(server_name)
77
107
  if session:
78
- return await session.list_resources()
108
+ result = await session.list_resources()
109
+ return result.resources
79
110
  return []
80
111
 
81
112
  # List from all servers
82
113
  all_resources = []
83
114
  for name, session in self.sessions.items():
84
- resources = await session.list_resources()
85
- for resource in resources:
115
+ result = await session.list_resources()
116
+ for resource in result.resources:
86
117
  resource.metadata = resource.metadata or {}
87
118
  resource.metadata["server"] = name
88
- all_resources.extend(resources)
119
+ all_resources.extend(result.resources)
89
120
  return all_resources
90
121
 
91
122
  async def read_resource(self, server_name: str, uri: str) -> List[TextContent]:
@@ -94,5 +125,12 @@ class MCPClientManager:
94
125
  if not session:
95
126
  raise ValueError(f"Not connected to server: {server_name}")
96
127
 
97
- result = await session.read_resource(uri)
98
- return result.contents
128
+ try:
129
+ from mcp.types import ReadResourceRequest
130
+ request = ReadResourceRequest(uri=uri)
131
+ result = await session.read_resource(request)
132
+ except ImportError:
133
+ # Try direct call if Request types not available
134
+ result = await session.read_resource(uri)
135
+
136
+ return result.contents if hasattr(result, 'contents') else result
@@ -22,13 +22,8 @@ import json
22
22
  import asyncio
23
23
  from functools import partial
24
24
 
25
- try:
26
- from mcp.server import Server
27
- from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
28
- except ImportError:
29
- Server = None
30
- Tool = None
31
- TextContent = None
25
+ from mcp.server import Server
26
+ from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
32
27
 
33
28
  from ..custom_logging import log
34
29
  from ..streaming import start_streaming_chat_async
@@ -45,8 +40,7 @@ class VACMCPServer:
45
40
  stream_interpreter: The streaming interpreter function
46
41
  vac_interpreter: The static VAC interpreter function (optional)
47
42
  """
48
- if Server is None:
49
- raise ImportError("MCP server requires `pip install sunholo[anthropic]`")
43
+ # MCP server is always available with current SDK
50
44
 
51
45
  self.stream_interpreter = stream_interpreter
52
46
  self.vac_interpreter = vac_interpreter
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sunholo
3
- Version: 0.143.3
3
+ Version: 0.143.7
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
@@ -190,5 +190,4 @@ tests/test_async_genai2.py
190
190
  tests/test_chat_history.py
191
191
  tests/test_config.py
192
192
  tests/test_genai2.py
193
- tests/test_unstructured.py
194
- tests/test_vac_routes_mcp.py
193
+ tests/test_unstructured.py
@@ -1,339 +0,0 @@
1
- # Copyright [2024] [Holosun ApS]
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
-
9
- import pytest
10
- import json
11
- import asyncio
12
- from unittest.mock import Mock, AsyncMock, patch, MagicMock
13
- from flask import Flask
14
-
15
- # Import the classes under test
16
- from sunholo.agents.flask.vac_routes import VACRoutes
17
- from sunholo.mcp.mcp_manager import MCPClientManager
18
-
19
-
20
- @pytest.fixture
21
- def app():
22
- """Create a Flask app for testing."""
23
- return Flask(__name__)
24
-
25
-
26
- @pytest.fixture
27
- def mock_stream_interpreter():
28
- """Mock stream interpreter function."""
29
- def mock_func(question, vector_name, chat_history, **kwargs):
30
- return {"answer": f"Mock response to: {question}"}
31
- return mock_func
32
-
33
-
34
- @pytest.fixture
35
- def mock_vac_interpreter():
36
- """Mock VAC interpreter function."""
37
- def mock_func(question, vector_name, chat_history, **kwargs):
38
- return {"answer": f"Mock static response to: {question}"}
39
- return mock_func
40
-
41
-
42
- @pytest.fixture
43
- def mcp_servers_config():
44
- """Mock MCP server configuration."""
45
- return [
46
- {
47
- "name": "test_server",
48
- "command": "python",
49
- "args": ["-m", "test_mcp_server"]
50
- }
51
- ]
52
-
53
-
54
- class TestMCPClientManager:
55
- """Test the MCPClientManager class."""
56
-
57
- def test_init(self):
58
- """Test MCPClientManager initialization."""
59
- manager = MCPClientManager()
60
- assert manager.sessions == {}
61
- assert manager.server_configs == {}
62
-
63
- @pytest.mark.asyncio
64
- async def test_connect_to_server(self):
65
- """Test connecting to an MCP server."""
66
- manager = MCPClientManager()
67
-
68
- # Mock the MCP client components
69
- with patch('sunholo.mcp.mcp_manager.stdio_client') as mock_stdio, \
70
- patch('sunholo.mcp.mcp_manager.ClientSession') as mock_session_class:
71
-
72
- # Setup mocks
73
- mock_session = AsyncMock()
74
- mock_session_class.return_value = mock_session
75
- mock_stdio.return_value.__aenter__.return_value = (Mock(), Mock())
76
-
77
- # Test connection
78
- result = await manager.connect_to_server("test_server", "python", ["-m", "test"])
79
-
80
- # Verify
81
- assert result == mock_session
82
- assert "test_server" in manager.sessions
83
- assert manager.server_configs["test_server"]["command"] == "python"
84
-
85
- @pytest.mark.asyncio
86
- async def test_list_tools(self):
87
- """Test listing tools from MCP servers."""
88
- manager = MCPClientManager()
89
-
90
- # Mock session with tools
91
- mock_session = AsyncMock()
92
- mock_tool = Mock()
93
- mock_tool.name = "test_tool"
94
- mock_tool.description = "Test tool"
95
- mock_tool.metadata = {}
96
- mock_session.list_tools.return_value = [mock_tool]
97
-
98
- manager.sessions["test_server"] = mock_session
99
-
100
- # Test listing tools from specific server
101
- tools = await manager.list_tools("test_server")
102
- assert len(tools) == 1
103
- assert tools[0].name == "test_tool"
104
-
105
- # Test listing all tools
106
- all_tools = await manager.list_tools()
107
- assert len(all_tools) == 1
108
- assert all_tools[0].metadata["server"] == "test_server"
109
-
110
- @pytest.mark.asyncio
111
- async def test_call_tool(self):
112
- """Test calling a tool on an MCP server."""
113
- manager = MCPClientManager()
114
-
115
- # Mock session
116
- mock_session = AsyncMock()
117
- mock_result = Mock()
118
- mock_session.call_tool.return_value = mock_result
119
- manager.sessions["test_server"] = mock_session
120
-
121
- # Test calling tool
122
- result = await manager.call_tool("test_server", "test_tool", {"arg": "value"})
123
-
124
- assert result == mock_result
125
- mock_session.call_tool.assert_called_once_with("test_tool", {"arg": "value"})
126
-
127
- @pytest.mark.asyncio
128
- async def test_call_tool_server_not_found(self):
129
- """Test calling a tool on non-existent server."""
130
- manager = MCPClientManager()
131
-
132
- with pytest.raises(ValueError, match="Not connected to server: nonexistent"):
133
- await manager.call_tool("nonexistent", "test_tool", {})
134
-
135
-
136
- class TestVACRoutes:
137
- """Test the VACRoutes class."""
138
-
139
- def test_init_basic(self, app, mock_stream_interpreter):
140
- """Test basic VACRoutes initialization."""
141
- vac_routes = VACRoutes(app, mock_stream_interpreter)
142
-
143
- assert vac_routes.app == app
144
- assert vac_routes.stream_interpreter == mock_stream_interpreter
145
- assert vac_routes.mcp_servers == []
146
- assert vac_routes.async_stream == False
147
-
148
- def test_init_with_mcp_servers(self, app, mock_stream_interpreter, mcp_servers_config):
149
- """Test VACRoutes initialization with MCP servers."""
150
- with patch('sunholo.agents.flask.vac_routes.MCPClientManager') as mock_manager_class, \
151
- patch('asyncio.create_task') as mock_create_task:
152
- mock_manager = Mock()
153
- mock_manager_class.return_value = mock_manager
154
-
155
- vac_routes = VACRoutes(
156
- app,
157
- mock_stream_interpreter,
158
- mcp_servers=mcp_servers_config
159
- )
160
-
161
- assert vac_routes.mcp_servers == mcp_servers_config
162
- assert vac_routes.mcp_client_manager == mock_manager
163
- mock_create_task.assert_called_once()
164
-
165
- def test_home_route(self, app, mock_stream_interpreter):
166
- """Test the home route."""
167
- vac_routes = VACRoutes(app, mock_stream_interpreter)
168
-
169
- with app.test_client() as client:
170
- response = client.get('/')
171
- assert response.status_code == 200
172
- assert response.get_json() == "OK"
173
-
174
- def test_health_route(self, app, mock_stream_interpreter):
175
- """Test the health route."""
176
- vac_routes = VACRoutes(app, mock_stream_interpreter)
177
-
178
- with app.test_client() as client:
179
- response = client.get('/health')
180
- assert response.status_code == 200
181
- assert response.get_json() == {"status": "healthy"}
182
-
183
- @patch('sunholo.agents.flask.vac_routes.ConfigManager')
184
- def test_handle_process_vac(self, mock_config_manager, app, mock_vac_interpreter):
185
- """Test processing a VAC request."""
186
- # Mock ConfigManager
187
- mock_config = Mock()
188
- mock_config.configs_by_kind = {}
189
- mock_config.vacConfig.return_value = "test_model"
190
- mock_config_manager.return_value = mock_config
191
-
192
- vac_routes = VACRoutes(app, None, mock_vac_interpreter, add_langfuse_eval=False)
193
-
194
- with app.test_client() as client:
195
- response = client.post(
196
- '/vac/test_vector',
197
- json={
198
- 'user_input': 'Hello, world!',
199
- 'chat_history': []
200
- },
201
- content_type='application/json'
202
- )
203
-
204
- assert response.status_code == 200
205
- data = response.get_json()
206
- assert 'answer' in data
207
- assert 'Hello, world!' in data['answer']
208
-
209
- def test_mcp_routes_registration(self, app, mock_stream_interpreter, mcp_servers_config):
210
- """Test that MCP routes are registered when MCP servers are provided."""
211
- with patch('sunholo.agents.flask.vac_routes.MCPClientManager'), \
212
- patch('asyncio.create_task'):
213
- vac_routes = VACRoutes(
214
- app,
215
- mock_stream_interpreter,
216
- mcp_servers=mcp_servers_config
217
- )
218
-
219
- # Check that MCP routes are registered
220
- rules = [rule.rule for rule in app.url_map.iter_rules()]
221
- assert '/mcp/tools' in rules
222
- assert '/mcp/tools/<server_name>' in rules
223
- assert '/mcp/call' in rules
224
- assert '/mcp/resources' in rules
225
- assert '/mcp/resources/read' in rules
226
-
227
- @patch('sunholo.agents.flask.vac_routes.MCPClientManager')
228
- @patch('asyncio.create_task')
229
- def test_mcp_list_tools_endpoint(self, mock_create_task, mock_manager_class, app, mock_stream_interpreter, mcp_servers_config):
230
- """Test the MCP list tools endpoint."""
231
- # Setup mock manager
232
- mock_manager = Mock()
233
- mock_manager_class.return_value = mock_manager
234
-
235
- # Mock async method
236
- async def mock_list_tools(server_name=None):
237
- mock_tool = Mock()
238
- mock_tool.name = "test_tool"
239
- mock_tool.description = "Test tool description"
240
- mock_tool.inputSchema = {"type": "object"}
241
- mock_tool.metadata = {"server": "test_server"}
242
- return [mock_tool]
243
-
244
- mock_manager.list_tools = mock_list_tools
245
-
246
- vac_routes = VACRoutes(
247
- app,
248
- mock_stream_interpreter,
249
- mcp_servers=mcp_servers_config
250
- )
251
-
252
- with app.test_client() as client:
253
- response = client.get('/mcp/tools')
254
- assert response.status_code == 200
255
- data = response.get_json()
256
- assert 'tools' in data
257
- assert len(data['tools']) == 1
258
- assert data['tools'][0]['name'] == 'test_tool'
259
-
260
- @patch('sunholo.agents.flask.vac_routes.MCPClientManager')
261
- @patch('asyncio.create_task')
262
- def test_mcp_call_tool_endpoint(self, mock_create_task, mock_manager_class, app, mock_stream_interpreter, mcp_servers_config):
263
- """Test the MCP call tool endpoint."""
264
- # Setup mock manager
265
- mock_manager = Mock()
266
- mock_manager_class.return_value = mock_manager
267
-
268
- # Mock async method
269
- async def mock_call_tool(server_name, tool_name, arguments):
270
- mock_result = Mock()
271
- mock_result.content = Mock()
272
- mock_result.content.text = f"Tool {tool_name} executed with {arguments}"
273
- return mock_result
274
-
275
- mock_manager.call_tool = mock_call_tool
276
-
277
- vac_routes = VACRoutes(
278
- app,
279
- mock_stream_interpreter,
280
- mcp_servers=mcp_servers_config
281
- )
282
-
283
- with app.test_client() as client:
284
- response = client.post(
285
- '/mcp/call',
286
- json={
287
- 'server': 'test_server',
288
- 'tool': 'test_tool',
289
- 'arguments': {'param': 'value'}
290
- },
291
- content_type='application/json'
292
- )
293
-
294
- assert response.status_code == 200
295
- data = response.get_json()
296
- assert 'result' in data
297
- assert 'test_tool executed' in data['result']
298
-
299
- def test_openai_compatible_endpoint(self, app, mock_vac_interpreter):
300
- """Test OpenAI compatible endpoint."""
301
- with patch('sunholo.agents.flask.vac_routes.ConfigManager'):
302
- vac_routes = VACRoutes(app, None, mock_vac_interpreter, add_langfuse_eval=False)
303
-
304
- # Mock the before_request handler to skip auth
305
- app.before_request_funcs = {}
306
-
307
- with app.test_client() as client:
308
- response = client.post(
309
- '/openai/v1/chat/completions',
310
- json={
311
- 'model': 'test_model',
312
- 'messages': [
313
- {'role': 'user', 'content': 'Hello AI'}
314
- ]
315
- },
316
- content_type='application/json'
317
- )
318
-
319
- assert response.status_code == 200
320
- data = response.get_json()
321
- assert 'choices' in data
322
- assert data['choices'][0]['message']['role'] == 'assistant'
323
-
324
- def test_vac_interpreter_default(self, app, mock_stream_interpreter):
325
- """Test the default VAC interpreter behavior."""
326
- vac_routes = VACRoutes(app, mock_stream_interpreter)
327
-
328
- result = vac_routes.vac_interpreter_default(
329
- question="Test question",
330
- vector_name="test_vector",
331
- chat_history=[]
332
- )
333
-
334
- assert result is not None
335
- # The default interpreter should call the stream interpreter with a NoOpCallback
336
-
337
-
338
- if __name__ == "__main__":
339
- pytest.main([__file__, "-v"])
File without changes
File without changes
File without changes
File without changes