UNPKG

@flatfile/improv

Version:

A powerful TypeScript library for building AI agents with multi-threaded conversations, tool execution, and event handling capabilities

417 lines (316 loc) 11.4 kB
# Event System Guide The Improv library uses an event-driven architecture that allows you to monitor and react to various operations throughout the system. This guide covers how to use events effectively in your applications. ## Table of Contents - [Overview](#overview) - [Event Categories](#event-categories) - [Listening to Events](#listening-to-events) - [Event Patterns](#event-patterns) - [Best Practices](#best-practices) - [Complete Event Reference](#complete-event-reference) ## Overview All major components in Improv extend the `EventSource` class, which provides a consistent event interface. Events are emitted for key operations like: - Message lifecycle (adding, sending, receiving) - Thread state changes (errors, tool additions) - Agent operations (thread creation, knowledge updates) - Tool execution (started, completed, failed) - Workflow orchestration (gig and piece lifecycle) ## Event Categories ### Message Events Events related to individual message operations: ```typescript import { MESSAGE_EVENTS } from '@flatfile/improv'; // Available events: MESSAGE_EVENTS.ADDED // message.added MESSAGE_EVENTS.REMOVED // message.removed MESSAGE_EVENTS.SENT // message.sent MESSAGE_EVENTS.RECEIVED // message.received MESSAGE_EVENTS.CLEARED // messages.cleared ``` ### Thread Events Events related to thread-level operations: ```typescript import { THREAD_EVENTS } from '@flatfile/improv'; // Available events: THREAD_EVENTS.MAX_STEPS_REACHED // thread.max-steps-reached THREAD_EVENTS.ERROR_OCCURRED // thread.error-occurred THREAD_EVENTS.TOOL_ADDED // thread.tool-added ``` ### Tool Events Events related to tool execution: ```typescript import { TOOL_EVENTS } from '@flatfile/improv'; // Available events: TOOL_EVENTS.STARTED // tool.started TOOL_EVENTS.COMPLETED // tool.completed TOOL_EVENTS.FAILED // tool.failed TOOL_EVENTS.FOLLOWUP_ADDED // tool.followup-added TOOL_EVENTS.MESSAGES_GENERATED // tool.messages-generated ``` ### Agent Events Events related to agent operations: ```typescript import { AGENT_EVENTS } from '@flatfile/improv'; // Available events: AGENT_EVENTS.TOOL_ADDED // agent.tool-added AGENT_EVENTS.THREAD_CREATED // agent.thread-created AGENT_EVENTS.THREAD_CLOSED // agent.thread-closed AGENT_EVENTS.KNOWLEDGE_ADDED // agent.knowledge-added AGENT_EVENTS.INSTRUCTION_ADDED // agent.instruction-added ``` ### Gig & Piece Events Events related to workflow orchestration: ```typescript import { GIG_EVENTS, PIECE_EVENTS } from '@flatfile/improv'; // Gig events: GIG_EVENTS.PERFORMANCE_COMPLETED // gig.performance-completed GIG_EVENTS.PERFORMANCE_FAILED // gig.performance-failed // Piece events: PIECE_EVENTS.EXECUTION_STARTED // piece.execution-started PIECE_EVENTS.EXECUTION_COMPLETED // piece.execution-completed PIECE_EVENTS.EXECUTION_FAILED // piece.execution-failed PIECE_EVENTS.EXECUTION_SKIPPED // piece.execution-skipped PIECE_EVENTS.RETRY_ATTEMPTED // piece.retry-attempted ``` ## Listening to Events ### Basic Event Listening ```typescript import { Agent, MESSAGE_EVENTS, THREAD_EVENTS } from '@flatfile/improv'; const agent = new Agent({ driver }); // Listen to message events agent.on(MESSAGE_EVENTS.ADDED, ({ thread, message }) => { console.log(`Message added to thread ${thread.id}: ${message.content}`); }); // Listen to thread events agent.on(THREAD_EVENTS.ERROR_OCCURRED, ({ thread, error }) => { console.error(`Thread ${thread.id} error:`, error.message); }); ``` ### Tool Event Monitoring ```typescript import { Tool, TOOL_EVENTS } from '@flatfile/improv'; const tool = new Tool({ name: 'calculator', description: 'Performs calculations', parameters: z.object({ operation: z.string() }), executeFn: async (args) => { // Tool implementation } }); // Monitor tool execution tool.on(TOOL_EVENTS.STARTED, ({ tool, name, args }) => { console.log(`Tool ${name} started with args:`, args); }); tool.on(TOOL_EVENTS.COMPLETED, ({ tool, name, args, result }) => { console.log(`Tool ${name} completed with result:`, result); }); tool.on(TOOL_EVENTS.FAILED, ({ tool, name, args, error }) => { console.error(`Tool ${name} failed:`, error); }); ``` ### Workflow Event Monitoring ```typescript import { Gig, PIECE_EVENTS, GIG_EVENTS } from '@flatfile/improv'; const gig = new Gig({ label: 'Data Processing', driver }); // Monitor individual pieces gig.on(PIECE_EVENTS.EXECUTION_STARTED, ({ piece }) => { console.log(`Starting piece: ${piece}`); }); gig.on(PIECE_EVENTS.EXECUTION_COMPLETED, ({ piece, recording }) => { console.log(`Completed piece ${piece} with result:`, recording); }); // Monitor overall gig performance gig.on(GIG_EVENTS.PERFORMANCE_COMPLETED, ({ gig, results }) => { console.log('Gig completed successfully:', results); }); gig.on(GIG_EVENTS.PERFORMANCE_FAILED, ({ gig, error, results }) => { console.error('Gig failed:', error, results); }); ``` ## Event Patterns ### Event Forwarding Events are automatically forwarded with context: ```typescript const agent = new Agent({ driver }); // When you listen on the agent, you'll receive events from all its threads agent.on(MESSAGE_EVENTS.ADDED, ({ thread, message, agent }) => { // The agent context is automatically added console.log(`Agent ${agent} received message in thread ${thread.id}`); }); ``` ### Wildcard Event Listening ```typescript // Listen to all events from a component agent.on('**', (eventData) => { console.log('Any event occurred:', eventData); }); // Listen to all message events agent.on('message.*', (eventData) => { console.log('Message event occurred:', eventData); }); // Listen to all tool events agent.on('tool.*', (eventData) => { console.log('Tool event occurred:', eventData); }); ``` ### Error Handling Pattern ```typescript // Centralized error handling function setupErrorHandling(component) { component.on(THREAD_EVENTS.ERROR_OCCURRED, ({ thread, error }) => { logError('Thread Error', { threadId: thread.id, error: error.message }); }); component.on(TOOL_EVENTS.FAILED, ({ tool, name, error }) => { logError('Tool Error', { toolName: name, error }); }); component.on(GIG_EVENTS.PERFORMANCE_FAILED, ({ gig, error }) => { logError('Gig Error', { gigLabel: gig.label, error: error.message }); }); } function logError(type, details) { console.error(`[${type}]`, details); // Send to monitoring service, etc. } ``` ### Progress Monitoring Pattern ```typescript // Monitor workflow progress function setupProgressMonitoring(gig) { const progress = { total: 0, completed: 0, failed: 0, skipped: 0 }; gig.on(PIECE_EVENTS.EXECUTION_STARTED, () => { progress.total++; updateProgress(progress); }); gig.on(PIECE_EVENTS.EXECUTION_COMPLETED, () => { progress.completed++; updateProgress(progress); }); gig.on(PIECE_EVENTS.EXECUTION_FAILED, () => { progress.failed++; updateProgress(progress); }); gig.on(PIECE_EVENTS.EXECUTION_SKIPPED, () => { progress.skipped++; updateProgress(progress); }); } function updateProgress(progress) { const percentage = ((progress.completed + progress.failed + progress.skipped) / progress.total) * 100; console.log(`Progress: ${percentage.toFixed(1)}% (${progress.completed} completed, ${progress.failed} failed, ${progress.skipped} skipped)`); } ``` ## Best Practices ### 1. Use Event Constants Always use the provided event constants instead of string literals: ```typescript // ✅ Good agent.on(MESSAGE_EVENTS.ADDED, handler); // ❌ Bad agent.on('message.added', handler); ``` ### 2. Handle Errors Gracefully Always handle potential errors in event handlers: ```typescript agent.on(MESSAGE_EVENTS.ADDED, ({ thread, message }) => { try { // Your event handling logic processMessage(message); } catch (error) { console.error('Error processing message event:', error); } }); ``` ### 3. Clean Up Event Listeners Remove event listeners when they're no longer needed: ```typescript const handler = ({ thread, message }) => { // Handle event }; // Add listener agent.on(MESSAGE_EVENTS.ADDED, handler); // Remove listener when done agent.off(MESSAGE_EVENTS.ADDED, handler); ``` ### 4. Use Semantic Event Names Choose the right event category for your use case: ```typescript // ✅ Good - Listening to message lifecycle thread.on(MESSAGE_EVENTS.ADDED, handleNewMessage); // ✅ Good - Listening to thread state changes thread.on(THREAD_EVENTS.ERROR_OCCURRED, handleThreadError); // ✅ Good - Listening to tool execution tool.on(TOOL_EVENTS.COMPLETED, handleToolResult); ``` ### 5. Centralize Event Monitoring Create dedicated modules for event monitoring: ```typescript // events/monitor.ts export class EventMonitor { constructor() { this.setupGlobalHandlers(); } private setupGlobalHandlers() { // Global error handling // Performance monitoring // Logging } monitorAgent(agent: Agent) { agent.on(AGENT_EVENTS.THREAD_CREATED, this.handleThreadCreated); agent.on(AGENT_EVENTS.THREAD_CLOSED, this.handleThreadClosed); } monitorGig(gig: Gig) { gig.on(GIG_EVENTS.PERFORMANCE_COMPLETED, this.handleGigCompleted); gig.on(GIG_EVENTS.PERFORMANCE_FAILED, this.handleGigFailed); } } ``` ## Complete Event Reference For a complete list of all events, their payloads, and descriptions, see the [Events Reference](./events.md). ## TypeScript Support All events are fully typed in TypeScript with multiple approaches for different use cases: ### Individual Event Payload Types ```typescript import { MessageEventPayloads, MESSAGE_EVENTS } from '@flatfile/improv'; // Event handlers are properly typed const handler = (payload: MessageEventPayloads[typeof MESSAGE_EVENTS.ADDED]) => { // payload.thread and payload.message are properly typed console.log(payload.thread.id, payload.message.content); }; ``` ### Universal Event Payload Helper ```typescript import { EventPayload, MESSAGE_EVENTS, TOOL_EVENTS } from '@flatfile/improv'; // Generic helper type that works with any event const messageHandler = (payload: EventPayload<typeof MESSAGE_EVENTS.ADDED>) => { // Fully typed: payload.thread and payload.message console.log(payload.thread.id, payload.message.content); }; const toolHandler = (payload: EventPayload<typeof TOOL_EVENTS.COMPLETED>) => { // Fully typed: payload.tool, payload.name, payload.args, payload.result console.log(`Tool ${payload.name} completed with result:`, payload.result); }; ``` ### Union Type for All Events ```typescript import { EventPayloads } from '@flatfile/improv'; // Handle any event payload const universalHandler = (payload: EventPayloads) => { // TypeScript will properly narrow the type based on usage console.log('Event received:', payload); }; ``` ### Typed Event Listeners ```typescript import { Agent, EventPayload, MESSAGE_EVENTS } from '@flatfile/improv'; const agent = new Agent({ driver }); // TypeScript infers the correct payload type agent.on(MESSAGE_EVENTS.ADDED, (payload: EventPayload<typeof MESSAGE_EVENTS.ADDED>) => { // payload.thread and payload.message are fully typed console.log(`Message added: ${payload.message.content}`); }); ``` This ensures type safety and excellent IDE support with autocomplete and error checking.