mirror of https://github.com/sysown/proxysql
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
6.5 KiB
228 lines
6.5 KiB
#ifdef PROXYSQLGENAI
|
|
|
|
|
|
#include "proxysql.h"
|
|
/**
|
|
* @file AI_Tool_Handler.cpp
|
|
* @brief Implementation of AI Tool Handler for MCP protocol
|
|
*
|
|
* Implements AI-powered tools through MCP protocol, primarily
|
|
* the ai_nl2sql_convert tool for natural language to SQL conversion.
|
|
*
|
|
* @see AI_Tool_Handler.h
|
|
*/
|
|
|
|
#include "AI_Tool_Handler.h"
|
|
#include "LLM_Bridge.h"
|
|
#include "Anomaly_Detector.h"
|
|
#include "AI_Features_Manager.h"
|
|
#include "proxysql_debug.h"
|
|
#include "cpp.h"
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
|
|
// JSON library
|
|
#include "../deps/json/json.hpp"
|
|
using json = nlohmann::json;
|
|
#define PROXYJSON
|
|
|
|
// ============================================================================
|
|
// Constructor/Destructor
|
|
// ============================================================================
|
|
|
|
/**
|
|
* @brief Constructor using existing AI components
|
|
*/
|
|
AI_Tool_Handler::AI_Tool_Handler(LLM_Bridge* llm, Anomaly_Detector* anomaly)
|
|
: llm_bridge(llm),
|
|
anomaly_detector(anomaly),
|
|
owns_components(false)
|
|
{
|
|
proxy_debug(PROXY_DEBUG_GENAI, 3, "AI_Tool_Handler created (wrapping existing components)\n");
|
|
}
|
|
|
|
/**
|
|
* @brief Constructor - creates own components
|
|
* Note: This implementation uses global instances
|
|
*/
|
|
AI_Tool_Handler::AI_Tool_Handler()
|
|
: llm_bridge(NULL),
|
|
anomaly_detector(NULL),
|
|
owns_components(false)
|
|
{
|
|
// Use global instances from AI_Features_Manager
|
|
if (GloAI) {
|
|
llm_bridge = GloAI->get_llm_bridge();
|
|
anomaly_detector = GloAI->get_anomaly_detector();
|
|
}
|
|
proxy_debug(PROXY_DEBUG_GENAI, 3, "AI_Tool_Handler created (using global instances)\n");
|
|
}
|
|
|
|
/**
|
|
* @brief Destructor
|
|
*/
|
|
AI_Tool_Handler::~AI_Tool_Handler() {
|
|
close();
|
|
proxy_debug(PROXY_DEBUG_GENAI, 3, "AI_Tool_Handler destroyed\n");
|
|
}
|
|
|
|
// ============================================================================
|
|
// Lifecycle
|
|
// ============================================================================
|
|
|
|
/**
|
|
* @brief Initialize the tool handler
|
|
*/
|
|
int AI_Tool_Handler::init() {
|
|
if (!llm_bridge) {
|
|
proxy_error("AI_Tool_Handler: LLM bridge not available\n");
|
|
return -1;
|
|
}
|
|
proxy_info("AI_Tool_Handler initialized\n");
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Close and cleanup
|
|
*/
|
|
void AI_Tool_Handler::close() {
|
|
if (owns_components) {
|
|
// Components would be cleaned up here
|
|
// For now, we use global instances managed by AI_Features_Manager
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Helper Functions
|
|
// ============================================================================
|
|
|
|
/**
|
|
* @brief Extract string parameter from JSON
|
|
*/
|
|
std::string AI_Tool_Handler::get_json_string(const json& j, const std::string& key,
|
|
const std::string& default_val) {
|
|
if (j.contains(key) && !j[key].is_null()) {
|
|
if (j[key].is_string()) {
|
|
return j[key].get<std::string>();
|
|
} else {
|
|
// Convert to string if not already
|
|
return j[key].dump();
|
|
}
|
|
}
|
|
return default_val;
|
|
}
|
|
|
|
/**
|
|
* @brief Extract int parameter from JSON
|
|
*/
|
|
int AI_Tool_Handler::get_json_int(const json& j, const std::string& key, int default_val) {
|
|
if (j.contains(key) && !j[key].is_null()) {
|
|
if (j[key].is_number()) {
|
|
return j[key].get<int>();
|
|
} else if (j[key].is_string()) {
|
|
try {
|
|
return std::stoi(j[key].get<std::string>());
|
|
} catch (const std::exception& e) {
|
|
proxy_error("AI_Tool_Handler: Failed to convert string to int for key '%s': %s\n",
|
|
key.c_str(), e.what());
|
|
return default_val;
|
|
}
|
|
}
|
|
}
|
|
return default_val;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Tool List
|
|
// ============================================================================
|
|
|
|
/**
|
|
* @brief Get list of available AI tools
|
|
*/
|
|
json AI_Tool_Handler::get_tool_list() {
|
|
json tools = json::array();
|
|
|
|
// NL2SQL tool
|
|
json nl2sql_params = json::object();
|
|
nl2sql_params["type"] = "object";
|
|
nl2sql_params["properties"] = json::object();
|
|
nl2sql_params["properties"]["natural_language"] = {
|
|
{"type", "string"},
|
|
{"description", "Natural language query to convert to SQL"}
|
|
};
|
|
nl2sql_params["properties"]["schema"] = {
|
|
{"type", "string"},
|
|
{"description", "Database/schema name for context"}
|
|
};
|
|
nl2sql_params["properties"]["context_tables"] = {
|
|
{"type", "string"},
|
|
{"description", "Comma-separated list of relevant tables (optional)"}
|
|
};
|
|
nl2sql_params["properties"]["max_latency_ms"] = {
|
|
{"type", "integer"},
|
|
{"description", "Maximum acceptable latency in milliseconds (optional)"}
|
|
};
|
|
nl2sql_params["properties"]["allow_cache"] = {
|
|
{"type", "boolean"},
|
|
{"description", "Whether to check semantic cache (default: true)"}
|
|
};
|
|
nl2sql_params["required"] = json::array({"natural_language"});
|
|
|
|
tools.push_back({
|
|
{"name", "ai_nl2sql_convert"},
|
|
{"description", "Convert natural language query to SQL using LLM"},
|
|
{"inputSchema", nl2sql_params}
|
|
});
|
|
|
|
json result;
|
|
result["tools"] = tools;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Get description of a specific tool
|
|
*/
|
|
json AI_Tool_Handler::get_tool_description(const std::string& tool_name) {
|
|
json tools_list = get_tool_list();
|
|
for (const auto& tool : tools_list["tools"]) {
|
|
if (tool["name"] == tool_name) {
|
|
return tool;
|
|
}
|
|
}
|
|
return create_error_response("Tool not found: " + tool_name);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Tool Execution
|
|
// ============================================================================
|
|
|
|
/**
|
|
* @brief Execute an AI tool
|
|
*/
|
|
json AI_Tool_Handler::execute_tool(const std::string& tool_name, const json& arguments) {
|
|
proxy_debug(PROXY_DEBUG_GENAI, 3, "AI_Tool_Handler: execute_tool(%s)\n", tool_name.c_str());
|
|
|
|
try {
|
|
// LLM processing tool (generic, replaces NL2SQL)
|
|
if (tool_name == "ai_nl2sql_convert") {
|
|
// NOTE: The ai_nl2sql_convert tool is deprecated.
|
|
// NL2SQL functionality has been replaced with a generic LLM bridge.
|
|
// Future NL2SQL will be implemented as a Web UI using external agents (Claude Code + MCP server).
|
|
return create_error_response("The ai_nl2sql_convert tool is deprecated. "
|
|
"Use the generic LLM: queries via MySQL protocol instead.");
|
|
}
|
|
|
|
// Unknown tool
|
|
return create_error_response("Unknown tool: " + tool_name);
|
|
|
|
} catch (const std::exception& e) {
|
|
proxy_error("AI_Tool_Handler: Exception in execute_tool: %s\n", e.what());
|
|
return create_error_response(std::string("Exception: ") + e.what());
|
|
} catch (...) {
|
|
proxy_error("AI_Tool_Handler: Unknown exception in execute_tool\n");
|
|
return create_error_response("Unknown exception");
|
|
}
|
|
}
|
|
|
|
#endif /* PROXYSQLGENAI */
|