prompt-version-manager
Version:
Centralized prompt management system for Human Behavior AI agents
382 lines • 14.2 kB
JavaScript
;
/**
* Chain management for tracking agent chains in TypeScript.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChainManager = void 0;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const uuid_1 = require("uuid");
const models_1 = require("../core/models");
const exceptions_1 = require("../core/exceptions");
class ChainManager {
// private repoPath: string;
activeChains = new Map();
chainMetadataPath;
constructor(repoPath = '.pvm') {
// this.repoPath = repoPath;
this.chainMetadataPath = path.join(repoPath, 'chains.json');
this.loadChainMetadata();
}
async loadChainMetadata() {
try {
const data = await fs.readFile(this.chainMetadataPath, 'utf-8');
const chainData = JSON.parse(data);
for (const [chainId, chainInfo] of Object.entries(chainData)) {
const chain = this.deserializeChain(chainInfo);
this.activeChains.set(chainId, chain);
}
}
catch (error) {
// File doesn't exist or invalid JSON - start with empty chains
console.warn(`Warning: Failed to load chain metadata: ${error}`);
}
}
async saveChainMetadata() {
try {
// Ensure directory exists
await fs.mkdir(path.dirname(this.chainMetadataPath), { recursive: true });
const data = {};
for (const [chainId, chain] of this.activeChains.entries()) {
data[chainId] = this.serializeChain(chain);
}
await fs.writeFile(this.chainMetadataPath, JSON.stringify(data, null, 2));
}
catch (error) {
console.warn(`Warning: Failed to save chain metadata: ${error}`);
}
}
serializeChain(chain) {
return {
id: chain.id,
name: chain.name,
description: chain.description,
status: chain.status,
startTime: chain.startTime?.toISOString(),
endTime: chain.endTime?.toISOString(),
totalCost: chain.totalCost,
totalTokens: chain.totalTokens,
metadata: chain.metadata || {},
nodes: Object.fromEntries(Object.entries(chain.nodes).map(([nodeId, node]) => [
nodeId,
{
id: node.id,
tag: node.tag,
model: node.model,
provider: node.provider,
language: node.language,
dependencies: node.dependencies,
status: node.status,
startTime: node.startTime?.toISOString(),
endTime: node.endTime?.toISOString(),
result: node.result,
error: node.error,
retryCount: node.retryCount,
metadata: node.metadata || {}
}
]))
};
}
deserializeChain(data) {
const chain = {
id: data.id,
name: data.name,
description: data.description,
status: data.status,
startTime: data.startTime ? new Date(data.startTime) : undefined,
endTime: data.endTime ? new Date(data.endTime) : undefined,
totalCost: data.totalCost,
totalTokens: data.totalTokens,
metadata: data.metadata,
nodes: {}
};
// Deserialize nodes
for (const [nodeId, nodeData] of Object.entries(data.nodes)) {
const node = {
id: nodeData.id,
tag: nodeData.tag,
model: nodeData.model,
provider: nodeData.provider,
language: nodeData.language,
dependencies: nodeData.dependencies,
status: nodeData.status,
startTime: nodeData.startTime ? new Date(nodeData.startTime) : undefined,
endTime: nodeData.endTime ? new Date(nodeData.endTime) : undefined,
result: nodeData.result,
error: nodeData.error,
retryCount: nodeData.retryCount,
metadata: nodeData.metadata
};
chain.nodes[nodeId] = node;
}
return chain;
}
createChain(name, description, chainId) {
if (!chainId) {
chainId = (0, uuid_1.v4)();
}
if (this.activeChains.has(chainId)) {
throw new exceptions_1.ChainError(`Chain ${chainId} already exists`);
}
const chain = {
id: chainId,
name,
description,
status: models_1.ChainStatus.PENDING,
nodes: {},
totalCost: 0.0,
totalTokens: { input: 0, output: 0, total: 0 }
};
this.activeChains.set(chainId, chain);
this.saveChainMetadata();
return chainId;
}
addNode(chainId, tag, model, provider, language = 'typescript', dependencies = [], nodeId) {
if (!nodeId) {
nodeId = (0, uuid_1.v4)();
}
const chain = this.activeChains.get(chainId);
if (!chain) {
throw new exceptions_1.ChainNotFoundError(`Chain ${chainId} not found`);
}
if (chain.nodes[nodeId]) {
throw new exceptions_1.ChainError(`Node ${nodeId} already exists in chain ${chainId}`);
}
// Validate dependencies exist
for (const depId of dependencies) {
if (!chain.nodes[depId]) {
throw new exceptions_1.ChainError(`Dependency ${depId} not found in chain ${chainId}`);
}
}
// Check for circular dependencies
this.checkCircularDependency(chain, nodeId, dependencies);
const node = {
id: nodeId,
tag,
model,
provider,
language,
dependencies,
status: models_1.ChainStatus.PENDING,
retryCount: 0
};
chain.nodes[nodeId] = node;
this.saveChainMetadata();
return nodeId;
}
checkCircularDependency(chain, newNodeId, dependencies) {
const hasPathTo = (fromNode, toNode, visited) => {
if (fromNode === toNode) {
return true;
}
if (visited.has(fromNode)) {
return false;
}
visited.add(fromNode);
const node = chain.nodes[fromNode];
if (node) {
for (const dep of node.dependencies) {
if (hasPathTo(dep, toNode, visited)) {
return true;
}
}
}
return false;
};
// Check if adding this node would create a cycle
for (const depId of dependencies) {
if (hasPathTo(depId, newNodeId, new Set())) {
throw new exceptions_1.CircularDependencyError(`Adding node ${newNodeId} with dependency ${depId} would create a circular dependency`);
}
}
}
startChain(chainId) {
const chain = this.activeChains.get(chainId);
if (!chain) {
throw new exceptions_1.ChainNotFoundError(`Chain ${chainId} not found`);
}
chain.status = models_1.ChainStatus.RUNNING;
chain.startTime = new Date();
this.saveChainMetadata();
}
completeChain(chainId) {
const chain = this.activeChains.get(chainId);
if (!chain) {
throw new exceptions_1.ChainNotFoundError(`Chain ${chainId} not found`);
}
chain.status = models_1.ChainStatus.COMPLETED;
chain.endTime = new Date();
this.saveChainMetadata();
}
failChain(chainId, error) {
const chain = this.activeChains.get(chainId);
if (!chain) {
throw new exceptions_1.ChainNotFoundError(`Chain ${chainId} not found`);
}
chain.status = models_1.ChainStatus.FAILED;
chain.endTime = new Date();
if (!chain.metadata) {
chain.metadata = {};
}
chain.metadata.error = error;
this.saveChainMetadata();
}
startNode(chainId, nodeId) {
const chain = this.activeChains.get(chainId);
if (!chain) {
throw new exceptions_1.ChainNotFoundError(`Chain ${chainId} not found`);
}
const node = chain.nodes[nodeId];
if (!node) {
throw new exceptions_1.ChainError(`Node ${nodeId} not found in chain ${chainId}`);
}
node.status = models_1.ChainStatus.RUNNING;
node.startTime = new Date();
this.saveChainMetadata();
}
completeNode(chainId, nodeId, result) {
const chain = this.activeChains.get(chainId);
if (!chain) {
throw new exceptions_1.ChainNotFoundError(`Chain ${chainId} not found`);
}
const node = chain.nodes[nodeId];
if (!node) {
throw new exceptions_1.ChainError(`Node ${nodeId} not found in chain ${chainId}`);
}
node.status = models_1.ChainStatus.COMPLETED;
node.endTime = new Date();
node.result = result;
// Update chain totals
chain.totalCost += result.cost;
chain.totalTokens.input += result.tokens.input;
chain.totalTokens.output += result.tokens.output;
chain.totalTokens.total += result.tokens.total;
this.saveChainMetadata();
}
failNode(chainId, nodeId, error) {
const chain = this.activeChains.get(chainId);
if (!chain) {
throw new exceptions_1.ChainNotFoundError(`Chain ${chainId} not found`);
}
const node = chain.nodes[nodeId];
if (!node) {
throw new exceptions_1.ChainError(`Node ${nodeId} not found in chain ${chainId}`);
}
node.status = models_1.ChainStatus.FAILED;
node.endTime = new Date();
node.error = error;
this.saveChainMetadata();
}
getChain(chainId) {
const chain = this.activeChains.get(chainId);
if (!chain) {
throw new exceptions_1.ChainNotFoundError(`Chain ${chainId} not found`);
}
return chain;
}
listChains() {
return Array.from(this.activeChains.values());
}
getReadyNodes(chainId) {
const chain = this.activeChains.get(chainId);
if (!chain) {
throw new exceptions_1.ChainNotFoundError(`Chain ${chainId} not found`);
}
const readyNodes = [];
for (const [nodeId, node] of Object.entries(chain.nodes)) {
if (node.status !== models_1.ChainStatus.PENDING) {
continue;
}
// Check if all dependencies are completed
const allDepsCompleted = node.dependencies.every(depId => {
const depNode = chain.nodes[depId];
return depNode && depNode.status === models_1.ChainStatus.COMPLETED;
});
if (allDepsCompleted) {
readyNodes.push(nodeId);
}
}
return readyNodes;
}
getChainStats(chainId) {
const chain = this.activeChains.get(chainId);
if (!chain) {
throw new exceptions_1.ChainNotFoundError(`Chain ${chainId} not found`);
}
const nodeStats = {
total: Object.keys(chain.nodes).length,
pending: 0,
inProgress: 0,
completed: 0,
failed: 0
};
for (const node of Object.values(chain.nodes)) {
switch (node.status) {
case models_1.ChainStatus.PENDING:
nodeStats.pending++;
break;
case models_1.ChainStatus.RUNNING:
nodeStats.inProgress++;
break;
case models_1.ChainStatus.COMPLETED:
nodeStats.completed++;
break;
case models_1.ChainStatus.FAILED:
nodeStats.failed++;
break;
}
}
return {
chainId,
name: chain.name,
status: chain.status,
nodes: nodeStats,
totalCost: chain.totalCost,
totalTokens: chain.totalTokens,
duration: chain.startTime && chain.endTime
? (chain.endTime.getTime() - chain.startTime.getTime()) / 1000
: null
};
}
async getChainNodes(chainId) {
const chain = await this.getChain(chainId);
if (!chain || !chain.nodes)
return [];
return Object.values(chain.nodes);
}
}
exports.ChainManager = ChainManager;
//# sourceMappingURL=manager.js.map