UNPKG

ruv-swarm

Version:

High-performance neural network swarm orchestration in WebAssembly

457 lines (386 loc) 12 kB
/** * RuvSwarm - WASM-based swarm orchestration with cognitive diversity */ import { SwarmOptions, SwarmState, SwarmTopology, SwarmEventEmitter, SwarmEvent, AgentConfig, Task, TaskStatus, Message, MessageType, SwarmMetrics, Connection, WasmModule, } from './types'; import { generateId, validateSwarmOptions, formatMetrics, recommendTopology, priorityToNumber, } from './utils'; import { BaseAgent, createAgent, AgentPool, } from './agent'; export * from './types'; export * from './utils'; export * from './agent'; export * from './neural-network'; /** * Main RuvSwarm class for orchestrating swarm operations */ export class RuvSwarm implements SwarmEventEmitter { private options: Required<SwarmOptions>; private state: SwarmState; private agentPool: AgentPool; private eventHandlers: Map<SwarmEvent, Set<(data: any) => void>>; private wasmModule?: WasmModule; private swarmId?: number; private isInitialized: boolean = false; constructor(options: SwarmOptions = {}) { const errors = validateSwarmOptions(options); if (errors.length > 0) { throw new Error(`Invalid swarm options: ${errors.join(', ')}`); } this.options = { topology: options.topology || 'mesh', maxAgents: options.maxAgents || 10, connectionDensity: options.connectionDensity || 0.5, syncInterval: options.syncInterval || 1000, wasmPath: options.wasmPath || './wasm/ruv_swarm_wasm.js', }; this.agentPool = new AgentPool(); this.eventHandlers = new Map(); this.state = { agents: new Map(), tasks: new Map(), topology: this.options.topology, connections: [], metrics: { totalTasks: 0, completedTasks: 0, failedTasks: 0, averageCompletionTime: 0, agentUtilization: new Map(), throughput: 0, }, }; } /** * Initialize the swarm with WASM module */ async init(): Promise<void> { if (this.isInitialized) { throw new Error('Swarm is already initialized'); } try { // Load WASM module if (typeof window !== 'undefined') { // Browser environment const wasmModule = await import(this.options.wasmPath); await wasmModule.default(); this.wasmModule = wasmModule as any; } else { // Node.js environment console.log('WASM support in Node.js - using pure JS implementation'); } // Initialize swarm in WASM if available if (this.wasmModule) { await this.wasmModule.init(); this.swarmId = this.wasmModule.createSwarm(this.options); } this.isInitialized = true; this.startSyncLoop(); this.emit('swarm:initialized', { options: this.options }); } catch (error) { throw new Error(`Failed to initialize swarm: ${error}`); } } /** * Static factory method for easy initialization */ static async create(options?: SwarmOptions): Promise<RuvSwarm> { const swarm = new RuvSwarm(options); await swarm.init(); return swarm; } /** * Add an agent to the swarm */ addAgent(config: AgentConfig): string { if (!this.isInitialized) { throw new Error('Swarm must be initialized before adding agents'); } if (this.state.agents.size >= this.options.maxAgents) { throw new Error(`Maximum agent limit (${this.options.maxAgents}) reached`); } const agent = createAgent(config); this.state.agents.set(agent.id, agent); this.agentPool.addAgent(agent); // Add to WASM if available if (this.wasmModule && this.swarmId !== undefined) { const wasmAgentId = this.wasmModule.addAgent(this.swarmId, config); (agent as BaseAgent).setWasmAgentId(wasmAgentId); } // Create connections based on topology this.updateConnections(agent.id); this.emit('agent:added', { agentId: agent.id, config }); return agent.id; } /** * Remove an agent from the swarm */ removeAgent(agentId: string): void { const agent = this.state.agents.get(agentId); if (!agent) { throw new Error(`Agent ${agentId} not found`); } if (agent.state.status === 'busy') { throw new Error(`Cannot remove busy agent ${agentId}`); } this.state.agents.delete(agentId); this.agentPool.removeAgent(agentId); // Remove connections this.state.connections = this.state.connections.filter( conn => conn.from !== agentId && conn.to !== agentId, ); this.emit('agent:removed', { agentId }); } /** * Submit a task to the swarm */ async submitTask(task: Omit<Task, 'id' | 'status'>): Promise<string> { if (!this.isInitialized) { throw new Error('Swarm must be initialized before submitting tasks'); } const fullTask: Task = { ...task, id: generateId('task'), status: 'pending', }; this.state.tasks.set(fullTask.id, fullTask); this.state.metrics.totalTasks++; this.emit('task:created', { task: fullTask }); // Assign to WASM if available if (this.wasmModule && this.swarmId !== undefined) { this.wasmModule.assignTask(this.swarmId, fullTask); } else { // Use JS implementation await this.assignTask(fullTask); } return fullTask.id; } /** * Get the current state of a task */ getTaskStatus(taskId: string): Task | undefined { return this.state.tasks.get(taskId); } /** * Get all tasks with a specific status */ getTasksByStatus(status: TaskStatus): Task[] { return Array.from(this.state.tasks.values()).filter(task => task.status === status); } /** * Get current swarm metrics */ getMetrics(): SwarmMetrics { return { ...this.state.metrics }; } /** * Get formatted metrics string */ getFormattedMetrics(): string { return formatMetrics(this.state.metrics); } /** * Event emitter implementation */ on(event: SwarmEvent, handler: (data: any) => void): void { if (!this.eventHandlers.has(event)) { this.eventHandlers.set(event, new Set()); } this.eventHandlers.get(event)!.add(handler); } off(event: SwarmEvent, handler: (data: any) => void): void { const handlers = this.eventHandlers.get(event); if (handlers) { handlers.delete(handler); } } emit(event: SwarmEvent, data: any): void { const handlers = this.eventHandlers.get(event); if (handlers) { handlers.forEach(handler => { try { handler(data); } catch (error) { console.error(`Error in event handler for ${event}:`, error); } }); } } /** * Shutdown the swarm */ async destroy(): Promise<void> { if (!this.isInitialized) { return; } // Cancel any pending tasks for (const task of this.state.tasks.values()) { if (task.status === 'pending' || task.status === 'in_progress') { task.status = 'failed'; task.error = new Error('Swarm shutdown'); } } // Destroy WASM resources if (this.wasmModule && this.swarmId !== undefined) { this.wasmModule.destroy(this.swarmId); } this.isInitialized = false; this.emit('swarm:destroyed', {}); } /** * Private methods */ private async assignTask(task: Task): Promise<void> { // Find suitable agent based on task requirements const agent = this.agentPool.getAvailableAgent(); if (!agent) { // No available agents, queue the task console.log(`No available agents for task ${task.id}, queuing...`); return; } task.status = 'assigned'; task.assignedAgents = [agent.id]; this.emit('task:assigned', { taskId: task.id, agentId: agent.id }); // Send task assignment message const message: Message = { id: generateId('msg'), from: 'swarm', to: agent.id, type: 'task_assignment', payload: task, timestamp: Date.now(), }; await agent.communicate(message); // Execute task try { task.status = 'in_progress'; const startTime = Date.now(); const result = await agent.execute(task); task.status = 'completed'; task.result = result; const executionTime = Date.now() - startTime; this.updateMetrics(true, executionTime); this.emit('task:completed', { taskId: task.id, result }); } catch (error) { task.status = 'failed'; task.error = error as Error; this.updateMetrics(false, 0); this.emit('task:failed', { taskId: task.id, error }); } finally { this.agentPool.releaseAgent(agent.id); // Check for pending tasks const pendingTasks = this.getTasksByStatus('pending'); if (pendingTasks.length > 0) { // Sort by priority pendingTasks.sort((a, b) => priorityToNumber(b.priority) - priorityToNumber(a.priority)); await this.assignTask(pendingTasks[0]); } } } private updateConnections(newAgentId: string): void { const agents = Array.from(this.state.agents.keys()); switch (this.options.topology) { case 'mesh': // Connect to all other agents for (const agentId of agents) { if (agentId !== newAgentId) { this.state.connections.push({ from: newAgentId, to: agentId, weight: 1, type: 'coordination', }); } } break; case 'hierarchical': // Connect to parent/children based on position if (agents.length > 1) { const parentIndex = Math.floor((agents.indexOf(newAgentId) - 1) / 2); if (parentIndex >= 0) { this.state.connections.push({ from: newAgentId, to: agents[parentIndex], weight: 1, type: 'control', }); } } break; case 'distributed': // Random connections based on density const numConnections = Math.floor(agents.length * this.options.connectionDensity); const shuffled = agents.filter(id => id !== newAgentId).sort(() => Math.random() - 0.5); for (let i = 0; i < Math.min(numConnections, shuffled.length); i++) { this.state.connections.push({ from: newAgentId, to: shuffled[i], weight: Math.random(), type: 'data', }); } break; } } private updateMetrics(success: boolean, executionTime: number): void { if (success) { this.state.metrics.completedTasks++; } else { this.state.metrics.failedTasks++; } // Update average completion time if (success && executionTime > 0) { const totalCompleted = this.state.metrics.completedTasks; const currentAvg = this.state.metrics.averageCompletionTime; this.state.metrics.averageCompletionTime = (currentAvg * (totalCompleted - 1) + executionTime) / totalCompleted; } // Update throughput (tasks per second) // This is a simplified calculation - in production, use a sliding window const totalProcessed = this.state.metrics.completedTasks + this.state.metrics.failedTasks; const elapsedSeconds = (Date.now() - this.startTime) / 1000; this.state.metrics.throughput = totalProcessed / elapsedSeconds; } private startTime: number = Date.now(); private startSyncLoop(): void { setInterval(() => { if (this.wasmModule && this.swarmId !== undefined) { // Sync state with WASM const wasmState = this.wasmModule.getState(this.swarmId); // Update local state as needed } // Update agent utilization metrics for (const agent of this.state.agents.values()) { this.state.metrics.agentUtilization.set( agent.id, agent.state.status === 'busy' ? 1 : 0, ); } }, this.options.syncInterval); } } /** * Default export for convenience */ export default RuvSwarm;