backpackflow
Version:
A config-driven LLM framework built on top of PocketFlow
340 lines • 11.4 kB
JavaScript
"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