sunholo 0.129.2__tar.gz → 0.131.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 (189) hide show
  1. {sunholo-0.129.2/src/sunholo.egg-info → sunholo-0.131.1}/PKG-INFO +1 -1
  2. {sunholo-0.129.2 → sunholo-0.131.1}/pyproject.toml +1 -1
  3. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/flask/vac_routes.py +2 -7
  4. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/alloydb_client.py +3 -1
  5. sunholo-0.131.1/src/sunholo/discovery_engine/cli.py +507 -0
  6. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/discovery_engine/discovery_engine_client.py +315 -9
  7. {sunholo-0.129.2 → sunholo-0.131.1/src/sunholo.egg-info}/PKG-INFO +1 -1
  8. sunholo-0.129.2/src/sunholo/discovery_engine/cli.py +0 -244
  9. {sunholo-0.129.2 → sunholo-0.131.1}/LICENSE.txt +0 -0
  10. {sunholo-0.129.2 → sunholo-0.131.1}/MANIFEST.in +0 -0
  11. {sunholo-0.129.2 → sunholo-0.131.1}/README.md +0 -0
  12. {sunholo-0.129.2 → sunholo-0.131.1}/setup.cfg +0 -0
  13. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/__init__.py +0 -0
  14. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/__init__.py +0 -0
  15. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/chat_history.py +0 -0
  16. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/dispatch_to_qa.py +0 -0
  17. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/fastapi/__init__.py +0 -0
  18. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/fastapi/base.py +0 -0
  19. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/fastapi/qna_routes.py +0 -0
  20. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/flask/__init__.py +0 -0
  21. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/flask/base.py +0 -0
  22. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/flask/qna_routes.py +0 -0
  23. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/langserve.py +0 -0
  24. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/pubsub.py +0 -0
  25. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/route.py +0 -0
  26. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/special_commands.py +0 -0
  27. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/agents/swagger.py +0 -0
  28. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/archive/__init__.py +0 -0
  29. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/archive/archive.py +0 -0
  30. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/auth/__init__.py +0 -0
  31. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/auth/gcloud.py +0 -0
  32. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/auth/refresh.py +0 -0
  33. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/auth/run.py +0 -0
  34. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/azure/__init__.py +0 -0
  35. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/azure/auth.py +0 -0
  36. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/azure/blobs.py +0 -0
  37. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/azure/event_grid.py +0 -0
  38. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/bots/__init__.py +0 -0
  39. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/bots/discord.py +0 -0
  40. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/bots/github_webhook.py +0 -0
  41. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/bots/webapp.py +0 -0
  42. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/chunker/__init__.py +0 -0
  43. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/chunker/azure.py +0 -0
  44. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/chunker/doc_handling.py +0 -0
  45. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/chunker/encode_metadata.py +0 -0
  46. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/chunker/images.py +0 -0
  47. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/chunker/loaders.py +0 -0
  48. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/chunker/message_data.py +0 -0
  49. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/chunker/pdfs.py +0 -0
  50. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/chunker/process_chunker_data.py +0 -0
  51. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/chunker/publish.py +0 -0
  52. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/chunker/pubsub.py +0 -0
  53. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/chunker/splitter.py +0 -0
  54. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/cli/__init__.py +0 -0
  55. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/cli/chat_vac.py +0 -0
  56. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/cli/cli.py +0 -0
  57. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/cli/cli_init.py +0 -0
  58. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/cli/configs.py +0 -0
  59. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/cli/deploy.py +0 -0
  60. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/cli/embedder.py +0 -0
  61. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/cli/merge_texts.py +0 -0
  62. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/cli/run_proxy.py +0 -0
  63. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/cli/sun_rich.py +0 -0
  64. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/cli/swagger.py +0 -0
  65. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/cli/vertex.py +0 -0
  66. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/components/__init__.py +0 -0
  67. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/components/llm.py +0 -0
  68. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/components/retriever.py +0 -0
  69. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/components/vectorstore.py +0 -0
  70. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/custom_logging.py +0 -0
  71. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/__init__.py +0 -0
  72. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/alloydb.py +0 -0
  73. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/database.py +0 -0
  74. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/lancedb.py +0 -0
  75. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/sql/sb/create_function.sql +0 -0
  76. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/sql/sb/create_function_time.sql +0 -0
  77. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/sql/sb/create_table.sql +0 -0
  78. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/sql/sb/delete_source_row.sql +0 -0
  79. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/sql/sb/return_sources.sql +0 -0
  80. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/sql/sb/setup.sql +0 -0
  81. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/static_dbs.py +0 -0
  82. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/database/uuid.py +0 -0
  83. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/discovery_engine/__init__.py +0 -0
  84. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/discovery_engine/chunker_handler.py +0 -0
  85. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/discovery_engine/create_new.py +0 -0
  86. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/discovery_engine/get_ai_search_chunks.py +0 -0
  87. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/embedder/__init__.py +0 -0
  88. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/embedder/embed_chunk.py +0 -0
  89. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/embedder/embed_metadata.py +0 -0
  90. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/excel/__init__.py +0 -0
  91. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/excel/plugin.py +0 -0
  92. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/gcs/__init__.py +0 -0
  93. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/gcs/add_file.py +0 -0
  94. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/gcs/download_folder.py +0 -0
  95. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/gcs/download_url.py +0 -0
  96. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/gcs/extract_and_sign.py +0 -0
  97. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/gcs/metadata.py +0 -0
  98. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/genai/__init__.py +0 -0
  99. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/genai/file_handling.py +0 -0
  100. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/genai/genaiv2.py +0 -0
  101. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/genai/images.py +0 -0
  102. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/genai/init.py +0 -0
  103. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/genai/process_funcs_cls.py +0 -0
  104. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/genai/safety.py +0 -0
  105. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/invoke/__init__.py +0 -0
  106. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/invoke/async_class.py +0 -0
  107. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/invoke/direct_vac_func.py +0 -0
  108. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/invoke/invoke_vac_utils.py +0 -0
  109. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/langchain_types.py +0 -0
  110. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/langfuse/__init__.py +0 -0
  111. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/langfuse/callback.py +0 -0
  112. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/langfuse/evals.py +0 -0
  113. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/langfuse/prompts.py +0 -0
  114. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/llamaindex/__init__.py +0 -0
  115. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/llamaindex/get_files.py +0 -0
  116. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/llamaindex/import_files.py +0 -0
  117. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/llamaindex/llamaindex_class.py +0 -0
  118. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/llamaindex/user_history.py +0 -0
  119. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/lookup/__init__.py +0 -0
  120. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/lookup/model_lookup.yaml +0 -0
  121. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/mcp/__init__.py +0 -0
  122. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/mcp/cli.py +0 -0
  123. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/ollama/__init__.py +0 -0
  124. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/ollama/ollama_images.py +0 -0
  125. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/pubsub/__init__.py +0 -0
  126. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/pubsub/process_pubsub.py +0 -0
  127. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/pubsub/pubsub_manager.py +0 -0
  128. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/qna/__init__.py +0 -0
  129. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/qna/parsers.py +0 -0
  130. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/qna/retry.py +0 -0
  131. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/senses/__init__.py +0 -0
  132. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/senses/stream_voice.py +0 -0
  133. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/streaming/__init__.py +0 -0
  134. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/streaming/content_buffer.py +0 -0
  135. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/streaming/langserve.py +0 -0
  136. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/streaming/stream_lookup.py +0 -0
  137. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/streaming/streaming.py +0 -0
  138. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/summarise/__init__.py +0 -0
  139. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/summarise/summarise.py +0 -0
  140. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/agent/__init__.py +0 -0
  141. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/agent/agent_service.py +0 -0
  142. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/agent/app.py +0 -0
  143. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/agent/my_log.py +0 -0
  144. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/agent/tools/__init__.py +0 -0
  145. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/agent/tools/your_agent.py +0 -0
  146. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/agent/vac_service.py +0 -0
  147. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/project/__init__.py +0 -0
  148. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/project/app.py +0 -0
  149. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/project/my_log.py +0 -0
  150. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/project/vac_service.py +0 -0
  151. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/system_services/__init__.py +0 -0
  152. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/system_services/app.py +0 -0
  153. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/templates/system_services/my_log.py +0 -0
  154. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/terraform/__init__.py +0 -0
  155. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/terraform/tfvars_editor.py +0 -0
  156. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/tools/__init__.py +0 -0
  157. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/tools/web_browser.py +0 -0
  158. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/__init__.py +0 -0
  159. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/api_key.py +0 -0
  160. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/big_context.py +0 -0
  161. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/config.py +0 -0
  162. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/config_class.py +0 -0
  163. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/config_schema.py +0 -0
  164. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/gcp.py +0 -0
  165. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/gcp_project.py +0 -0
  166. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/mime.py +0 -0
  167. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/parsers.py +0 -0
  168. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/timedelta.py +0 -0
  169. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/user_ids.py +0 -0
  170. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/utils/version.py +0 -0
  171. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/vertex/__init__.py +0 -0
  172. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/vertex/extensions_call.py +0 -0
  173. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/vertex/extensions_class.py +0 -0
  174. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/vertex/genai_functions.py +0 -0
  175. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/vertex/init.py +0 -0
  176. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/vertex/memory_tools.py +0 -0
  177. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/vertex/safety.py +0 -0
  178. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo/vertex/type_dict_to_json.py +0 -0
  179. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo.egg-info/SOURCES.txt +0 -0
  180. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo.egg-info/dependency_links.txt +0 -0
  181. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo.egg-info/entry_points.txt +0 -0
  182. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo.egg-info/requires.txt +0 -0
  183. {sunholo-0.129.2 → sunholo-0.131.1}/src/sunholo.egg-info/top_level.txt +0 -0
  184. {sunholo-0.129.2 → sunholo-0.131.1}/tests/test_async.py +0 -0
  185. {sunholo-0.129.2 → sunholo-0.131.1}/tests/test_async_genai2.py +0 -0
  186. {sunholo-0.129.2 → sunholo-0.131.1}/tests/test_chat_history.py +0 -0
  187. {sunholo-0.129.2 → sunholo-0.131.1}/tests/test_config.py +0 -0
  188. {sunholo-0.129.2 → sunholo-0.131.1}/tests/test_genai2.py +0 -0
  189. {sunholo-0.129.2 → sunholo-0.131.1}/tests/test_unstructured.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sunholo
