UNPKG

ruv-swarm

Version:

High-performance neural network swarm orchestration in WebAssembly

480 lines (414 loc) 14.3 kB
/** * SQLite Persistence Layer for ruv-swarm MCP */ import Database from 'better-sqlite3'; import path from 'path'; import fs from 'fs'; class SwarmPersistence { constructor(dbPath = path.join(new URL('.', import.meta.url).pathname, '..', 'data', 'ruv-swarm.db')) { // Ensure data directory exists const dataDir = path.dirname(dbPath); if (!fs.existsSync(dataDir)) { fs.mkdirSync(dataDir, { recursive: true }); } this.db = new Database(dbPath); this.initDatabase(); } initDatabase() { // Enable foreign keys this.db.exec('PRAGMA foreign_keys = ON'); // Create tables this.db.exec(` CREATE TABLE IF NOT EXISTS swarms ( id TEXT PRIMARY KEY, name TEXT NOT NULL, topology TEXT NOT NULL, max_agents INTEGER NOT NULL, strategy TEXT, status TEXT DEFAULT 'active', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, metadata TEXT ); CREATE TABLE IF NOT EXISTS agents ( id TEXT PRIMARY KEY, swarm_id TEXT, name TEXT NOT NULL, type TEXT NOT NULL, status TEXT DEFAULT 'idle', capabilities TEXT, neural_config TEXT, metrics TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (swarm_id) REFERENCES swarms(id) ); CREATE TABLE IF NOT EXISTS tasks ( id TEXT PRIMARY KEY, swarm_id TEXT, description TEXT, priority TEXT DEFAULT 'medium', status TEXT DEFAULT 'pending', assigned_agents TEXT, result TEXT, error TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, completed_at DATETIME, execution_time_ms INTEGER, FOREIGN KEY (swarm_id) REFERENCES swarms(id) ); CREATE TABLE IF NOT EXISTS task_results ( id TEXT PRIMARY KEY, task_id TEXT NOT NULL, agent_id TEXT NOT NULL, output TEXT, metrics TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (task_id) REFERENCES tasks(id), FOREIGN KEY (agent_id) REFERENCES agents(id) ); CREATE TABLE IF NOT EXISTS agent_memory ( id TEXT PRIMARY KEY, agent_id TEXT NOT NULL, key TEXT NOT NULL, value TEXT, ttl_secs INTEGER, expires_at DATETIME, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (agent_id) REFERENCES agents(id), UNIQUE(agent_id, key) ); CREATE TABLE IF NOT EXISTS metrics ( id TEXT PRIMARY KEY, entity_type TEXT NOT NULL, entity_id TEXT NOT NULL, metric_name TEXT NOT NULL, metric_value REAL, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS neural_networks ( id TEXT PRIMARY KEY, agent_id TEXT NOT NULL, architecture TEXT NOT NULL, weights TEXT, training_data TEXT, performance_metrics TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (agent_id) REFERENCES agents(id) ); CREATE TABLE IF NOT EXISTS events ( id INTEGER PRIMARY KEY AUTOINCREMENT, swarm_id TEXT, event_type TEXT NOT NULL, event_data TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP ); -- Create indexes CREATE INDEX IF NOT EXISTS idx_agents_swarm ON agents(swarm_id); CREATE INDEX IF NOT EXISTS idx_tasks_swarm ON tasks(swarm_id); CREATE INDEX IF NOT EXISTS idx_task_results_task ON task_results(task_id); CREATE INDEX IF NOT EXISTS idx_task_results_agent ON task_results(agent_id); CREATE INDEX IF NOT EXISTS idx_agent_memory_agent ON agent_memory(agent_id); CREATE INDEX IF NOT EXISTS idx_metrics_entity ON metrics(entity_type, entity_id); CREATE INDEX IF NOT EXISTS idx_events_swarm ON events(swarm_id); CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp); `); } // Swarm operations createSwarm(swarm) { const stmt = this.db.prepare(` INSERT INTO swarms (id, name, topology, max_agents, strategy, metadata) VALUES (?, ?, ?, ?, ?, ?) `); return stmt.run( swarm.id, swarm.name, swarm.topology, swarm.maxAgents, swarm.strategy, JSON.stringify(swarm.metadata || {}), ); } getActiveSwarms() { const stmt = this.db.prepare('SELECT * FROM swarms WHERE status = ?'); const swarms = stmt.all('active'); return swarms.map(s => { s.metadata = JSON.parse(s.metadata || '{}'); return s; }); } // Agent operations createAgent(agent) { const stmt = this.db.prepare(` INSERT INTO agents (id, swarm_id, name, type, capabilities, neural_config, metrics) VALUES (?, ?, ?, ?, ?, ?, ?) `); return stmt.run( agent.id, agent.swarmId, agent.name, agent.type, JSON.stringify(agent.capabilities || []), JSON.stringify(agent.neuralConfig || {}), JSON.stringify(agent.metrics || {}), ); } updateAgentStatus(agentId, status) { const stmt = this.db.prepare('UPDATE agents SET status = ? WHERE id = ?'); return stmt.run(status, agentId); } getAgent(id) { const stmt = this.db.prepare('SELECT * FROM agents WHERE id = ?'); const agent = stmt.get(id); if (agent) { agent.capabilities = JSON.parse(agent.capabilities || '[]'); agent.neural_config = JSON.parse(agent.neural_config || '{}'); agent.metrics = JSON.parse(agent.metrics || '{}'); } return agent; } getSwarmAgents(swarmId, filter = 'all') { let query = 'SELECT * FROM agents WHERE swarm_id = ?'; const params = [swarmId]; if (filter !== 'all') { query += ' AND status = ?'; params.push(filter); } const stmt = this.db.prepare(query); const agents = stmt.all(...params); return agents.map(a => { a.capabilities = JSON.parse(a.capabilities || '[]'); a.neural_config = JSON.parse(a.neural_config || '{}'); a.metrics = JSON.parse(a.metrics || '{}'); return a; }); } // Task operations createTask(task) { const stmt = this.db.prepare(` INSERT INTO tasks (id, swarm_id, description, priority, status, assigned_agents) VALUES (?, ?, ?, ?, ?, ?) `); return stmt.run( task.id, task.swarmId, task.description, task.priority || 'medium', task.status || 'pending', JSON.stringify(task.assignedAgents || []), ); } updateTask(taskId, updates) { const fields = []; const values = []; Object.entries(updates).forEach(([key, value]) => { if (key === 'assignedAgents' || key === 'result') { fields.push(`${key} = ?`); values.push(JSON.stringify(value)); } else { fields.push(`${key} = ?`); values.push(value); } }); values.push(taskId); const stmt = this.db.prepare(`UPDATE tasks SET ${fields.join(', ')} WHERE id = ?`); return stmt.run(...values); } getTask(id) { const stmt = this.db.prepare('SELECT * FROM tasks WHERE id = ?'); const task = stmt.get(id); if (task) { task.assigned_agents = JSON.parse(task.assigned_agents || '[]'); task.result = task.result ? JSON.parse(task.result) : null; } return task; } getSwarmTasks(swarmId, status = null) { let query = 'SELECT * FROM tasks WHERE swarm_id = ?'; const params = [swarmId]; if (status) { query += ' AND status = ?'; params.push(status); } const stmt = this.db.prepare(query); const tasks = stmt.all(...params); return tasks.map(t => { t.assigned_agents = JSON.parse(t.assigned_agents || '[]'); t.result = t.result ? JSON.parse(t.result) : null; return t; }); } // Memory operations storeAgentMemory(agentId, key, value) { const stmt = this.db.prepare(` INSERT INTO agent_memory (id, agent_id, key, value) VALUES (?, ?, ?, ?) ON CONFLICT(agent_id, key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP `); const id = `mem_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; return stmt.run(id, agentId, key, JSON.stringify(value)); } getAgentMemory(agentId, key = null) { if (key) { const stmt = this.db.prepare('SELECT * FROM agent_memory WHERE agent_id = ? AND key = ?'); const memory = stmt.get(agentId, key); if (memory) { memory.value = JSON.parse(memory.value); } return memory; } const stmt = this.db.prepare('SELECT * FROM agent_memory WHERE agent_id = ?'); const memories = stmt.all(agentId); return memories.map(m => { m.value = JSON.parse(m.value); return m; }); } // Neural network operations storeNeuralNetwork(network) { const stmt = this.db.prepare(` INSERT INTO neural_networks (id, agent_id, architecture, weights, training_data, performance_metrics) VALUES (?, ?, ?, ?, ?, ?) `); const id = `nn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; return stmt.run( id, network.agentId, JSON.stringify(network.architecture), JSON.stringify(network.weights), JSON.stringify(network.trainingData || {}), JSON.stringify(network.performanceMetrics || {}), ); } updateNeuralNetwork(id, updates) { const fields = []; const values = []; Object.entries(updates).forEach(([key, value]) => { fields.push(`${key} = ?`); values.push(JSON.stringify(value)); }); fields.push('updated_at = CURRENT_TIMESTAMP'); values.push(id); const stmt = this.db.prepare(`UPDATE neural_networks SET ${fields.join(', ')} WHERE id = ?`); return stmt.run(...values); } getAgentNeuralNetworks(agentId) { const stmt = this.db.prepare('SELECT * FROM neural_networks WHERE agent_id = ?'); const networks = stmt.all(agentId); return networks.map(n => { n.architecture = JSON.parse(n.architecture); n.weights = JSON.parse(n.weights); n.training_data = JSON.parse(n.training_data || '{}'); n.performance_metrics = JSON.parse(n.performance_metrics || '{}'); return n; }); } // Metrics operations recordMetric(entityType, entityId, metricName, metricValue) { const stmt = this.db.prepare(` INSERT INTO metrics (id, entity_type, entity_id, metric_name, metric_value) VALUES (?, ?, ?, ?, ?) `); const id = `metric_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; return stmt.run(id, entityType, entityId, metricName, metricValue); } getMetrics(entityType, entityId, metricName = null) { let query = 'SELECT * FROM metrics WHERE entity_type = ? AND entity_id = ?'; const params = [entityType, entityId]; if (metricName) { query += ' AND metric_name = ?'; params.push(metricName); } query += ' ORDER BY timestamp DESC LIMIT 100'; const stmt = this.db.prepare(query); return stmt.all(...params); } // Event logging logEvent(swarmId, eventType, eventData) { const stmt = this.db.prepare(` INSERT INTO events (swarm_id, event_type, event_data) VALUES (?, ?, ?) `); return stmt.run(swarmId, eventType, JSON.stringify(eventData)); } getSwarmEvents(swarmId, limit = 100) { const stmt = this.db.prepare(` SELECT * FROM events WHERE swarm_id = ? ORDER BY timestamp DESC LIMIT ? `); const events = stmt.all(swarmId, limit); return events.map(e => { e.event_data = JSON.parse(e.event_data || '{}'); return e; }); } // Memory operations storeMemory(agentId, key, value, ttlSecs = null) { const expiresAt = ttlSecs ? new Date(Date.now() + ttlSecs * 1000).toISOString() : null; const stmt = this.db.prepare(` INSERT OR REPLACE INTO agent_memory (id, agent_id, key, value, ttl_secs, expires_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) `); const id = `mem_${agentId}_${Date.now()}`; return stmt.run(id, agentId, key, JSON.stringify(value), ttlSecs, expiresAt); } getMemory(agentId, key) { // First cleanup expired entries this.cleanupExpiredMemory(); const stmt = this.db.prepare(` SELECT * FROM agent_memory WHERE agent_id = ? AND key = ? AND (expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP) `); const memory = stmt.get(agentId, key); return memory ? { ...memory, value: JSON.parse(memory.value), } : null; } getAllMemory(agentId) { // First cleanup expired entries this.cleanupExpiredMemory(); const stmt = this.db.prepare(` SELECT * FROM agent_memory WHERE agent_id = ? AND (expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP) ORDER BY updated_at DESC `); const memories = stmt.all(agentId); return memories.map(m => ({ ...m, value: JSON.parse(m.value), })); } deleteMemory(agentId, key) { const stmt = this.db.prepare('DELETE FROM agent_memory WHERE agent_id = ? AND key = ?'); return stmt.run(agentId, key); } cleanupExpiredMemory() { const stmt = this.db.prepare('DELETE FROM agent_memory WHERE expires_at IS NOT NULL AND expires_at <= CURRENT_TIMESTAMP'); return stmt.run(); } // Cleanup operations cleanup() { // Delete expired memories this.cleanupExpiredMemory(); // Delete old events (older than 7 days) const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(); this.db.prepare('DELETE FROM events WHERE timestamp < ?').run(sevenDaysAgo); // Delete old metrics (older than 30 days) const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(); this.db.prepare('DELETE FROM metrics WHERE timestamp < ?').run(thirtyDaysAgo); // Vacuum to reclaim space this.db.exec('VACUUM'); } // Close database connection close() { this.db.close(); } } export { SwarmPersistence };