tasq/node_modules/agentic-flow/dist/sdk/e2b-swarm.js

366 lines
13 KiB
JavaScript

/**
* E2B Swarm Orchestrator - Multi-agent swarm with E2B sandbox isolation
*
* Spawns specialized agents in isolated E2B sandboxes for:
* - Parallel code execution
* - Secure multi-agent coordination
* - Resource-isolated task processing
*/
import { logger } from "../utils/logger.js";
import { E2BSandboxManager } from "./e2b-sandbox.js";
/**
* E2B Swarm Orchestrator
*/
export class E2BSwarmOrchestrator {
agents = new Map();
taskQueue = [];
taskResults = new Map();
isRunning = false;
config;
constructor(config) {
this.config = {
maxAgents: config?.maxAgents || 10,
defaultTimeout: config?.defaultTimeout || 300000,
retryAttempts: config?.retryAttempts || 3,
loadBalancing: config?.loadBalancing || 'capability-match'
};
}
/**
* Initialize the swarm
*/
async initialize() {
logger.info('E2B Swarm initializing', { maxAgents: this.config.maxAgents });
this.isRunning = true;
return true;
}
/**
* Spawn a new E2B agent with specific capability
*/
async spawnAgent(config) {
if (this.agents.size >= this.config.maxAgents) {
logger.warn('Max agents reached', { max: this.config.maxAgents });
return null;
}
const sandbox = new E2BSandboxManager({
apiKey: process.env.E2B_API_KEY,
template: config.template || this.getTemplateForCapability(config.capability),
timeout: config.timeout || this.config.defaultTimeout,
envVars: config.envVars
});
const agent = {
id: config.id,
name: config.name,
capability: config.capability,
sandbox,
status: 'initializing',
tasksCompleted: 0,
totalExecutionTime: 0,
errors: 0,
createdAt: Date.now()
};
this.agents.set(agent.id, agent);
try {
const useCodeInterpreter = ['python-executor', 'data-analyst', 'ml-trainer'].includes(config.capability);
const created = await sandbox.create(useCodeInterpreter);
if (!created) {
agent.status = 'error';
logger.error('Failed to create E2B sandbox for agent', { agentId: agent.id });
return null;
}
// Install required packages for capability
if (config.packages && config.packages.length > 0) {
const pkgManager = ['python-executor', 'data-analyst', 'ml-trainer'].includes(config.capability) ? 'pip' : 'npm';
await sandbox.installPackages(config.packages, pkgManager);
}
agent.status = 'ready';
logger.info('E2B agent spawned', { id: agent.id, name: agent.name, capability: config.capability });
return agent;
}
catch (error) {
agent.status = 'error';
agent.errors++;
logger.error('Error spawning E2B agent', { agentId: agent.id, error: error.message });
return null;
}
}
/**
* Spawn multiple agents in parallel
*/
async spawnAgents(configs) {
const results = await Promise.allSettled(configs.map(config => this.spawnAgent(config)));
return results
.filter((r) => r.status === 'fulfilled')
.map(r => r.value)
.filter((a) => a !== null);
}
/**
* Execute a task on the swarm
*/
async executeTask(task) {
const startTime = Date.now();
// Find best agent for task
const agent = this.selectAgent(task);
if (!agent) {
return {
taskId: task.id,
agentId: 'none',
success: false,
output: '',
error: 'No suitable agent available',
executionTime: 0
};
}
agent.status = 'busy';
try {
let result;
switch (task.type) {
case 'python':
result = await agent.sandbox.runPython(task.code);
break;
case 'javascript':
result = await agent.sandbox.runJavaScript(task.code);
break;
case 'shell':
result = await agent.sandbox.runCommand('sh', ['-c', task.code]);
break;
case 'file-write':
const writeResult = await agent.sandbox.writeFile(task.metadata?.path || '/tmp/output.txt', task.code);
result = { success: writeResult.success, output: writeResult.path, error: writeResult.error, logs: [] };
break;
case 'file-read':
const readResult = await agent.sandbox.readFile(task.code);
result = { success: readResult.success, output: readResult.content || '', error: readResult.error, logs: [] };
break;
default:
result = { success: false, output: '', error: `Unknown task type: ${task.type}`, logs: [] };
}
const executionTime = Date.now() - startTime;
agent.tasksCompleted++;
agent.totalExecutionTime += executionTime;
agent.status = 'ready';
const taskResult = {
taskId: task.id,
agentId: agent.id,
success: result.success,
output: result.output,
error: result.error,
executionTime,
metadata: task.metadata
};
this.taskResults.set(task.id, taskResult);
return taskResult;
}
catch (error) {
agent.errors++;
agent.status = 'ready';
const taskResult = {
taskId: task.id,
agentId: agent.id,
success: false,
output: '',
error: error.message,
executionTime: Date.now() - startTime
};
this.taskResults.set(task.id, taskResult);
return taskResult;
}
}
/**
* Execute multiple tasks in parallel
*/
async executeTasks(tasks) {
// Group by priority
const critical = tasks.filter(t => t.priority === 'critical');
const high = tasks.filter(t => t.priority === 'high');
const medium = tasks.filter(t => t.priority === 'medium' || !t.priority);
const low = tasks.filter(t => t.priority === 'low');
const orderedTasks = [...critical, ...high, ...medium, ...low];
// Execute with concurrency based on available agents
const concurrency = Math.min(this.getReadyAgents().length, orderedTasks.length);
const results = [];
for (let i = 0; i < orderedTasks.length; i += concurrency) {
const batch = orderedTasks.slice(i, i + concurrency);
const batchResults = await Promise.all(batch.map(task => this.executeTask(task)));
results.push(...batchResults);
}
return results;
}
/**
* Select best agent for a task
*/
selectAgent(task) {
const readyAgents = this.getReadyAgents();
if (readyAgents.length === 0)
return null;
// If specific agent requested
if (task.targetAgent) {
const agent = this.agents.get(task.targetAgent);
if (agent && agent.status === 'ready')
return agent;
}
switch (this.config.loadBalancing) {
case 'round-robin':
return readyAgents[0];
case 'least-busy':
return readyAgents.reduce((min, agent) => agent.tasksCompleted < min.tasksCompleted ? agent : min);
case 'capability-match':
default:
// Match task type to capability
const capabilityMap = {
'python': ['python-executor', 'data-analyst', 'ml-trainer'],
'javascript': ['javascript-executor', 'api-tester'],
'shell': ['shell-executor', 'security-scanner', 'test-runner'],
'file-write': ['shell-executor', 'python-executor', 'javascript-executor'],
'file-read': ['shell-executor', 'python-executor', 'javascript-executor']
};
const preferredCapabilities = capabilityMap[task.type] || [];
const matchingAgents = readyAgents.filter(a => task.capability ? a.capability === task.capability : preferredCapabilities.includes(a.capability));
return matchingAgents.length > 0 ? matchingAgents[0] : readyAgents[0];
}
}
/**
* Get ready agents
*/
getReadyAgents() {
return Array.from(this.agents.values()).filter(a => a.status === 'ready');
}
/**
* Get template for capability
*/
getTemplateForCapability(capability) {
const templates = {
'python-executor': 'base',
'javascript-executor': 'base',
'shell-executor': 'base',
'data-analyst': 'base',
'code-reviewer': 'base',
'test-runner': 'base',
'security-scanner': 'base',
'performance-profiler': 'base',
'ml-trainer': 'base',
'api-tester': 'base'
};
return templates[capability] || 'base';
}
/**
* Get swarm metrics
*/
getMetrics() {
const agents = Array.from(this.agents.values());
const totalTasks = agents.reduce((sum, a) => sum + a.tasksCompleted, 0);
const totalErrors = agents.reduce((sum, a) => sum + a.errors, 0);
const totalTime = agents.reduce((sum, a) => sum + a.totalExecutionTime, 0);
const utilization = {};
for (const agent of agents) {
utilization[agent.id] = agent.tasksCompleted > 0
? agent.totalExecutionTime / (Date.now() - agent.createdAt)
: 0;
}
return {
totalAgents: agents.length,
activeAgents: agents.filter(a => a.status === 'busy').length,
tasksCompleted: totalTasks,
tasksInProgress: agents.filter(a => a.status === 'busy').length,
totalExecutionTime: totalTime,
averageExecutionTime: totalTasks > 0 ? totalTime / totalTasks : 0,
errorRate: totalTasks > 0 ? totalErrors / (totalTasks + totalErrors) : 0,
agentUtilization: utilization
};
}
/**
* Get all agents
*/
getAgents() {
return Array.from(this.agents.values());
}
/**
* Get agent by ID
*/
getAgent(id) {
return this.agents.get(id);
}
/**
* Terminate an agent
*/
async terminateAgent(id) {
const agent = this.agents.get(id);
if (!agent)
return false;
try {
await agent.sandbox.close();
agent.status = 'terminated';
this.agents.delete(id);
logger.info('E2B agent terminated', { id });
return true;
}
catch (error) {
logger.error('Error terminating agent', { id, error: error.message });
return false;
}
}
/**
* Shutdown the swarm
*/
async shutdown() {
this.isRunning = false;
const terminatePromises = Array.from(this.agents.keys()).map(id => this.terminateAgent(id));
await Promise.allSettled(terminatePromises);
this.agents.clear();
this.taskResults.clear();
logger.info('E2B Swarm shutdown complete');
}
/**
* Health check
*/
async healthCheck() {
const agentHealth = await Promise.all(Array.from(this.agents.values()).map(async (agent) => {
let healthy = agent.status === 'ready' || agent.status === 'busy';
// Quick ping test
if (healthy && agent.status === 'ready') {
try {
const result = await agent.sandbox.runCommand('echo', ['ping']);
healthy = result.success;
}
catch {
healthy = false;
}
}
return { id: agent.id, status: agent.status, healthy };
}));
return {
healthy: agentHealth.every(a => a.healthy),
agents: agentHealth
};
}
}
/**
* Create default swarm with standard agent types
*/
export async function createDefaultE2BSwarm() {
const swarm = new E2BSwarmOrchestrator({
maxAgents: 8,
loadBalancing: 'capability-match'
});
await swarm.initialize();
const defaultAgents = [
{ id: 'python-1', name: 'Python Executor', capability: 'python-executor', packages: ['numpy', 'pandas'] },
{ id: 'js-1', name: 'JavaScript Executor', capability: 'javascript-executor' },
{ id: 'shell-1', name: 'Shell Executor', capability: 'shell-executor' },
{ id: 'data-1', name: 'Data Analyst', capability: 'data-analyst', packages: ['numpy', 'pandas', 'matplotlib'] },
{ id: 'test-1', name: 'Test Runner', capability: 'test-runner' },
{ id: 'security-1', name: 'Security Scanner', capability: 'security-scanner' }
];
await swarm.spawnAgents(defaultAgents);
return swarm;
}
/**
* Quick helper to run code in E2B swarm
*/
export async function runInSwarm(swarm, code, type = 'python') {
return swarm.executeTask({
id: `task-${Date.now()}`,
type,
code
});
}
//# sourceMappingURL=e2b-swarm.js.map