MindsDB 25.5.3.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 +127 -79
- 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 +47 -27
- mindsdb/api/executor/datahub/classes/response.py +5 -2
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +39 -72
- mindsdb/api/executor/planner/query_planner.py +10 -1
- 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 +9 -12
- 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 +99 -83
- mindsdb/api/http/namespaces/analysis.py +3 -3
- mindsdb/api/http/namespaces/file.py +8 -2
- mindsdb/api/http/namespaces/sql.py +13 -27
- 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 +82 -115
- mindsdb/api/mysql/mysql_proxy/utilities/dump.py +351 -0
- mindsdb/api/postgres/postgres_proxy/executor/executor.py +1 -1
- 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/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/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/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_calendar_handler/google_calendar_handler.py +4 -4
- mindsdb/integrations/handlers/google_calendar_handler/google_calendar_tables.py +2 -6
- 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_fit_handler/google_fit_handler.py +10 -12
- mindsdb/integrations/handlers/google_fit_handler/google_fit_tables.py +11 -13
- 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/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.py +4 -4
- mindsdb/integrations/handlers/jira_handler/jira_tables.py +9 -9
- 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/lightwood_handler/functions.py +2 -2
- mindsdb/integrations/handlers/lightwood_handler/lightwood_handler.py +0 -1
- mindsdb/integrations/handlers/lightwood_handler/tests/test_lightwood_handler.py +11 -11
- 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_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/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/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/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/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/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/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 +15 -0
- mindsdb/interfaces/agents/langchain_agent.py +16 -4
- mindsdb/interfaces/agents/mindsdb_database_agent.py +101 -2
- mindsdb/interfaces/knowledge_base/controller.py +25 -0
- mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +13 -10
- mindsdb/interfaces/knowledge_base/preprocessing/json_chunker.py +434 -0
- mindsdb/interfaces/knowledge_base/preprocessing/models.py +54 -0
- 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 +205 -17
- 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 -7
- mindsdb/migrations/versions/2025-05-21_9f150e4f9a05_checkpoint_1.py +360 -0
- mindsdb/utilities/config.py +331 -219
- mindsdb/utilities/starters.py +13 -0
- {mindsdb-25.5.3.0.dist-info → mindsdb-25.5.4.0.dist-info}/METADATA +641 -695
- {mindsdb-25.5.3.0.dist-info → mindsdb-25.5.4.0.dist-info}/RECORD +309 -288
- {mindsdb-25.5.3.0.dist-info → mindsdb-25.5.4.0.dist-info}/WHEEL +1 -1
- mindsdb/integrations/handlers/monkeylearn_handler/requirements.txt +0 -1
- {mindsdb-25.5.3.0.dist-info → mindsdb-25.5.4.0.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.5.3.0.dist-info → mindsdb-25.5.4.0.dist-info}/top_level.txt +0 -0
mindsdb/api/http/initialize.py
CHANGED
|
@@ -4,9 +4,11 @@ import mimetypes
|
|
|
4
4
|
import threading
|
|
5
5
|
import traceback
|
|
6
6
|
import webbrowser
|
|
7
|
+
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from http import HTTPStatus
|
|
9
10
|
|
|
11
|
+
|
|
10
12
|
import requests
|
|
11
13
|
from flask import Flask, url_for, make_response, request, send_from_directory
|
|
12
14
|
from flask.json import dumps
|
|
@@ -68,7 +70,9 @@ try:
|
|
|
68
70
|
from opentelemetry.instrumentation.flask import FlaskInstrumentor
|
|
69
71
|
from opentelemetry.instrumentation.requests import RequestsInstrumentor
|
|
70
72
|
except ImportError:
|
|
71
|
-
logger.debug(
|
|
73
|
+
logger.debug(
|
|
74
|
+
"OpenTelemetry is not avaiable. Please run `pip install -r requirements/requirements-opentelemetry.txt` to use it."
|
|
75
|
+
)
|
|
72
76
|
FlaskInstrumentor = _NoOpFlaskInstrumentor
|
|
73
77
|
RequestsInstrumentor = _NoOpRequestsInstrumentor
|
|
74
78
|
|
|
@@ -78,6 +82,7 @@ class Swagger_Api(Api):
|
|
|
78
82
|
This is a modification of the base Flask Restplus Api class due to the issue described here
|
|
79
83
|
https://github.com/noirbizarre/flask-restplus/issues/223
|
|
80
84
|
"""
|
|
85
|
+
|
|
81
86
|
@property
|
|
82
87
|
def specs_url(self):
|
|
83
88
|
return url_for(self.endpoint("specs"), _external=False)
|
|
@@ -92,7 +97,10 @@ def custom_output_json(data, code, headers=None):
|
|
|
92
97
|
def get_last_compatible_gui_version() -> Version:
|
|
93
98
|
logger.debug("Getting last compatible frontend..")
|
|
94
99
|
try:
|
|
95
|
-
res = requests.get(
|
|
100
|
+
res = requests.get(
|
|
101
|
+
"https://mindsdb-web-builds.s3.amazonaws.com/compatible-config.json",
|
|
102
|
+
timeout=5,
|
|
103
|
+
)
|
|
96
104
|
except (ConnectionError, requests.exceptions.ConnectionError) as e:
|
|
97
105
|
logger.error(f"Is no connection. {e}")
|
|
98
106
|
return False
|
|
@@ -118,13 +126,16 @@ def get_last_compatible_gui_version() -> Version:
|
|
|
118
126
|
gui_versions = {}
|
|
119
127
|
max_mindsdb_lv = None
|
|
120
128
|
max_gui_lv = None
|
|
121
|
-
for el in versions[
|
|
122
|
-
if el[
|
|
123
|
-
gui_lv = parse_version(el[
|
|
129
|
+
for el in versions["mindsdb"]:
|
|
130
|
+
if el["mindsdb_version"] is None:
|
|
131
|
+
gui_lv = parse_version(el["gui_version"])
|
|
124
132
|
else:
|
|
125
|
-
mindsdb_lv = parse_version(el[
|
|
126
|
-
gui_lv = parse_version(el[
|
|
127
|
-
if
|
|
133
|
+
mindsdb_lv = parse_version(el["mindsdb_version"])
|
|
134
|
+
gui_lv = parse_version(el["gui_version"])
|
|
135
|
+
if (
|
|
136
|
+
mindsdb_lv.base_version not in gui_versions
|
|
137
|
+
or gui_lv > gui_versions[mindsdb_lv.base_version]
|
|
138
|
+
):
|
|
128
139
|
gui_versions[mindsdb_lv.base_version] = gui_lv
|
|
129
140
|
if max_mindsdb_lv is None or max_mindsdb_lv < mindsdb_lv:
|
|
130
141
|
max_mindsdb_lv = mindsdb_lv
|
|
@@ -139,7 +150,11 @@ def get_last_compatible_gui_version() -> Version:
|
|
|
139
150
|
elif current_mindsdb_lv > all_mindsdb_lv[-1]:
|
|
140
151
|
gui_version_lv = max_gui_lv
|
|
141
152
|
else:
|
|
142
|
-
lower_versions = {
|
|
153
|
+
lower_versions = {
|
|
154
|
+
key: value
|
|
155
|
+
for key, value in gui_versions.items()
|
|
156
|
+
if parse_version(key) < current_mindsdb_lv
|
|
157
|
+
}
|
|
143
158
|
if len(lower_versions) == 0:
|
|
144
159
|
gui_version_lv = gui_versions[all_mindsdb_lv[0].base_version]
|
|
145
160
|
else:
|
|
@@ -156,12 +171,12 @@ def get_last_compatible_gui_version() -> Version:
|
|
|
156
171
|
def get_current_gui_version() -> Version:
|
|
157
172
|
logger.debug("Getting current frontend version..")
|
|
158
173
|
config = Config()
|
|
159
|
-
static_path = Path(config[
|
|
160
|
-
version_txt_path = static_path.joinpath(
|
|
174
|
+
static_path = Path(config["paths"]["static"])
|
|
175
|
+
version_txt_path = static_path.joinpath("version.txt")
|
|
161
176
|
|
|
162
177
|
current_gui_version = None
|
|
163
178
|
if version_txt_path.is_file():
|
|
164
|
-
with open(version_txt_path,
|
|
179
|
+
with open(version_txt_path, "rt") as f:
|
|
165
180
|
current_gui_version = f.readline()
|
|
166
181
|
|
|
167
182
|
current_gui_lv = (
|
|
@@ -177,7 +192,7 @@ def initialize_static():
|
|
|
177
192
|
config = Config()
|
|
178
193
|
last_gui_version_lv = get_last_compatible_gui_version()
|
|
179
194
|
current_gui_version_lv = get_current_gui_version()
|
|
180
|
-
required_gui_version = config[
|
|
195
|
+
required_gui_version = config["gui"].get("version")
|
|
181
196
|
|
|
182
197
|
if required_gui_version is not None:
|
|
183
198
|
required_gui_version_lv = parse_version(required_gui_version)
|
|
@@ -193,7 +208,10 @@ def initialize_static():
|
|
|
193
208
|
return False
|
|
194
209
|
|
|
195
210
|
# ignore versions like '23.9.2.2'
|
|
196
|
-
if
|
|
211
|
+
if (
|
|
212
|
+
current_gui_version_lv is not None
|
|
213
|
+
and len(current_gui_version_lv.release) < 3
|
|
214
|
+
):
|
|
197
215
|
if current_gui_version_lv == last_gui_version_lv:
|
|
198
216
|
return True
|
|
199
217
|
logger.debug("Updating gui..")
|
|
@@ -204,37 +222,40 @@ def initialize_static():
|
|
|
204
222
|
|
|
205
223
|
|
|
206
224
|
def initialize_app(config, no_studio):
|
|
207
|
-
static_root = config[
|
|
225
|
+
static_root = config["paths"]["static"]
|
|
208
226
|
logger.debug(f"Static route: {static_root}")
|
|
209
|
-
gui_exists = Path(static_root).joinpath(
|
|
227
|
+
gui_exists = Path(static_root).joinpath("index.html").is_file()
|
|
210
228
|
logger.debug(f"Does GUI already exist.. {'YES' if gui_exists else 'NO'}")
|
|
211
229
|
init_static_thread = None
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
and (
|
|
215
|
-
config['gui']['autoupdate'] is True
|
|
216
|
-
or gui_exists is False
|
|
217
|
-
)
|
|
230
|
+
if no_studio is False and (
|
|
231
|
+
config["gui"]["autoupdate"] is True or gui_exists is False
|
|
218
232
|
):
|
|
219
|
-
init_static_thread = threading.Thread(
|
|
233
|
+
init_static_thread = threading.Thread(
|
|
234
|
+
target=initialize_static, name="initialize_static"
|
|
235
|
+
)
|
|
220
236
|
init_static_thread.start()
|
|
221
237
|
|
|
238
|
+
# Wait for static initialization.
|
|
239
|
+
if not no_studio and init_static_thread is not None:
|
|
240
|
+
init_static_thread.join()
|
|
241
|
+
|
|
222
242
|
app, api = initialize_flask(config, init_static_thread, no_studio)
|
|
223
243
|
Compress(app)
|
|
244
|
+
|
|
224
245
|
initialize_interfaces(app)
|
|
225
246
|
|
|
226
247
|
if os.path.isabs(static_root) is False:
|
|
227
248
|
static_root = os.path.join(os.getcwd(), static_root)
|
|
228
249
|
static_root = Path(static_root)
|
|
229
250
|
|
|
230
|
-
@app.route(
|
|
231
|
-
@app.route(
|
|
251
|
+
@app.route("/", defaults={"path": ""}, methods=["GET"])
|
|
252
|
+
@app.route("/<path:path>", methods=["GET"])
|
|
232
253
|
def root_index(path):
|
|
233
|
-
if path.startswith(
|
|
254
|
+
if path.startswith("api/"):
|
|
234
255
|
return http_error(
|
|
235
256
|
HTTPStatus.NOT_FOUND,
|
|
236
|
-
|
|
237
|
-
|
|
257
|
+
"Not found",
|
|
258
|
+
"The endpoint you are trying to access does not exist on the server.",
|
|
238
259
|
)
|
|
239
260
|
|
|
240
261
|
# Normalize the path.
|
|
@@ -244,14 +265,14 @@ def initialize_app(config, no_studio):
|
|
|
244
265
|
if not full_path.startswith(str(static_root)):
|
|
245
266
|
return http_error(
|
|
246
267
|
HTTPStatus.FORBIDDEN,
|
|
247
|
-
|
|
248
|
-
|
|
268
|
+
"Forbidden",
|
|
269
|
+
"You are not allowed to access the requested resource.",
|
|
249
270
|
)
|
|
250
271
|
|
|
251
272
|
if os.path.isfile(full_path):
|
|
252
273
|
return send_from_directory(static_root, path)
|
|
253
274
|
else:
|
|
254
|
-
return send_from_directory(static_root,
|
|
275
|
+
return send_from_directory(static_root, "index.html")
|
|
255
276
|
|
|
256
277
|
protected_namespaces = [
|
|
257
278
|
tab_ns,
|
|
@@ -270,7 +291,7 @@ def initialize_app(config, no_studio):
|
|
|
270
291
|
skills_ns,
|
|
271
292
|
agents_ns,
|
|
272
293
|
jobs_ns,
|
|
273
|
-
knowledge_bases_ns
|
|
294
|
+
knowledge_bases_ns,
|
|
274
295
|
]
|
|
275
296
|
|
|
276
297
|
for ns in protected_namespaces:
|
|
@@ -285,20 +306,20 @@ def initialize_app(config, no_studio):
|
|
|
285
306
|
# pass through HTTP errors
|
|
286
307
|
# NOTE flask_restx require 'message', also it modyfies 'application/problem+json' to 'application/json'
|
|
287
308
|
if isinstance(e, HTTPException):
|
|
288
|
-
return (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
309
|
+
return (
|
|
310
|
+
{"title": e.name, "detail": e.description, "message": e.description},
|
|
311
|
+
e.code,
|
|
312
|
+
{"Content-Type": "application/problem+json"},
|
|
313
|
+
)
|
|
314
|
+
return (
|
|
315
|
+
{
|
|
316
|
+
"title": getattr(type(e), "__name__") or "Unknown error",
|
|
317
|
+
"detail": str(e),
|
|
318
|
+
"message": str(e),
|
|
319
|
+
},
|
|
320
|
+
500,
|
|
321
|
+
{"Content-Type": "application/problem+json"},
|
|
322
|
+
)
|
|
302
323
|
|
|
303
324
|
@app.teardown_appcontext
|
|
304
325
|
def remove_session(*args, **kwargs):
|
|
@@ -312,31 +333,34 @@ def initialize_app(config, no_studio):
|
|
|
312
333
|
|
|
313
334
|
# region routes where auth is required
|
|
314
335
|
if (
|
|
315
|
-
config[
|
|
316
|
-
and any(
|
|
336
|
+
config["auth"]["http_auth_enabled"] is True
|
|
337
|
+
and any(
|
|
338
|
+
request.path.startswith(f"/api{ns.path}") for ns in protected_namespaces
|
|
339
|
+
)
|
|
317
340
|
and check_auth() is False
|
|
318
341
|
):
|
|
319
342
|
return http_error(
|
|
320
|
-
HTTPStatus.UNAUTHORIZED,
|
|
321
|
-
|
|
343
|
+
HTTPStatus.UNAUTHORIZED,
|
|
344
|
+
"Unauthorized",
|
|
345
|
+
"Authorization is required to complete the request",
|
|
322
346
|
)
|
|
323
347
|
# endregion
|
|
324
348
|
|
|
325
|
-
company_id = request.headers.get(
|
|
326
|
-
user_class = request.headers.get(
|
|
349
|
+
company_id = request.headers.get("company-id")
|
|
350
|
+
user_class = request.headers.get("user-class")
|
|
327
351
|
|
|
328
352
|
try:
|
|
329
|
-
email_confirmed = int(request.headers.get(
|
|
353
|
+
email_confirmed = int(request.headers.get("email-confirmed", 1))
|
|
330
354
|
except Exception:
|
|
331
355
|
email_confirmed = 1
|
|
332
356
|
|
|
333
357
|
try:
|
|
334
|
-
user_id = int(request.headers.get(
|
|
358
|
+
user_id = int(request.headers.get("user-id", 0))
|
|
335
359
|
except Exception:
|
|
336
360
|
user_id = 0
|
|
337
361
|
|
|
338
362
|
try:
|
|
339
|
-
session_id = request.cookies.get(
|
|
363
|
+
session_id = request.cookies.get("session")
|
|
340
364
|
except Exception:
|
|
341
365
|
session_id = "unknown"
|
|
342
366
|
|
|
@@ -366,10 +390,6 @@ def initialize_app(config, no_studio):
|
|
|
366
390
|
ctx.user_class = user_class
|
|
367
391
|
ctx.email_confirmed = email_confirmed
|
|
368
392
|
|
|
369
|
-
# Wait for static initialization.
|
|
370
|
-
if not no_studio and init_static_thread is not None:
|
|
371
|
-
init_static_thread.join()
|
|
372
|
-
|
|
373
393
|
logger.debug("Done initializing app.")
|
|
374
394
|
return app
|
|
375
395
|
|
|
@@ -377,13 +397,13 @@ def initialize_app(config, no_studio):
|
|
|
377
397
|
def initialize_flask(config, init_static_thread, no_studio):
|
|
378
398
|
logger.debug("Initializing flask..")
|
|
379
399
|
# region required for windows https://github.com/mindsdb/mindsdb/issues/2526
|
|
380
|
-
mimetypes.add_type(
|
|
381
|
-
mimetypes.add_type(
|
|
400
|
+
mimetypes.add_type("text/css", ".css")
|
|
401
|
+
mimetypes.add_type("text/javascript", ".js")
|
|
382
402
|
# endregion
|
|
383
403
|
|
|
384
404
|
kwargs = {}
|
|
385
405
|
if no_studio is not True:
|
|
386
|
-
static_path = os.path.join(config[
|
|
406
|
+
static_path = os.path.join(config["paths"]["static"], "static/")
|
|
387
407
|
if os.path.isabs(static_path) is False:
|
|
388
408
|
static_path = os.path.join(os.getcwd(), static_path)
|
|
389
409
|
kwargs["static_url_path"] = "/static"
|
|
@@ -397,35 +417,31 @@ def initialize_flask(config, init_static_thread, no_studio):
|
|
|
397
417
|
FlaskInstrumentor().instrument_app(app)
|
|
398
418
|
RequestsInstrumentor().instrument()
|
|
399
419
|
|
|
400
|
-
app.config[
|
|
401
|
-
app.config[
|
|
402
|
-
app.config[
|
|
403
|
-
|
|
404
|
-
|
|
420
|
+
app.config["SECRET_KEY"] = os.environ.get("FLASK_SECRET_KEY", secrets.token_hex(32))
|
|
421
|
+
app.config["SESSION_COOKIE_NAME"] = "session"
|
|
422
|
+
app.config["PERMANENT_SESSION_LIFETIME"] = config["auth"][
|
|
423
|
+
"http_permanent_session_lifetime"
|
|
424
|
+
]
|
|
425
|
+
app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 60
|
|
426
|
+
app.config["SWAGGER_HOST"] = "http://localhost:8000/mindsdb"
|
|
405
427
|
app.json = CustomJSONProvider()
|
|
406
428
|
|
|
407
|
-
authorizations = {
|
|
408
|
-
'apikey': {
|
|
409
|
-
'type': 'session',
|
|
410
|
-
'in': 'query',
|
|
411
|
-
'name': 'session'
|
|
412
|
-
}
|
|
413
|
-
}
|
|
429
|
+
authorizations = {"apikey": {"type": "session", "in": "query", "name": "session"}}
|
|
414
430
|
|
|
415
431
|
logger.debug("Creating swagger API..")
|
|
416
432
|
api = Swagger_Api(
|
|
417
433
|
app,
|
|
418
434
|
authorizations=authorizations,
|
|
419
|
-
security=[
|
|
420
|
-
url_prefix=
|
|
421
|
-
prefix=
|
|
422
|
-
doc=
|
|
435
|
+
security=["apikey"],
|
|
436
|
+
url_prefix=":8000",
|
|
437
|
+
prefix="/api",
|
|
438
|
+
doc="/doc/",
|
|
423
439
|
)
|
|
424
440
|
|
|
425
|
-
api.representations[
|
|
441
|
+
api.representations["application/json"] = custom_output_json
|
|
426
442
|
|
|
427
|
-
port = config[
|
|
428
|
-
host = config[
|
|
443
|
+
port = config["api"]["http"]["port"]
|
|
444
|
+
host = config["api"]["http"]["host"]
|
|
429
445
|
|
|
430
446
|
# NOTE rewrite it, that hotfix to see GUI link
|
|
431
447
|
if not no_studio:
|
|
@@ -438,9 +454,9 @@ def initialize_flask(config, init_static_thread, no_studio):
|
|
|
438
454
|
pid = os.getpid()
|
|
439
455
|
thread = threading.Thread(
|
|
440
456
|
target=_open_webbrowser,
|
|
441
|
-
args=(url, pid, port, init_static_thread, config[
|
|
457
|
+
args=(url, pid, port, init_static_thread, config["paths"]["static"]),
|
|
442
458
|
daemon=True,
|
|
443
|
-
name=
|
|
459
|
+
name="open_webbrowser",
|
|
444
460
|
)
|
|
445
461
|
thread.start()
|
|
446
462
|
|
|
@@ -77,8 +77,8 @@ class QueryAnalysis(Resource):
|
|
|
77
77
|
if result.type != SQL_RESPONSE_TYPE.TABLE:
|
|
78
78
|
return http_error(500, "Error", "Query does not return data")
|
|
79
79
|
|
|
80
|
-
column_names = [
|
|
81
|
-
df =
|
|
80
|
+
column_names = [column.name for column in result.result_set.columns]
|
|
81
|
+
df = result.result_set.to_df()
|
|
82
82
|
try:
|
|
83
83
|
analysis = analyze_df(df)
|
|
84
84
|
except ImportError:
|
|
@@ -95,7 +95,7 @@ class QueryAnalysis(Resource):
|
|
|
95
95
|
return {
|
|
96
96
|
"analysis": analysis,
|
|
97
97
|
"column_names": column_names,
|
|
98
|
-
"row_count": len(result.
|
|
98
|
+
"row_count": len(result.result_set),
|
|
99
99
|
"timestamp": time.time(),
|
|
100
100
|
"tables": query_tables,
|
|
101
101
|
}
|
|
@@ -84,8 +84,14 @@ class File(Resource):
|
|
|
84
84
|
parser.finalize()
|
|
85
85
|
parser.close()
|
|
86
86
|
|
|
87
|
-
if file_object is not None
|
|
88
|
-
file_object.
|
|
87
|
+
if file_object is not None:
|
|
88
|
+
if not file_object.closed:
|
|
89
|
+
try:
|
|
90
|
+
file_object.flush()
|
|
91
|
+
except (AttributeError, ValueError, OSError):
|
|
92
|
+
logger.debug("Failed to flush file_object before closing.", exc_info=True)
|
|
93
|
+
file_object.close()
|
|
94
|
+
file_object = None
|
|
89
95
|
else:
|
|
90
96
|
data = request.json
|
|
91
97
|
|
|
@@ -8,6 +8,7 @@ import mindsdb.utilities.hooks as hooks
|
|
|
8
8
|
import mindsdb.utilities.profiler as profiler
|
|
9
9
|
from mindsdb.api.http.utils import http_error
|
|
10
10
|
from mindsdb.api.http.namespaces.configs.sql import ns_conf
|
|
11
|
+
from mindsdb.api.mysql.mysql_proxy.mysql_proxy import SQLAnswer
|
|
11
12
|
from mindsdb.api.mysql.mysql_proxy.classes.fake_mysql_proxy import FakeMysqlProxy
|
|
12
13
|
from mindsdb.api.executor.data_types.response_type import (
|
|
13
14
|
RESPONSE_TYPE as SQL_RESPONSE_TYPE,
|
|
@@ -56,23 +57,8 @@ class Query(Resource):
|
|
|
56
57
|
mysql_proxy = FakeMysqlProxy()
|
|
57
58
|
mysql_proxy.set_context(context)
|
|
58
59
|
try:
|
|
59
|
-
result = mysql_proxy.process_query(query)
|
|
60
|
-
|
|
61
|
-
if result.type == SQL_RESPONSE_TYPE.OK:
|
|
62
|
-
query_response = {
|
|
63
|
-
"type": SQL_RESPONSE_TYPE.OK,
|
|
64
|
-
"affected_rows": result.affected_rows
|
|
65
|
-
}
|
|
66
|
-
elif result.type == SQL_RESPONSE_TYPE.TABLE:
|
|
67
|
-
data = result.data.to_lists(json_types=True)
|
|
68
|
-
query_response = {
|
|
69
|
-
"type": SQL_RESPONSE_TYPE.TABLE,
|
|
70
|
-
"data": data,
|
|
71
|
-
"column_names": [
|
|
72
|
-
x["alias"] or x["name"] if "alias" in x else x["name"]
|
|
73
|
-
for x in result.columns
|
|
74
|
-
],
|
|
75
|
-
}
|
|
60
|
+
result: SQLAnswer = mysql_proxy.process_query(query)
|
|
61
|
+
query_response: dict = result.dump_http_response()
|
|
76
62
|
except ExecutorException as e:
|
|
77
63
|
# classified error
|
|
78
64
|
error_type = "expected"
|
|
@@ -134,7 +120,7 @@ class ListDatabases(Resource):
|
|
|
134
120
|
listing_query = "SHOW DATABASES"
|
|
135
121
|
mysql_proxy = FakeMysqlProxy()
|
|
136
122
|
try:
|
|
137
|
-
result = mysql_proxy.process_query(listing_query)
|
|
123
|
+
result: SQLAnswer = mysql_proxy.process_query(listing_query)
|
|
138
124
|
|
|
139
125
|
# iterate over result.data and perform a query on each item to get the name of the tables
|
|
140
126
|
if result.type == SQL_RESPONSE_TYPE.ERROR:
|
|
@@ -147,15 +133,15 @@ class ListDatabases(Resource):
|
|
|
147
133
|
listing_query_response = {"type": "ok"}
|
|
148
134
|
elif result.type == SQL_RESPONSE_TYPE.TABLE:
|
|
149
135
|
listing_query_response = {
|
|
150
|
-
"data": [
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
]
|
|
136
|
+
"data": [{
|
|
137
|
+
"name": db_row[0],
|
|
138
|
+
"tables": [
|
|
139
|
+
table_row[0]
|
|
140
|
+
for table_row in mysql_proxy.process_query(
|
|
141
|
+
"SHOW TABLES FROM `{}`".format(db_row[0])
|
|
142
|
+
).result_set.to_lists()
|
|
143
|
+
]
|
|
144
|
+
} for db_row in result.result_set.to_lists()]
|
|
159
145
|
}
|
|
160
146
|
except Exception as e:
|
|
161
147
|
listing_query_response = {
|
mindsdb/api/mcp/start.py
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from contextlib import asynccontextmanager
|
|
2
3
|
from collections.abc import AsyncIterator
|
|
3
4
|
from typing import Optional, Dict, Any
|
|
4
5
|
from dataclasses import dataclass
|
|
5
6
|
|
|
7
|
+
import uvicorn
|
|
8
|
+
import anyio
|
|
6
9
|
from mcp.server.fastmcp import FastMCP
|
|
10
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
11
|
+
from starlette.requests import Request
|
|
12
|
+
from starlette.responses import Response
|
|
13
|
+
|
|
7
14
|
from mindsdb.api.mysql.mysql_proxy.classes.fake_mysql_proxy import FakeMysqlProxy
|
|
8
15
|
from mindsdb.api.executor.data_types.response_type import RESPONSE_TYPE as SQL_RESPONSE_TYPE
|
|
9
16
|
from mindsdb.utilities import log
|
|
@@ -70,10 +77,10 @@ def query(query: str, context: Optional[Dict] = None) -> Dict[str, Any]:
|
|
|
70
77
|
if result.type == SQL_RESPONSE_TYPE.TABLE:
|
|
71
78
|
return {
|
|
72
79
|
"type": SQL_RESPONSE_TYPE.TABLE,
|
|
73
|
-
"data": result.
|
|
80
|
+
"data": result.result_set.to_lists(json_types=True),
|
|
74
81
|
"column_names": [
|
|
75
|
-
|
|
76
|
-
for
|
|
82
|
+
column.alias or column.name
|
|
83
|
+
for column in result.result_set.columns
|
|
77
84
|
],
|
|
78
85
|
}
|
|
79
86
|
else:
|
|
@@ -116,7 +123,7 @@ def list_databases() -> Dict[str, Any]:
|
|
|
116
123
|
return {"type": "ok"}
|
|
117
124
|
|
|
118
125
|
elif result.type == SQL_RESPONSE_TYPE.TABLE:
|
|
119
|
-
data = result.
|
|
126
|
+
data = result.result_set.to_lists(json_types=True)
|
|
120
127
|
return data
|
|
121
128
|
|
|
122
129
|
except Exception as e:
|
|
@@ -127,6 +134,36 @@ def list_databases() -> Dict[str, Any]:
|
|
|
127
134
|
}
|
|
128
135
|
|
|
129
136
|
|
|
137
|
+
class CustomAuthMiddleware(BaseHTTPMiddleware):
|
|
138
|
+
"""Custom middleware to handle authentication basing on header 'Authorization'
|
|
139
|
+
"""
|
|
140
|
+
async def dispatch(self, request: Request, call_next):
|
|
141
|
+
mcp_access_token = os.environ.get('MINDSDB_MCP_ACCESS_TOKEN')
|
|
142
|
+
if mcp_access_token is not None:
|
|
143
|
+
auth_token = request.headers.get('Authorization', '').partition('Bearer ')[-1]
|
|
144
|
+
if mcp_access_token != auth_token:
|
|
145
|
+
return Response(status_code=401, content="Unauthorized", media_type="text/plain")
|
|
146
|
+
|
|
147
|
+
response = await call_next(request)
|
|
148
|
+
|
|
149
|
+
return response
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
async def run_sse_async() -> None:
|
|
153
|
+
"""Run the server using SSE transport."""
|
|
154
|
+
starlette_app = mcp.sse_app()
|
|
155
|
+
starlette_app.add_middleware(CustomAuthMiddleware)
|
|
156
|
+
|
|
157
|
+
config = uvicorn.Config(
|
|
158
|
+
starlette_app,
|
|
159
|
+
host=mcp.settings.host,
|
|
160
|
+
port=mcp.settings.port,
|
|
161
|
+
log_level=mcp.settings.log_level.lower(),
|
|
162
|
+
)
|
|
163
|
+
server = uvicorn.Server(config)
|
|
164
|
+
await server.serve()
|
|
165
|
+
|
|
166
|
+
|
|
130
167
|
def start(*args, **kwargs):
|
|
131
168
|
"""Start the MCP server
|
|
132
169
|
Args:
|
|
@@ -142,7 +179,7 @@ def start(*args, **kwargs):
|
|
|
142
179
|
mcp.settings.port = port
|
|
143
180
|
|
|
144
181
|
try:
|
|
145
|
-
|
|
182
|
+
anyio.run(run_sse_async)
|
|
146
183
|
except Exception as e:
|
|
147
184
|
logger.error(f"Error starting MCP server: {str(e)}")
|
|
148
185
|
raise
|