MindsDB 25.4.5.0__py3-none-any.whl → 25.5.4.0__py3-none-any.whl

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.

Potentially problematic release.


This version of MindsDB might be problematic. Click here for more details.

Files changed (350) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +215 -185
  3. mindsdb/api/a2a/__init__.py +0 -0
  4. mindsdb/api/a2a/__main__.py +114 -0
  5. mindsdb/api/a2a/a2a_client.py +439 -0
  6. mindsdb/api/a2a/agent.py +308 -0
  7. mindsdb/api/a2a/common/__init__.py +0 -0
  8. mindsdb/api/a2a/common/client/__init__.py +4 -0
  9. mindsdb/api/a2a/common/client/card_resolver.py +21 -0
  10. mindsdb/api/a2a/common/client/client.py +86 -0
  11. mindsdb/api/a2a/common/server/__init__.py +4 -0
  12. mindsdb/api/a2a/common/server/server.py +164 -0
  13. mindsdb/api/a2a/common/server/task_manager.py +287 -0
  14. mindsdb/api/a2a/common/server/utils.py +28 -0
  15. mindsdb/api/a2a/common/types.py +365 -0
  16. mindsdb/api/a2a/constants.py +9 -0
  17. mindsdb/api/a2a/run_a2a.py +129 -0
  18. mindsdb/api/a2a/task_manager.py +594 -0
  19. mindsdb/api/executor/command_executor.py +49 -28
  20. mindsdb/api/executor/datahub/classes/response.py +5 -2
  21. mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +8 -0
  22. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +39 -72
  23. mindsdb/api/executor/datahub/datanodes/system_tables.py +10 -13
  24. mindsdb/api/executor/planner/query_planner.py +14 -2
  25. mindsdb/api/executor/sql_query/result_set.py +185 -52
  26. mindsdb/api/executor/sql_query/sql_query.py +1 -1
  27. mindsdb/api/executor/sql_query/steps/apply_predictor_step.py +11 -13
  28. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +8 -10
  29. mindsdb/api/executor/sql_query/steps/fetch_dataframe_partition.py +5 -44
  30. mindsdb/api/executor/sql_query/steps/insert_step.py +24 -15
  31. mindsdb/api/executor/sql_query/steps/join_step.py +1 -1
  32. mindsdb/api/executor/sql_query/steps/project_step.py +1 -1
  33. mindsdb/api/executor/sql_query/steps/sql_steps.py +1 -1
  34. mindsdb/api/executor/sql_query/steps/subselect_step.py +4 -8
  35. mindsdb/api/executor/sql_query/steps/union_step.py +1 -3
  36. mindsdb/api/http/initialize.py +118 -85
  37. mindsdb/api/http/namespaces/analysis.py +17 -4
  38. mindsdb/api/http/namespaces/file.py +8 -2
  39. mindsdb/api/http/namespaces/sql.py +13 -27
  40. mindsdb/api/http/namespaces/tree.py +1 -1
  41. mindsdb/api/http/start.py +7 -2
  42. mindsdb/api/mcp/start.py +42 -5
  43. mindsdb/api/mysql/mysql_proxy/data_types/mysql_packet.py +0 -1
  44. mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/binary_resultset_row_package.py +52 -19
  45. mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +8 -10
  46. mindsdb/api/mysql/mysql_proxy/libs/constants/mysql.py +54 -38
  47. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +86 -123
  48. mindsdb/api/mysql/mysql_proxy/utilities/dump.py +351 -0
  49. mindsdb/api/mysql/mysql_proxy/utilities/exceptions.py +0 -4
  50. mindsdb/api/postgres/postgres_proxy/executor/executor.py +1 -1
  51. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_formats.py +2 -2
  52. mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +5 -6
  53. mindsdb/integrations/handlers/altibase_handler/altibase_handler.py +26 -27
  54. mindsdb/integrations/handlers/altibase_handler/connection_args.py +13 -13
  55. mindsdb/integrations/handlers/altibase_handler/tests/test_altibase_handler.py +8 -8
  56. mindsdb/integrations/handlers/altibase_handler/tests/test_altibase_handler_dsn.py +13 -13
  57. mindsdb/integrations/handlers/anthropic_handler/__init__.py +2 -2
  58. mindsdb/integrations/handlers/anthropic_handler/anthropic_handler.py +1 -3
  59. mindsdb/integrations/handlers/aurora_handler/aurora_handler.py +1 -0
  60. mindsdb/integrations/handlers/autosklearn_handler/autosklearn_handler.py +1 -1
  61. mindsdb/integrations/handlers/autosklearn_handler/config.py +0 -1
  62. mindsdb/integrations/handlers/bigquery_handler/bigquery_handler.py +1 -1
  63. mindsdb/integrations/handlers/bigquery_handler/requirements.txt +1 -0
  64. mindsdb/integrations/handlers/bigquery_handler/tests/test_bigquery_handler.py +1 -1
  65. mindsdb/integrations/handlers/binance_handler/binance_handler.py +1 -0
  66. mindsdb/integrations/handlers/binance_handler/binance_tables.py +3 -4
  67. mindsdb/integrations/handlers/byom_handler/__init__.py +0 -1
  68. mindsdb/integrations/handlers/chromadb_handler/requirements.txt +1 -0
  69. mindsdb/integrations/handlers/ckan_handler/ckan_handler.py +3 -0
  70. mindsdb/integrations/handlers/clickhouse_handler/__init__.py +1 -1
  71. mindsdb/integrations/handlers/cloud_spanner_handler/tests/test_cloud_spanner_handler.py +0 -2
  72. mindsdb/integrations/handlers/cloud_sql_handler/cloud_sql_handler.py +0 -1
  73. mindsdb/integrations/handlers/cohere_handler/__init__.py +1 -1
  74. mindsdb/integrations/handlers/cohere_handler/cohere_handler.py +11 -13
  75. mindsdb/integrations/handlers/confluence_handler/confluence_tables.py +6 -0
  76. mindsdb/integrations/handlers/databend_handler/connection_args.py +1 -1
  77. mindsdb/integrations/handlers/databend_handler/databend_handler.py +4 -4
  78. mindsdb/integrations/handlers/databend_handler/tests/__init__.py +0 -1
  79. mindsdb/integrations/handlers/databend_handler/tests/test_databend_handler.py +1 -1
  80. mindsdb/integrations/handlers/derby_handler/connection_args.py +1 -1
  81. mindsdb/integrations/handlers/derby_handler/derby_handler.py +14 -22
  82. mindsdb/integrations/handlers/derby_handler/tests/test_derby_handler.py +6 -6
  83. mindsdb/integrations/handlers/discord_handler/discord_handler.py +5 -5
  84. mindsdb/integrations/handlers/discord_handler/discord_tables.py +3 -3
  85. mindsdb/integrations/handlers/discord_handler/tests/test_discord.py +5 -3
  86. mindsdb/integrations/handlers/dockerhub_handler/dockerhub.py +3 -3
  87. mindsdb/integrations/handlers/dockerhub_handler/dockerhub_handler.py +2 -2
  88. mindsdb/integrations/handlers/dockerhub_handler/dockerhub_tables.py +57 -54
  89. mindsdb/integrations/handlers/dremio_handler/__init__.py +2 -2
  90. mindsdb/integrations/handlers/druid_handler/__init__.py +1 -1
  91. mindsdb/integrations/handlers/druid_handler/druid_handler.py +2 -2
  92. mindsdb/integrations/handlers/edgelessdb_handler/tests/test_edgelessdb_handler.py +9 -9
  93. mindsdb/integrations/handlers/email_handler/email_client.py +1 -1
  94. mindsdb/integrations/handlers/email_handler/email_ingestor.py +1 -1
  95. mindsdb/integrations/handlers/email_handler/email_tables.py +0 -1
  96. mindsdb/integrations/handlers/email_handler/settings.py +0 -1
  97. mindsdb/integrations/handlers/eventstoredb_handler/eventstoredb_handler.py +2 -1
  98. mindsdb/integrations/handlers/firebird_handler/firebird_handler.py +1 -1
  99. mindsdb/integrations/handlers/flaml_handler/flaml_handler.py +9 -9
  100. mindsdb/integrations/handlers/frappe_handler/frappe_client.py +5 -5
  101. mindsdb/integrations/handlers/frappe_handler/frappe_handler.py +6 -5
  102. mindsdb/integrations/handlers/frappe_handler/frappe_tables.py +2 -2
  103. mindsdb/integrations/handlers/github_handler/connection_args.py +2 -2
  104. mindsdb/integrations/handlers/github_handler/github_handler.py +1 -8
  105. mindsdb/integrations/handlers/github_handler/github_tables.py +13 -24
  106. mindsdb/integrations/handlers/gitlab_handler/gitlab_handler.py +2 -1
  107. mindsdb/integrations/handlers/gitlab_handler/gitlab_tables.py +1 -4
  108. mindsdb/integrations/handlers/gmail_handler/gmail_handler.py +6 -13
  109. mindsdb/integrations/handlers/gmail_handler/requirements.txt +1 -0
  110. mindsdb/integrations/handlers/google_analytics_handler/requirements.txt +2 -1
  111. mindsdb/integrations/handlers/google_books_handler/google_books_handler.py +2 -1
  112. mindsdb/integrations/handlers/google_books_handler/google_books_tables.py +0 -3
  113. mindsdb/integrations/handlers/google_books_handler/requirements.txt +1 -1
  114. mindsdb/integrations/handlers/google_calendar_handler/google_calendar_handler.py +4 -4
  115. mindsdb/integrations/handlers/google_calendar_handler/google_calendar_tables.py +2 -6
  116. mindsdb/integrations/handlers/google_calendar_handler/requirements.txt +1 -0
  117. mindsdb/integrations/handlers/google_content_shopping_handler/google_content_shopping_handler.py +3 -2
  118. mindsdb/integrations/handlers/google_content_shopping_handler/google_content_shopping_tables.py +0 -3
  119. mindsdb/integrations/handlers/google_content_shopping_handler/requirements.txt +1 -1
  120. mindsdb/integrations/handlers/google_fit_handler/google_fit_handler.py +10 -12
  121. mindsdb/integrations/handlers/google_fit_handler/google_fit_tables.py +11 -13
  122. mindsdb/integrations/handlers/google_fit_handler/requirements.txt +2 -0
  123. mindsdb/integrations/handlers/google_search_handler/google_search_handler.py +2 -1
  124. mindsdb/integrations/handlers/google_search_handler/google_search_tables.py +0 -3
  125. mindsdb/integrations/handlers/google_search_handler/requirements.txt +1 -1
  126. mindsdb/integrations/handlers/groq_handler/__init__.py +3 -3
  127. mindsdb/integrations/handlers/hackernews_handler/hn_handler.py +5 -7
  128. mindsdb/integrations/handlers/hackernews_handler/hn_table.py +6 -7
  129. mindsdb/integrations/handlers/hive_handler/tests/test_hive_handler.py +1 -1
  130. mindsdb/integrations/handlers/hsqldb_handler/connection_args.py +6 -6
  131. mindsdb/integrations/handlers/hsqldb_handler/hsqldb_handler.py +4 -3
  132. mindsdb/integrations/handlers/huggingface_api_handler/exceptions.py +1 -1
  133. mindsdb/integrations/handlers/huggingface_api_handler/huggingface_api_handler.py +1 -8
  134. mindsdb/integrations/handlers/huggingface_handler/huggingface_handler.py +6 -6
  135. mindsdb/integrations/handlers/huggingface_handler/requirements.txt +1 -1
  136. mindsdb/integrations/handlers/huggingface_handler/requirements_cpu.txt +1 -1
  137. mindsdb/integrations/handlers/ignite_handler/ignite_handler.py +2 -1
  138. mindsdb/integrations/handlers/impala_handler/impala_handler.py +9 -12
  139. mindsdb/integrations/handlers/impala_handler/tests/test_impala_handler.py +11 -11
  140. mindsdb/integrations/handlers/influxdb_handler/influxdb_handler.py +10 -13
  141. mindsdb/integrations/handlers/influxdb_handler/influxdb_tables.py +20 -20
  142. mindsdb/integrations/handlers/informix_handler/__about__.py +8 -8
  143. mindsdb/integrations/handlers/informix_handler/__init__.py +12 -5
  144. mindsdb/integrations/handlers/informix_handler/informix_handler.py +99 -133
  145. mindsdb/integrations/handlers/informix_handler/tests/test_informix_handler.py +13 -11
  146. mindsdb/integrations/handlers/ingres_handler/__about__.py +0 -1
  147. mindsdb/integrations/handlers/ingres_handler/ingres_handler.py +1 -0
  148. mindsdb/integrations/handlers/jira_handler/jira_handler.archived.py +75 -0
  149. mindsdb/integrations/handlers/jira_handler/jira_handler.py +113 -38
  150. mindsdb/integrations/handlers/jira_handler/jira_tables.py +229 -0
  151. mindsdb/integrations/handlers/jira_handler/requirements.txt +1 -0
  152. mindsdb/integrations/handlers/kinetica_handler/__init__.py +0 -1
  153. mindsdb/integrations/handlers/langchain_handler/langchain_handler.py +4 -4
  154. mindsdb/integrations/handlers/langchain_handler/tools.py +9 -10
  155. mindsdb/integrations/handlers/leonardoai_handler/__init__.py +1 -1
  156. mindsdb/integrations/handlers/lightfm_handler/requirements.txt +1 -0
  157. mindsdb/integrations/handlers/lightwood_handler/functions.py +2 -2
  158. mindsdb/integrations/handlers/lightwood_handler/lightwood_handler.py +0 -3
  159. mindsdb/integrations/handlers/lightwood_handler/requirements.txt +4 -4
  160. mindsdb/integrations/handlers/lightwood_handler/tests/test_lightwood_handler.py +11 -11
  161. mindsdb/integrations/handlers/lindorm_handler/requirements.txt +1 -0
  162. mindsdb/integrations/handlers/llama_index_handler/llama_index_handler.py +4 -4
  163. mindsdb/integrations/handlers/llama_index_handler/settings.py +10 -9
  164. mindsdb/integrations/handlers/materialize_handler/tests/test_materialize_handler.py +8 -10
  165. mindsdb/integrations/handlers/matrixone_handler/matrixone_handler.py +4 -4
  166. mindsdb/integrations/handlers/matrixone_handler/tests/test_matrixone_handler.py +8 -9
  167. mindsdb/integrations/handlers/maxdb_handler/connection_args.py +25 -25
  168. mindsdb/integrations/handlers/maxdb_handler/maxdb_handler.py +1 -0
  169. mindsdb/integrations/handlers/mediawiki_handler/mediawiki_handler.py +3 -2
  170. mindsdb/integrations/handlers/mediawiki_handler/mediawiki_tables.py +1 -1
  171. mindsdb/integrations/handlers/mendeley_handler/__about__.py +1 -1
  172. mindsdb/integrations/handlers/mendeley_handler/__init__.py +2 -2
  173. mindsdb/integrations/handlers/mendeley_handler/mendeley_handler.py +48 -56
  174. mindsdb/integrations/handlers/mendeley_handler/mendeley_tables.py +24 -29
  175. mindsdb/integrations/handlers/mendeley_handler/tests/test_mendeley_handler.py +19 -17
  176. mindsdb/integrations/handlers/merlion_handler/merlion_handler.py +5 -4
  177. mindsdb/integrations/handlers/minds_endpoint_handler/__init__.py +3 -3
  178. mindsdb/integrations/handlers/mlflow_handler/mlflow_handler.py +58 -36
  179. mindsdb/integrations/handlers/monetdb_handler/__about__.py +8 -8
  180. mindsdb/integrations/handlers/monetdb_handler/__init__.py +15 -5
  181. mindsdb/integrations/handlers/monetdb_handler/connection_args.py +17 -18
  182. mindsdb/integrations/handlers/monetdb_handler/monetdb_handler.py +40 -57
  183. mindsdb/integrations/handlers/monetdb_handler/tests/test_monetdb_handler.py +7 -8
  184. mindsdb/integrations/handlers/monetdb_handler/utils/monet_get_id.py +13 -14
  185. mindsdb/integrations/handlers/monkeylearn_handler/__about__.py +1 -1
  186. mindsdb/integrations/handlers/monkeylearn_handler/__init__.py +1 -1
  187. mindsdb/integrations/handlers/monkeylearn_handler/monkeylearn_handler.py +2 -5
  188. mindsdb/integrations/handlers/ms_one_drive_handler/ms_graph_api_one_drive_client.py +1 -0
  189. mindsdb/integrations/handlers/ms_one_drive_handler/ms_one_drive_handler.py +1 -1
  190. mindsdb/integrations/handlers/ms_one_drive_handler/requirements.txt +2 -0
  191. mindsdb/integrations/handlers/ms_teams_handler/ms_graph_api_teams_client.py +23 -23
  192. mindsdb/integrations/handlers/ms_teams_handler/ms_teams_handler.py +3 -3
  193. mindsdb/integrations/handlers/ms_teams_handler/ms_teams_tables.py +10 -5
  194. mindsdb/integrations/handlers/ms_teams_handler/requirements.txt +3 -1
  195. mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +73 -8
  196. mindsdb/integrations/handlers/mysql_handler/__about__.py +8 -8
  197. mindsdb/integrations/handlers/mysql_handler/__init__.py +15 -5
  198. mindsdb/integrations/handlers/mysql_handler/connection_args.py +43 -47
  199. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +101 -34
  200. mindsdb/integrations/handlers/mysql_handler/settings.py +15 -13
  201. mindsdb/integrations/handlers/neuralforecast_handler/neuralforecast_handler.py +1 -1
  202. mindsdb/integrations/handlers/newsapi_handler/newsapi_handler.py +1 -1
  203. mindsdb/integrations/handlers/newsapi_handler/tests/test_newsapi_handler.py +4 -4
  204. mindsdb/integrations/handlers/nuo_jdbc_handler/connection_args.py +2 -2
  205. mindsdb/integrations/handlers/nuo_jdbc_handler/nuo_jdbc_handler.py +28 -36
  206. mindsdb/integrations/handlers/nuo_jdbc_handler/tests/test_nuo_handler.py +5 -5
  207. mindsdb/integrations/handlers/oceanbase_handler/oceanbase_handler.py +0 -1
  208. mindsdb/integrations/handlers/oceanbase_handler/tests/test_oceanbase_handler.py +8 -10
  209. mindsdb/integrations/handlers/ollama_handler/ollama_handler.py +3 -3
  210. mindsdb/integrations/handlers/openai_handler/openai_handler.py +5 -4
  211. mindsdb/integrations/handlers/opengauss_handler/tests/test_opengauss_handler.py +1 -2
  212. mindsdb/integrations/handlers/openstreetmap_handler/__init__.py +7 -7
  213. mindsdb/integrations/handlers/oracle_handler/connection_args.py +6 -0
  214. mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +77 -11
  215. mindsdb/integrations/handlers/orioledb_handler/tests/test_orioledb_handler.py +8 -10
  216. mindsdb/integrations/handlers/palm_handler/__about__.py +1 -1
  217. mindsdb/integrations/handlers/palm_handler/__init__.py +1 -1
  218. mindsdb/integrations/handlers/palm_handler/palm_handler.py +1 -3
  219. mindsdb/integrations/handlers/paypal_handler/paypal_handler.py +2 -2
  220. mindsdb/integrations/handlers/paypal_handler/paypal_tables.py +15 -14
  221. mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +53 -10
  222. mindsdb/integrations/handlers/phoenix_handler/__init__.py +1 -1
  223. mindsdb/integrations/handlers/phoenix_handler/phoenix_handler.py +1 -0
  224. mindsdb/integrations/handlers/pinot_handler/__init__.py +1 -1
  225. mindsdb/integrations/handlers/pinot_handler/pinot_handler.py +3 -2
  226. mindsdb/integrations/handlers/plaid_handler/plaid_handler.py +13 -13
  227. mindsdb/integrations/handlers/plaid_handler/plaid_tables.py +10 -12
  228. mindsdb/integrations/handlers/plaid_handler/utils.py +4 -6
  229. mindsdb/integrations/handlers/planetscale_handler/planetscale_handler.py +1 -4
  230. mindsdb/integrations/handlers/portkey_handler/__init__.py +2 -2
  231. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +105 -24
  232. mindsdb/integrations/handlers/postgres_handler/tests/test_postgres_handler.py +11 -6
  233. mindsdb/integrations/handlers/questdb_handler/questdb_handler.py +1 -2
  234. mindsdb/integrations/handlers/questdb_handler/tests/test_questdb_handler.py +2 -3
  235. mindsdb/integrations/handlers/quickbooks_handler/quickbooks_handler.py +6 -8
  236. mindsdb/integrations/handlers/quickbooks_handler/quickbooks_table.py +10 -10
  237. mindsdb/integrations/handlers/rag_handler/ingest.py +2 -2
  238. mindsdb/integrations/handlers/rag_handler/rag_handler.py +1 -1
  239. mindsdb/integrations/handlers/rag_handler/settings.py +1 -1
  240. mindsdb/integrations/handlers/reddit_handler/reddit_handler.py +2 -7
  241. mindsdb/integrations/handlers/reddit_handler/reddit_tables.py +2 -3
  242. mindsdb/integrations/handlers/replicate_handler/replicate_handler.py +6 -6
  243. mindsdb/integrations/handlers/rocket_chat_handler/rocket_chat_handler.py +1 -2
  244. mindsdb/integrations/handlers/rocket_chat_handler/rocket_chat_tables.py +0 -3
  245. mindsdb/integrations/handlers/rockset_handler/connection_args.py +14 -14
  246. mindsdb/integrations/handlers/rockset_handler/tests/test_rockset_handler.py +1 -0
  247. mindsdb/integrations/handlers/scylla_handler/scylla_handler.py +6 -5
  248. mindsdb/integrations/handlers/sendinblue_handler/sendinblue_handler.py +2 -1
  249. mindsdb/integrations/handlers/sendinblue_handler/sendinblue_tables.py +16 -16
  250. mindsdb/integrations/handlers/sentence_transformers_handler/__init__.py +1 -1
  251. mindsdb/integrations/handlers/sheets_handler/connection_args.py +1 -1
  252. mindsdb/integrations/handlers/shopify_handler/shopify_handler.py +7 -6
  253. mindsdb/integrations/handlers/shopify_handler/shopify_tables.py +38 -41
  254. mindsdb/integrations/handlers/singlestore_handler/__about__.py +1 -1
  255. mindsdb/integrations/handlers/singlestore_handler/__init__.py +0 -1
  256. mindsdb/integrations/handlers/singlestore_handler/singlestore_handler.py +1 -0
  257. mindsdb/integrations/handlers/singlestore_handler/tests/test_singlestore_handler.py +3 -3
  258. mindsdb/integrations/handlers/slack_handler/__init__.py +3 -3
  259. mindsdb/integrations/handlers/snowflake_handler/requirements.txt +1 -1
  260. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +100 -6
  261. mindsdb/integrations/handlers/solr_handler/connection_args.py +7 -7
  262. mindsdb/integrations/handlers/solr_handler/solr_handler.py +2 -1
  263. mindsdb/integrations/handlers/solr_handler/tests/test_solr_handler.py +2 -1
  264. mindsdb/integrations/handlers/sqlany_handler/sqlany_handler.py +3 -2
  265. mindsdb/integrations/handlers/sqlite_handler/sqlite_handler.py +1 -0
  266. mindsdb/integrations/handlers/sqreamdb_handler/connection_args.py +1 -1
  267. mindsdb/integrations/handlers/sqreamdb_handler/sqreamdb_handler.py +15 -20
  268. mindsdb/integrations/handlers/sqreamdb_handler/tests/test_sqreamdb_handler.py +4 -4
  269. mindsdb/integrations/handlers/stabilityai_handler/__init__.py +1 -1
  270. mindsdb/integrations/handlers/starrocks_handler/starrocks_handler.py +0 -1
  271. mindsdb/integrations/handlers/starrocks_handler/tests/test_starrocks_handler.py +8 -10
  272. mindsdb/integrations/handlers/statsforecast_handler/statsforecast_handler.py +2 -2
  273. mindsdb/integrations/handlers/strava_handler/strava_handler.py +4 -8
  274. mindsdb/integrations/handlers/strava_handler/strava_tables.py +22 -30
  275. mindsdb/integrations/handlers/stripe_handler/stripe_handler.py +3 -2
  276. mindsdb/integrations/handlers/stripe_handler/stripe_tables.py +11 -27
  277. mindsdb/integrations/handlers/supabase_handler/tests/test_supabase_handler.py +1 -1
  278. mindsdb/integrations/handlers/surrealdb_handler/surrealdb_handler.py +4 -4
  279. mindsdb/integrations/handlers/tdengine_handler/tdengine_handler.py +25 -27
  280. mindsdb/integrations/handlers/tdengine_handler/tests/test_tdengine_handler.py +8 -8
  281. mindsdb/integrations/handlers/tidb_handler/tests/test_tidb_handler.py +1 -2
  282. mindsdb/integrations/handlers/timegpt_handler/timegpt_handler.py +5 -5
  283. mindsdb/integrations/handlers/tpot_handler/tpot_handler.py +21 -26
  284. mindsdb/integrations/handlers/trino_handler/trino_handler.py +14 -14
  285. mindsdb/integrations/handlers/twitter_handler/twitter_handler.py +2 -4
  286. mindsdb/integrations/handlers/unify_handler/tests/test_unify_handler.py +7 -8
  287. mindsdb/integrations/handlers/unify_handler/unify_handler.py +9 -9
  288. mindsdb/integrations/handlers/vertex_handler/requirements.txt +1 -0
  289. mindsdb/integrations/handlers/vertex_handler/vertex_client.py +1 -1
  290. mindsdb/integrations/handlers/vertica_handler/tests/test_vertica_handler.py +11 -11
  291. mindsdb/integrations/handlers/vertica_handler/vertica_handler.py +11 -14
  292. mindsdb/integrations/handlers/vitess_handler/tests/test_vitess_handler.py +9 -11
  293. mindsdb/integrations/handlers/vitess_handler/vitess_handler.py +0 -1
  294. mindsdb/integrations/handlers/web_handler/web_handler.py +1 -0
  295. mindsdb/integrations/handlers/whatsapp_handler/__init__.py +3 -3
  296. mindsdb/integrations/handlers/writer_handler/evaluate.py +1 -1
  297. mindsdb/integrations/handlers/writer_handler/settings.py +0 -1
  298. mindsdb/integrations/handlers/writer_handler/writer_handler.py +1 -0
  299. mindsdb/integrations/handlers/youtube_handler/requirements.txt +1 -0
  300. mindsdb/integrations/handlers/youtube_handler/youtube_handler.py +5 -5
  301. mindsdb/integrations/handlers/youtube_handler/youtube_tables.py +26 -27
  302. mindsdb/integrations/handlers/yugabyte_handler/tests/test_yugabyte_handler.py +3 -3
  303. mindsdb/integrations/handlers/yugabyte_handler/yugabyte_handler.py +0 -6
  304. mindsdb/integrations/libs/response.py +67 -52
  305. mindsdb/integrations/libs/vectordatabase_handler.py +6 -0
  306. mindsdb/integrations/utilities/files/file_reader.py +5 -2
  307. mindsdb/integrations/utilities/handler_utils.py +15 -3
  308. mindsdb/integrations/utilities/handlers/api_utilities/__init__.py +0 -1
  309. mindsdb/integrations/utilities/handlers/auth_utilities/__init__.py +0 -2
  310. mindsdb/integrations/utilities/utils.py +3 -3
  311. mindsdb/interfaces/agents/agents_controller.py +164 -1
  312. mindsdb/interfaces/agents/constants.py +29 -2
  313. mindsdb/interfaces/agents/langchain_agent.py +18 -8
  314. mindsdb/interfaces/agents/mindsdb_database_agent.py +101 -2
  315. mindsdb/interfaces/database/projects.py +1 -7
  316. mindsdb/interfaces/functions/controller.py +11 -14
  317. mindsdb/interfaces/functions/to_markdown.py +9 -124
  318. mindsdb/interfaces/knowledge_base/controller.py +47 -19
  319. mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +41 -15
  320. mindsdb/interfaces/knowledge_base/preprocessing/json_chunker.py +434 -0
  321. mindsdb/interfaces/knowledge_base/preprocessing/models.py +54 -0
  322. mindsdb/interfaces/knowledge_base/utils.py +10 -15
  323. mindsdb/interfaces/model/model_controller.py +0 -2
  324. mindsdb/interfaces/query_context/context_controller.py +66 -10
  325. mindsdb/interfaces/skills/custom/text2sql/mindsdb_kb_tools.py +190 -0
  326. mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +92 -0
  327. mindsdb/interfaces/skills/skill_tool.py +202 -57
  328. mindsdb/interfaces/skills/sql_agent.py +238 -28
  329. mindsdb/interfaces/storage/fs.py +1 -0
  330. mindsdb/interfaces/variables/__init__.py +0 -0
  331. mindsdb/interfaces/variables/variables_controller.py +97 -0
  332. mindsdb/migrations/env.py +5 -7
  333. mindsdb/migrations/migrate.py +47 -9
  334. mindsdb/migrations/versions/2025-05-21_9f150e4f9a05_checkpoint_1.py +360 -0
  335. mindsdb/utilities/config.py +333 -220
  336. mindsdb/utilities/context.py +1 -1
  337. mindsdb/utilities/functions.py +0 -36
  338. mindsdb/utilities/langfuse.py +19 -10
  339. mindsdb/utilities/otel/__init__.py +9 -193
  340. mindsdb/utilities/otel/metric_handlers/__init__.py +5 -1
  341. mindsdb/utilities/otel/prepare.py +198 -0
  342. mindsdb/utilities/sql.py +83 -0
  343. mindsdb/utilities/starters.py +13 -0
  344. {mindsdb-25.4.5.0.dist-info → mindsdb-25.5.4.0.dist-info}/METADATA +351 -338
  345. {mindsdb-25.4.5.0.dist-info → mindsdb-25.5.4.0.dist-info}/RECORD +348 -322
  346. {mindsdb-25.4.5.0.dist-info → mindsdb-25.5.4.0.dist-info}/WHEEL +1 -1
  347. mindsdb/api/mysql/mysql_proxy/classes/sql_statement_parser.py +0 -151
  348. mindsdb/integrations/handlers/monkeylearn_handler/requirements.txt +0 -1
  349. {mindsdb-25.4.5.0.dist-info → mindsdb-25.5.4.0.dist-info}/licenses/LICENSE +0 -0
  350. {mindsdb-25.4.5.0.dist-info → mindsdb-25.5.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,114 @@
1
+ import sys
2
+ import logging
3
+ import click
4
+ from dotenv import load_dotenv
5
+
6
+ # A2A specific imports
7
+ from .common.types import (
8
+ AgentCard,
9
+ AgentCapabilities,
10
+ AgentSkill,
11
+ MissingAPIKeyError,
12
+ )
13
+ from .common.server.server import A2AServer
14
+ from .task_manager import AgentTaskManager
15
+ from .agent import MindsDBAgent
16
+
17
+ logger = logging.getLogger(__name__)
18
+ logging.basicConfig(
19
+ level=logging.INFO,
20
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
21
+ )
22
+
23
+
24
+ @click.command()
25
+ @click.option("--host", default="localhost", help="A2A server host")
26
+ @click.option("--port", default=10002, help="A2A server port", type=int)
27
+ @click.option("--mindsdb-host", default="localhost", help="MindsDB server host")
28
+ @click.option("--mindsdb-port", default=47334, help="MindsDB server port", type=int)
29
+ @click.option("--project-name", default="mindsdb", help="MindsDB project name")
30
+ @click.option(
31
+ "--log-level",
32
+ default="INFO",
33
+ help="Logging level (DEBUG, INFO, WARNING, ERROR)",
34
+ )
35
+ def main(
36
+ host: str,
37
+ port: int,
38
+ mindsdb_host: str,
39
+ mindsdb_port: int,
40
+ project_name: str,
41
+ log_level: str,
42
+ ):
43
+ """Entry-point that starts an A2A compliant server which forwards tasks to an existing MindsDB agent."""
44
+
45
+ # Configure logging level
46
+ logging.getLogger().setLevel(getattr(logging, log_level.upper(), logging.INFO))
47
+
48
+ # Load .env if present
49
+ load_dotenv()
50
+
51
+ try:
52
+ logger.info(
53
+ "Starting MindsDB A2A connector on http://%s:%s (project=%s)",
54
+ host,
55
+ port,
56
+ project_name,
57
+ )
58
+
59
+ # --- 1. Prepare A2A artefacts (agent card & task-manager) --------------
60
+ capabilities = AgentCapabilities(streaming=True)
61
+ skill = AgentSkill(
62
+ id="mindsdb_query",
63
+ name="MindsDB Query",
64
+ description="Executes natural-language queries via MindsDB agents.",
65
+ tags=["database", "mindsdb", "query", "analytics"],
66
+ examples=[
67
+ "What trends exist in my sales data?",
68
+ "Generate insights from the support tickets dataset.",
69
+ ],
70
+ inputModes=MindsDBAgent.SUPPORTED_CONTENT_TYPES,
71
+ outputModes=MindsDBAgent.SUPPORTED_CONTENT_TYPES,
72
+ )
73
+
74
+ agent_card = AgentCard(
75
+ name="MindsDB Agent Connector",
76
+ description=(
77
+ "A2A connector that proxies requests to MindsDB agents "
78
+ f"in project '{project_name}'."
79
+ ),
80
+ url=f"http://{host}:{port}",
81
+ version="1.0.0",
82
+ defaultInputModes=MindsDBAgent.SUPPORTED_CONTENT_TYPES,
83
+ defaultOutputModes=MindsDBAgent.SUPPORTED_CONTENT_TYPES,
84
+ capabilities=capabilities,
85
+ skills=[skill],
86
+ )
87
+
88
+ task_manager = AgentTaskManager(
89
+ project_name=project_name,
90
+ mindsdb_host=mindsdb_host,
91
+ mindsdb_port=mindsdb_port,
92
+ )
93
+
94
+ # --- 2. Start A2A server ----------------------------------------------
95
+ logger.info("Starting A2A server…")
96
+ server = A2AServer(
97
+ agent_card=agent_card,
98
+ task_manager=task_manager,
99
+ host=host,
100
+ port=port,
101
+ endpoint="/a2a", # Keep the same route used by the client
102
+ )
103
+ server.start()
104
+
105
+ except MissingAPIKeyError as exc:
106
+ logger.error("Authentication error: %s", exc)
107
+ sys.exit(1)
108
+ except Exception as exc: # noqa: BLE001
109
+ logger.exception("Unexpected error: %s", exc)
110
+ sys.exit(1)
111
+
112
+
113
+ if __name__ == "__main__":
114
+ main()
@@ -0,0 +1,439 @@
1
+ #!/usr/bin/env python
2
+ """Simple command-line tool to interact with an A2A server.
3
+
4
+ This script can:
5
+ • fetch the agent card (metadata) from the server
6
+ • send a single (non-streaming) task request
7
+ • send a streaming request and live-print SSE events
8
+
9
+ Originally located at the repository root; restored to that location.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import json
16
+ import sys
17
+ import uuid
18
+ from typing import Iterator, Optional
19
+ import logging
20
+
21
+ import requests
22
+
23
+ DEFAULT_HOST = "localhost"
24
+ DEFAULT_PORT = 10002
25
+
26
+ ###############################################################################
27
+ # Helper functions
28
+ ###############################################################################
29
+
30
+
31
+ def _log_json(log_func, obj: dict, *, prefix: str = "") -> None:
32
+ """Log JSON helper."""
33
+ log_func(prefix + json.dumps(obj, indent=2))
34
+
35
+
36
+ def get_agent_info(
37
+ a2a_host: str = DEFAULT_HOST,
38
+ a2a_port: int = DEFAULT_PORT,
39
+ ) -> Optional[dict]:
40
+ """Retrieve the agent card from `/.well-known/agent.json` (or legacy path)."""
41
+
42
+ url = f"http://{a2a_host}:{a2a_port}/.well-known/agent.json"
43
+
44
+ logging.debug(f"Fetching agent info from {url} …")
45
+
46
+ try:
47
+ response = requests.get(url, timeout=10)
48
+ if response.status_code == 404:
49
+ legacy_url = f"http://{a2a_host}:{a2a_port}/agent-card"
50
+ logging.debug(f"Not found, trying legacy path {legacy_url}")
51
+ response = requests.get(legacy_url, timeout=10)
52
+
53
+ if response.ok:
54
+ card = response.json()
55
+ _log_json(logging.debug, card, prefix="Received agent card:\n")
56
+ return card
57
+
58
+ logging.debug(f"Failed to fetch agent card – status: {response.status_code}")
59
+ except requests.RequestException as exc:
60
+ logging.debug(f"Connection error while fetching agent card: {exc}")
61
+
62
+ return None
63
+
64
+
65
+ ###############################################################################
66
+ # Task helpers
67
+ ###############################################################################
68
+
69
+
70
+ def _new_ids() -> tuple[str, str, str]:
71
+ """Generate fresh UUID4 strings for taskId, sessionId, requestId."""
72
+
73
+ return uuid.uuid4().hex, uuid.uuid4().hex, uuid.uuid4().hex
74
+
75
+
76
+ def _post_json(url: str, payload: dict, *, stream: bool = False) -> requests.Response:
77
+ headers = {"Content-Type": "application/json"}
78
+ if stream:
79
+ headers["Accept"] = "text/event-stream"
80
+ return requests.post(
81
+ url, json=payload, headers=headers, stream=stream, timeout=None
82
+ )
83
+
84
+
85
+ ###############################################################################
86
+ # Non-streaming request
87
+ ###############################################################################
88
+
89
+
90
+ def send_a2a_query(
91
+ query: str,
92
+ *,
93
+ a2a_host: str = DEFAULT_HOST,
94
+ a2a_port: int = DEFAULT_PORT,
95
+ ) -> bool:
96
+ """Send a *blocking* `tasks/send` request and print the result."""
97
+
98
+ task_id, session_id, request_id = _new_ids()
99
+
100
+ payload = {
101
+ "jsonrpc": "2.0",
102
+ "id": request_id,
103
+ "method": "tasks/send",
104
+ "params": {
105
+ "id": task_id,
106
+ "sessionId": session_id,
107
+ "message": {
108
+ "role": "user",
109
+ "parts": [
110
+ {"type": "text", "text": query},
111
+ ],
112
+ },
113
+ "acceptedOutputModes": ["text/plain"],
114
+ },
115
+ }
116
+
117
+ url = f"http://{a2a_host}:{a2a_port}/a2a"
118
+ logging.debug(f"POST {url}")
119
+ _log_json(logging.debug, payload, prefix="Request →\n")
120
+ logging.info("Sending query …")
121
+
122
+ try:
123
+ response = _post_json(url, payload)
124
+ except requests.RequestException as exc:
125
+ logging.error(f"⚠️ Network error: {exc}")
126
+ return False
127
+
128
+ if not response.ok:
129
+ logging.error(f"⚠️ HTTP {response.status_code}\n{response.text}")
130
+ return False
131
+
132
+ try:
133
+ data = response.json()
134
+ except ValueError:
135
+ logging.error(f"⚠️ Invalid JSON response: {response.text[:200]}")
136
+ return False
137
+
138
+ _log_json(logging.debug, data, prefix="Full response ←\n")
139
+
140
+ if "error" in data:
141
+ logging.error(f"⚠️ RPC error: {data['error'].get('message')}")
142
+ return False
143
+
144
+ result = data.get("result")
145
+ if not result:
146
+ logging.error("⚠️ No result field in response")
147
+ return False
148
+
149
+ # Print status/"thinking" message
150
+ status = result.get("status", {})
151
+ msg_parts = status.get("message", {}).get("parts", [])
152
+ if msg_parts:
153
+ logging.info("\nAgent thinking:")
154
+ _log_parts(logging.info, msg_parts)
155
+
156
+ # Print artifacts (agent answer)
157
+ artifacts = result.get("artifacts") or []
158
+ if artifacts:
159
+ logging.info("\nAgent response:")
160
+ for artifact in artifacts:
161
+ _log_parts(logging.info, artifact.get("parts", []))
162
+ else:
163
+ logging.info("(No artifacts returned)")
164
+
165
+ return True
166
+
167
+
168
+ ###############################################################################
169
+ # Streaming request helpers
170
+ ###############################################################################
171
+
172
+
173
+ def _iter_sse_lines(resp: requests.Response) -> Iterator[str]:
174
+ """Yield raw Server-Sent-Event lines (decoded)."""
175
+ buf = ""
176
+ for chunk in resp.iter_content(chunk_size=1024):
177
+ if not chunk:
178
+ continue
179
+ buf += chunk.decode()
180
+ while "\n" in buf:
181
+ line, buf = buf.split("\n", 1)
182
+ yield line.rstrip("\r")
183
+
184
+
185
+ def _parse_sse_event(lines: list[str]) -> dict | None:
186
+ """Parse an SSE event block into a dict."""
187
+ data_lines = [line_content[6:] for line_content in lines if line_content.startswith("data: ")]
188
+ if not data_lines:
189
+ return None
190
+ try:
191
+ return json.loads("\n".join(data_lines))
192
+ except json.JSONDecodeError:
193
+ return None
194
+
195
+
196
+ def send_streaming_query(
197
+ query: str,
198
+ *,
199
+ a2a_host: str = DEFAULT_HOST,
200
+ a2a_port: int = DEFAULT_PORT,
201
+ ) -> bool:
202
+ """Send a `tasks/sendSubscribe` request and stream responses as they arrive."""
203
+
204
+ task_id, session_id, request_id = _new_ids()
205
+
206
+ payload = {
207
+ "jsonrpc": "2.0",
208
+ "id": request_id,
209
+ "method": "tasks/sendSubscribe",
210
+ "params": {
211
+ "id": task_id,
212
+ "sessionId": session_id,
213
+ "message": {
214
+ "role": "user",
215
+ "parts": [
216
+ {"type": "text", "text": query},
217
+ ],
218
+ },
219
+ "acceptedOutputModes": ["text/plain"],
220
+ },
221
+ }
222
+
223
+ url = f"http://{a2a_host}:{a2a_port}/a2a"
224
+ logging.debug(f"POST (stream) {url}")
225
+ _log_json(logging.debug, payload, prefix="Request →\n")
226
+
227
+ try:
228
+ response = _post_json(url, payload, stream=True)
229
+ except requests.RequestException as exc:
230
+ logging.error(f"⚠️ Network error: {exc}")
231
+ return False
232
+
233
+ if not response.ok:
234
+ logging.error(f"⚠️ HTTP {response.status_code}\n{response.text}")
235
+ return False
236
+
237
+ logging.info("Streaming events (Ctrl-C to abort):")
238
+
239
+ try:
240
+ event_lines: list[str] = []
241
+ for line in _iter_sse_lines(response):
242
+ if line == "": # blank = dispatch event
243
+ event = _parse_sse_event(event_lines)
244
+ event_lines.clear()
245
+ if event is not None:
246
+ _handle_stream_event(event)
247
+ continue
248
+ event_lines.append(line)
249
+ except KeyboardInterrupt:
250
+ logging.info("\nInterrupted by user.")
251
+ return False
252
+
253
+ return True
254
+
255
+
256
+ ###############################################################################
257
+ # Output helpers
258
+ ###############################################################################
259
+
260
+
261
+ def _log_parts(log_func, parts: list[dict]) -> None:
262
+ for part in parts:
263
+ if part.get("type") == "text":
264
+ log_func(part.get("text", ""))
265
+ elif part.get("type") == "data":
266
+ log_func("\nStructured data:")
267
+ _log_json(log_func, part.get("data", {}))
268
+
269
+
270
+ def _handle_stream_event(event: dict) -> None:
271
+ """Handle a single SSE event parsed as JSON.
272
+
273
+ The server sends JSON-RPC messages of the form::
274
+
275
+ {"jsonrpc":"2.0","id":"…","result":{…}}
276
+
277
+ where *result* is either a TaskStatusUpdateEvent or a TaskArtifactUpdateEvent.
278
+ These objects do not carry an explicit ``type`` field, so we infer it based
279
+ on their keys.
280
+ """
281
+
282
+ # If logging level is DEBUG, log the raw event and mimic the original 'return' behavior
283
+ if logging.getLogger().isEnabledFor(logging.DEBUG):
284
+ _log_json(logging.debug, event, prefix="Raw Event (DEBUG) ← ")
285
+ # This return mimics the original behavior where verbose mode would
286
+ # print the raw event and skip further processing for this event.
287
+ return
288
+
289
+ # If the server uses an older "typed" envelope just keep legacy handling
290
+ if "type" in event:
291
+ etype = event["type"]
292
+ if etype == "status":
293
+ message = event.get("status", {}).get("message", {})
294
+ parts = message.get("parts", [])
295
+ if parts:
296
+ _log_parts(logging.info, parts)
297
+ elif etype == "artifact":
298
+ artifacts = event.get("artifacts") or []
299
+ for artifact in artifacts:
300
+ _log_parts(logging.info, artifact.get("parts", []))
301
+ elif etype == "end":
302
+ logging.info("\n[stream end]")
303
+ return
304
+
305
+ # ---- New A2A 0.2 style messages ---------------------------------------
306
+ # Extract "result" or "error" from the JSON-RPC envelope.
307
+ if "error" in event:
308
+ err = event["error"]
309
+ logging.error(f"RPC error: {err.get('message')}")
310
+ return
311
+
312
+ result = event.get("result")
313
+ if not result:
314
+ # Nothing useful to display
315
+ return
316
+
317
+ # Status update? (TaskStatusUpdateEvent)
318
+ if "status" in result:
319
+ message = result["status"].get("message", {})
320
+ parts = message.get("parts", [])
321
+ if parts:
322
+ _log_parts(logging.info, parts)
323
+
324
+ # If final flag present we can acknowledge.
325
+ if result.get("final"):
326
+ logging.info("\n[completed]")
327
+ return
328
+
329
+ # Artifact update? (TaskArtifactUpdateEvent)
330
+ if "artifact" in result:
331
+ artifact = result["artifact"]
332
+ _log_parts(logging.info, artifact.get("parts", []))
333
+ return
334
+
335
+
336
+ ###############################################################################
337
+ # CLI argument parsing
338
+ ###############################################################################
339
+
340
+
341
+ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
342
+ parser = argparse.ArgumentParser(description="Interact with an A2A server")
343
+
344
+ parser.add_argument(
345
+ "query", nargs="*", help="Query to send. If omitted, just fetch agent card."
346
+ )
347
+
348
+ parser.add_argument(
349
+ "--host", default=DEFAULT_HOST, help="A2A host (default: %(default)s)"
350
+ )
351
+ parser.add_argument(
352
+ "--port", type=int, default=DEFAULT_PORT, help="A2A port (default: %(default)s)"
353
+ )
354
+
355
+ parser.add_argument(
356
+ "--stream",
357
+ action="store_true",
358
+ help="Use streaming subscribe request instead of blocking send",
359
+ )
360
+
361
+ parser.add_argument("--verbose", action="store_true", help="Verbose output (debug)")
362
+
363
+ return parser.parse_args(argv)
364
+
365
+
366
+ ###############################################################################
367
+ # Main entry-point
368
+ ###############################################################################
369
+
370
+
371
+ def main(argv: list[str] | None = None) -> None:
372
+ args = parse_args(argv)
373
+
374
+ # Configure logging
375
+ log_level = logging.DEBUG if args.verbose else logging.INFO
376
+ logging.basicConfig(format="%(message)s", level=log_level, stream=sys.stdout)
377
+ if args.verbose:
378
+ logging.debug("Verbose mode enabled.")
379
+
380
+ # 1. Always fetch agent-card first (helps verify server is live)
381
+ agent_info = get_agent_info(args.host, args.port)
382
+ if agent_info is None:
383
+ logging.critical("⚠️ Could not retrieve agent info – abort")
384
+ sys.exit(1)
385
+
386
+ if not args.query:
387
+ # Enter interactive REPL style mode
388
+ logging.info("Interactive mode – type 'exit' or Ctrl-D to quit.")
389
+ try:
390
+ while True:
391
+ try:
392
+ user_input = input("> ").strip()
393
+ except EOFError:
394
+ # Ctrl-D
395
+ logging.info("")
396
+ break
397
+
398
+ if user_input.lower() in {"exit", "quit"}:
399
+ break
400
+
401
+ if not user_input:
402
+ continue
403
+
404
+ ok = (
405
+ send_streaming_query(
406
+ user_input,
407
+ a2a_host=args.host,
408
+ a2a_port=args.port,
409
+ )
410
+ if args.stream
411
+ else send_a2a_query(
412
+ user_input,
413
+ a2a_host=args.host,
414
+ a2a_port=args.port,
415
+ )
416
+ )
417
+
418
+ # Add separator between queries
419
+ if ok:
420
+ logging.info("\n" + "—" * 30)
421
+ except KeyboardInterrupt:
422
+ # Ctrl-C to exit
423
+ logging.info("")
424
+ sys.exit(0)
425
+
426
+ # Single query passed via CLI
427
+ query_str = " ".join(args.query)
428
+
429
+ ok = (
430
+ send_streaming_query(query_str, a2a_host=args.host, a2a_port=args.port)
431
+ if args.stream
432
+ else send_a2a_query(query_str, a2a_host=args.host, a2a_port=args.port)
433
+ )
434
+
435
+ sys.exit(0 if ok else 2)
436
+
437
+
438
+ if __name__ == "__main__":
439
+ main()