UNPKG

@juspay/neurolink

Version:

The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002

479 lines (478 loc) • 17.6 kB
/** * Network Orchestrator - High-level orchestration of agent networks * * The Orchestrator manages: * - Network lifecycle (creation, execution, shutdown) * - Multi-network coordination * - Network-level policies and constraints * - Resource management and scaling */ import { EventEmitter } from "events"; import { randomUUID } from "crypto"; import { AgentCoordinator } from "../coordination/coordinator.js"; import { MessageBus } from "../communication/message-bus.js"; import { logger } from "../../utils/logger.js"; /** * Network Orchestrator - Central controller for agent networks */ export class NetworkOrchestrator { neurolink; networks = new Map(); networkInfo = new Map(); coordinators = new Map(); messageBus; config; emitter; executionQueue = []; // `void` admits the resolved placeholder used by streamNetwork to track // an in-flight stream; only .size/.set/.delete are ever used on this map. activeExecutions = new Map(); constructor(neurolink, config) { this.neurolink = neurolink; this.config = { defaultMode: "autonomous", maxConcurrentExecutions: 5, defaultTimeout: 120000, enableHierarchy: true, maxHierarchyDepth: 3, enableSharedMessageBus: true, resourceLimits: { maxNetworks: 10, maxAgentsPerNetwork: 20, maxTotalAgents: 100, }, ...config, }; this.emitter = new EventEmitter(); this.messageBus = new MessageBus(); logger.info("[NetworkOrchestrator] Initialized", { maxConcurrentExecutions: this.config.maxConcurrentExecutions, enableHierarchy: this.config.enableHierarchy, }); } /** * Create a new agent network */ async createNetwork(config, mode) { // Check resource limits if (this.config.resourceLimits?.maxNetworks && this.networks.size >= this.config.resourceLimits.maxNetworks) { throw new Error("Maximum number of networks reached"); } if (this.config.resourceLimits?.maxAgentsPerNetwork && config.agents.length > this.config.resourceLimits.maxAgentsPerNetwork) { throw new Error(`Maximum agents per network (${this.config.resourceLimits.maxAgentsPerNetwork}) exceeded`); } if (this.config.resourceLimits?.maxTotalAgents) { const currentTotalAgents = Array.from(this.networkInfo.values()).reduce((sum, info) => sum + info.agentCount, 0); if (currentTotalAgents + config.agents.length > this.config.resourceLimits.maxTotalAgents) { throw new Error(`Maximum total agents (${this.config.resourceLimits.maxTotalAgents}) exceeded`); } } // Create the network via NeuroLink const network = await this.neurolink.createNetwork(config); // Track network info const info = { id: network.id, name: network.name, state: "ready", agentCount: config.agents.length, mode: mode ?? this.config.defaultMode, createdAt: Date.now(), executionCount: 0, childNetworkIds: [], }; this.networkInfo.set(network.id, info); this.networks.set(network.id, network); // Create coordinator for the network const coordinator = new AgentCoordinator({ strategy: "sequential", maxConcurrency: 3, }); for (const agent of network.getAllAgents()) { coordinator.registerAgent(agent); } this.coordinators.set(network.id, coordinator); this.emitter.emit("network:created", { networkId: network.id, name: network.name, }); logger.info(`[NetworkOrchestrator] Network created: ${network.name}`, { networkId: network.id, agentCount: config.agents.length, }); return network; } /** * Create a hierarchical network */ async createHierarchicalNetwork(config, parentNetworkId) { if (!this.config.enableHierarchy) { throw new Error("Hierarchical networks are disabled"); } // Check hierarchy constraints if (parentNetworkId) { const parentInfo = this.networkInfo.get(parentNetworkId); if (!parentInfo) { throw new Error(`Parent network not found: ${parentNetworkId}`); } // Check depth let depth = 0; let currentParent = parentInfo; while (currentParent.parentNetworkId) { depth++; currentParent = this.networkInfo.get(currentParent.parentNetworkId); if (depth >= this.config.maxHierarchyDepth) { throw new Error(`Maximum hierarchy depth (${this.config.maxHierarchyDepth}) exceeded`); } } } const network = await this.createNetwork(config, config.supervisionMode ?? "hierarchical"); // Set up hierarchy relationships if (parentNetworkId) { const info = this.networkInfo.get(network.id); info.parentNetworkId = parentNetworkId; const parentInfo = this.networkInfo.get(parentNetworkId); parentInfo.childNetworkIds.push(network.id); } return network; } /** * Get a network by ID */ getNetwork(networkId) { return this.networks.get(networkId); } /** * Get network info */ getNetworkInfo(networkId) { return this.networkInfo.get(networkId); } /** * Get all networks */ getAllNetworks() { return Array.from(this.networkInfo.values()); } /** * Execute a network */ async executeNetwork(networkId, input, options) { const network = this.networks.get(networkId); if (!network) { throw new Error(`Network not found: ${networkId}`); } const info = this.networkInfo.get(networkId); if (info.state === "paused") { throw new Error(`Network is paused: ${networkId}`); } if (info.state === "shutdown") { throw new Error(`Network is shut down: ${networkId}`); } if (info.state === "executing") { // Queue the execution return this.queueExecution({ networkId, input, options }); } // Check concurrent execution limit if (this.activeExecutions.size >= this.config.maxConcurrentExecutions) { return this.queueExecution({ networkId, input, options }); } return this.executeNetworkInternal(network, info, input, options); } /** * Internal network execution */ async executeNetworkInternal(network, info, input, options) { info.state = "executing"; info.executionCount++; const executionPromise = (async () => { try { this.emitter.emit("network:execution:start", { networkId: network.id, input, }); const result = await network.execute(input, { ...options, timeout: options?.timeout ?? this.config.defaultTimeout, }); info.lastExecutionAt = Date.now(); info.state = "ready"; this.emitter.emit("network:execution:complete", { networkId: network.id, result, }); return result; } catch (error) { info.state = "error"; this.emitter.emit("network:execution:error", { networkId: network.id, error: error instanceof Error ? error.message : String(error), }); throw error; } finally { this.activeExecutions.delete(network.id); this.processExecutionQueue(); } })(); this.activeExecutions.set(network.id, executionPromise); return executionPromise; } /** * Queue an execution request */ async queueExecution(request) { return new Promise((resolve, reject) => { const _queuedRequest = { ...request, resolve, reject, }; this.executionQueue.push(_queuedRequest); this.executionQueue.sort((a, b) => { const priorityOrder = { high: 0, normal: 1, low: 2 }; return ((priorityOrder[a.priority ?? "normal"] ?? 1) - (priorityOrder[b.priority ?? "normal"] ?? 1)); }); this.emitter.emit("execution:queued", { networkId: request.networkId }); }); } /** * Process queued executions */ async processExecutionQueue() { while (this.executionQueue.length > 0 && this.activeExecutions.size < this.config.maxConcurrentExecutions) { const request = this.executionQueue.shift(); const network = this.networks.get(request.networkId); const info = this.networkInfo.get(request.networkId); if (network && info && info.state !== "executing") { // Fire-and-forget: executeNetworkInternal is NOT awaited so the while // loop drains the entire queue synchronously before any launched // execution sets info.state = "executing" (that happens at the first // await inside executeNetworkInternal). This is intentional — it lets // one queue-drain pass start multiple networks in parallel up to // maxConcurrentExecutions. this.executeNetworkInternal(network, info, request.input, request.options) .then((result) => request.resolve?.(result)) .catch((err) => request.reject?.(err)); } else if (request.reject) { request.reject(new Error(`Network unavailable for execution: ${request.networkId}`)); } } } /** * Stream network execution */ async *streamNetwork(networkId, input, options) { const network = this.networks.get(networkId); if (!network) { throw new Error(`Network not found: ${networkId}`); } const info = this.networkInfo.get(networkId); if (info.state === "paused") { throw new Error(`Network is paused: ${networkId}`); } if (info.state === "shutdown") { throw new Error(`Network is shut down: ${networkId}`); } if (info.state === "executing") { throw new Error(`Network is already executing: ${networkId}. Streaming does not support queuing.`); } if (this.activeExecutions.size >= this.config.maxConcurrentExecutions) { throw new Error(`Maximum concurrent executions (${this.config.maxConcurrentExecutions}) reached. Streaming does not support queuing.`); } info.state = "executing"; info.executionCount++; const streamPromise = Promise.resolve(); // placeholder to track in activeExecutions this.activeExecutions.set(networkId, streamPromise); try { yield* network.stream(input, options); info.lastExecutionAt = Date.now(); } finally { info.state = "ready"; this.activeExecutions.delete(networkId); this.processExecutionQueue(); } } /** * Execute hierarchical network with delegation */ async executeHierarchical(networkId, input, options) { const network = this.networks.get(networkId); const info = this.networkInfo.get(networkId); if (!network || !info) { throw new Error(`Network not found: ${networkId}`); } const traceId = randomUUID(); const trace = { traceId, steps: [], routingDecisions: [], startTime: Date.now(), hierarchyLevel: 0, childTraces: [], }; // Calculate hierarchy level let currentInfo = info; while (currentInfo.parentNetworkId) { trace.hierarchyLevel++; trace.parentTraceId = currentInfo.parentNetworkId; currentInfo = this.networkInfo.get(currentInfo.parentNetworkId); } // Execute main network const result = await this.executeNetwork(networkId, input, options); trace.steps = result.trace.steps; trace.routingDecisions = result.trace.routingDecisions; trace.endTime = Date.now(); // Execute child networks if needed based on delegation rules for (const childId of info.childNetworkIds) { const childNetwork = this.networks.get(childId); if (childNetwork) { const childTrace = await this.executeHierarchical(childId, { message: result.content, context: input.context }, options); trace.childTraces?.push(childTrace); } } return trace; } /** * Pause a network */ pauseNetwork(networkId) { const info = this.networkInfo.get(networkId); if (info && info.state === "ready") { info.state = "paused"; this.emitter.emit("network:paused", { networkId }); } } /** * Resume a network */ resumeNetwork(networkId) { const info = this.networkInfo.get(networkId); if (info && info.state === "paused") { info.state = "ready"; this.emitter.emit("network:resumed", { networkId }); } } /** * Shutdown a network */ async shutdownNetwork(networkId) { const info = this.networkInfo.get(networkId); if (!info) { return; } // Shutdown child networks first for (const childId of info.childNetworkIds) { await this.shutdownNetwork(childId); } // Remove from parent's child list if (info.parentNetworkId) { const parentInfo = this.networkInfo.get(info.parentNetworkId); if (parentInfo) { parentInfo.childNetworkIds = parentInfo.childNetworkIds.filter((id) => id !== networkId); } } info.state = "shutdown"; this.networks.delete(networkId); this.networkInfo.delete(networkId); this.coordinators.delete(networkId); this.emitter.emit("network:shutdown", { networkId }); logger.info(`[NetworkOrchestrator] Network shutdown: ${networkId}`); } /** * Coordinate multiple networks */ async coordinateNetworks(networkIds, task, strategy = "parallel") { const results = new Map(); switch (strategy) { case "sequential": case "pipeline": { // Each network receives the previous network's output as its input let currentInput = task; for (const networkId of networkIds) { const result = await this.executeNetwork(networkId, { message: currentInput, }); results.set(networkId, result); currentInput = result.content; } break; } case "parallel": { await Promise.all(networkIds.map(async (networkId) => { const result = await this.executeNetwork(networkId, { message: task, }); results.set(networkId, result); })); break; } default: throw new Error(`Unsupported coordination strategy: ${strategy}`); } return results; } /** * Get orchestrator statistics */ getStats() { const networksByState = { idle: 0, initializing: 0, ready: 0, executing: 0, paused: 0, error: 0, shutdown: 0, }; let totalExecutions = 0; for (const info of this.networkInfo.values()) { networksByState[info.state]++; totalExecutions += info.executionCount; } return { totalNetworks: this.networks.size, activeExecutions: this.activeExecutions.size, queuedExecutions: this.executionQueue.length, totalExecutions, networksByState, }; } /** * Get the shared message bus */ getMessageBus() { return this.messageBus; } /** * Subscribe to orchestrator events */ on(event, handler) { this.emitter.on(event, handler); } /** * Unsubscribe from orchestrator events */ off(event, handler) { this.emitter.off(event, handler); } /** * Shutdown the orchestrator */ async shutdown() { // Shutdown all networks for (const networkId of this.networks.keys()) { await this.shutdownNetwork(networkId); } // Shutdown message bus this.messageBus.shutdown(); logger.info("[NetworkOrchestrator] Shutdown complete"); } }