UNPKG

@hotmeshio/hotmesh

Version:

Permanent-Memory Workflows & AI Agents

331 lines (330 loc) 10.4 kB
import { ContextType, WorkflowInterceptor } from '../../types/memflow'; import { guid } from '../../modules/utils'; import { ClientService } from './client'; import { ConnectionService } from './connection'; import { Search } from './search'; import { Entity } from './entity'; import { WorkerService } from './worker'; import { WorkflowService } from './workflow'; import { WorkflowHandleService } from './handle'; import { didInterrupt } from './workflow/interruption'; /** * The MemFlow service provides a Temporal-compatible workflow framework backed by * Postgres. It offers durable execution, entity-based memory management, * and composable workflows. * * ## Core Features * * ### 1. Entity-Based Memory Model * Each workflow has a durable JSONB entity that serves as its memory: * ```typescript * export async function researchAgent(query: string) { * const agent = await MemFlow.workflow.entity(); * * // Initialize entity state * await agent.set({ * query, * findings: [], * status: 'researching' * }); * * // Update state atomically * await agent.merge({ status: 'analyzing' }); * await agent.append('findings', newFinding); * } * ``` * * ### 2. Hook Functions & Workflow Coordination * Spawn and coordinate multiple perspectives/phases: * ```typescript * // Launch parallel research perspectives * await MemFlow.workflow.execHook({ * taskQueue: 'research', * workflowName: 'optimisticView', * args: [query], * signalId: 'optimistic-complete' * }); * * await MemFlow.workflow.execHook({ * taskQueue: 'research', * workflowName: 'skepticalView', * args: [query], * signalId: 'skeptical-complete' * }); * * // Wait for both perspectives * await Promise.all([ * MemFlow.workflow.waitFor('optimistic-complete'), * MemFlow.workflow.waitFor('skeptical-complete') * ]); * ``` * * ### 3. Durable Activities & Proxies * Define and execute durable activities with automatic retry: * ```typescript * // Default: activities use workflow's task queue * const activities = MemFlow.workflow.proxyActivities<{ * analyzeDocument: typeof analyzeDocument; * validateFindings: typeof validateFindings; * }>({ * activities: { analyzeDocument, validateFindings }, * retryPolicy: { * maximumAttempts: 3, * backoffCoefficient: 2 * } * }); * * // Activities are durable and automatically retried * const analysis = await activities.analyzeDocument(data); * const validation = await activities.validateFindings(analysis); * ``` * * ### 4. Explicit Activity Registration * Register activity workers explicitly before workflows start: * ```typescript * // Register shared activity pool for interceptors * await MemFlow.registerActivityWorker({ * connection: { * class: Postgres, * options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' } * }, * taskQueue: 'shared-activities' * }, sharedActivities, 'shared-activities'); * * // Register custom activity pool for specific use cases * await MemFlow.registerActivityWorker({ * connection: { * class: Postgres, * options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' } * }, * taskQueue: 'priority-activities' * }, priorityActivities, 'priority-activities'); * ``` * * ### 5. Workflow Composition * Build complex workflows through composition: * ```typescript * // Start a child workflow * const childResult = await MemFlow.workflow.execChild({ * taskQueue: 'analysis', * workflowName: 'detailedAnalysis', * args: [data], * // Child workflow config * config: { * maximumAttempts: 5, * backoffCoefficient: 2 * } * }); * * // Fire-and-forget child workflow * await MemFlow.workflow.startChild({ * taskQueue: 'notifications', * workflowName: 'sendUpdates', * args: [updates] * }); * ``` * * ### 6. Workflow Interceptors * Add cross-cutting concerns through interceptors that run as durable functions: * ```typescript * // First register shared activity worker for interceptors * await MemFlow.registerActivityWorker({ * connection: { * class: Postgres, * options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' } * }, * taskQueue: 'interceptor-activities' * }, { auditLog }, 'interceptor-activities'); * * // Add audit interceptor that uses activities with explicit taskQueue * MemFlow.registerInterceptor({ * async execute(ctx, next) { * try { * // Interceptors use explicit taskQueue to prevent per-workflow queues * const { auditLog } = MemFlow.workflow.proxyActivities<typeof activities>({ * activities: { auditLog }, * taskQueue: 'interceptor-activities', // Explicit shared queue * retryPolicy: { maximumAttempts: 3 } * }); * * await auditLog(ctx.get('workflowId'), 'started'); * * const result = await next(); * * await auditLog(ctx.get('workflowId'), 'completed'); * * return result; * } catch (err) { * // CRITICAL: Always check for HotMesh interruptions * if (MemFlow.didInterrupt(err)) { * throw err; // Rethrow for replay system * } * throw err; * } * } * }); * ``` * * ## Basic Usage Example * * ```typescript * import { Client, Worker, MemFlow } from '@hotmeshio/hotmesh'; * import { Client as Postgres } from 'pg'; * import * as activities from './activities'; * * // (Optional) Register shared activity workers for interceptors * await MemFlow.registerActivityWorker({ * connection: { * class: Postgres, * options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' } * }, * taskQueue: 'shared-activities' * }, sharedActivities, 'shared-activities'); * * // Initialize worker * await Worker.create({ * connection: { * class: Postgres, * options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' } * }, * taskQueue: 'default', * workflow: workflows.example * }); * * // Initialize client * const client = new Client({ * connection: { * class: Postgres, * options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' } * } * }); * * // Start workflow * const handle = await client.workflow.start({ * args: ['input data'], * taskQueue: 'default', * workflowName: 'example', * workflowId: MemFlow.guid() * }); * * // Get result * const result = await handle.result(); * * // Cleanup * await MemFlow.shutdown(); * ``` */ declare class MemFlowClass { /** * @private */ constructor(); /** * The MemFlow `Client` service is functionally * equivalent to the Temporal `Client` service. */ static Client: typeof ClientService; /** * The MemFlow `Connection` service is functionally * equivalent to the Temporal `Connection` service. */ static Connection: typeof ConnectionService; /** * @private */ static Search: typeof Search; /** * @private */ static Entity: typeof Entity; /** * The Handle provides methods to interact with a running * workflow. This includes exporting the workflow, sending signals, and * querying the state of the workflow. An instance of the Handle service * is typically accessed via the MemFlow.Client class (workflow.getHandle). */ static Handle: typeof WorkflowHandleService; /** * The MemFlow `Worker` service is functionally * equivalent to the Temporal `Worker` service. */ static Worker: typeof WorkerService; /** * Register activity workers for a task queue. Activities execute via message queue * and can run on different servers from workflows. * * @example * ```typescript * // Activity worker * const activities = { * async processPayment(amount: number) { return `Processed $${amount}`; }, * async sendEmail(to: string, msg: string) { /* ... *\/ } * }; * * await MemFlow.registerActivityWorker({ * connection: { * class: Postgres, * options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' } * }, * taskQueue: 'payment' * }, activities, 'payment'); * * // Workflow worker (can be on different server) * async function orderWorkflow(amount: number) { * const { processPayment, sendEmail } = MemFlow.workflow.proxyActivities<{ * processPayment: (amount: number) => Promise<string>; * sendEmail: (to: string, msg: string) => Promise<void>; * }>({ * taskQueue: 'payment', * retryPolicy: { maximumAttempts: 3 } * }); * * const result = await processPayment(amount); * await sendEmail('customer@example.com', result); * return result; * } * * await MemFlow.Worker.create({ * connection: { class: Postgres, options: { connectionString: '...' } }, * taskQueue: 'orders', * workflow: orderWorkflow * }); * ``` */ static registerActivityWorker: typeof WorkerService.registerActivityWorker; /** * The MemFlow `workflow` service is functionally * equivalent to the Temporal `Workflow` service * with additional methods for managing workflows, * including: `execChild`, `waitFor`, `sleep`, etc */ static workflow: typeof WorkflowService; /** * Checks if an error is a HotMesh reserved error type that indicates * a workflow interruption rather than a true error condition. * * @see {@link utils/interruption.didInterrupt} for detailed documentation */ static didInterrupt: typeof didInterrupt; private static interceptorService; /** * Register a workflow interceptor * @param interceptor The interceptor to register */ static registerInterceptor(interceptor: WorkflowInterceptor): void; /** * Clear all registered workflow interceptors */ static clearInterceptors(): void; /** * Generate a unique identifier for workflow IDs */ static guid: typeof guid; /** * Shutdown everything. All connections, workers, and clients will be closed. * Include in your signal handlers to ensure a clean shutdown. */ static shutdown(): Promise<void>; } export { MemFlowClass as MemFlow }; export type { ContextType };