UNPKG

prompt-version-manager

Version:

Centralized prompt management system for Human Behavior AI agents

259 lines 10.2 kB
"use strict"; /** * Chain executor for managing chain execution workflows. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ChainExecutor = void 0; const manager_1 = require("./manager"); const models_1 = require("../core/models"); const exceptions_1 = require("../core/exceptions"); const registry_1 = require("../providers/registry"); class ChainExecutor { manager; constructor(repoPath = '.pvm') { this.manager = new manager_1.ChainManager(repoPath); } /** * Create an execution plan for a chain. */ createExecutionPlan(chainId, inputs) { const chain = this.manager.getChain(chainId); // Convert inputs to Messages const nodeInputs = {}; for (const [tag, input] of Object.entries(inputs)) { if (Array.isArray(input)) { nodeInputs[tag] = input.map(msg => typeof msg === 'object' && 'role' in msg ? msg : { role: 'user', content: String(msg) }); } else { nodeInputs[tag] = [{ role: 'user', content: String(input) }]; } } // Create execution phases using topological sort const phases = this.createExecutionPhases(chain); return { chainId, phases, nodeInputs }; } /** * Create execution phases using topological sorting. * Returns an array of phases, where each phase contains nodes that can execute in parallel. */ createExecutionPhases(chain) { const phases = []; const inDegree = {}; const adjList = {}; // Initialize in-degree and adjacency list for (const nodeId of Object.keys(chain.nodes)) { inDegree[nodeId] = 0; adjList[nodeId] = []; } // Build dependency graph for (const [nodeId, node] of Object.entries(chain.nodes)) { for (const depId of node.dependencies) { adjList[depId].push(nodeId); inDegree[nodeId]++; } } // Find nodes with no dependencies for first phase let currentPhase = Object.keys(inDegree).filter(nodeId => inDegree[nodeId] === 0); while (currentPhase.length > 0) { phases.push([...currentPhase]); const nextPhase = []; // Remove current phase nodes and update in-degrees for (const nodeId of currentPhase) { for (const neighbor of adjList[nodeId]) { inDegree[neighbor]--; if (inDegree[neighbor] === 0) { nextPhase.push(neighbor); } } } currentPhase = nextPhase; } // Verify all nodes are included const totalNodes = Object.keys(chain.nodes).length; const plannedNodes = phases.flat().length; if (plannedNodes !== totalNodes) { throw new exceptions_1.ChainError(`Circular dependency detected in chain ${chain.id}`); } return phases; } /** * Execute a chain according to an execution plan. */ async executeChain(plan) { const startTime = Date.now(); const chain = this.manager.getChain(plan.chainId); const results = {}; const errors = {}; this.manager.startChain(plan.chainId); try { // Execute each phase sequentially for (const phase of plan.phases) { // Execute nodes in current phase in parallel const phasePromises = phase.map(nodeId => this.executeNode(plan.chainId, nodeId, plan.nodeInputs, results)); const phaseResults = await Promise.allSettled(phasePromises); // Process results for (let i = 0; i < phase.length; i++) { const nodeId = phase[i]; const node = chain.nodes[nodeId]; const result = phaseResults[i]; if (result.status === 'fulfilled') { results[node.tag] = result.value; } else { errors[node.tag] = result.reason?.message || 'Unknown error'; } } } this.manager.completeChain(plan.chainId); const endTime = Date.now(); const updatedChain = this.manager.getChain(plan.chainId); return { chainId: plan.chainId, success: Object.keys(errors).length === 0, results, errors, totalCost: updatedChain.totalCost, totalTokens: updatedChain.totalTokens, duration: (endTime - startTime) / 1000 }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.manager.failChain(plan.chainId, errorMessage); const endTime = Date.now(); const updatedChain = this.manager.getChain(plan.chainId); return { chainId: plan.chainId, success: false, results, errors: { ...errors, chain: errorMessage }, totalCost: updatedChain.totalCost, totalTokens: updatedChain.totalTokens, duration: (endTime - startTime) / 1000 }; } } /** * Execute a single node within a chain. */ async executeNode(chainId, nodeId, allInputs, previousResults) { const chain = this.manager.getChain(chainId); const node = chain.nodes[nodeId]; if (!node) { throw new exceptions_1.ChainError(`Node ${nodeId} not found in chain ${chainId}`); } // Start node execution this.manager.startNode(chainId, nodeId); try { // Get messages for this node let messages = allInputs[node.tag] || []; // If node has dependencies, incorporate their results if (node.dependencies.length > 0) { const depMessages = []; // Add context from dependency results for (const depId of node.dependencies) { const depNode = chain.nodes[depId]; if (depNode && previousResults[depNode.tag]) { depMessages.push({ role: 'assistant', content: `Result from ${depNode.tag}: ${previousResults[depNode.tag].content}` }); } } // Combine dependency context with node input messages = [...depMessages, ...messages]; } // Execute the LLM call const response = await registry_1.registry.chatCompletion(node.model, messages); // Complete node this.manager.completeNode(chainId, nodeId, response); return response; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.manager.failNode(chainId, nodeId, errorMessage); throw error; } } /** * Execute a simple chain with sequential execution. */ async executeSimpleChain(chainId, inputs) { const plan = this.createExecutionPlan(chainId, inputs); return await this.executeChain(plan); } /** * Get execution statistics for a chain. */ getExecutionStats(chainId) { return this.manager.getChainStats(chainId); } /** * Retry failed nodes in a chain. */ async retryFailedNodes(chainId, inputs, maxRetries = 3) { const chain = this.manager.getChain(chainId); // Find failed nodes const failedNodes = Object.entries(chain.nodes) .filter(([_, node]) => node.status === models_1.ChainStatus.FAILED) .map(([nodeId, _]) => nodeId); if (failedNodes.length === 0) { throw new exceptions_1.ChainError(`No failed nodes found in chain ${chainId}`); } // Reset failed nodes to pending for (const nodeId of failedNodes) { const node = chain.nodes[nodeId]; node.status = models_1.ChainStatus.PENDING; node.error = undefined; node.retryCount = Math.min(node.retryCount + 1, maxRetries); if (node.retryCount >= maxRetries) { throw new exceptions_1.ChainError(`Node ${nodeId} has exceeded maximum retry attempts (${maxRetries})`); } } // Re-execute the chain return await this.executeSimpleChain(chainId, inputs); } /** * Cancel a running chain. */ cancelChain(chainId, reason = 'Cancelled by user') { const chain = this.manager.getChain(chainId); if (chain.status === models_1.ChainStatus.RUNNING) { this.manager.failChain(chainId, reason); // Cancel all in-progress nodes for (const [nodeId, node] of Object.entries(chain.nodes)) { if (node.status === models_1.ChainStatus.RUNNING) { this.manager.failNode(chainId, nodeId, reason); } } } } /** * Get chain execution progress. */ getExecutionProgress(chainId) { const chain = this.manager.getChain(chainId); const nodes = Object.values(chain.nodes); const completed = nodes.filter(node => node.status === models_1.ChainStatus.COMPLETED).length; const total = nodes.length; const percentage = total > 0 ? (completed / total) * 100 : 0; // Find currently executing nodes const currentPhase = Object.entries(chain.nodes) .filter(([_, node]) => node.status === models_1.ChainStatus.RUNNING) .map(([_, node]) => node.tag); return { completed, total, percentage, currentPhase }; } } exports.ChainExecutor = ChainExecutor; //# sourceMappingURL=executor.js.map