UNPKG

backpackflow

Version:

A config-driven LLM framework built on top of PocketFlow

340 lines 11.4 kB
"use strict"; /** * EventFlow - Enhanced Flow with comprehensive event streaming * * Extends the base Flow class to provide: * - Flow lifecycle events (start/complete/error) * - Node transition events * - Enhanced error handling with events * - Flow statistics and monitoring */ Object.defineProperty(exports, "__esModule", { value: true }); exports.EventParallelBatchFlow = exports.EventBatchFlow = exports.EventFlow = void 0; exports.createEventFlow = createEventFlow; exports.createEventBatchFlow = createEventBatchFlow; exports.createEventParallelBatchFlow = createEventParallelBatchFlow; exports.enhanceFlowWithEvents = enhanceFlowWithEvents; const pocketflow_1 = require("../pocketflow"); /** * EventFlow - Flow with comprehensive event streaming */ class EventFlow extends pocketflow_1.Flow { constructor(startNode, config = {}) { super(startNode); this.flowId = config.flowId || `flow-${Date.now()}`; this.eventStream = config.eventStream; this.enableTransitionEvents = config.enableTransitionEvents ?? true; this.enableTimingEvents = config.enableTimingEvents ?? true; } async _orchestrate(shared, params) { this.startTime = Date.now(); // Emit flow start event this.eventStream?.emit('flow:start', { flowId: this.flowId, startNode: this.getNodeId(this.start), timestamp: this.startTime }); let current = this.start.clone(); let previousNodeId = null; const mergedParams = params || this._params; try { while (current) { const currentNodeId = this.getNodeId(current); // Emit flow transition event if (this.enableTransitionEvents && previousNodeId) { this.eventStream?.emit('flow:transition', { flowId: this.flowId, fromNode: previousNodeId, toNode: currentNodeId, action: 'default', // Could be enhanced to track actual action timestamp: Date.now() }); } // Set parameters and run node current.setParams(mergedParams); const nodeStartTime = Date.now(); const action = await current._run(shared); const nodeEndTime = Date.now(); // Emit timing event if enabled if (this.enableTimingEvents) { this.eventStream?.emit('node:stop', { nodeType: current.constructor.name, nodeId: currentNodeId, phase: 'exec', duration: nodeEndTime - nodeStartTime, result: action, timestamp: nodeEndTime }); } // Move to next node previousNodeId = currentNodeId; current = current.getNextNode(action); current = current?.clone(); } // Emit flow complete event const duration = Date.now() - this.startTime; this.eventStream?.emit('flow:complete', { flowId: this.flowId, duration, finalNode: previousNodeId || 'unknown', timestamp: Date.now() }); } catch (error) { // Emit flow error event this.eventStream?.emit('flow:error', { flowId: this.flowId, error: error.message, currentNode: current ? this.getNodeId(current) : undefined, timestamp: Date.now() }); // Re-throw the error throw error; } } /** * Enhanced run method with flow events */ async run(shared) { try { return await super.run(shared); } catch (error) { // Additional error handling for flow-level errors this.eventStream?.emit('error:flow', { error: error.message, currentNode: this.getNodeId(this.start), stack: error.stack, timestamp: Date.now() }); throw error; } } /** * Set the event stream for this flow */ setEventStream(eventStream) { this.eventStream = eventStream; // Propagate to all nodes if they support event streaming this.propagateEventStreamToNodes(this.start, eventStream); } /** * Get the current event stream */ getEventStream() { return this.eventStream; } /** * Get the flow ID */ getFlowId() { return this.flowId; } /** * Enable or disable transition events */ setTransitionEventsEnabled(enabled) { this.enableTransitionEvents = enabled; } /** * Enable or disable timing events */ setTimingEventsEnabled(enabled) { this.enableTimingEvents = enabled; } /** * Get flow statistics */ getFlowStats() { return { flowId: this.flowId, runtime: this.startTime ? Date.now() - this.startTime : undefined, eventsEnabled: !!this.eventStream, eventStream: this.eventStream?.getStats() }; } // Private helper methods getNodeId(node) { // Try to get nodeId from the node if it has one if ('getNodeId' in node && typeof node.getNodeId === 'function') { return node.getNodeId(); } // Try to get nodeId property if ('nodeId' in node) { return node.nodeId; } // Fall back to constructor name with timestamp return `${node.constructor.name}-${Date.now()}`; } propagateEventStreamToNodes(node, eventStream) { // Set event stream on the node if it supports it if ('setEventStream' in node && typeof node.setEventStream === 'function') { node.setEventStream(eventStream); } // Recursively propagate to successor nodes const successors = node._successors; if (successors && successors instanceof Map) { for (const successor of successors.values()) { this.propagateEventStreamToNodes(successor, eventStream); } } } } exports.EventFlow = EventFlow; /** * EventBatchFlow - Batch Flow with event streaming */ class EventBatchFlow extends pocketflow_1.BatchFlow { constructor(startNode, config = {}) { super(startNode); this.flowId = config.flowId || `batch-flow-${Date.now()}`; this.eventStream = config.eventStream; } async _run(shared) { const startTime = Date.now(); // Emit flow start event this.eventStream?.emit('flow:start', { flowId: this.flowId, startNode: this.getNodeId(this.start), timestamp: startTime }); try { const result = await super._run(shared); // Emit flow complete event const duration = Date.now() - startTime; this.eventStream?.emit('flow:complete', { flowId: this.flowId, duration, finalNode: this.getNodeId(this.start), timestamp: Date.now() }); return result; } catch (error) { // Emit flow error event this.eventStream?.emit('flow:error', { flowId: this.flowId, error: error.message, currentNode: this.getNodeId(this.start), timestamp: Date.now() }); throw error; } } setEventStream(eventStream) { this.eventStream = eventStream; } getEventStream() { return this.eventStream; } getFlowId() { return this.flowId; } getNodeId(node) { if ('getNodeId' in node && typeof node.getNodeId === 'function') { return node.getNodeId(); } if ('nodeId' in node) { return node.nodeId; } return `${node.constructor.name}-${Date.now()}`; } } exports.EventBatchFlow = EventBatchFlow; /** * EventParallelBatchFlow - Parallel Batch Flow with event streaming */ class EventParallelBatchFlow extends pocketflow_1.ParallelBatchFlow { constructor(startNode, config = {}) { super(startNode); this.flowId = config.flowId || `parallel-batch-flow-${Date.now()}`; this.eventStream = config.eventStream; } async _run(shared) { const startTime = Date.now(); // Emit flow start event this.eventStream?.emit('flow:start', { flowId: this.flowId, startNode: this.getNodeId(this.start), timestamp: startTime }); try { const result = await super._run(shared); // Emit flow complete event const duration = Date.now() - startTime; this.eventStream?.emit('flow:complete', { flowId: this.flowId, duration, finalNode: this.getNodeId(this.start), timestamp: Date.now() }); return result; } catch (error) { // Emit flow error event this.eventStream?.emit('flow:error', { flowId: this.flowId, error: error.message, currentNode: this.getNodeId(this.start), timestamp: Date.now() }); throw error; } } setEventStream(eventStream) { this.eventStream = eventStream; } getEventStream() { return this.eventStream; } getFlowId() { return this.flowId; } getNodeId(node) { if ('getNodeId' in node && typeof node.getNodeId === 'function') { return node.getNodeId(); } if ('nodeId' in node) { return node.nodeId; } return `${node.constructor.name}-${Date.now()}`; } } exports.EventParallelBatchFlow = EventParallelBatchFlow; /** * Factory functions for creating event-enabled flows */ /** * Create an EventFlow with the specified configuration */ function createEventFlow(startNode, config) { return new EventFlow(startNode, config); } /** * Create an EventBatchFlow with the specified configuration */ function createEventBatchFlow(startNode, config) { return new EventBatchFlow(startNode, config); } /** * Create an EventParallelBatchFlow with the specified configuration */ function createEventParallelBatchFlow(startNode, config) { return new EventParallelBatchFlow(startNode, config); } /** * Utility function to convert a regular Flow to an EventFlow */ function enhanceFlowWithEvents(flow, eventStream, flowId) { const eventFlow = new EventFlow(flow.start, { flowId, eventStream, enableTransitionEvents: true, enableTimingEvents: true }); // Copy any parameters from the original flow // Note: _params is protected, so we use a type assertion for this utility function eventFlow._params = flow._params; return eventFlow; } //# sourceMappingURL=event-flow.js.map