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.
- mindsdb/__about__.py +1 -1
- mindsdb/__main__.py +215 -185
- mindsdb/api/a2a/__init__.py +0 -0
- mindsdb/api/a2a/__main__.py +114 -0
- mindsdb/api/a2a/a2a_client.py +439 -0
- mindsdb/api/a2a/agent.py +308 -0
- mindsdb/api/a2a/common/__init__.py +0 -0
- mindsdb/api/a2a/common/client/__init__.py +4 -0
- mindsdb/api/a2a/common/client/card_resolver.py +21 -0
- mindsdb/api/a2a/common/client/client.py +86 -0
- mindsdb/api/a2a/common/server/__init__.py +4 -0
- mindsdb/api/a2a/common/server/server.py +164 -0
- mindsdb/api/a2a/common/server/task_manager.py +287 -0
- mindsdb/api/a2a/common/server/utils.py +28 -0
- mindsdb/api/a2a/common/types.py +365 -0
- mindsdb/api/a2a/constants.py +9 -0
- mindsdb/api/a2a/run_a2a.py +129 -0
- mindsdb/api/a2a/task_manager.py +594 -0
- mindsdb/api/executor/command_executor.py +49 -28
- mindsdb/api/executor/datahub/classes/response.py +5 -2
- mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +8 -0
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +39 -72
- mindsdb/api/executor/datahub/datanodes/system_tables.py +10 -13
- mindsdb/api/executor/planner/query_planner.py +14 -2
- mindsdb/api/executor/sql_query/result_set.py +185 -52
- mindsdb/api/executor/sql_query/sql_query.py +1 -1
- mindsdb/api/executor/sql_query/steps/apply_predictor_step.py +11 -13
- mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +8 -10
- mindsdb/api/executor/sql_query/steps/fetch_dataframe_partition.py +5 -44
- mindsdb/api/executor/sql_query/steps/insert_step.py +24 -15
- mindsdb/api/executor/sql_query/steps/join_step.py +1 -1
- mindsdb/api/executor/sql_query/steps/project_step.py +1 -1
- mindsdb/api/executor/sql_query/steps/sql_steps.py +1 -1
- mindsdb/api/executor/sql_query/steps/subselect_step.py +4 -8
- mindsdb/api/executor/sql_query/steps/union_step.py +1 -3
- mindsdb/api/http/initialize.py +118 -85
- mindsdb/api/http/namespaces/analysis.py +17 -4
- mindsdb/api/http/namespaces/file.py +8 -2
- mindsdb/api/http/namespaces/sql.py +13 -27
- mindsdb/api/http/namespaces/tree.py +1 -1
- mindsdb/api/http/start.py +7 -2
- mindsdb/api/mcp/start.py +42 -5
- mindsdb/api/mysql/mysql_proxy/data_types/mysql_packet.py +0 -1
- mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/binary_resultset_row_package.py +52 -19
- mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +8 -10
- mindsdb/api/mysql/mysql_proxy/libs/constants/mysql.py +54 -38
- mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +86 -123
- mindsdb/api/mysql/mysql_proxy/utilities/dump.py +351 -0
- mindsdb/api/mysql/mysql_proxy/utilities/exceptions.py +0 -4
- mindsdb/api/postgres/postgres_proxy/executor/executor.py +1 -1
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_formats.py +2 -2
- mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +5 -6
- mindsdb/integrations/handlers/altibase_handler/altibase_handler.py +26 -27
- mindsdb/integrations/handlers/altibase_handler/connection_args.py +13 -13
- mindsdb/integrations/handlers/altibase_handler/tests/test_altibase_handler.py +8 -8
- mindsdb/integrations/handlers/altibase_handler/tests/test_altibase_handler_dsn.py +13 -13
- mindsdb/integrations/handlers/anthropic_handler/__init__.py +2 -2
- mindsdb/integrations/handlers/anthropic_handler/anthropic_handler.py +1 -3
- mindsdb/integrations/handlers/aurora_handler/aurora_handler.py +1 -0
- mindsdb/integrations/handlers/autosklearn_handler/autosklearn_handler.py +1 -1
- mindsdb/integrations/handlers/autosklearn_handler/config.py +0 -1
- mindsdb/integrations/handlers/bigquery_handler/bigquery_handler.py +1 -1
- mindsdb/integrations/handlers/bigquery_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/bigquery_handler/tests/test_bigquery_handler.py +1 -1
- mindsdb/integrations/handlers/binance_handler/binance_handler.py +1 -0
- mindsdb/integrations/handlers/binance_handler/binance_tables.py +3 -4
- mindsdb/integrations/handlers/byom_handler/__init__.py +0 -1
- mindsdb/integrations/handlers/chromadb_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/ckan_handler/ckan_handler.py +3 -0
- mindsdb/integrations/handlers/clickhouse_handler/__init__.py +1 -1
- mindsdb/integrations/handlers/cloud_spanner_handler/tests/test_cloud_spanner_handler.py +0 -2
- mindsdb/integrations/handlers/cloud_sql_handler/cloud_sql_handler.py +0 -1
- mindsdb/integrations/handlers/cohere_handler/__init__.py +1 -1
- mindsdb/integrations/handlers/cohere_handler/cohere_handler.py +11 -13
- mindsdb/integrations/handlers/confluence_handler/confluence_tables.py +6 -0
- mindsdb/integrations/handlers/databend_handler/connection_args.py +1 -1
- mindsdb/integrations/handlers/databend_handler/databend_handler.py +4 -4
- mindsdb/integrations/handlers/databend_handler/tests/__init__.py +0 -1
- mindsdb/integrations/handlers/databend_handler/tests/test_databend_handler.py +1 -1
- mindsdb/integrations/handlers/derby_handler/connection_args.py +1 -1
- mindsdb/integrations/handlers/derby_handler/derby_handler.py +14 -22
- mindsdb/integrations/handlers/derby_handler/tests/test_derby_handler.py +6 -6
- mindsdb/integrations/handlers/discord_handler/discord_handler.py +5 -5
- mindsdb/integrations/handlers/discord_handler/discord_tables.py +3 -3
- mindsdb/integrations/handlers/discord_handler/tests/test_discord.py +5 -3
- mindsdb/integrations/handlers/dockerhub_handler/dockerhub.py +3 -3
- mindsdb/integrations/handlers/dockerhub_handler/dockerhub_handler.py +2 -2
- mindsdb/integrations/handlers/dockerhub_handler/dockerhub_tables.py +57 -54
- mindsdb/integrations/handlers/dremio_handler/__init__.py +2 -2
- mindsdb/integrations/handlers/druid_handler/__init__.py +1 -1
- mindsdb/integrations/handlers/druid_handler/druid_handler.py +2 -2
- mindsdb/integrations/handlers/edgelessdb_handler/tests/test_edgelessdb_handler.py +9 -9
- mindsdb/integrations/handlers/email_handler/email_client.py +1 -1
- mindsdb/integrations/handlers/email_handler/email_ingestor.py +1 -1
- mindsdb/integrations/handlers/email_handler/email_tables.py +0 -1
- mindsdb/integrations/handlers/email_handler/settings.py +0 -1
- mindsdb/integrations/handlers/eventstoredb_handler/eventstoredb_handler.py +2 -1
- mindsdb/integrations/handlers/firebird_handler/firebird_handler.py +1 -1
- mindsdb/integrations/handlers/flaml_handler/flaml_handler.py +9 -9
- mindsdb/integrations/handlers/frappe_handler/frappe_client.py +5 -5
- mindsdb/integrations/handlers/frappe_handler/frappe_handler.py +6 -5
- mindsdb/integrations/handlers/frappe_handler/frappe_tables.py +2 -2
- mindsdb/integrations/handlers/github_handler/connection_args.py +2 -2
- mindsdb/integrations/handlers/github_handler/github_handler.py +1 -8
- mindsdb/integrations/handlers/github_handler/github_tables.py +13 -24
- mindsdb/integrations/handlers/gitlab_handler/gitlab_handler.py +2 -1
- mindsdb/integrations/handlers/gitlab_handler/gitlab_tables.py +1 -4
- mindsdb/integrations/handlers/gmail_handler/gmail_handler.py +6 -13
- mindsdb/integrations/handlers/gmail_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/google_analytics_handler/requirements.txt +2 -1
- mindsdb/integrations/handlers/google_books_handler/google_books_handler.py +2 -1
- mindsdb/integrations/handlers/google_books_handler/google_books_tables.py +0 -3
- mindsdb/integrations/handlers/google_books_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/google_calendar_handler/google_calendar_handler.py +4 -4
- mindsdb/integrations/handlers/google_calendar_handler/google_calendar_tables.py +2 -6
- mindsdb/integrations/handlers/google_calendar_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/google_content_shopping_handler/google_content_shopping_handler.py +3 -2
- mindsdb/integrations/handlers/google_content_shopping_handler/google_content_shopping_tables.py +0 -3
- mindsdb/integrations/handlers/google_content_shopping_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/google_fit_handler/google_fit_handler.py +10 -12
- mindsdb/integrations/handlers/google_fit_handler/google_fit_tables.py +11 -13
- mindsdb/integrations/handlers/google_fit_handler/requirements.txt +2 -0
- mindsdb/integrations/handlers/google_search_handler/google_search_handler.py +2 -1
- mindsdb/integrations/handlers/google_search_handler/google_search_tables.py +0 -3
- mindsdb/integrations/handlers/google_search_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/groq_handler/__init__.py +3 -3
- mindsdb/integrations/handlers/hackernews_handler/hn_handler.py +5 -7
- mindsdb/integrations/handlers/hackernews_handler/hn_table.py +6 -7
- mindsdb/integrations/handlers/hive_handler/tests/test_hive_handler.py +1 -1
- mindsdb/integrations/handlers/hsqldb_handler/connection_args.py +6 -6
- mindsdb/integrations/handlers/hsqldb_handler/hsqldb_handler.py +4 -3
- mindsdb/integrations/handlers/huggingface_api_handler/exceptions.py +1 -1
- mindsdb/integrations/handlers/huggingface_api_handler/huggingface_api_handler.py +1 -8
- mindsdb/integrations/handlers/huggingface_handler/huggingface_handler.py +6 -6
- mindsdb/integrations/handlers/huggingface_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/huggingface_handler/requirements_cpu.txt +1 -1
- mindsdb/integrations/handlers/ignite_handler/ignite_handler.py +2 -1
- mindsdb/integrations/handlers/impala_handler/impala_handler.py +9 -12
- mindsdb/integrations/handlers/impala_handler/tests/test_impala_handler.py +11 -11
- mindsdb/integrations/handlers/influxdb_handler/influxdb_handler.py +10 -13
- mindsdb/integrations/handlers/influxdb_handler/influxdb_tables.py +20 -20
- mindsdb/integrations/handlers/informix_handler/__about__.py +8 -8
- mindsdb/integrations/handlers/informix_handler/__init__.py +12 -5
- mindsdb/integrations/handlers/informix_handler/informix_handler.py +99 -133
- mindsdb/integrations/handlers/informix_handler/tests/test_informix_handler.py +13 -11
- mindsdb/integrations/handlers/ingres_handler/__about__.py +0 -1
- mindsdb/integrations/handlers/ingres_handler/ingres_handler.py +1 -0
- mindsdb/integrations/handlers/jira_handler/jira_handler.archived.py +75 -0
- mindsdb/integrations/handlers/jira_handler/jira_handler.py +113 -38
- mindsdb/integrations/handlers/jira_handler/jira_tables.py +229 -0
- mindsdb/integrations/handlers/jira_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/kinetica_handler/__init__.py +0 -1
- mindsdb/integrations/handlers/langchain_handler/langchain_handler.py +4 -4
- mindsdb/integrations/handlers/langchain_handler/tools.py +9 -10
- mindsdb/integrations/handlers/leonardoai_handler/__init__.py +1 -1
- mindsdb/integrations/handlers/lightfm_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/lightwood_handler/functions.py +2 -2
- mindsdb/integrations/handlers/lightwood_handler/lightwood_handler.py +0 -3
- mindsdb/integrations/handlers/lightwood_handler/requirements.txt +4 -4
- mindsdb/integrations/handlers/lightwood_handler/tests/test_lightwood_handler.py +11 -11
- mindsdb/integrations/handlers/lindorm_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/llama_index_handler/llama_index_handler.py +4 -4
- mindsdb/integrations/handlers/llama_index_handler/settings.py +10 -9
- mindsdb/integrations/handlers/materialize_handler/tests/test_materialize_handler.py +8 -10
- mindsdb/integrations/handlers/matrixone_handler/matrixone_handler.py +4 -4
- mindsdb/integrations/handlers/matrixone_handler/tests/test_matrixone_handler.py +8 -9
- mindsdb/integrations/handlers/maxdb_handler/connection_args.py +25 -25
- mindsdb/integrations/handlers/maxdb_handler/maxdb_handler.py +1 -0
- mindsdb/integrations/handlers/mediawiki_handler/mediawiki_handler.py +3 -2
- mindsdb/integrations/handlers/mediawiki_handler/mediawiki_tables.py +1 -1
- mindsdb/integrations/handlers/mendeley_handler/__about__.py +1 -1
- mindsdb/integrations/handlers/mendeley_handler/__init__.py +2 -2
- mindsdb/integrations/handlers/mendeley_handler/mendeley_handler.py +48 -56
- mindsdb/integrations/handlers/mendeley_handler/mendeley_tables.py +24 -29
- mindsdb/integrations/handlers/mendeley_handler/tests/test_mendeley_handler.py +19 -17
- mindsdb/integrations/handlers/merlion_handler/merlion_handler.py +5 -4
- mindsdb/integrations/handlers/minds_endpoint_handler/__init__.py +3 -3
- mindsdb/integrations/handlers/mlflow_handler/mlflow_handler.py +58 -36
- mindsdb/integrations/handlers/monetdb_handler/__about__.py +8 -8
- mindsdb/integrations/handlers/monetdb_handler/__init__.py +15 -5
- mindsdb/integrations/handlers/monetdb_handler/connection_args.py +17 -18
- mindsdb/integrations/handlers/monetdb_handler/monetdb_handler.py +40 -57
- mindsdb/integrations/handlers/monetdb_handler/tests/test_monetdb_handler.py +7 -8
- mindsdb/integrations/handlers/monetdb_handler/utils/monet_get_id.py +13 -14
- mindsdb/integrations/handlers/monkeylearn_handler/__about__.py +1 -1
- mindsdb/integrations/handlers/monkeylearn_handler/__init__.py +1 -1
- mindsdb/integrations/handlers/monkeylearn_handler/monkeylearn_handler.py +2 -5
- mindsdb/integrations/handlers/ms_one_drive_handler/ms_graph_api_one_drive_client.py +1 -0
- mindsdb/integrations/handlers/ms_one_drive_handler/ms_one_drive_handler.py +1 -1
- mindsdb/integrations/handlers/ms_one_drive_handler/requirements.txt +2 -0
- mindsdb/integrations/handlers/ms_teams_handler/ms_graph_api_teams_client.py +23 -23
- mindsdb/integrations/handlers/ms_teams_handler/ms_teams_handler.py +3 -3
- mindsdb/integrations/handlers/ms_teams_handler/ms_teams_tables.py +10 -5
- mindsdb/integrations/handlers/ms_teams_handler/requirements.txt +3 -1
- mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +73 -8
- mindsdb/integrations/handlers/mysql_handler/__about__.py +8 -8
- mindsdb/integrations/handlers/mysql_handler/__init__.py +15 -5
- mindsdb/integrations/handlers/mysql_handler/connection_args.py +43 -47
- mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +101 -34
- mindsdb/integrations/handlers/mysql_handler/settings.py +15 -13
- mindsdb/integrations/handlers/neuralforecast_handler/neuralforecast_handler.py +1 -1
- mindsdb/integrations/handlers/newsapi_handler/newsapi_handler.py +1 -1
- mindsdb/integrations/handlers/newsapi_handler/tests/test_newsapi_handler.py +4 -4
- mindsdb/integrations/handlers/nuo_jdbc_handler/connection_args.py +2 -2
- mindsdb/integrations/handlers/nuo_jdbc_handler/nuo_jdbc_handler.py +28 -36
- mindsdb/integrations/handlers/nuo_jdbc_handler/tests/test_nuo_handler.py +5 -5
- mindsdb/integrations/handlers/oceanbase_handler/oceanbase_handler.py +0 -1
- mindsdb/integrations/handlers/oceanbase_handler/tests/test_oceanbase_handler.py +8 -10
- mindsdb/integrations/handlers/ollama_handler/ollama_handler.py +3 -3
- mindsdb/integrations/handlers/openai_handler/openai_handler.py +5 -4
- mindsdb/integrations/handlers/opengauss_handler/tests/test_opengauss_handler.py +1 -2
- mindsdb/integrations/handlers/openstreetmap_handler/__init__.py +7 -7
- mindsdb/integrations/handlers/oracle_handler/connection_args.py +6 -0
- mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +77 -11
- mindsdb/integrations/handlers/orioledb_handler/tests/test_orioledb_handler.py +8 -10
- mindsdb/integrations/handlers/palm_handler/__about__.py +1 -1
- mindsdb/integrations/handlers/palm_handler/__init__.py +1 -1
- mindsdb/integrations/handlers/palm_handler/palm_handler.py +1 -3
- mindsdb/integrations/handlers/paypal_handler/paypal_handler.py +2 -2
- mindsdb/integrations/handlers/paypal_handler/paypal_tables.py +15 -14
- mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +53 -10
- mindsdb/integrations/handlers/phoenix_handler/__init__.py +1 -1
- mindsdb/integrations/handlers/phoenix_handler/phoenix_handler.py +1 -0
- mindsdb/integrations/handlers/pinot_handler/__init__.py +1 -1
- mindsdb/integrations/handlers/pinot_handler/pinot_handler.py +3 -2
- mindsdb/integrations/handlers/plaid_handler/plaid_handler.py +13 -13
- mindsdb/integrations/handlers/plaid_handler/plaid_tables.py +10 -12
- mindsdb/integrations/handlers/plaid_handler/utils.py +4 -6
- mindsdb/integrations/handlers/planetscale_handler/planetscale_handler.py +1 -4
- mindsdb/integrations/handlers/portkey_handler/__init__.py +2 -2
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +105 -24
- mindsdb/integrations/handlers/postgres_handler/tests/test_postgres_handler.py +11 -6
- mindsdb/integrations/handlers/questdb_handler/questdb_handler.py +1 -2
- mindsdb/integrations/handlers/questdb_handler/tests/test_questdb_handler.py +2 -3
- mindsdb/integrations/handlers/quickbooks_handler/quickbooks_handler.py +6 -8
- mindsdb/integrations/handlers/quickbooks_handler/quickbooks_table.py +10 -10
- mindsdb/integrations/handlers/rag_handler/ingest.py +2 -2
- mindsdb/integrations/handlers/rag_handler/rag_handler.py +1 -1
- mindsdb/integrations/handlers/rag_handler/settings.py +1 -1
- mindsdb/integrations/handlers/reddit_handler/reddit_handler.py +2 -7
- mindsdb/integrations/handlers/reddit_handler/reddit_tables.py +2 -3
- mindsdb/integrations/handlers/replicate_handler/replicate_handler.py +6 -6
- mindsdb/integrations/handlers/rocket_chat_handler/rocket_chat_handler.py +1 -2
- mindsdb/integrations/handlers/rocket_chat_handler/rocket_chat_tables.py +0 -3
- mindsdb/integrations/handlers/rockset_handler/connection_args.py +14 -14
- mindsdb/integrations/handlers/rockset_handler/tests/test_rockset_handler.py +1 -0
- mindsdb/integrations/handlers/scylla_handler/scylla_handler.py +6 -5
- mindsdb/integrations/handlers/sendinblue_handler/sendinblue_handler.py +2 -1
- mindsdb/integrations/handlers/sendinblue_handler/sendinblue_tables.py +16 -16
- mindsdb/integrations/handlers/sentence_transformers_handler/__init__.py +1 -1
- mindsdb/integrations/handlers/sheets_handler/connection_args.py +1 -1
- mindsdb/integrations/handlers/shopify_handler/shopify_handler.py +7 -6
- mindsdb/integrations/handlers/shopify_handler/shopify_tables.py +38 -41
- mindsdb/integrations/handlers/singlestore_handler/__about__.py +1 -1
- mindsdb/integrations/handlers/singlestore_handler/__init__.py +0 -1
- mindsdb/integrations/handlers/singlestore_handler/singlestore_handler.py +1 -0
- mindsdb/integrations/handlers/singlestore_handler/tests/test_singlestore_handler.py +3 -3
- mindsdb/integrations/handlers/slack_handler/__init__.py +3 -3
- mindsdb/integrations/handlers/snowflake_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +100 -6
- mindsdb/integrations/handlers/solr_handler/connection_args.py +7 -7
- mindsdb/integrations/handlers/solr_handler/solr_handler.py +2 -1
- mindsdb/integrations/handlers/solr_handler/tests/test_solr_handler.py +2 -1
- mindsdb/integrations/handlers/sqlany_handler/sqlany_handler.py +3 -2
- mindsdb/integrations/handlers/sqlite_handler/sqlite_handler.py +1 -0
- mindsdb/integrations/handlers/sqreamdb_handler/connection_args.py +1 -1
- mindsdb/integrations/handlers/sqreamdb_handler/sqreamdb_handler.py +15 -20
- mindsdb/integrations/handlers/sqreamdb_handler/tests/test_sqreamdb_handler.py +4 -4
- mindsdb/integrations/handlers/stabilityai_handler/__init__.py +1 -1
- mindsdb/integrations/handlers/starrocks_handler/starrocks_handler.py +0 -1
- mindsdb/integrations/handlers/starrocks_handler/tests/test_starrocks_handler.py +8 -10
- mindsdb/integrations/handlers/statsforecast_handler/statsforecast_handler.py +2 -2
- mindsdb/integrations/handlers/strava_handler/strava_handler.py +4 -8
- mindsdb/integrations/handlers/strava_handler/strava_tables.py +22 -30
- mindsdb/integrations/handlers/stripe_handler/stripe_handler.py +3 -2
- mindsdb/integrations/handlers/stripe_handler/stripe_tables.py +11 -27
- mindsdb/integrations/handlers/supabase_handler/tests/test_supabase_handler.py +1 -1
- mindsdb/integrations/handlers/surrealdb_handler/surrealdb_handler.py +4 -4
- mindsdb/integrations/handlers/tdengine_handler/tdengine_handler.py +25 -27
- mindsdb/integrations/handlers/tdengine_handler/tests/test_tdengine_handler.py +8 -8
- mindsdb/integrations/handlers/tidb_handler/tests/test_tidb_handler.py +1 -2
- mindsdb/integrations/handlers/timegpt_handler/timegpt_handler.py +5 -5
- mindsdb/integrations/handlers/tpot_handler/tpot_handler.py +21 -26
- mindsdb/integrations/handlers/trino_handler/trino_handler.py +14 -14
- mindsdb/integrations/handlers/twitter_handler/twitter_handler.py +2 -4
- mindsdb/integrations/handlers/unify_handler/tests/test_unify_handler.py +7 -8
- mindsdb/integrations/handlers/unify_handler/unify_handler.py +9 -9
- mindsdb/integrations/handlers/vertex_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/vertex_handler/vertex_client.py +1 -1
- mindsdb/integrations/handlers/vertica_handler/tests/test_vertica_handler.py +11 -11
- mindsdb/integrations/handlers/vertica_handler/vertica_handler.py +11 -14
- mindsdb/integrations/handlers/vitess_handler/tests/test_vitess_handler.py +9 -11
- mindsdb/integrations/handlers/vitess_handler/vitess_handler.py +0 -1
- mindsdb/integrations/handlers/web_handler/web_handler.py +1 -0
- mindsdb/integrations/handlers/whatsapp_handler/__init__.py +3 -3
- mindsdb/integrations/handlers/writer_handler/evaluate.py +1 -1
- mindsdb/integrations/handlers/writer_handler/settings.py +0 -1
- mindsdb/integrations/handlers/writer_handler/writer_handler.py +1 -0
- mindsdb/integrations/handlers/youtube_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/youtube_handler/youtube_handler.py +5 -5
- mindsdb/integrations/handlers/youtube_handler/youtube_tables.py +26 -27
- mindsdb/integrations/handlers/yugabyte_handler/tests/test_yugabyte_handler.py +3 -3
- mindsdb/integrations/handlers/yugabyte_handler/yugabyte_handler.py +0 -6
- mindsdb/integrations/libs/response.py +67 -52
- mindsdb/integrations/libs/vectordatabase_handler.py +6 -0
- mindsdb/integrations/utilities/files/file_reader.py +5 -2
- mindsdb/integrations/utilities/handler_utils.py +15 -3
- mindsdb/integrations/utilities/handlers/api_utilities/__init__.py +0 -1
- mindsdb/integrations/utilities/handlers/auth_utilities/__init__.py +0 -2
- mindsdb/integrations/utilities/utils.py +3 -3
- mindsdb/interfaces/agents/agents_controller.py +164 -1
- mindsdb/interfaces/agents/constants.py +29 -2
- mindsdb/interfaces/agents/langchain_agent.py +18 -8
- mindsdb/interfaces/agents/mindsdb_database_agent.py +101 -2
- mindsdb/interfaces/database/projects.py +1 -7
- mindsdb/interfaces/functions/controller.py +11 -14
- mindsdb/interfaces/functions/to_markdown.py +9 -124
- mindsdb/interfaces/knowledge_base/controller.py +47 -19
- mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +41 -15
- mindsdb/interfaces/knowledge_base/preprocessing/json_chunker.py +434 -0
- mindsdb/interfaces/knowledge_base/preprocessing/models.py +54 -0
- mindsdb/interfaces/knowledge_base/utils.py +10 -15
- mindsdb/interfaces/model/model_controller.py +0 -2
- mindsdb/interfaces/query_context/context_controller.py +66 -10
- mindsdb/interfaces/skills/custom/text2sql/mindsdb_kb_tools.py +190 -0
- mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +92 -0
- mindsdb/interfaces/skills/skill_tool.py +202 -57
- mindsdb/interfaces/skills/sql_agent.py +238 -28
- mindsdb/interfaces/storage/fs.py +1 -0
- mindsdb/interfaces/variables/__init__.py +0 -0
- mindsdb/interfaces/variables/variables_controller.py +97 -0
- mindsdb/migrations/env.py +5 -7
- mindsdb/migrations/migrate.py +47 -9
- mindsdb/migrations/versions/2025-05-21_9f150e4f9a05_checkpoint_1.py +360 -0
- mindsdb/utilities/config.py +333 -220
- mindsdb/utilities/context.py +1 -1
- mindsdb/utilities/functions.py +0 -36
- mindsdb/utilities/langfuse.py +19 -10
- mindsdb/utilities/otel/__init__.py +9 -193
- mindsdb/utilities/otel/metric_handlers/__init__.py +5 -1
- mindsdb/utilities/otel/prepare.py +198 -0
- mindsdb/utilities/sql.py +83 -0
- mindsdb/utilities/starters.py +13 -0
- {mindsdb-25.4.5.0.dist-info → mindsdb-25.5.4.0.dist-info}/METADATA +351 -338
- {mindsdb-25.4.5.0.dist-info → mindsdb-25.5.4.0.dist-info}/RECORD +348 -322
- {mindsdb-25.4.5.0.dist-info → mindsdb-25.5.4.0.dist-info}/WHEEL +1 -1
- mindsdb/api/mysql/mysql_proxy/classes/sql_statement_parser.py +0 -151
- mindsdb/integrations/handlers/monkeylearn_handler/requirements.txt +0 -1
- {mindsdb-25.4.5.0.dist-info → mindsdb-25.5.4.0.dist-info}/licenses/LICENSE +0 -0
- {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()
|