prompt-version-manager
Version:
Centralized prompt management system for Human Behavior AI agents
259 lines • 10.2 kB
JavaScript
;
/**
* 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