UNPKG

query-agent

Version:

An AI-powered database query agent that integrates with existing Express apps using Socket.IO and HTTP routes

604 lines (448 loc) 16.4 kB
# Query Agent An AI-powered database query agent using Google Generative AI (Gemini) with Socket.IO and Express integration. Features a dedicated `/query-agent` namespace for Socket.IO and HTTP routes that integrate seamlessly with your existing Express application. ## Features - 🤖 AI-powered natural language to SQL conversion - 🔒 Security-first approach (only SELECT queries allowed) - 🗄️ Support for multiple database types (MySQL, PostgreSQL, SQLite, etc.) - 📊 Intelligent query result formatting - 🔄 Iterative query refinement - 📝 HTML-formatted responses - 🔌 Socket.IO with dedicated namespace (`/query-agent`) - 🛤️ Express POST route (`/query-agent`) - 📡 Real-time status updates via WebSocket - 🔧 Flexible database integration (Sequelize, Prisma, custom) - 🎯 **No conflicts with existing Express apps** - integrates seamlessly ## Installation ```bash npm install query-agent ``` ## Module System Support This package supports both **ES Modules (ESM)** and **CommonJS (CJS)**, with full **TypeScript** support: ### ESM Usage (Recommended) ```javascript import queryAgent from "query-agent"; // or import { init, setDatabaseQueryFunction } from "query-agent"; ``` ### CommonJS Usage ```javascript const queryAgent = require("query-agent"); // or const { init, setDatabaseQueryFunction } = require("query-agent"); ``` ### TypeScript Usage ```typescript import queryAgent, { QueryAgentOptions } from "query-agent"; const options: QueryAgentOptions = { executeSQLQuery: async (query: string) => { // Your database query execution logic return await yourDatabase.execute(query); }, corsOptions: { origin: "*", methods: ["GET", "POST"], }, }; const { io, queryAgentNamespace } = queryAgent(serverOrIo, options); ``` The package automatically detects your module system and provides the appropriate format. ## Quick Start ### 1. Basic Integration with Existing Express App ```javascript import express from "express"; import { createServer } from "http"; import { init, setDatabaseQueryFunction } from "query-agent"; // Your existing Express app const app = express(); const server = createServer(app); app.use(express.json()); // Set up your database query function const myQueryFunction = async (query) => { // Your database logic here (Sequelize, Prisma, etc.) return await yourDatabase.query(query); }; setDatabaseQueryFunction(myQueryFunction); // Initialize Query Agent with your existing app and server const { io, queryAgentNamespace } = init(app, server); // Your existing routes continue to work app.get("/", (req, res) => { res.json({ message: "My app with Query Agent!" }); }); // Start your server server.listen(3000, () => { console.log("Server running on port 3000"); }); ``` ### 2. Environment Variables Create a `.env` file: ```env GOOGLE_API_KEY=your_google_api_key_here PORT=3000 ``` ## API Endpoints Query Agent adds the following endpoints to your existing Express app: ### HTTP POST Route - **URL**: `POST /query-agent` - **Purpose**: HTTP-based queries ### Socket.IO Namespace - **Namespace**: `/query-agent` - **Socket Path**: `/query-agent` (customizable) - **Purpose**: Real-time WebSocket communication - **Events**: `query`, `result`, `error`, `status` ### Health Check - **URL**: `GET /query-agent/health` - **Purpose**: Server health monitoring ## API Reference ### `init(app, server, options)` Initializes Query Agent with your existing Express app and HTTP server. **Parameters:** - `app` (Express): Your existing Express application - `server` (HTTP Server): Your HTTP server instance - `options` (object, optional): Configuration options **Options:** ```javascript { corsOptions: { origin: "*", methods: ["GET", "POST"] }, socketPath: "/query-agent", // Socket.IO path routePrefix: "/query-agent" // HTTP route prefix } ``` **Returns:** ```javascript { io, // Socket.IO server instance queryAgentNamespace; // Socket.IO namespace instance } ``` ### `setDatabaseQueryFunction(queryFunction)` Sets the database query function before initializing. **Parameters:** - `queryFunction` (async function): Function that executes SQL queries **Example:** ```javascript const myQueryFunction = async (query) => { const results = await sequelize.query(query); return results[0]; // Return the data part }; setDatabaseQueryFunction(myQueryFunction); ``` ### `runAIControlledWorkflow(userQuery, dbType, otherDetails)` Core AI workflow function (can be used directly). **Parameters:** - `userQuery` (string): Natural language query - `dbType` (string, optional): Database type (default: "MySQL") - `otherDetails` (string, optional): Additional context **Returns:** ```javascript { success: boolean, data: string, // HTML formatted response iterations: number, query: string, dbType: string, error?: string // Only if success is false } ``` ## Usage Examples ### 1. HTTP POST Request ```javascript const response = await fetch("http://localhost:3000/query-agent", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userQuery: "Show me all users from the users table", dbType: "MySQL", otherDetails: "Production database", }), }); const result = await response.text(); // HTML response ``` ### 2. Socket.IO Client (Node.js) ```javascript import { io } from "socket.io-client"; const socket = io("http://localhost:3000/query-agent"); socket.on("connect", () => { console.log("Connected to Query Agent"); // Send query socket.emit("query", { userQuery: "How many users are in the system?", dbType: "MySQL", }); }); // Listen for real-time updates socket.on("status", (data) => console.log("Status:", data.message)); socket.on("result", (data) => console.log("Result:", data.data)); socket.on("error", (data) => console.error("Error:", data.error)); ``` ### 3. TypeScript Integration ```typescript import { createServer } from "http"; import { Server } from "socket.io"; import queryAgent, { QueryAgentOptions } from "query-agent"; // Create HTTP server const server = createServer(); // Create Socket.IO server const io = new Server(server, { cors: { origin: "*", methods: ["GET", "POST"], }, }); // Define your SQL execution function with proper typing const executeSQLQuery = async (query: string): Promise<any> => { // Your database query execution logic here console.log("Executing SQL query:", query); // Example: return await yourDatabase.execute(query); return { result: "query executed successfully" }; }; // Initialize Query Agent with proper TypeScript types const queryAgentOptions: QueryAgentOptions = { executeSQLQuery, corsOptions: { origin: "*", methods: ["GET", "POST"], }, }; const { io: queryAgentIo, queryAgentNamespace } = queryAgent( io, queryAgentOptions ); // Start the server server.listen(3000, () => { console.log("Server with Query Agent running on port 3000"); }); // Example of using the returned objects with proper typing queryAgentNamespace.on("connection", (socket) => { console.log("Client connected to query agent namespace"); }); ``` ### 4. Browser Integration ```html <!DOCTYPE html> <html> <head> <script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script> </head> <body> <input type="text" id="queryInput" placeholder="Enter your query..." /> <button onclick="sendQuery()">Send Query</button> <div id="result"></div> <script> const socket = io("http://localhost:3000/query-agent"); socket.on("result", (data) => { document.getElementById("result").innerHTML = data.data; }); function sendQuery() { const query = document.getElementById("queryInput").value; socket.emit("query", { userQuery: query, dbType: "MySQL", }); } </script> </body> </html> ``` ## Database Integration Examples ### Sequelize Integration ```javascript import express from "express"; import { createServer } from "http"; import { Sequelize } from "sequelize"; import { init, setDatabaseQueryFunction } from "query-agent"; const app = express(); const server = createServer(app); app.use(express.json()); const sequelize = new Sequelize(/* your config */); const sequelizeQueryFunction = async (query) => { const [results] = await sequelize.query(query); return results; }; setDatabaseQueryFunction(sequelizeQueryFunction); init(app, server); server.listen(3000); ``` ### Prisma Integration ```javascript import express from "express"; import { createServer } from "http"; import { PrismaClient } from "@prisma/client"; import { init, setDatabaseQueryFunction } from "query-agent"; const app = express(); const server = createServer(app); app.use(express.json()); const prisma = new PrismaClient(); const prismaQueryFunction = async (query) => { return await prisma.$queryRawUnsafe(query); }; setDatabaseQueryFunction(prismaQueryFunction); init(app, server); server.listen(3000); ``` ### Custom Database Integration ```javascript import express from "express"; import { createServer } from "http"; import { init, setDatabaseQueryFunction } from "query-agent"; const app = express(); const server = createServer(app); app.use(express.json()); const customQueryFunction = async (query) => { // Your custom database logic return await yourCustomDB.execute(query); }; setDatabaseQueryFunction(customQueryFunction); init(app, server); server.listen(3000); ``` ## Configuration ### Environment Variables | Variable | Description | Default | | ---------------- | ---------------------------- | ------------- | | `GOOGLE_API_KEY` | Google Generative AI API Key | **Required** | | `PORT` | Server port | `3000` | | `NODE_ENV` | Environment mode | `development` | ### Socket.IO Events #### Client Server - `query`: Send a query to the AI agent #### Server Client - `result`: Query completed successfully - `error`: Query failed with error - `status`: Real-time status updates - `connect`: Connection established - `disconnect`: Connection closed ### Custom Configuration ```javascript const { io, queryAgentNamespace } = init(app, server, { corsOptions: { origin: ["http://localhost:3000", "https://yourdomain.com"], methods: ["GET", "POST"], credentials: true, }, socketPath: "/custom-socket-path", routePrefix: "/api/query-agent", }); ``` ## Development Scripts ```bash # Build for both ESM and CJS npm run build # Run tests npm test # Run example server npm run example # Run Sequelize example npm run example:sequelize # Run Prisma example npm run example:prisma # Test client examples npm run client:test ``` ## Build Process The package uses a dual build system to support both ESM and CJS: - **ESM Build**: `dist/index.js` - For modern Node.js applications - **CJS Build**: `dist/index.cjs` - For CommonJS applications The build process automatically: 1. Bundles dependencies using esbuild 2. Creates optimized builds for both module systems 3. Ensures compatibility across different Node.js versions ## Architecture ``` ┌─────────────────────────────────────────────────────────────────┐ Your Express App ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ Your Routes Your Middleware Your Features └─────────────────┘ └─────────────────┘ └─────────────────┘ ┌─────────────────────────────────────────────────────────────┐ Query Agent Integration ┌─────────────────┐ ┌─────────────────┐ Express POST Socket.IO /query-agent │ │ /query-agent └─────────────────┘ └─────────────────┘ ┌─────────────────┐ AI Workflow (Gemini API) └─────────────────┘ └─────────────────────────────────────────────────────────────┘ ┌─────────────────┐ Your Database Query Function └─────────────────┘ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────┐ Your Database (MySQL/Postgres /SQLite/etc.) └─────────────────┘ ``` ## Security Features - Only SELECT queries allowed - Dangerous SQL keywords blocked - Input validation and sanitization - Isolated namespace (`/query-agent`) - Environment-based configuration - Custom database query function control - No conflicts with existing routes ## Error Handling The package includes comprehensive error handling for both HTTP and Socket.IO: ```javascript // HTTP Error Response { success: false, error: "Error message", query: "original query", dbType: "MySQL" } // Socket.IO Error Event socket.on('error', (data) => { console.error('Query failed:', data.error); }); ``` ## Migration Guide If you're using an existing Express app, integration is simple: ### Before (your existing app): ```javascript import express from "express"; import { createServer } from "http"; const app = express(); const server = createServer(app); app.use(express.json()); app.get("/", (req, res) => res.json({ message: "Hello" })); server.listen(3000); ``` ### After (with Query Agent): ```javascript import express from "express"; import { createServer } from "http"; import { init, setDatabaseQueryFunction } from "query-agent"; const app = express(); const server = createServer(app); app.use(express.json()); // Your existing routes stay the same app.get("/", (req, res) => res.json({ message: "Hello" })); // Add Query Agent setDatabaseQueryFunction(yourQueryFunction); init(app, server); server.listen(3000); ``` ## License MIT License - see LICENSE file for details ## Support - 📧 Email: [your-email@example.com] - 🐛 Issues: [GitHub Issues](https://github.com/yourusername/query-agent/issues) - 📖 Documentation: [GitHub Wiki](https://github.com/yourusername/query-agent/wiki) ## Changelog ### Version 1.0.0 - Initial release with init method integration - Dedicated `/query-agent` namespace - Flexible database integration - Real-time status updates - Seamless Express app integration - Comprehensive examples and documentation