MindsDB 25.4.3.1__py3-none-any.whl → 25.4.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 (43) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +18 -4
  3. mindsdb/api/executor/data_types/response_type.py +1 -0
  4. mindsdb/api/executor/datahub/classes/tables_row.py +3 -10
  5. mindsdb/api/executor/datahub/datanodes/datanode.py +7 -2
  6. mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +44 -10
  7. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +57 -38
  8. mindsdb/api/executor/datahub/datanodes/project_datanode.py +39 -7
  9. mindsdb/api/executor/datahub/datanodes/system_tables.py +116 -109
  10. mindsdb/api/executor/planner/query_planner.py +10 -1
  11. mindsdb/api/executor/planner/steps.py +8 -2
  12. mindsdb/api/executor/sql_query/steps/apply_predictor_step.py +5 -5
  13. mindsdb/api/executor/sql_query/steps/fetch_dataframe_partition.py +1 -1
  14. mindsdb/api/executor/sql_query/steps/insert_step.py +2 -1
  15. mindsdb/api/executor/sql_query/steps/prepare_steps.py +2 -3
  16. mindsdb/api/litellm/start.py +82 -0
  17. mindsdb/api/mysql/mysql_proxy/libs/constants/mysql.py +133 -0
  18. mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +7 -2
  19. mindsdb/integrations/handlers/chromadb_handler/settings.py +1 -0
  20. mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +13 -4
  21. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +14 -5
  22. mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +14 -4
  23. mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +34 -19
  24. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +21 -18
  25. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +14 -4
  26. mindsdb/integrations/handlers/web_handler/urlcrawl_helpers.py +1 -1
  27. mindsdb/integrations/libs/response.py +80 -32
  28. mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +208 -13
  29. mindsdb/interfaces/agents/litellm_server.py +345 -0
  30. mindsdb/interfaces/agents/mcp_client_agent.py +252 -0
  31. mindsdb/interfaces/agents/run_mcp_agent.py +205 -0
  32. mindsdb/interfaces/knowledge_base/controller.py +17 -7
  33. mindsdb/interfaces/skills/skill_tool.py +7 -1
  34. mindsdb/interfaces/skills/sql_agent.py +8 -3
  35. mindsdb/utilities/config.py +8 -1
  36. mindsdb/utilities/starters.py +7 -0
  37. {mindsdb-25.4.3.1.dist-info → mindsdb-25.4.4.0.dist-info}/METADATA +232 -230
  38. {mindsdb-25.4.3.1.dist-info → mindsdb-25.4.4.0.dist-info}/RECORD +42 -39
  39. {mindsdb-25.4.3.1.dist-info → mindsdb-25.4.4.0.dist-info}/WHEEL +1 -1
  40. mindsdb/integrations/handlers/snowflake_handler/tests/test_snowflake_handler.py +0 -230
  41. /mindsdb/{integrations/handlers/snowflake_handler/tests → api/litellm}/__init__.py +0 -0
  42. {mindsdb-25.4.3.1.dist-info → mindsdb-25.4.4.0.dist-info}/licenses/LICENSE +0 -0
  43. {mindsdb-25.4.3.1.dist-info → mindsdb-25.4.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,252 @@
1
+ import json
2
+ import asyncio
3
+ from typing import Dict, List, Any, Iterator, ClassVar
4
+ from contextlib import AsyncExitStack
5
+
6
+ import pandas as pd
7
+ from mcp import ClientSession, StdioServerParameters
8
+ from mcp.client.stdio import stdio_client
9
+
10
+ from mindsdb.utilities import log
11
+ from mindsdb.interfaces.agents.langchain_agent import LangchainAgent
12
+ from mindsdb.interfaces.storage import db
13
+ from langchain_core.tools import BaseTool
14
+
15
+ logger = log.getLogger(__name__)
16
+
17
+
18
+ class MCPQueryTool(BaseTool):
19
+ """Tool that executes queries via MCP server"""
20
+
21
+ name: ClassVar[str] = "mcp_query"
22
+ description: ClassVar[str] = "Execute SQL queries against the MindsDB server via MCP protocol"
23
+
24
+ def __init__(self, session: ClientSession):
25
+ super().__init__()
26
+ self.session = session
27
+
28
+ async def _arun(self, query: str) -> str:
29
+ """Execute a query via MCP asynchronously"""
30
+ try:
31
+ logger.info(f"Executing MCP query: {query}")
32
+ # Find the appropriate tool for SQL queries
33
+ tools_response = await self.session.list_tools()
34
+ query_tool = None
35
+
36
+ for tool in tools_response.tools:
37
+ if tool.name == "query":
38
+ query_tool = tool
39
+ break
40
+
41
+ if not query_tool:
42
+ return "Error: No 'query' tool found in the MCP server"
43
+
44
+ # Call the query tool
45
+ result = await self.session.call_tool("query", {"query": query})
46
+
47
+ # Process the results
48
+ if isinstance(result.content, dict) and "data" in result.content and "column_names" in result.content:
49
+ # Create a DataFrame from the results
50
+ df = pd.DataFrame(result.content["data"], columns=result.content["column_names"])
51
+ return df.to_string()
52
+
53
+ # Return raw result for other types
54
+ return f"Query executed successfully: {json.dumps(result.content)}"
55
+
56
+ except Exception as e:
57
+ logger.error(f"Error executing MCP query: {str(e)}")
58
+ return f"Error executing query: {str(e)}"
59
+
60
+ def _run(self, query: str) -> str:
61
+ """Synchronous wrapper for async query function"""
62
+ loop = asyncio.get_event_loop()
63
+ return loop.run_until_complete(self._arun(query))
64
+
65
+
66
+ class MCPLangchainAgent(LangchainAgent):
67
+ """Extension of LangchainAgent that delegates to MCP server"""
68
+
69
+ def __init__(self, agent: db.Agents, model: dict = None, mcp_host: str = "127.0.0.1", mcp_port: int = 47337):
70
+ super().__init__(agent, model)
71
+ self.mcp_host = mcp_host
72
+ self.mcp_port = mcp_port
73
+ self.exit_stack = AsyncExitStack()
74
+ self.session = None
75
+ self.stdio = None
76
+ self.write = None
77
+
78
+ async def connect_to_mcp(self):
79
+ """Connect to the MCP server using stdio transport"""
80
+ if self.session is None:
81
+ logger.info(f"Connecting to MCP server at {self.mcp_host}:{self.mcp_port}")
82
+ try:
83
+ # For connecting to an already running MCP server
84
+ # Set up server parameters to connect to existing process
85
+ server_params = StdioServerParameters(
86
+ command="python",
87
+ args=["-m", "mindsdb", "--api=mcp"],
88
+ env={"MCP_HOST": self.mcp_host, "MCP_PORT": str(self.mcp_port)}
89
+ )
90
+
91
+ logger.info(f"Connecting to MCP server at {self.mcp_host}:{self.mcp_port}")
92
+
93
+ # Connect to the server
94
+ stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
95
+ self.stdio, self.write = stdio_transport
96
+ self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
97
+
98
+ await self.session.initialize()
99
+
100
+ # Test the connection by listing tools
101
+ tools_response = await self.session.list_tools()
102
+ logger.info(f"Successfully connected to MCP server. Available tools: {[tool.name for tool in tools_response.tools]}")
103
+
104
+ except Exception as e:
105
+ logger.error(f"Failed to connect to MCP server: {str(e)}")
106
+ raise ConnectionError(f"Failed to connect to MCP server: {str(e)}")
107
+
108
+ def _langchain_tools_from_skills(self, llm):
109
+ """Override to add MCP query tool along with other tools"""
110
+ # Get tools from parent implementation
111
+ tools = super()._langchain_tools_from_skills(llm)
112
+
113
+ # Initialize MCP connection
114
+ try:
115
+ # Using the event loop directly instead of asyncio.run()
116
+ loop = asyncio.get_event_loop()
117
+ if self.session is None:
118
+ loop.run_until_complete(self.connect_to_mcp())
119
+
120
+ # Add MCP query tool if session is established
121
+ if self.session:
122
+ tools.append(MCPQueryTool(self.session))
123
+ logger.info("Added MCP query tool to agent tools")
124
+ except Exception as e:
125
+ logger.error(f"Failed to add MCP query tool: {str(e)}")
126
+
127
+ return tools
128
+
129
+ def get_completion(self, messages, stream: bool = False):
130
+ """Override to ensure MCP connection is established before getting completion"""
131
+ try:
132
+ # Ensure connection to MCP is established
133
+ if self.session is None:
134
+ # Using the event loop directly instead of asyncio.run()
135
+ loop = asyncio.get_event_loop()
136
+ loop.run_until_complete(self.connect_to_mcp())
137
+ except Exception as e:
138
+ logger.error(f"Failed to connect to MCP server: {str(e)}")
139
+
140
+ # Call parent implementation to get completion
141
+ response = super().get_completion(messages, stream)
142
+
143
+ # Ensure response is a string (not a DataFrame)
144
+ if hasattr(response, 'to_string'): # It's a DataFrame
145
+ return response.to_string()
146
+
147
+ return response
148
+
149
+ async def cleanup(self):
150
+ """Clean up resources"""
151
+ if self.exit_stack:
152
+ await self.exit_stack.aclose()
153
+ self.session = None
154
+ self.stdio = None
155
+ self.write = None
156
+
157
+
158
+ class LiteLLMAgentWrapper:
159
+ """Wrapper for MCPLangchainAgent that provides LiteLLM-compatible interface"""
160
+
161
+ def __init__(self, agent: MCPLangchainAgent):
162
+ self.agent = agent
163
+
164
+ async def acompletion(self, messages: List[Dict[str, str]], **kwargs) -> Dict[str, Any]:
165
+ """Async completion interface compatible with LiteLLM"""
166
+ # Convert messages to format expected by agent
167
+ formatted_messages = [
168
+ {
169
+ "question": msg["content"] if msg["role"] == "user" else "",
170
+ "answer": msg["content"] if msg["role"] == "assistant" else ""
171
+ }
172
+ for msg in messages
173
+ ]
174
+
175
+ # Get completion from agent
176
+ response = self.agent.get_completion(formatted_messages)
177
+
178
+ # Ensure response is a string
179
+ if not isinstance(response, str):
180
+ if hasattr(response, 'to_string'): # It's a DataFrame
181
+ response = response.to_string()
182
+ else:
183
+ response = str(response)
184
+
185
+ # Format response in LiteLLM expected format
186
+ return {
187
+ "choices": [
188
+ {
189
+ "message": {
190
+ "role": "assistant",
191
+ "content": response
192
+ }
193
+ }
194
+ ],
195
+ "model": self.agent.args["model_name"],
196
+ "object": "chat.completion"
197
+ }
198
+
199
+ async def acompletion_stream(self, messages: List[Dict[str, str]], **kwargs) -> Iterator[Dict[str, Any]]:
200
+ """Async streaming completion interface compatible with LiteLLM"""
201
+ # Convert messages to format expected by agent
202
+ formatted_messages = [
203
+ {
204
+ "question": msg["content"] if msg["role"] == "user" else "",
205
+ "answer": msg["content"] if msg["role"] == "assistant" else ""
206
+ }
207
+ for msg in messages
208
+ ]
209
+
210
+ # Stream completion from agent
211
+ model_name = kwargs.get("model", self.agent.args.get("model_name", "mcp-agent"))
212
+ try:
213
+ # Handle synchronous generator from _get_completion_stream
214
+ for chunk in self.agent._get_completion_stream(formatted_messages):
215
+ content = chunk.get("output", "")
216
+ if content and isinstance(content, str):
217
+ yield {
218
+ "choices": [{"delta": {"role": "assistant", "content": content}}],
219
+ "model": model_name,
220
+ "object": "chat.completion.chunk"
221
+ }
222
+ # Allow async context switch
223
+ await asyncio.sleep(0)
224
+ except Exception as e:
225
+ logger.error(f"Streaming error: {str(e)}")
226
+ raise
227
+
228
+ async def cleanup(self):
229
+ """Clean up resources"""
230
+ await self.agent.cleanup()
231
+
232
+
233
+ def create_mcp_agent(agent_name: str, project_name: str, mcp_host: str = "127.0.0.1", mcp_port: int = 47337) -> LiteLLMAgentWrapper:
234
+ """Create an MCP agent and wrap it for LiteLLM compatibility"""
235
+ from mindsdb.interfaces.agents.agents_controller import AgentsController
236
+ from mindsdb.interfaces.storage import db
237
+
238
+ # Initialize database
239
+ db.init()
240
+
241
+ # Get the agent from database
242
+ agent_controller = AgentsController()
243
+ agent_db = agent_controller.get_agent(agent_name, project_name)
244
+
245
+ if agent_db is None:
246
+ raise ValueError(f"Agent {agent_name} not found in project {project_name}")
247
+
248
+ # Create MCP agent
249
+ mcp_agent = MCPLangchainAgent(agent_db, mcp_host=mcp_host, mcp_port=mcp_port)
250
+
251
+ # Wrap for LiteLLM compatibility
252
+ return LiteLLMAgentWrapper(mcp_agent)
@@ -0,0 +1,205 @@
1
+ import sys
2
+ import argparse
3
+ import asyncio
4
+ from typing import List, Dict
5
+ from contextlib import AsyncExitStack
6
+
7
+ from mcp import ClientSession, StdioServerParameters
8
+ from mcp.client.stdio import stdio_client
9
+
10
+ from mindsdb.utilities import log
11
+ from mindsdb.interfaces.agents.mcp_client_agent import create_mcp_agent
12
+
13
+ logger = log.getLogger(__name__)
14
+
15
+
16
+ async def run_conversation(agent_wrapper, messages: List[Dict[str, str]], stream: bool = False):
17
+ """Run a conversation with the agent and print responses"""
18
+ try:
19
+ if stream:
20
+ logger.info("Streaming response:")
21
+ async for chunk in agent_wrapper.acompletion_stream(messages):
22
+ content = chunk["choices"][0]["delta"].get("content", "")
23
+ if content:
24
+ # We still need to print content for streaming display
25
+ # but we'll log it as debug as well
26
+ logger.debug(f"Stream content: {content}")
27
+ sys.stdout.write(content)
28
+ sys.stdout.flush()
29
+ logger.debug("End of stream")
30
+ sys.stdout.write("\n\n")
31
+ sys.stdout.flush()
32
+ else:
33
+ logger.info("Getting response...")
34
+ response = await agent_wrapper.acompletion(messages)
35
+ content = response["choices"][0]["message"]["content"]
36
+ logger.info(f"Response: {content}")
37
+ # We still need to display the response to the user
38
+ sys.stdout.write(f"{content}\n")
39
+ sys.stdout.flush()
40
+ except Exception as e:
41
+ logger.error(f"Error during agent conversation: {str(e)}")
42
+
43
+
44
+ async def execute_direct_query(query):
45
+ """Execute a direct SQL query using MCP"""
46
+ logger.info(f"Executing direct SQL query: {query}")
47
+
48
+ # Set up MCP client to connect to the running server
49
+ async with AsyncExitStack() as stack:
50
+ # Connect to MCP server
51
+ server_params = StdioServerParameters(
52
+ command="python",
53
+ args=["-m", "mindsdb", "--api=mcp"],
54
+ env=None
55
+ )
56
+
57
+ try:
58
+ stdio_transport = await stack.enter_async_context(stdio_client(server_params))
59
+ stdio, write = stdio_transport
60
+ session = await stack.enter_async_context(ClientSession(stdio, write))
61
+
62
+ await session.initialize()
63
+
64
+ # List available tools
65
+ tools_response = await session.list_tools()
66
+ tool_names = [tool.name for tool in tools_response.tools]
67
+ logger.info(f"Available tools: {tool_names}")
68
+
69
+ # Find query tool
70
+ query_tool = None
71
+ for tool in tools_response.tools:
72
+ if tool.name == "query":
73
+ query_tool = tool
74
+ break
75
+
76
+ if not query_tool:
77
+ logger.error("No 'query' tool found in MCP server")
78
+ return
79
+
80
+ # Execute query
81
+ result = await session.call_tool("query", {"query": query})
82
+ logger.info(f"Query result: {result.content}")
83
+ except Exception as e:
84
+ logger.error(f"Error executing query: {str(e)}")
85
+ logger.info("Make sure the MindsDB server is running with MCP enabled: python -m mindsdb --api=mysql,mcp,http")
86
+
87
+
88
+ async def main():
89
+ parser = argparse.ArgumentParser(description="Run an agent as an MCP client")
90
+ parser.add_argument("--agent", type=str, help="Name of the agent to use")
91
+ parser.add_argument("--project", type=str, default="mindsdb", help="Project containing the agent")
92
+ parser.add_argument("--host", type=str, default="127.0.0.1", help="MCP server host")
93
+ parser.add_argument("--port", type=int, default=47337, help="MCP server port")
94
+ parser.add_argument("--query", type=str, help="Query to send to the agent")
95
+ parser.add_argument("--stream", action="store_true", help="Stream the response")
96
+ parser.add_argument("--execute-direct", type=str, help="Execute a direct SQL query via MCP (for testing)")
97
+
98
+ args = parser.parse_args()
99
+
100
+ try:
101
+ # Initialize database connection
102
+ from mindsdb.interfaces.storage import db
103
+ db.init()
104
+
105
+ # Direct SQL execution mode (for testing MCP connection)
106
+ if args.execute_direct:
107
+ await execute_direct_query(args.execute_direct)
108
+ return 0
109
+
110
+ # Make sure agent name is provided
111
+ if not args.agent:
112
+ parser.error("the --agent argument is required unless --execute-direct is used")
113
+
114
+ # Create the agent
115
+ logger.info(f"Creating MCP client agent for '{args.agent}' in project '{args.project}'")
116
+ logger.info(f"Connecting to MCP server at {args.host}:{args.port}")
117
+ logger.info("Make sure MindsDB server is running with MCP enabled: python -m mindsdb --api=mysql,mcp,http")
118
+
119
+ agent_wrapper = create_mcp_agent(
120
+ agent_name=args.agent,
121
+ project_name=args.project,
122
+ mcp_host=args.host,
123
+ mcp_port=args.port
124
+ )
125
+
126
+ # Run an example query if provided
127
+ if args.query:
128
+ messages = [{"role": "user", "content": args.query}]
129
+ await run_conversation(agent_wrapper, messages, args.stream)
130
+ else:
131
+ # Interactive mode
132
+ logger.info("Entering interactive mode. Type 'exit' to quit.")
133
+ logger.info("Available commands: exit/quit, clear, sql:")
134
+
135
+ # We still need to show these instructions to the user
136
+ sys.stdout.write("\nEntering interactive mode. Type 'exit' to quit.\n")
137
+ sys.stdout.write("\nAvailable commands:\n")
138
+ sys.stdout.write(" exit, quit - Exit the program\n")
139
+ sys.stdout.write(" clear - Clear conversation history\n")
140
+ sys.stdout.write(" sql: <query> - Execute a direct SQL query via MCP\n")
141
+ sys.stdout.flush()
142
+
143
+ messages = []
144
+
145
+ while True:
146
+ # We need to keep input for user interaction
147
+ user_input = input("\nYou: ")
148
+
149
+ # Check for special commands
150
+ if user_input.lower() in ["exit", "quit"]:
151
+ logger.info("Exiting interactive mode")
152
+ break
153
+ elif user_input.lower() == "clear":
154
+ messages = []
155
+ logger.info("Conversation history cleared")
156
+ sys.stdout.write("Conversation history cleared\n")
157
+ sys.stdout.flush()
158
+ continue
159
+ elif user_input.lower().startswith("sql:"):
160
+ # Direct SQL execution using the agent's session
161
+ sql_query = user_input[4:].strip()
162
+ logger.info(f"Executing SQL: {sql_query}")
163
+ try:
164
+ # Use the tool from the agent
165
+ if hasattr(agent_wrapper.agent, "session") and agent_wrapper.agent.session:
166
+ result = await agent_wrapper.agent.session.call_tool("query", {"query": sql_query})
167
+ logger.info(f"SQL result: {result.content}")
168
+ # We need to show the result to the user
169
+ sys.stdout.write(f"Result: {result.content}\n")
170
+ sys.stdout.flush()
171
+ else:
172
+ logger.error("No active MCP session")
173
+ sys.stdout.write("Error: No active MCP session\n")
174
+ sys.stdout.flush()
175
+ except Exception as e:
176
+ logger.error(f"SQL Error: {str(e)}")
177
+ sys.stdout.write(f"SQL Error: {str(e)}\n")
178
+ sys.stdout.flush()
179
+ continue
180
+
181
+ messages.append({"role": "user", "content": user_input})
182
+ await run_conversation(agent_wrapper, messages, args.stream)
183
+
184
+ # Add assistant's response to the conversation history
185
+ if not args.stream:
186
+ response = await agent_wrapper.acompletion(messages)
187
+ messages.append({
188
+ "role": "assistant",
189
+ "content": response["choices"][0]["message"]["content"]
190
+ })
191
+
192
+ # Clean up resources
193
+ logger.info("Cleaning up resources")
194
+ await agent_wrapper.cleanup()
195
+
196
+ except Exception as e:
197
+ logger.error(f"Error running MCP agent: {str(e)}")
198
+ logger.info("Make sure the MindsDB server is running with MCP enabled: python -m mindsdb --api=mysql,mcp,http")
199
+ return 1
200
+
201
+ return 0
202
+
203
+
204
+ if __name__ == "__main__":
205
+ sys.exit(asyncio.run(main()))
@@ -65,6 +65,11 @@ def get_embedding_model_from_params(embedding_model_params: dict):
65
65
  if provider == 'azure_openai':
66
66
  # Azure OpenAI expects the api_key to be passed as 'openai_api_key'.
67
67
  params_copy['openai_api_key'] = api_key
68
+ params_copy['azure_endpoint'] = params_copy.pop('base_url')
69
+ if 'chunk_size' not in params_copy:
70
+ params_copy['chunk_size'] = 2048
71
+ if 'api_version' in params_copy:
72
+ params_copy['openai_api_version'] = params_copy['api_version']
68
73
  else:
69
74
  params_copy[f"{provider}_api_key"] = api_key
70
75
  params_copy.pop('api_key', None)
@@ -78,11 +83,10 @@ def get_reranking_model_from_params(reranking_model_params: dict):
78
83
  Create reranking model from parameters.
79
84
  """
80
85
  params_copy = copy.deepcopy(reranking_model_params)
81
- provider = params_copy.pop('provider', "openai").lower()
82
- if provider != 'openai':
83
- raise ValueError("Only OpenAI provider is supported for the reranking model.")
84
- params_copy[f"{provider}_api_key"] = get_api_key(provider, params_copy, strict=False) or params_copy.get('api_key')
85
- params_copy.pop('api_key', None)
86
+ provider = params_copy.get('provider', "openai").lower()
87
+
88
+ if "api_key" not in params_copy:
89
+ params_copy["api_key"] = get_api_key(provider, params_copy, strict=False)
86
90
  params_copy['model'] = params_copy.pop('model_name', None)
87
91
 
88
92
  return LLMReranker(**params_copy)
@@ -424,11 +428,12 @@ class KnowledgeBaseTable:
424
428
  db_handler = self.get_vector_db()
425
429
  db_handler.delete(self._kb.vector_database_table)
426
430
 
427
- def insert(self, df: pd.DataFrame):
431
+ def insert(self, df: pd.DataFrame, params: dict = None):
428
432
  """Insert dataframe to KB table.
429
433
 
430
434
  Args:
431
435
  df: DataFrame to insert
436
+ params: User parameters of insert
432
437
  """
433
438
  if df.empty:
434
439
  return
@@ -497,7 +502,12 @@ class KnowledgeBaseTable:
497
502
  df_emb = self._df_to_embeddings(df)
498
503
  df = pd.concat([df, df_emb], axis=1)
499
504
  db_handler = self.get_vector_db()
500
- db_handler.do_upsert(self._kb.vector_database_table, df)
505
+
506
+ if params is not None and params.get('kb_no_upsert', False):
507
+ # speed up inserting by disable checking existing records
508
+ db_handler.insert(self._kb.vector_database_table, df)
509
+ else:
510
+ db_handler.do_upsert(self._kb.vector_database_table, df)
501
511
 
502
512
  def _adapt_column_names(self, df: pd.DataFrame) -> pd.DataFrame:
503
513
  '''
@@ -243,7 +243,13 @@ class SkillToolController:
243
243
  kb_table = session_controller.kb_controller.get_table(knowledge_base_name, skill.project_id)
244
244
 
245
245
  res = kb_table.select_query(query)
246
- return '\n'.join(res.content)
246
+ # Handle both chunk_content and content column names
247
+ if hasattr(res, 'chunk_content'):
248
+ return '\n'.join(res.chunk_content)
249
+ elif hasattr(res, 'content'):
250
+ return '\n'.join(res.content)
251
+ else:
252
+ return "No content or chunk_content found in knowledge base response"
247
253
 
248
254
  return _answer_question
249
255
 
@@ -12,6 +12,7 @@ from mindsdb_sql_parser.ast import Select, Show, Describe, Explain, Identifier
12
12
  from mindsdb.utilities import log
13
13
  from mindsdb.utilities.context import context as ctx
14
14
  from mindsdb.integrations.utilities.query_traversal import query_traversal
15
+ from mindsdb.integrations.libs.response import INF_SCHEMA_COLUMNS_NAMES
15
16
 
16
17
  logger = log.getLogger(__name__)
17
18
 
@@ -275,9 +276,13 @@ class SQLAgent:
275
276
  dn = self._command_executor.session.datahub.get(integration)
276
277
 
277
278
  fields, dtypes = [], []
278
- for column in dn.get_table_columns(table_name, schema_name):
279
- fields.append(column['name'])
280
- dtypes.append(column.get('type', ''))
279
+ for df in dn.get_table_columns_df(table_name, schema_name):
280
+ df_records = df.to_dict(orient='records')
281
+ fields.append(df_records[INF_SCHEMA_COLUMNS_NAMES.COLUMN_NAME])
282
+ if df_records[INF_SCHEMA_COLUMNS_NAMES.MYSQL_DATA_TYPE] is not None:
283
+ dtypes.append(df_records[INF_SCHEMA_COLUMNS_NAMES.MYSQL_DATA_TYPE].value)
284
+ else:
285
+ dtypes.append(df_records[INF_SCHEMA_COLUMNS_NAMES.DATA_TYPE])
281
286
 
282
287
  info = f'Table named `{table_str}`:\n'
283
288
  info += f"\nSample with first {self._sample_rows_in_table_info} rows from table {table_str} in CSV format (dialect is 'excel'):\n"
@@ -209,6 +209,10 @@ class Config:
209
209
  "restart_on_failure": True,
210
210
  "max_restart_count": 1,
211
211
  "max_restart_interval_seconds": 60
212
+ },
213
+ "litellm": {
214
+ "host": "0.0.0.0", # API server binds to all interfaces by default
215
+ "port": "8000"
212
216
  }
213
217
  },
214
218
  "cache": {
@@ -378,7 +382,8 @@ class Config:
378
382
  ):
379
383
  self._cmd_args = argparse.Namespace(
380
384
  api=None, config=None, install_handlers=None, verbose=False,
381
- no_studio=False, version=False, ml_task_queue_consumer=None
385
+ no_studio=False, version=False, ml_task_queue_consumer=None,
386
+ agent=None, project=None
382
387
  )
383
388
  return
384
389
 
@@ -390,6 +395,8 @@ class Config:
390
395
  parser.add_argument('--no_studio', action='store_true')
391
396
  parser.add_argument('-v', '--version', action='store_true')
392
397
  parser.add_argument('--ml_task_queue_consumer', action='store_true', default=None)
398
+ parser.add_argument('--agent', type=str, default=None, help='Name of the agent to use with litellm APIs')
399
+ parser.add_argument('--project', type=str, default=None, help='Project containing the agent (default: mindsdb)')
393
400
  self._cmd_args = parser.parse_args()
394
401
 
395
402
  def fetch_auto_config(self) -> bool:
@@ -38,3 +38,10 @@ def start_mcp(*args, **kwargs):
38
38
  from mindsdb.api.mcp.start import start
39
39
 
40
40
  start(*args, **kwargs)
41
+
42
+
43
+ def start_litellm(*args, **kwargs):
44
+ """Start the LiteLLM server"""
45
+ from mindsdb.api.litellm.start import start
46
+
47
+ start(*args, **kwargs)