UNPKG

@codai/cbd

Version:

Codai Better Database - High-Performance Vector Memory System with HPKV-inspired architecture and MCP server

340 lines 13.9 kB
/** * CBD Engine Service - Standalone Vector Database Service * Provides HTTP API for MemorAI MCP Server integration * Port: 4180 (as expected by MCP adapters) */ import express from 'express'; import cors from 'cors'; import helmet from 'helmet'; import compression from 'compression'; import rateLimit from 'express-rate-limit'; import { CBDMemoryEngine } from './index.js'; import { validateCBDConfig } from './index.js'; class CBDEngineService { port; host; cbdConfig; engine; app; startupTime; requestCount; isHealthy; constructor(options = {}) { this.port = options.port || parseInt(process.env.CBD_PORT || '4180'); this.host = options.host || process.env.CBD_HOST || 'localhost'; // Initialize CBD Memory Engine with production config using Azure AI this.cbdConfig = { storage: { type: 'cbd-native', dataPath: options.dataPath || process.env.CBD_DATA_PATH || './cbd-data' }, embedding: { model: 'openai', apiKey: process.env.AZURE_AI_FOUNDRY_KEY || process.env.OPENAI_API_KEY, modelName: process.env.AZURE_OPENAI_EMBEDDING_LARGE_DEPLOYMENT || 'text-embedding-3-large', dimensions: parseInt(process.env.EMBEDDING_DIMENSIONS || '1536'), baseURL: process.env.AZURE_AI_FOUNDRY_ENDPOINT }, vector: { indexType: 'faiss', dimensions: parseInt(process.env.EMBEDDING_DIMENSIONS || '1536'), similarityMetric: 'cosine' }, cache: { enabled: true, maxSize: 1000, ttl: 3600000 // 1 hour } }; // Validate configuration const validation = validateCBDConfig(this.cbdConfig); if (!validation.valid) { throw new Error(`Invalid CBD configuration: ${validation.errors.join(', ')}`); } this.engine = new CBDMemoryEngine(this.cbdConfig); this.app = express(); this.setupMiddleware(); this.setupRoutes(); this.startupTime = Date.now(); this.requestCount = 0; this.isHealthy = false; } setupMiddleware() { // Security middleware this.app.use(helmet({ contentSecurityPolicy: false // Allow for development })); // CORS - allow all origins for development this.app.use(cors({ origin: true, credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'] })); // Compression this.app.use(compression()); // Rate limiting const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 1000, // Limit each IP to 1000 requests per windowMs message: 'Too many requests from this IP, please try again later.' }); this.app.use('/api/', limiter); // Body parsing this.app.use(express.json({ limit: '10mb' })); this.app.use(express.urlencoded({ extended: true, limit: '10mb' })); // Request logging and metrics this.app.use((req, res, next) => { this.requestCount++; const startTime = Date.now(); res.on('finish', () => { const duration = Date.now() - startTime; console.log(`${req.method} ${req.path} - ${res.statusCode} (${duration}ms)`); }); next(); }); } setupRoutes() { // Health check endpoint this.app.get('/health', (_req, res) => { const uptime = Date.now() - this.startupTime; res.json({ status: this.isHealthy ? 'healthy' : 'unhealthy', timestamp: new Date().toISOString(), uptime: uptime, service: 'CBD Engine Service', version: '1.0.0', requests: this.requestCount }); }); // Service info endpoint this.app.get('/', (_req, res) => { res.json({ service: 'CBD Engine Service', description: 'Standalone Vector Database Service for MemorAI MCP integration', version: '1.0.0', endpoints: { health: '/health', statistics: '/api/admin/statistics', memories: '/api/data/memories', search: '/api/search/memories', vectorSearch: '/api/vector/search' }, status: this.isHealthy ? 'operational' : 'initializing' }); }); // Statistics endpoint this.app.get('/api/admin/statistics', async (_req, res) => { try { const stats = await this.engine.get_statistics(); const uptime = Date.now() - this.startupTime; res.json({ ...stats, service: { requests: this.requestCount, uptime: uptime, avgRequestsPerSecond: Math.round(this.requestCount / (uptime / 1000) * 100) / 100, status: this.isHealthy ? 'healthy' : 'unhealthy' } }); } catch (error) { res.status(500).json({ error: 'Failed to get statistics', message: error instanceof Error ? error.message : 'Unknown error occurred' }); } }); // Memory storage endpoints this.app.post('/api/data/memories', async (req, res) => { try { const { userRequest, assistantResponse, metadata } = req.body; if (!userRequest || !assistantResponse || !metadata) { res.status(400).json({ success: false, message: 'Missing required fields: userRequest, assistantResponse, metadata' }); return; } const result = await this.engine.store_memory(userRequest, assistantResponse, metadata); res.json({ success: true, data: { structuredKey: result } }); } catch (error) { res.status(500).json({ error: 'Failed to store memory', message: error instanceof Error ? error.message : 'Unknown error occurred' }); } }); // Memory retrieval endpoint this.app.get('/api/data/memories', async (req, res) => { try { const { filter, limit } = req.query; // Handle specific memory key lookup if (typeof filter === 'string' && filter.includes('structured_key')) { const keyMatch = filter.match(/structured_key = '([^']+)'/); if (keyMatch && keyMatch[1]) { const memory = await this.engine.get_memory(keyMatch[1]); res.json({ success: true, data: memory ? [memory] : [], count: memory ? 1 : 0 }); return; } } // Default: return recent memories using search const searchOptions = { limit: typeof limit === 'string' ? parseInt(limit) || 10 : 10, confidenceThreshold: 0.0 // Get all memories }; // Use search_memory for general queries const results = await this.engine.search_memory('', // Empty query to get all memories searchOptions.limit, searchOptions.confidenceThreshold); res.json({ success: true, data: results.memories.map(r => r.memory), count: results.memories.length, summary: results.summary }); } catch (error) { res.status(500).json({ error: 'Failed to retrieve memories', message: error instanceof Error ? error.message : 'Unknown error occurred' }); } }); // Memory deletion endpoint this.app.delete('/api/data/memories', async (req, res) => { try { const { filter } = req.query; if (typeof filter === 'string' && filter.includes('structured_key')) { const keyMatch = filter.match(/structured_key = '([^']+)'/); if (keyMatch && keyMatch[1]) { const result = await this.engine.delete_memory(keyMatch[1]); res.json({ success: true, deleted: result }); return; } } res.status(400).json({ success: false, message: 'Must specify structured_key filter for deletion' }); return; } catch (error) { res.status(500).json({ error: 'Failed to delete memory', message: error instanceof Error ? error.message : 'Unknown error occurred' }); } }); // Memory search endpoint this.app.post('/api/search/memories', async (req, res) => { try { const { query, limit, minImportance } = req.body; const searchLimit = limit || 10; const confidenceThreshold = minImportance || 0.0; const results = await this.engine.search_memory(query || '', searchLimit, confidenceThreshold); res.json({ success: true, data: results.memories, count: results.memories.length, summary: results.summary }); } catch (error) { res.status(500).json({ error: 'Failed to search memories', message: error instanceof Error ? error.message : 'Unknown error occurred' }); } }); // Vector search endpoint this.app.post('/api/vector/search', async (req, res) => { try { const { vector, limit, threshold } = req.body; const searchOptions = { topK: limit || 10, minScore: threshold || 0.0 }; const results = await this.engine.vector_search(vector, searchOptions); res.json({ success: true, data: results, count: results.length }); } catch (error) { res.status(500).json({ error: 'Failed to perform vector search', message: error instanceof Error ? error.message : 'Unknown error occurred' }); } }); // Error handling middleware const errorHandler = (error, _req, res, _next) => { console.error('Unhandled error:', error); res.status(500).json({ error: 'Internal server error', message: error.message }); }; this.app.use(errorHandler); // 404 handler this.app.use((_req, res) => { res.status(404).json({ error: 'Not found', message: 'The requested endpoint does not exist' }); }); } async start() { try { console.log('🚀 Starting CBD Engine Service...'); // Initialize the engine await this.engine.initialize(); // Mark as healthy this.isHealthy = true; console.log('✅ CBD Memory Engine initialized'); // Start the server return new Promise((resolve, reject) => { const server = this.app.listen(this.port, this.host, () => { console.log(''); console.log('🎯 CBD Engine Service Started'); console.log('================================'); console.log(`📊 Service: http://${this.host}:${this.port}`); console.log(`💾 Data Path: ${this.cbdConfig.storage.dataPath}`); console.log(`🔍 Vector Dimensions: ${this.cbdConfig.vector.dimensions}`); console.log(`🧠 Embedding Model: ${this.cbdConfig.embedding.modelName}`); console.log('================================'); console.log(''); resolve(); }); server.on('error', (error) => { console.error('❌ Failed to start server:', error.message); reject(error); }); }); } catch (error) { console.error('❌ Failed to start CBD Engine Service:', error); this.isHealthy = false; await this.engine.shutdown(); throw error; } } } export { CBDEngineService }; // Start server if this is the main module if (import.meta.url === `file://${process.argv[1]}`) { const service = new CBDEngineService(); service.start().catch(console.error); } //# sourceMappingURL=service.js.map