UNPKG

@flatfile/improv

Version:

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

564 lines (449 loc) โ€ข 16.6 kB
# @flatfile/improv A powerful TypeScript library for building AI agents with multi-threaded conversations, tool execution, and event handling capabilities. Built on top of AWS Bedrock (Claude) with support for advanced features like attachments, tool calling, and event streaming. ## Core Features - ๐Ÿค– **AI Agent Management**: Create and manage AI agents with knowledge bases, instructions, and memory - ๐Ÿงต **Multi-threaded Conversations**: Handle parallel conversation threads with state management - ๐Ÿ› ๏ธ **Tool Integration**: Define and execute custom tools with type-safe parameters using Zod schemas - ๐Ÿ“Ž **Attachment Support**: Handle various types of attachments (documents, images, videos) - ๐Ÿ”„ **Event Streaming**: Built-in event system for real-time monitoring and response handling - ๐Ÿ’พ **Memory Management**: Track and persist conversation history and agent state - ๐Ÿ”ง **AWS Bedrock Integration**: Native support for AWS Bedrock (Claude) with optimized prompting ## Table of Contents - [Quick Start](#quick-start) - [Core Components](#core-components) - [Agent](#agent) - [Thread](#thread) - [Tool](#tool) - [Message](#message) - [Event System](#event-system) - [Best Practices](#best-practices) - [License](#license) - [Contributing](#contributing) - [Follow-up Messages](#follow-up-messages) - [State Management](#state-management) - [Agent State](#agent-state) - [Thread State](#thread-state) - [Tool State](#tool-state) - [Evaluator System](#evaluator-system) - [State Management Patterns](#state-management-patterns) - [Three-Keyed Lock Pattern](#three-keyed-lock-pattern) - [AWS Bedrock Integration](#aws-bedrock-integration) - [Model Drivers](#model-drivers) - [Tool Decorators](#tool-decorators) - [Streaming Support](#streaming-support) - [Reasoning Capabilities](#reasoning-capabilities) ## Quick Start ```typescript import { Agent, Tool, BedrockThreadDriver } from '@flatfile/improv'; import { z } from 'zod'; // Create a custom tool const calculatorTool = new Tool({ name: 'calculator', description: 'Performs basic arithmetic operations', parameters: z.object({ operation: z.enum(['add', 'subtract', 'multiply', 'divide']), a: z.number(), b: z.number(), }), executeFn: async (args) => { const { operation, a, b } = args; switch (operation) { case 'add': return a + b; case 'subtract': return a - b; case 'multiply': return a * b; case 'divide': return a / b; } } }); // Initialize the Bedrock driver const driver = new BedrockThreadDriver({ model: 'anthropic.claude-3-haiku-20240307-v1:0', temperature: 0.7, }); // Create an agent const agent = new Agent({ knowledge: [ { fact: 'The agent can perform basic arithmetic operations.' } ], instructions: [ { instruction: 'Use the calculator tool for arithmetic operations.', priority: 1 } ], tools: [calculatorTool], driver, }); // Create and use a thread const thread = agent.createThread({ prompt: 'What is 25 multiplied by 4?', onResponse: async (message) => { console.log('Agent response:', message.content); } }); // Send the thread await thread.send(); // Stream the response const stream = await thread.stream(); for await (const text of stream) { process.stdout.write(text); // Print each chunk as it arrives } ``` ## Reasoning Capabilities Improv supports advanced reasoning capabilities through the `reasoning_config` option in the thread driver. This allows the AI to perform step-by-step reasoning before providing a final answer. ```typescript import { Agent, BedrockThreadDriver } from '@flatfile/improv'; const driver = new BedrockThreadDriver({ model: 'anthropic.claude-3-7-sonnet-20250219-v1:0', temperature: 1, reasoning_config: { budget_tokens: 1024, type: 'enabled', }, }); const agent = new Agent({ driver, }); const thread = agent.createThread({ systemPrompt: 'You are a helpful assistant that can answer questions about the world.', prompt: 'How many people will live in the world in 2040?', }); const result = await thread.send(); console.log(result.last()); ``` This example enables the AI to work through its reasoning process with a token budget of 1024 tokens before providing a final answer about population projections. ## Core Components ### Agent The main agent class that manages knowledge, instructions, tools, and conversation threads. ```typescript const agent = new Agent({ knowledge?: AgentKnowledge[], // Array of facts with optional source and timestamp instructions?: AgentInstruction[], // Array of prioritized instructions memory?: AgentMemory[], // Array of stored thread histories systemPrompt?: string, // Base system prompt tools?: Tool[], // Array of available tools driver: ThreadDriver, // Thread driver implementation evaluators?: Evaluator[] // Array of evaluators for response processing }); ``` ### Thread Manages a single conversation thread with message history and tool execution. ```typescript const thread = new Thread({ messages?: Message[], // Array of conversation messages tools?: Tool[], // Array of available tools driver: ThreadDriver, // Thread driver implementation toolChoice?: 'auto' | 'any', // Tool selection mode maxSteps?: number // Maximum number of tool execution steps }); ``` ### Tool Define custom tools that the agent can use during conversations. ```typescript const tool = new Tool({ name: string, // Tool name description: string, // Tool description parameters: z.ZodTypeAny, // Zod schema for parameter validation followUpMessage?: string, // Optional message to guide response evaluation executeFn: (args: Record<string, any>, toolCall: ToolCall) => Promise<any> // Tool execution function }); ``` ### Message Represents a single message in a conversation thread. ```typescript const message = new Message({ content?: string, // Message content role: 'system' | 'user' | 'assistant' | 'tool', // Message role toolCalls?: ToolCall[], // Array of tool calls toolResults?: ToolResult[], // Array of tool results attachments?: Attachment[], // Array of attachments cache?: boolean // Whether to cache the message }); ``` ## Event System The library uses an event-driven architecture. All major components extend `EventSource`, allowing you to listen for various events: ```typescript // Agent events agent.on('agent.thread-added', ({ agent, thread }) => {}); agent.on('agent.thread-removed', ({ agent, thread }) => {}); agent.on('agent.knowledge-added', ({ agent, knowledge }) => {}); agent.on('agent.instruction-added', ({ agent, instruction }) => {}); // Thread events thread.on('thread.response', ({ thread, message }) => {}); thread.on('thread.max_steps_reached', ({ thread, steps }) => {}); // Tool events tool.on('tool.execution.started', ({ tool, name, args }) => {}); tool.on('tool.execution.completed', ({ tool, name, args, result }) => {}); tool.on('tool.execution.failed', ({ tool, name, args, error }) => {}); ``` ## Best Practices 1. **Tool Design** - Keep tools atomic and focused on a single responsibility - Use Zod schemas for robust parameter validation - Implement proper error handling in tool execution - Use follow-up messages to guide response evaluation 2. **Thread Management** - Set appropriate `maxSteps` to prevent infinite loops (default: 30) - Use `onResponse` handlers for processing responses - Clean up threads using `closeThread()` when done - Monitor thread events for debugging and logging 3. **Agent Management** - Prioritize instructions using the priority field - Use knowledge facts to provide context - Implement evaluators for complex workflows - Leverage the memory system for persistence 4. **Event Handling** - Subscribe to relevant events for monitoring and logging - Use event data for analytics and debugging - Implement proper error handling in event listeners - Forward events with appropriate context ## License MIT ## Contributing Contributions are welcome! Please read our contributing guidelines for details. ## Follow-up Messages Tools can include follow-up messages that guide the AI's evaluation of tool responses. This is particularly useful for: - Providing context for tool results - Guiding the AI's interpretation of data - Maintaining consistent response patterns - Suggesting next steps or actions ```typescript const tool = new Tool({ name: 'dataAnalyzer', description: 'Analyzes data and returns insights', parameters: z.object({ data: z.array(z.any()), metrics: z.array(z.string()) }), followUpMessage: `Review the analysis results: 1. What are the key insights from the data? 2. Are there any concerning patterns? 3. What actions should be taken based on these results?`, executeFn: async (args) => { // Tool implementation } }); ``` ## State Management The library provides several mechanisms for managing state: ### Agent State - Knowledge base for storing facts - Prioritized instructions for behavior guidance - Memory system for storing thread histories - System prompt for base context ### Thread State - Message history tracking - Tool execution state - Maximum step limits - Response handlers ### Tool State - Parameter validation - Execution tracking - Result processing - Event emission ### Evaluator System Evaluators provide a way to process and validate agent responses: ```typescript const evaluator: Evaluator = async ({ thread, agent }, complete) => { // Process the thread response const lastMessage = thread.last(); if (lastMessage?.content.includes('done')) { complete(); // Signal completion } else { // Continue processing thread.send(new Message({ content: 'Please continue with the task...' })); } }; const agent = new Agent({ // ... other options ... evaluators: [evaluator] }); ``` Evaluators can: - Process agent responses - Trigger additional actions - Control conversation flow - Validate results ## State Management Patterns ### Three-Keyed Lock Pattern The three-keyed lock pattern is a state management pattern that ensures controlled flow through tool execution, evaluation, and completion phases. It's implemented as a reusable evaluator: ```typescript import { threeKeyedLockEvaluator } from '@flatfile/improv'; const agent = new Agent({ // ... other options ... evaluators: [ threeKeyedLockEvaluator({ evalPrompt: "Are there other items to process? If not, say 'done'", exitPrompt: "Please provide a final summary of all actions taken." }) ] }); ``` The pattern works through three distinct states: ```mermaid stateDiagram-v2 [*] --> ToolExecution state "Tool Execution" as ToolExecution { [*] --> Running Running --> Complete Complete --> [*] } state "Evaluation" as Evaluation { [*] --> CheckMore CheckMore --> [*] } state "Summary" as Summary { [*] --> Summarize Summarize --> [*] } ToolExecution --> Evaluation: Non-tool response Evaluation --> ToolExecution: Tool called Evaluation --> Summary: No more items Summary --> [*]: Complete note right of ToolExecution isEvaluatingTools = true Handles tool execution end note note right of Evaluation isEvaluatingTools = false nextMessageIsSummary = false Checks for more work end note note right of Summary nextMessageIsSummary = true Gets final summary end note ``` The evaluator manages these states through: 1. **Tool Execution State** - Tracks when tools are being executed - Resets state when new tools are called - Handles multiple tool executions 2. **Evaluation State** - Triggered after tool completion - Prompts for more items to process - Can return to tool execution if needed 3. **Summary State** - Final state before completion - Gathers summary of actions - Signals completion when done Key features: - Automatic state transitions - Event-based flow control - Clean event listener management - Configurable prompts - Support for multiple tool executions Example usage with custom prompts: ```typescript const workflowAgent = new Agent({ // ... agent configuration ... evaluators: [ threeKeyedLockEvaluator({ evalPrompt: "Review the results. Should we process more items?", exitPrompt: "Provide a detailed summary of all processed items." }) ] }); // The evaluator will automatically: // 1. Let tools execute freely // 2. After each tool completion, check if more processing is needed // 3. When no more items need processing, request a final summary // 4. Complete the evaluation after receiving the summary ``` This pattern is particularly useful for: - Processing multiple items sequentially - Workflows requiring validation between steps - Tasks with dynamic tool usage - Operations requiring final summaries ## AWS Bedrock Integration The library uses AWS Bedrock (Claude) as its default LLM provider. Configure your AWS credentials: ```typescript // Required environment variables process.env.AWS_ACCESS_KEY_ID = 'your-access-key'; process.env.AWS_SECRET_ACCESS_KEY = 'your-secret-key'; process.env.AWS_REGION = 'your-region'; // Initialize the driver const driver = new BedrockThreadDriver({ model: 'anthropic.claude-3-haiku-20240307-v1:0', // Default model temperature?: number, // Default: 0.7 maxTokens?: number, // Default: 4096 cache?: boolean // Default: false }); ``` ## Model Drivers Improv supports multiple LLM providers through dedicated thread drivers: ### Available Drivers | Driver | Provider | Documentation | |--------|----------|---------------| | `BedrockThreadDriver` | AWS Bedrock (Claude) | [Bedrock Driver Documentation](src/model.drivers/bedrock.driver.md) | | `OpenAIThreadDriver` | OpenAI | [OpenAI Driver Documentation](src/model.drivers/openai.driver.md) | | `CohereThreadDriver` | Cohere | [Cohere Driver Documentation](src/model.drivers/cohere.driver.md) | | `GeminiThreadDriver` | Google Gemini | [Gemini Driver Documentation](src/model.drivers/gemini.driver.md) | | `CerebrasThreadDriver` | Cerebras | [Cerebras Driver Documentation](src/model.drivers/cerebras.driver.md) | Each driver provides a consistent interface while supporting model-specific features: ```typescript // OpenAI example import { OpenAIThreadDriver } from '@flatfile/improv'; const driver = new OpenAIThreadDriver({ model: 'gpt-4o', apiKey: process.env.OPENAI_API_KEY, temperature: 0.7 }); // Cohere example import { CohereThreadDriver } from '@flatfile/improv'; const driver = new CohereThreadDriver({ model: 'command-r-plus', apiKey: process.env.COHERE_API_KEY }); // Gemini example import { GeminiThreadDriver } from '@flatfile/improv'; const driver = new GeminiThreadDriver({ model: 'gemini-1.5-pro', apiKey: process.env.GOOGLE_API_KEY }); // Cerebras example import { CerebrasThreadDriver } from '@flatfile/improv'; const driver = new CerebrasThreadDriver({ model: 'llama-4-scout-17b-16e-instruct', apiKey: process.env.CEREBRAS_API_KEY }); ``` Refer to each driver's documentation for available models and specific configuration options. ## Tool Decorators The library provides decorators for creating tools directly on agent classes: ```typescript class CustomAgent extends Agent { @ToolName("sampleData") @ToolDescription("Sample the original data with the mapping program") private async sampleData( @ToolParam("count", "Number of records to sample", z.number()) count: number, @ToolParam("seed", "Random seed", z.number().optional()) seed?: number ): Promise<any> { return { count, seed }; } } ``` This provides: - Type-safe tool definitions - Automatic parameter validation - Clean method-based tools - Integrated error handling ## Streaming Support The library provides built-in support for streaming responses from the AI model. Keys features: - Real-time text chunks as they're generated - Automatic message management in the thread - Event emission for stream progress - Error handling and recovery - Compatible with all thread features including tool calls ```typescript const thread = agent.createThread({ prompt: 'What is 25 multiplied by 4?', }); const stream = await thread.stream(); for await (const text of stream) { process.stdout.write(text); } // The final response is also available in the thread console.log(thread.last()?.content); ```