3
- Version: 0.129.2
3
+ Version: 0.131.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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sunholo"
7
- version = "0.129.2"
7
+ version = "0.131.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"
@@ -238,12 +238,10 @@ if __name__ == "__main__":
238
238
 
239
239
  log.info(f'Streaming data with: {all_input}')
240
240
  if span:
241
- generation = span.generation(
241
+ span.update(
242
242
  name="start_streaming_chat",
243
243
  metadata=vac_config.configs_by_kind,
244
- input=all_input,
245
- completion_start_time=str(int(datetime.datetime.now().timestamp())),
246
- model=vac_config.vacConfig("model") or vac_config.vacConfig("llm")
244
+ input=all_input
247
245
  )
248
246
 
249
247
  def generate_response_content():
@@ -272,7 +270,6 @@ if __name__ == "__main__":
272
270
  if trace:
273
271
  chunk["trace_id"] = trace.id
274
272
  chunk["trace_url"] = trace.get_trace_url()
275
- generation.end(output=json.dumps(chunk))
276
273
  span.end(output=json.dumps(chunk))
277
274
  trace.update(output=json.dumps(chunk))
278
275
  archive_qa(chunk, vector_name)
@@ -314,7 +311,6 @@ if __name__ == "__main__":
314
311
  chunk["trace_url"] = trace.get_trace_url()
315
312
  archive_qa(chunk, vector_name)
316
313
  if trace:
317
- generation.end(output=json.dumps(chunk))
318
314
  span.end(output=json.dumps(chunk))
319
315
  trace.update(output=json.dumps(chunk))
320
316
  yield json.dumps(chunk)
@@ -330,7 +326,6 @@ if __name__ == "__main__":
330
326
 
331
327
  log.debug(f"streaming response: {response}")
332
328
  if trace:
333
- generation.end(output=response)
334
329
  span.end(output=response)
335
330
  trace.update(output=response)
336
331
  self.langfuse_eval_response(trace_id=trace.id, eval_percent=all_input.get('eval_percent'))
@@ -1167,7 +1167,8 @@ class AlloyDBClient:
1167
1167
  'total_rows': len(rows),
1168
1168
  'inserted_rows': 0,
1169
1169
  'failed_rows': 0,
1170
- 'errors': []
1170
+ 'errors': [],
1171
+ 'return_ids': []
1171
1172
  }
