UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with external MCP server integration, multi-provider support, and professional CLI. Connect to 65+ MCP servers for filesystem, GitHub, database operations, and more. Build, test, and deploy AI applications with 9 major pr

373 lines (372 loc) 15.1 kB
/** * NeuroLink MCP Tool Orchestration Engine * Central orchestrator for coordinated tool execution with pipeline management * Coordinates factory, registry, context, and AI tools for seamless operation */ import { MCPToolRegistry, defaultToolRegistry } from './registry.js'; import { ContextManager, defaultContextManager, createExecutionContext } from './context-manager.js'; import { aiCoreServer } from './servers/ai-providers/ai-core-server.js'; /** * NeuroLink MCP Tool Orchestrator * Central coordination engine for tool execution, pipelines, and AI operations */ export class MCPOrchestrator { registry; contextManager; pipelineCounter = 0; constructor(registry, contextManager) { this.registry = registry || defaultToolRegistry; this.contextManager = contextManager || defaultContextManager; // Initialize with AI Core Server this.initializeDefaultServers(); } /** * Initialize with default servers (AI Core) */ async initializeDefaultServers() { try { await this.registry.registerServer(aiCoreServer); console.log('[Orchestrator] Initialized with AI Core Server'); } catch (error) { console.warn('[Orchestrator] Failed to register AI Core Server:', error); } } /** * Execute a single tool with full orchestration * * @param toolName Tool name to execute * @param params Tool parameters * @param contextRequest Context creation request * @param options Execution options * @returns Tool execution result */ async executeTool(toolName, params, contextRequest = {}, options = {}) { // Create execution context const context = this.contextManager.createContext(contextRequest); console.log(`[Orchestrator] Executing tool '${toolName}' in session ${context.sessionId}`); // Execute tool through registry const result = await this.registry.executeTool(toolName, params, context, options); console.log(`[Orchestrator] Tool '${toolName}' execution ${result.success ? 'completed' : 'failed'}`); return result; } /** * Execute a pipeline of tools with dependency management * * @param steps Pipeline steps to execute * @param contextRequest Context creation request * @param options Pipeline execution options * @returns Pipeline execution result */ async executePipeline(steps, contextRequest = {}, options = {}) { const startTime = Date.now(); const pipelineId = this.generatePipelineId(); const { stopOnError = true, parallel = false, timeout = 60000, trackMetrics = true, validateInputs = true } = options; // Create shared execution context const context = this.contextManager.createContext({ ...contextRequest, sessionId: contextRequest.sessionId || pipelineId }); const results = new Map(); const errors = new Map(); let stepsExecuted = 0; let stepsSkipped = 0; console.log(`[Orchestrator] Starting pipeline ${pipelineId} with ${steps.length} steps`); try { if (parallel) { // Execute steps in parallel with dependency management await this.executeParallelPipeline(steps, context, results, errors, { timeout, trackMetrics, validateInputs, stopOnError }); } else { // Execute steps sequentially for (const step of steps) { const stepId = step.stepId || `step-${stepsExecuted + 1}`; try { console.log(`[Orchestrator] Executing step: ${stepId} (${step.toolName})`); const stepResult = await this.registry.executeTool(step.toolName, step.params, context, { ...step.options, validateInput: validateInputs, trackMetrics, timeoutMs: timeout / steps.length // Distribute timeout across steps }); results.set(stepId, stepResult); stepsExecuted++; if (!stepResult.success) { errors.set(stepId, stepResult.error || 'Unknown error'); if (stopOnError) { console.error(`[Orchestrator] Pipeline ${pipelineId} stopped due to error in step ${stepId}`); break; } } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); errors.set(stepId, errorMessage); if (stopOnError) { console.error(`[Orchestrator] Pipeline ${pipelineId} stopped due to exception in step ${stepId}: ${errorMessage}`); break; } stepsSkipped++; } } } const executionTime = Date.now() - startTime; const success = errors.size === 0 || !stopOnError; console.log(`[Orchestrator] Pipeline ${pipelineId} completed in ${executionTime}ms - ${success ? 'SUCCESS' : 'FAILED'}`); return { success, results, errors, executionTime, stepsExecuted, stepsSkipped, metadata: { pipelineId, sessionId: context.sessionId, timestamp: Date.now(), parallel } }; } catch (error) { const executionTime = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : String(error); console.error(`[Orchestrator] Pipeline ${pipelineId} failed: ${errorMessage}`); return { success: false, results, errors: new Map([['pipeline', errorMessage]]), executionTime, stepsExecuted, stepsSkipped, metadata: { pipelineId, sessionId: context.sessionId, timestamp: Date.now(), parallel } }; } } /** * Execute AI text generation pipeline (high-level convenience method) * * @param prompt Text prompt for generation * @param contextRequest Context creation request * @param options Additional generation options * @returns Text generation result */ async executeTextPipeline(prompt, contextRequest = {}, options = {}) { const startTime = Date.now(); // Create execution context const context = this.contextManager.createContext(contextRequest); try { console.log(`[Orchestrator] Starting text pipeline for prompt: "${prompt.substring(0, 50)}..."`); // Build pipeline steps const steps = []; // Step 1: Provider selection (if not specified) if (!options.provider) { steps.push({ stepId: 'select-provider', toolName: 'select-provider', params: { requirements: { maxTokens: options.maxTokens, costEfficient: true } } }); } // Step 2: Text generation steps.push({ stepId: 'generate-text', toolName: 'generate-text', params: { prompt, provider: options.provider, model: options.model, temperature: options.temperature, maxTokens: options.maxTokens, systemPrompt: options.systemPrompt }, dependsOn: options.provider ? [] : ['select-provider'] }); // Step 3: Custom tools (if specified) if (options.customTools && options.customTools.length > 0) { for (const toolName of options.customTools) { steps.push({ stepId: `custom-${toolName}`, toolName, params: { /* tool-specific params */}, dependsOn: ['generate-text'] }); } } // Execute pipeline const pipelineResult = await this.executePipeline(steps, contextRequest, { stopOnError: true, parallel: false, trackMetrics: true }); const executionTime = Date.now() - startTime; // Extract text generation result const textResult = pipelineResult.results.get('generate-text'); const providerResult = pipelineResult.results.get('select-provider'); if (!textResult || !textResult.success) { throw new Error('Text generation failed'); } const toolsUsed = Array.from(pipelineResult.results.keys()); console.log(`[Orchestrator] Text pipeline completed in ${executionTime}ms`); return { success: true, text: textResult.data?.text, provider: textResult.data?.provider || providerResult?.data?.provider, model: textResult.data?.model, executionTime, usage: textResult.usage, metadata: { sessionId: context.sessionId, timestamp: Date.now(), toolsUsed } }; } catch (error) { const executionTime = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : String(error); console.error(`[Orchestrator] Text pipeline failed: ${errorMessage}`); return { success: false, executionTime, metadata: { sessionId: context.sessionId, timestamp: Date.now(), toolsUsed: [] } }; } } /** * Get orchestrator statistics * * @returns Comprehensive orchestrator statistics */ getStats() { return { registry: this.registry.getStats(), context: this.contextManager.getStats(), orchestrator: { pipelinesExecuted: this.pipelineCounter } }; } /** * Execute parallel pipeline with dependency management * * @private */ async executeParallelPipeline(steps, context, results, errors, options) { // Build dependency graph const stepMap = new Map(); const dependencyGraph = new Map(); for (const step of steps) { const stepId = step.stepId || `step-${stepMap.size + 1}`; stepMap.set(stepId, { ...step, stepId }); dependencyGraph.set(stepId, step.dependsOn || []); } // Execute steps in dependency order const completed = new Set(); const executing = new Set(); while (completed.size < steps.length) { const readySteps = Array.from(stepMap.keys()).filter(stepId => { if (completed.has(stepId) || executing.has(stepId)) return false; const dependencies = dependencyGraph.get(stepId) || []; return dependencies.every(dep => completed.has(dep)); }); if (readySteps.length === 0) { throw new Error('Circular dependency detected in pipeline'); } // Execute ready steps in parallel const executePromises = readySteps.map(async (stepId) => { executing.add(stepId); const step = stepMap.get(stepId); try { const result = await this.registry.executeTool(step.toolName, step.params, context, { ...step.options, ...options }); results.set(stepId, result); if (!result.success) { errors.set(stepId, result.error || 'Unknown error'); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); errors.set(stepId, errorMessage); } finally { executing.delete(stepId); completed.add(stepId); } }); await Promise.all(executePromises); // Check for errors and stop if configured if (options.stopOnError && errors.size > 0) { break; } } } /** * Generate unique pipeline ID * * @private */ generatePipelineId() { this.pipelineCounter++; const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 8); return `nlpipe-${timestamp}-${this.pipelineCounter}-${random}`; } } /** * Default orchestrator instance * Ready-to-use orchestrator with pre-configured registry and context manager */ export const defaultOrchestrator = new MCPOrchestrator(); /** * Utility function to execute tool with default orchestrator * * @param toolName Tool name to execute * @param params Tool parameters * @param contextRequest Context creation request * @param options Execution options * @returns Tool execution result */ export async function executeTool(toolName, params, contextRequest, options) { return defaultOrchestrator.executeTool(toolName, params, contextRequest, options); } /** * Utility function to execute text generation pipeline * * @param prompt Text prompt for generation * @param contextRequest Context creation request * @param options Generation options * @returns Text generation result */ export async function executeTextPipeline(prompt, contextRequest, options) { return defaultOrchestrator.executeTextPipeline(prompt, contextRequest, options); } /** * Utility function to execute pipeline with default orchestrator * * @param steps Pipeline steps * @param contextRequest Context creation request * @param options Pipeline options * @returns Pipeline execution result */ export async function executePipeline(steps, contextRequest, options) { return defaultOrchestrator.executePipeline(steps, contextRequest, options); }