@codai/cbd
Version:
Codai Better Database - High-Performance Vector Memory System with HPKV-inspired architecture and MCP server
349 lines (347 loc) • 12.9 kB
JavaScript
/**
* CBD Engine Service - Simple Implementation for MCP Integration
* HTTP service that wraps the CBD Memory Engine
*/
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 './memory/MemoryEngine.js';
export class CBDEngineService {
constructor(options = {}) {
this.port = options.port || process.env.CBD_PORT || 4180;
this.host = options.host || process.env.CBD_HOST || 'localhost';
this.dataPath = options.dataPath || './cbd-data';
// Create proper CBD config
const cbdConfig = {
storage: {
dataPath: this.dataPath
},
vector: {
dimensions: 1536
},
embedding: {
model: 'local',
modelName: 'sentence-transformers/all-MiniLM-L6-v2'
}
};
this.engine = new CBDMemoryEngine(cbdConfig);
this.app = express();
this.server = null;
this.isHealthy = false;
this.startupTime = Date.now();
this.requestCount = 0;
this.setupMiddleware();
this.setupRoutes();
}
setupMiddleware() {
// Security
this.app.use(helmet({
contentSecurityPolicy: false
}));
// CORS
this.app.use(cors({
origin: ['http://localhost:3000', 'http://localhost:4180', 'http://localhost:4200'],
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: 100 // requests per window
});
this.app.use('/api/', limiter);
// Body parsing
this.app.use(express.json({ limit: '10mb' }));
this.app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// Request counting
this.app.use((req, res, next) => {
this.requestCount++;
next();
});
}
setupRoutes() {
// Health check
this.app.get('/health', (req, res) => {
const uptime = Date.now() - this.startupTime;
res.json({
status: this.isHealthy ? 'healthy' : 'unhealthy',
uptime: Math.floor(uptime / 1000),
service: 'cbd-engine',
version: '1.0.0',
requests: this.requestCount
});
});
// Root endpoint
this.app.get('/', (req, res) => {
res.json({
service: 'CBD Engine Service',
version: '1.0.0',
description: 'Codai Better Database - High-Performance Vector Memory System',
endpoints: [
'GET /health - Health check',
'POST /api/data/memories - Store memory',
'GET /api/data/memories - Get memories',
'POST /api/search/memories - Search memories',
'DELETE /api/data/memories - Delete memory'
],
status: this.isHealthy ? 'operational' : 'initializing'
});
});
// Store memory
this.app.post('/api/data/memories', async (req, res) => {
try {
const { agentId, content, metadata } = req.body;
if (!agentId || !content) {
return res.status(400).json({
error: 'Missing required fields: agentId, content'
});
}
const result = await this.engine.storeMemory({
agentId,
content,
metadata: metadata || {}
});
res.json({
success: true,
data: result
});
}
catch (error) {
res.status(500).json({
error: 'Failed to store memory',
message: error.message
});
}
});
// Get memories
this.app.get('/api/data/memories', async (req, res) => {
try {
const { agentId, project, session, limit } = req.query;
if (req.query.key) {
// Get specific memory by key
const memory = await this.engine.getMemoryByKey(req.query.key);
return res.json({
success: true,
data: memory ? [memory] : []
});
}
if (agentId) {
// Get recent memories for agent
const memories = await this.engine.getRecentMemories(agentId, {
project,
session,
limit: parseInt(limit) || 5
});
return res.json({
success: true,
data: memories
});
}
res.status(400).json({
error: 'Missing required parameter: agentId or key'
});
}
catch (error) {
res.status(500).json({
error: 'Failed to get memories',
message: error.message
});
}
});
// Search memories
this.app.post('/api/search/memories', async (req, res) => {
try {
const { query, limit, agentId, project, session, minImportance } = req.body;
const searchOptions = {
limit: parseInt(limit) || 10,
agentId,
project,
session,
minImportance: parseFloat(minImportance) || 0.0
};
const results = await this.engine.searchMemories(query, searchOptions);
res.json({
success: true,
data: results
});
}
catch (error) {
res.status(500).json({
error: 'Failed to search memories',
message: error.message
});
}
});
// Delete memory
this.app.delete('/api/data/memories', async (req, res) => {
try {
const { key } = req.query;
if (!key) {
return res.status(400).json({
error: 'Missing required parameter: key'
});
}
const result = await this.engine.deleteMemory(key);
res.json({
success: true,
data: result
});
}
catch (error) {
res.status(500).json({
error: 'Failed to delete memory',
message: error.message
});
}
});
// Create/initialize database
this.app.post('/api/admin/database', async (req, res) => {
try {
const { name, config } = req.body;
// For now, just acknowledge the database creation
// In a real implementation, this would create database instances
console.log(`📊 Database initialization request: ${name}`, config);
res.json({
success: true,
data: {
name: name || 'memorai',
status: 'initialized',
config: config || {},
message: 'Database initialized successfully'
}
});
}
catch (error) {
res.status(500).json({
error: 'Failed to initialize database',
message: error.message
});
}
});
// Create schema
this.app.post('/api/schema/create', async (req, res) => {
try {
console.log('📋 Schema creation request received');
// For now, just acknowledge the schema creation
// In a real implementation, this would create tables and indexes
res.json({
success: true,
data: {
message: 'Schema created successfully',
tables: ['memories', 'memory_embeddings', 'semantic_search_cache', 'database_info']
}
});
}
catch (error) {
res.status(500).json({
error: 'Failed to create schema',
message: error.message
});
}
});
// Database info endpoint
this.app.post('/api/data/memories/database_info', async (req, res) => {
try {
const { records } = req.body;
console.log('📊 Database info initialization:', records);
res.json({
success: true,
data: {
message: 'Database info stored successfully',
records: records || []
}
});
}
catch (error) {
res.status(500).json({
error: 'Failed to store database info',
message: error.message
});
}
});
// Statistics
this.app.get('/api/admin/statistics', async (req, res) => {
try {
const stats = await this.engine.getStatistics();
const uptime = Date.now() - this.startupTime;
res.json({
success: true,
data: {
...stats,
uptime: Math.floor(uptime / 1000),
requests: this.requestCount,
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.message
});
}
});
// Error handling
this.app.use((error, req, res, next) => {
console.error('Unhandled error:', error);
res.status(500).json({
error: 'Internal server error',
message: error.message
});
});
// 404 handler
this.app.use((req, res) => {
res.status(404).json({
error: 'Not found',
message: `Endpoint ${req.method} ${req.path} not found`
});
});
}
async start() {
try {
console.log('🔄 Initializing CBD Memory Engine...');
await this.engine.initialize();
console.log('🚀 Starting HTTP server...');
this.isHealthy = true;
return new Promise((resolve, reject) => {
const server = this.app.listen(this.port, this.host, (error) => {
if (error) {
this.isHealthy = false;
return reject(error);
}
this.server = server;
console.log(`✅ CBD Engine Service started successfully`);
console.log(`📊 Service: http://${this.host}:${this.port}`);
console.log(`💾 Data Path: ${this.dataPath}`);
console.log(`🔍 Memory Engine: CBD v1.0.0`);
resolve(server);
});
server.on('error', (error) => {
console.error('❌ Server error:', error);
this.isHealthy = false;
reject(error);
});
});
}
catch (error) {
console.error('❌ Failed to start CBD Engine Service:', error);
this.isHealthy = false;
await this.engine.shutdown();
throw error;
}
}
async stop() {
if (this.server) {
this.server.close();
}
this.isHealthy = false;
await this.engine.shutdown();
console.log('🔄 CBD Engine Service stopped');
}
}
//# sourceMappingURL=service-simple.js.map