1172
1173
 
1173
1174
  for i, row in enumerate(rows):
@@ -1197,6 +1198,7 @@ class AlloyDBClient:
1197
1198
 
1198
1199
  # Insert the row
1199
1200
  result = await self._insert_single_row(table_name, filtered_row, primary_key_column=primary_key_column)
1201
+ results['return_ids'].append(result)
1200
1202
  results['inserted_rows'] += 1
1201
1203
 
1202
1204
  except Exception as e:
@@ -0,0 +1,507 @@
1
+ import json
2
+ import argparse
3
+ import traceback # For detailed error logging
4
+
5
+ # --- Standard library imports first ---
6
+ # --- Third-party imports ---
7
+ try:
8
+ # Assuming sun_rich is in your project structure relative to this file
9
+ from ..cli.sun_rich import console
10
+ except ImportError:
11
+ # Fallback if rich is not available or path is wrong
12
+ class ConsoleFallback:
13
+ def print(self, *args, **kwargs):
14
+ print(*args)
15
+ console = ConsoleFallback()
16
+ print("Warning: rich console not found, using basic print.")
17
+
18
+ # --- Local application imports ---
19
+ # Assuming custom_logging is available
20
+ # from ..custom_logging import log # Not explicitly used in CLI functions here
21
+
22
+ # Make sure to adjust the relative import path if needed for your project structure
23
+ from .discovery_engine_client import DiscoveryEngineClient, _DISCOVERYENGINE_AVAILABLE
24
+
25
+ # Import necessary types only if library is available, for mapping CLI args
26
+ if _DISCOVERYENGINE_AVAILABLE:
27
+ from .discovery_engine_client import discoveryengine # Get the imported module
28
+ else:
29
+ discoveryengine = None # Set to None if import failed
30
+
31
+
32
+ # --- Command Handler Functions ---
33
+
34
+ def discovery_engine_command(args):
35
+ """
36
+ Handles the `discovery-engine` command and its subcommands.
37
+ """
38
+ # Dispatch based on subcommand
39
+ if args.subcommand == 'create-datastore':
40
+ create_datastore_command(args)
41
+ elif args.subcommand == 'import-documents':
42
+ import_documents_command(args)
43
+ elif args.subcommand == 'import-documents-with-metadata':
44
+ import_documents_with_metadata_command(args)
45
+ elif args.subcommand == 'import-document-with-metadata':
46
+ import_document_with_metadata_command(args)
47
+ elif args.subcommand == 'search': # Existing chunk search
48
+ search_command(args)
49
+ elif args.subcommand == 'search-by-id-and-or-date': # Existing chunk search
50
+ search_by_id_and_or_date_command(args)
51
+ elif args.subcommand == 'search-engine': # NEW engine search
52
+ search_engine_command(args)
53
+ # Add elif for create-engine if needed
54
+ # elif args.subcommand == 'create-engine':
55
+ # create_engine_command(args)
56
+ else:
57
+ console.print(f"[bold red]Unknown Discovery Engine subcommand: {args.subcommand}[/bold red]")
58
+
59
+ def create_datastore_command(args):
60
+ """Handles the `discovery-engine create-datastore` subcommand."""
61
+ console.print(f"[cyan]Initiating datastore creation for ID: {args.data_store_id}...[/cyan]")
62
+ try:
63
+ client = DiscoveryEngineClient(
64
+ project_id=args.project,
65
+ data_store_id=args.data_store_id, # ID for the one being created
66
+ location=args.location
67
+ )
68
+ # Assuming create_data_store exists and takes these args
69
+ operation_name = client.create_data_store(
70
+ type=args.type,
71
+ chunk_size=args.chunk_size,
72
+ collection=args.collection
73
+ )
74
+ console.print(f"[bold green]Datastore creation initiated. Operation name: {operation_name}[/bold green]")
75
+ console.print("[yellow]Note: Creation is asynchronous. Check operation status in Google Cloud Console.[/yellow]")
76
+ except Exception as e:
77
+ console.print(f"[bold red]Error creating datastore: {e}[/bold red]")
78
+ console.print(f"[red]{traceback.format_exc()}[/red]")
79
+
80
+ def import_documents_command(args):
81
+ """Handles the `discovery-engine import-documents` subcommand."""
82
+ console.print(f"[cyan]Initiating document import into datastore: {args.data_store_id}...[/cyan]")
83
+ try:
84
+ client = DiscoveryEngineClient(
85
+ project_id=args.project,
86
+ data_store_id=args.data_store_id, # Target datastore
87
+ location=args.location
88
+ )
89
+ operation_name = client.import_documents(
90
+ gcs_uri=args.gcs_uri,
91
+ data_schema=args.data_schema,
92
+ branch=args.branch,
93
+ bigquery_dataset=args.bigquery_dataset,
94
+ bigquery_table=args.bigquery_table,
95
+ bigquery_project_id=args.bigquery_project_id
96
+ )
97
+ if operation_name:
98
+ console.print(f"[bold green]Document import initiated. Operation name: {operation_name}[/bold green]")
99
+ console.print("[yellow]Note: Import is asynchronous. Check operation status in Google Cloud Console.[/yellow]")
100
+ else:
101
+ console.print("[bold yellow]Document import command executed, but no operation name returned (may indicate skipped due to existing data or other non-fatal issue). Check logs.[/bold yellow]")
102
+ except Exception as e:
103
+ console.print(f"[bold red]Error importing documents: {e}[/bold red]")
104
+ console.print(f"[red]{traceback.format_exc()}[/red]")
105
+
106
+ def import_documents_with_metadata_command(args):
107
+ """Handles the `discovery-engine import-documents-with-metadata` subcommand."""
108
+ console.print(f"[cyan]Initiating document import with metadata from {args.gcs_uri} into datastore: {args.data_store_id}...[/cyan]")
109
+ try:
110
+ client = DiscoveryEngineClient(
111
+ project_id=args.project,
112
+ data_store_id=args.data_store_id,
113
+ location=args.location
114
+ )
115
+ # Ensure the method exists in your client class
116
+ operation_name = client.import_documents_with_metadata(
117
+ gcs_uri=args.gcs_uri,
118
+ # data_schema=args.data_schema, # This method might not need data_schema explicitly
119
+ branch=args.branch
120
+ )
121
+ if operation_name:
122
+ console.print(f"[bold green]Document import with metadata initiated. Operation name: {operation_name}[/bold green]")
123
+ console.print("[yellow]Note: Import is asynchronous.[/yellow]")
124
+ else:
125
+ console.print("[bold yellow]Document import command executed, but no operation name returned.[/bold yellow]")
126
+ except Exception as e:
127
+ console.print(f"[bold red]Error importing documents with metadata: {e}[/bold red]")
128
+ console.print(f"[red]{traceback.format_exc()}[/red]")
129
+
130
+ def import_document_with_metadata_command(args):
131
+ """Handles the `discovery-engine import-document-with-metadata` subcommand."""
132
+ console.print(f"[cyan]Initiating single document import with metadata for {args.gcs_uri} into datastore: {args.data_store_id}...[/cyan]")
133
+ metadata = None
134
+ try:
135
+ if args.metadata_file:
136
+ console.print(f"Loading metadata from file: {args.metadata_file}")
137
+ with open(args.metadata_file, 'r') as f:
138
+ metadata = json.load(f)
139
+ elif args.metadata_string:
140
+ console.print("Loading metadata from string.")
141
+ metadata = json.loads(args.metadata_string)
142
+ else:
143
+ console.print("[bold red]Error: Must provide either --metadata-file or --metadata-string[/bold red]")
144
+ return
145
+
146
+ client = DiscoveryEngineClient(
147
+ project_id=args.project,
148
+ data_store_id=args.data_store_id,
149
+ location=args.location
150
+ )
151
+ operation_name = client.import_document_with_metadata(
152
+ gcs_uri=args.gcs_uri,
153
+ metadata=metadata,
154
+ branch=args.branch
155
+ )
156
+ if operation_name:
157
+ console.print(f"[bold green]Single document import initiated. Operation name: {operation_name}[/bold green]")
158
+ console.print("[yellow]Note: Import is asynchronous.[/yellow]")
159
+ else:
160
+ console.print("[bold yellow]Single document import command executed, but no operation name returned.[/bold yellow]")
161
+
162
+ except FileNotFoundError:
163
+ console.print(f"[bold red]Error: Metadata file not found at {args.metadata_file}[/bold red]")
164
+ except json.JSONDecodeError as e:
165
+ console.print(f"[bold red]Error decoding metadata JSON: {e}[/bold red]")
166
+ except Exception as e:
167
+ console.print(f"[bold red]Error importing document with metadata: {e}[/bold red]")
168
+ console.print(f"[red]{traceback.format_exc()}[/red]")
169
+
170
+ def search_command(args):
171
+ """Handles the `discovery-engine search` subcommand (Data Store Chunks)."""
172
+ console.print(f"[cyan]Searching data store '{args.data_store_id}' for query: '{args.query}' (mode: chunks)[/cyan]")
173
+ try:
174
+ client = DiscoveryEngineClient(
175
+ project_id=args.project,
176
+ data_store_id=args.data_store_id, # Target datastore
177
+ location=args.location
178
+ )
179
+ # This calls get_chunks which returns string or pager
180
+ results_data = client.get_chunks(
181
+ query=args.query,
182
+ # num_previous_chunks=args.num_previous_chunks, # Ensure these args are added to parser if needed
183
+ # num_next_chunks=args.num_next_chunks, # Ensure these args are added to parser if needed
184
+ page_size=args.page_size,
185
+ parse_chunks_to_string=args.parse_chunks_to_string,
186
+ serving_config=args.serving_config,
187
+ # data_store_ids=args.data_store_ids # Ensure these args are added to parser if needed
188
+ )
189
+
190
+ if args.parse_chunks_to_string:
191
+ console.print("\n[bold magenta]--- Combined Chunk String ---[/bold magenta]")
192
+ console.print(results_data if results_data else "[yellow]No results found or error occurred.[/yellow]")
193
+ elif results_data: # It's a pager object
194
+ console.print("\n[bold magenta]--- Individual Chunks ---[/bold magenta]")
195
+ chunk_count = 0
196
+ try:
197
+ # Iterate through the pager returned by get_chunks
198
+ for page in results_data.pages:
199
+ if not hasattr(page, 'results') or not page.results: continue
200
+ for result in page.results:
201
+ # Ensure the result structure is as expected by get_chunks
202
+ if hasattr(result, 'chunk'):
203
+ chunk_count += 1
204
+ console.print(f"\n[bold]Chunk {chunk_count}:[/bold]")
205
+ # Use the client's formatter if available
206
+ console.print(client.chunk_format(result.chunk))
207
+ elif hasattr(result, 'document') and hasattr(result.document, 'chunks'):
208
+ # Fallback if structure is different (e.g., document with chunks)
209
+ for chunk in result.document.chunks:
210
+ chunk_count += 1
211
+ console.print(f"\n[bold]Chunk {chunk_count} (from doc {result.document.id}):[/bold]")
212
+ console.print(f" Content: {getattr(chunk, 'content', 'N/A')}")
213
+ console.print(f" Doc Name: {getattr(chunk, 'document_metadata', {}).get('name', 'N/A')}") # Example access
214
+
215
+ if chunk_count == 0:
216
+ console.print("[yellow]No chunks found in the results.[/yellow]")
217
+
218
+ except Exception as page_err:
219
+ console.print(f"[bold red]Error processing search results pager: {page_err}[/bold red]")
220
+ console.print(f"[red]{traceback.format_exc()}[/red]")
221
+ else:
222
+ console.print("[yellow]No results found or error occurred.[/yellow]")
223
+
224
+ except Exception as e:
225
+ console.print(f"[bold red]Error during data store search: {e}[/bold red]")
226
+ console.print(f"[red]{traceback.format_exc()}[/red]")
227
+
228
+
229
+ def search_by_id_and_or_date_command(args):
230
+ """Handles the `discovery-engine search-by-id-and-or-date` subcommand (Data Store Chunks)."""
231
+ console.print(f"[cyan]Searching data store '{args.data_store_id}' by ID/Date for query: '{args.query}' (mode: chunks)[/cyan]")
232
+ # Similar implementation to search_command, but calls search_by_objectId_and_or_date
233
+ try:
234
+ client = DiscoveryEngineClient(
235
+ project_id=args.project,
236
+ data_store_id=args.data_store_id, # Target datastore
237
+ location=args.location
238
+ )
239
+ results_data = client.search_by_objectId_and_or_date(
240
+ query=args.query,
241
+ objectId=args.object_id,
242
+ date=args.date,
243
+ # num_previous_chunks=args.num_previous_chunks, # Pass these through
244
+ # num_next_chunks=args.num_next_chunks, # Pass these through
245
+ page_size=args.page_size,
246
+ parse_chunks_to_string=args.parse_chunks_to_string,
247
+ serving_config=args.serving_config,
248
+ data_store_ids=args.data_store_ids
249
+ )
250
+
251
+ # Output processing identical to search_command
252
+ if args.parse_chunks_to_string:
253
+ console.print("\n[bold magenta]--- Combined Chunk String (Filtered) ---[/bold magenta]")
254
+ console.print(results_data if results_data else "[yellow]No results found or error occurred.[/yellow]")
255
+ elif results_data:
256
+ console.print("\n[bold magenta]--- Individual Chunks (Filtered) ---[/bold magenta]")
257
+ chunk_count = 0
258
+ # ... (pager iteration identical to search_command) ...
259
+ try:
260
+ for page in results_data.pages:
261
+ # ... iterate results and chunks ...
262
+ pass # Replace with actual iteration and printing
263
+ if chunk_count == 0:
264
+ console.print("[yellow]No chunks found in the filtered results.[/yellow]")
265
+ except Exception as page_err:
266
+ console.print(f"[bold red]Error processing filtered search results pager: {page_err}[/bold red]")
267
+ else:
268
+ console.print("[yellow]No results found or error occurred.[/yellow]")
269
+
270
+ except Exception as e:
271
+ console.print(f"[bold red]Error during filtered data store search: {e}[/bold red]")
272
+ console.print(f"[red]{traceback.format_exc()}[/red]")
273
+
274
+
275
+ # --- NEW Search Engine Command ---
276
+ def search_engine_command(args):
277
+ """Handles the `discovery-engine search-engine` subcommand."""
278
+ if not _DISCOVERYENGINE_AVAILABLE:
279
+ console.print("[bold red]Error: google-cloud-discoveryengine library is required but not installed.[/bold red]")
280
+ return
281
+
282
+ console.print(f"[cyan]Searching engine '{args.engine_id}' for query: '{args.query}'[/cyan]")
283
+
284
+ try:
285
+ client = DiscoveryEngineClient(
286
+ project_id=args.project,
287
+ # data_store_id is required by __init__ but less relevant here.
288
+ # Provide a default or the primary one associated with the project/engine.
289
+ data_store_id=args.data_store_id_for_init,
290
+ location=args.location
291
+ )
292
+
293
+ # --- Map CLI string args to Enums ---
294
+ query_expansion_map = {
295
+ "AUTO": discoveryengine.SearchRequest.QueryExpansionSpec.Condition.AUTO,
296
+ "DISABLED": discoveryengine.SearchRequest.QueryExpansionSpec.Condition.DISABLED,
297
+ }
298
+ spell_correction_map = {
299
+ "AUTO": discoveryengine.SearchRequest.SpellCorrectionSpec.Mode.AUTO,
300
+ "SUGGEST": discoveryengine.SearchRequest.SpellCorrectionSpec.Mode.SUGGEST,
301
+ }
302
+
303
+ query_expansion_level = query_expansion_map.get(args.query_expansion, discoveryengine.SearchRequest.QueryExpansionSpec.Condition.AUTO)
304
+ spell_correction_mode = spell_correction_map.get(args.spell_correction, discoveryengine.SearchRequest.SpellCorrectionSpec.Mode.AUTO)
305
+
306
+ # --- Call the search_engine method ---
307
+ pager = client.search_engine(
308
+ search_query=args.query,
309
+ engine_id=args.engine_id,
310
+ serving_config_id=args.serving_config_id,
311
+ collection_id=args.collection_id,
312
+ page_size=args.page_size,
313
+ return_snippet=args.return_snippet,
314
+ summary_result_count=args.summary_count,
315
+ include_citations=args.include_citations,
316
+ custom_prompt=args.custom_prompt,
317
+ model_version=args.model_version,
318
+ query_expansion_level=query_expansion_level,
319
+ spell_correction_mode=spell_correction_mode,
320
+ filter_str=args.filter,
321
+ user_pseudo_id=args.user_id,
322
+ # boost_spec, params, custom_fine_tuning_spec could be added here if parsed from args
323
+ )
324
+
325
+ # --- Process and Print Results ---
326
+ if pager:
327
+ console.print("\n[bold magenta]--- Search Engine Results ---[/bold magenta]")
328
+ results_found_on_any_page = False
329
+ page_num = 0
330
+ try:
331
+ for page in pager.pages:
332
+ page_num += 1
333
+ results_found_on_this_page = False
334
+ console.print(f"\n[bold]--- Page {page_num} ---[/bold]")
335
+
336
+ # Print Summary (available on the page level)
337
+ if hasattr(page, 'summary') and page.summary and page.summary.summary_text:
338
+ results_found_on_any_page = True
339
+ results_found_on_this_page = True
340
+ console.print("\n[bold green]Search Summary:[/bold green]")
341
+ console.print(page.summary.summary_text)
342
+ if args.include_citations and hasattr(page.summary, 'summary_with_metadata') and page.summary.summary_with_metadata:
343
+ citations = page.summary.summary_with_metadata.citations
344
+ if citations:
345
+ console.print("[bold cyan]Citations:[/bold cyan]")
346
+ for i, citation in enumerate(citations):
347
+ source_info = ", ".join([f"'{s.citation_source}'" for s in citation.sources]) if citation.sources else "N/A"
348
+ console.print(f" [{i+1}] Sources: {source_info}")
349
+ references = page.summary.summary_with_metadata.references
350
+ if references:
351
+ console.print("[bold cyan]References:[/bold cyan]")
352
+ for ref in references:
353
+ console.print(f" - Title: {getattr(ref, 'title', 'N/A')}, URI: {getattr(ref, 'uri', 'N/A')}") # Adjust based on actual reference structure
354
+
355
+ console.print("-" * 20)
356
+
357
+
358
+ # Print Document Results (available on the page level)
359
+ if hasattr(page, 'results') and page.results:
360
+ console.print(f"[bold blue]Documents Found ({len(page.results)} on this page):[/bold blue]")
361
+ for i, result in enumerate(page.results):
362
+ results_found_on_any_page = True
363
+ results_found_on_this_page = True
364
+ console.print(f"\n[bold]Result {i+1}:[/bold]")
365
+ doc = result.document
366
+ console.print(f" ID: {doc.id}")
367
+ console.print(f" Name: {doc.name}")
368
+ # Display structData if present
369
+ if doc.struct_data:
370
+ try:
371
+ # Convert Struct to dict for nice printing
372
+ struct_dict = dict(doc.struct_data)
373
+ console.print(f" Metadata: {json.dumps(struct_dict, indent=2)}")
374
+ except Exception:
375
+ console.print(f" Metadata: {doc.struct_data}") # Fallback
376
+
377
+ # Display Snippets if requested and available
378
+ if args.return_snippet and 'snippets' in doc.derived_struct_data:
379
+ console.print("[bold cyan] Snippets:[/bold cyan]")
380
+ for snippet in doc.derived_struct_data['snippets']:
381
+ console.print(f" - {snippet.get('snippet', 'N/A').strip()}") # Adjust key if needed
382
+ elif args.return_snippet:
383
+ console.print("[yellow] (Snippets requested but not found in result)[/yellow]")
384
+ console.print("-" * 5)
385
+ console.print("-" * 20) # End of results list for page
386
+
387
+ if not results_found_on_this_page:
388
+ console.print("[yellow](No summary or document results on this page)[/yellow]")
389
+
390
+
391
+ if not results_found_on_any_page:
392
+ console.print("[yellow]No results found for the search query.[/yellow]")
393
+
394
+ except Exception as page_err:
395
+ console.print(f"[bold red]Error processing results pager: {page_err}[/bold red]")
396
+ console.print(f"[red]{traceback.format_exc()}[/red]")
397
+
398
+ else:
399
+ console.print("[yellow]Search call did not return a result object (check logs for errors).[/yellow]")
400
+
401
+ except Exception as e:
402
+ console.print(f"[bold red]Error during engine search: {e}[/bold red]")
403
+ console.print(f"[red]{traceback.format_exc()}[/red]")
404
+
405
+
406
+ # --- Argparse Setup ---
407
+
408
+ def setup_discovery_engine_subparser(subparsers):
409
+ """
410
+ Sets up the `discovery-engine` subparser and its subcommands.
411
+ """
412
+ discovery_engine_parser = subparsers.add_parser('discovery-engine', help='Interact with Google Cloud Discovery Engine')
413
+ # Add arguments common to most discovery engine commands
414
+ discovery_engine_parser.add_argument('--project', required=True, help='Google Cloud project ID')
415
+ discovery_engine_parser.add_argument('--location', default='global', help='Location (e.g., global, us, eu)')
416
+ # data_store_id is required by many commands, make it common if possible, else add per-command
417
+ # For simplicity here, adding it per command where needed or as a specific arg for client init
418
+
419
+ discovery_engine_subparsers = discovery_engine_parser.add_subparsers(dest='subcommand', required=True, title='Discovery Engine Subcommands')
420
+
421
+ # --- Create Datastore subcommand ---
422
+ create_datastore_parser = discovery_engine_subparsers.add_parser('create-datastore', help='Create a new Discovery Engine datastore')
423
+ create_datastore_parser.add_argument('--data-store-id', required=True, help='The ID for the new datastore')
424
+ create_datastore_parser.add_argument('--type', choices=['chunk'], default='chunk', help='The type of datastore (currently only chunk)')
425
+ create_datastore_parser.add_argument('--chunk-size', type=int, default=500, help='Chunk size for layout-based chunking (100-500)')
426
+ create_datastore_parser.add_argument('--collection', default='default_collection', help='Collection ID')
427
+ create_datastore_parser.set_defaults(func=discovery_engine_command)
428
+
429
+ # --- Import Documents subcommand ---
430
+ import_documents_parser = discovery_engine_subparsers.add_parser('import-documents', help='Import documents into a datastore')
431
+ import_documents_parser.add_argument('--data-store-id', required=True, help='The ID of the target datastore')
432
+ import_grp = import_documents_parser.add_mutually_exclusive_group(required=True)
433
+ import_grp.add_argument('--gcs-uri', help='GCS URI of documents (gs://bucket/...) or pattern (gs://bucket/*.json)')
434
+ import_grp.add_argument('--bigquery-source', nargs=2, metavar=('DATASET_ID', 'TABLE_ID'), help='BigQuery dataset and table ID')
435
+ import_documents_parser.add_argument('--data-schema', default='content', help='Data schema (content, document, custom, csv, user_event)')
436
+ import_documents_parser.add_argument('--branch', default='default_branch', help='Target branch')
437
+ import_documents_parser.add_argument('--bigquery-project-id', help='Project ID for BigQuery source (defaults to --project)')
438
+ import_documents_parser.set_defaults(func=discovery_engine_command)
439
+
440
+ # --- Import Documents with Metadata (JSONL) subcommand ---
441
+ import_docs_meta_parser = discovery_engine_subparsers.add_parser('import-documents-with-metadata', help='Import documents via JSONL metadata file')
442
+ import_docs_meta_parser.add_argument('--data-store-id', required=True, help='The ID of the target datastore')
443
+ import_docs_meta_parser.add_argument('--gcs-uri', required=True, help='GCS URI of the JSONL metadata file')
444
+ import_docs_meta_parser.add_argument('--branch', default='default_branch', help='Target branch')
445
+ # data_schema might not be needed if using inline source via metadata file
446
+ # import_docs_meta_parser.add_argument('--data-schema', default='content', help='Data schema')
447
+ import_docs_meta_parser.set_defaults(func=discovery_engine_command)
448
+
449
+ # --- Import Single Document with Metadata subcommand ---
450
+ import_doc_meta_parser = discovery_engine_subparsers.add_parser('import-document-with-metadata', help='Import a single document with metadata')
451
+ import_doc_meta_parser.add_argument('--data-store-id', required=True, help='The ID of the target datastore')
452
+ import_doc_meta_parser.add_argument('--gcs-uri', required=True, help='GCS URI of the document content')
453
+ meta_grp = import_doc_meta_parser.add_mutually_exclusive_group(required=True)
454
+ meta_grp.add_argument('--metadata-file', help='Path to a local JSON file containing metadata')
455
+ meta_grp.add_argument('--metadata-string', help='JSON string containing metadata')
456
+ import_doc_meta_parser.add_argument('--branch', default='default_branch', help='Target branch')
457
+ import_doc_meta_parser.set_defaults(func=discovery_engine_command)
458
+
459
+ # --- Search Data Store (Chunks) subcommand ---
460
+ search_parser = discovery_engine_subparsers.add_parser('search', help='Search a datastore (fetches chunks)')
461
+ search_parser.add_argument('--query', required=True, help='The search query')
462
+ search_parser.add_argument('--data-store-id', required=True, help='Data store ID to search')
463
+ search_parser.add_argument('--page-size', type=int, default=10, help='Max results per page')
464
+ search_parser.add_argument('--parse-chunks-to-string', action='store_true', help='Output results as one formatted string')
465
+ search_parser.add_argument('--serving-config', default='default_config', help='Serving config ID for the data store')
466
+ # Add arguments for num_previous_chunks, num_next_chunks, data_store_ids if needed
467
+ # search_parser.add_argument('--num-previous-chunks', type=int, default=3)
468
+ # search_parser.add_argument('--num-next-chunks', type=int, default=3)
469
+ # search_parser.add_argument('--data-store-ids', nargs='+', help='Search across multiple data stores')
470
+ search_parser.set_defaults(func=discovery_engine_command)
471
+
472
+ # --- Search Data Store By ID/Date (Chunks) subcommand ---
473
+ search_by_id_parser = discovery_engine_subparsers.add_parser('search-by-id-and-or-date', help='Search a datastore by ID/date (fetches chunks)')
474
+ search_by_id_parser.add_argument('--query', required=True, help='The search query')
475
+ search_by_id_parser.add_argument('--data-store-id', required=True, help='Data store ID to search')
476
+ search_by_id_parser.add_argument('--object-id', help='Object ID to filter by (exact match)')
477
+ search_by_id_parser.add_argument('--date', help='Date filter (YYYY-MM-DDTHH:MM:SSZ or similar ISO format)')
478
+ search_by_id_parser.add_argument('--page-size', type=int, default=10, help='Max results per page')
479
+ search_by_id_parser.add_argument('--parse-chunks-to-string', action='store_true', help='Output results as one formatted string')
480
+ search_by_id_parser.add_argument('--serving-config', default='default_config', help='Serving config ID')
481
+ # Add arguments for num_previous_chunks, num_next_chunks, data_store_ids if needed
482
+ # search_by_id_parser.add_argument('--num-previous-chunks', type=int, default=3)
483
+ # search_by_id_parser.add_argument('--num-next-chunks', type=int, default=3)
484
+ search_by_id_parser.add_argument('--data-store-ids', nargs='+', help='Search across multiple data stores (optional)')
485
+ search_by_id_parser.set_defaults(func=discovery_engine_command)
486
+
487
+ # --- NEW: Search Engine subcommand ---
488
+ search_engine_parser = discovery_engine_subparsers.add_parser('search-engine', help='Search a Discovery Engine (fetches documents/summary)')
489
+ search_engine_parser.add_argument('--query', required=True, help='The search query')
490
+ search_engine_parser.add_argument('--engine-id', required=True, help='Engine ID to search')
491
+ # Add data_store_id needed for client init, maybe make it optional if client handles it?
492
+ search_engine_parser.add_argument('--data-store-id-for-init', required=True, help='A primary data store ID associated with the project/engine (for client init)')
493
+ search_engine_parser.add_argument('--serving-config-id', default='default_config', help='Serving config ID for the engine')
494
+ search_engine_parser.add_argument('--collection-id', default='default_collection', help='Collection ID for the engine path')
495
+ search_engine_parser.add_argument('--page-size', type=int, default=10, help='Max results per page')
496
+ search_engine_parser.add_argument('--no-snippet', action='store_false', dest='return_snippet', help='Disable fetching snippets')
497
+ search_engine_parser.add_argument('--summary-count', type=int, default=5, help='Number of results for summary (0 to disable)')
498
+ search_engine_parser.add_argument('--no-citations', action='store_false', dest='include_citations', help='Disable citations in summary')
499
+ search_engine_parser.add_argument('--custom-prompt', help='Custom preamble for summary generation')
500
+ search_engine_parser.add_argument('--model-version', default='stable', help='Summary model version')
501
+ search_engine_parser.add_argument('--query-expansion', choices=['AUTO', 'DISABLED'], default='AUTO', help='Query expansion level')
502
+ search_engine_parser.add_argument('--spell-correction', choices=['AUTO', 'SUGGEST'], default='AUTO', help='Spell correction mode')
503
+ search_engine_parser.add_argument('--filter', help='Filter string to apply')
504
+ search_engine_parser.add_argument('--user-id', help='User pseudo ID for personalization/analytics')
505
+ search_engine_parser.set_defaults(func=discovery_engine_command)
506
+
507
+ # Add other subparsers for create-engine, etc. if needed