UNPKG

@flatfile/improv

Version:

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

1,643 lines (1,618 loc) 93.8 kB
import { z } from 'zod'; import { EventEmitter2 } from 'eventemitter2'; import { BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime'; import { Model } from '@anthropic-ai/sdk/resources/messages'; export { Type } from '@google/genai'; /** * Abstract base class for all event-emitting classes * Provides event forwarding functionality and type-safe event emission */ declare abstract class EventSource extends EventEmitter2 implements EventSourceInterface { constructor(); /** * Forward all events from a source EventEmitter2 instance * Preserves the original event name and merges any additional context */ protected forwardEvents(source: EventSourceInterface, context?: Record<string, any>): void; protected debug(message: string, data?: any): void; protected error(...args: any[]): void; } interface EventSourceInterface { on(event: string, listener: (...args: any[]) => void): any; } /** * Implement a class that tracks a thread of messages between the user and the assistant. * - constructor({messages: Message[], tools: Tool[], driver: BedrockThreadDriver}) - initialize the thread with a list of messages and tools * - push() - add a message to the thread * - shift() - remove the oldest message from the thread * - pop() - remove the newest message from the thread * - last() - get the oldest message from the thread * - size() - get the number of messages in the thread * - clear(filter?: (message: Message) => boolean) - remove all messages from the thread * - all() - get all messages from the thread * - handle(message: Message) - handle a message from the LLM * - includes tool calling or triggering events * - send(message?: Message) - send a message or the current thread state to the LLM * - fork() - create a new thread with the same history and tools */ interface TokenUsage { inputTokens: number; outputTokens: number; totalTokens: number; toolTokenBreakdown?: ToolTokenBreakdown[]; cacheCreationInputTokens?: number; cacheReadInputTokens?: number; newContentTokens?: number; cachedContentTokens?: number; } interface ToolTokenBreakdown { toolName: string; toolUseId: string; inputTokens: number; outputTokens: number; totalTokens: number; executionTimeMs?: number; startTime?: number; endTime?: number; } interface ThreadDriver { sendThread(thread: Thread, abortSignal?: AbortSignal): Promise<Thread>; streamThread(thread: Thread, abortSignal?: AbortSignal): AsyncGenerator<{ stream: AsyncGenerator<string, void>; message: Message; }, Thread>; } interface ThreadOptions { messages?: Message[]; tools?: ToolInterface[]; toolChoice?: "auto" | "any"; driver: ThreadDriver; maxSteps?: number; fallbackDrivers?: ThreadDriver[]; retry?: { maxAttempts?: number; delay?: number; exponentialBackoff?: boolean; }; onFallback?: (error: Error, driverIndex: number) => void; abortSignal?: AbortSignal; } declare class Thread extends EventSource { private messages; private readonly tools; readonly toolChoice: "auto" | "any"; private readonly driver; private readonly maxSteps; private currentSteps; private responseHandlers; readonly id: string; private readonly fallbackDrivers; private readonly retry; private readonly onFallback?; private tokenUsage; private lastMessageTokenUsage; private readonly abortSignal?; private toolTokenMap; constructor(options: ThreadOptions); /** * Register a handler to be called when an assistant message without tool calls is received */ onResponse(handler: (message: Message) => Promise<void>): void; /** * Execute all registered response handlers */ private executeResponseHandlers; /** * Add a message to the thread */ push(message: Message): void; /** * Remove the most recent message from the thread */ pop(): Message | undefined; /** * Remove the oldest message from the thread */ shift(): Message | undefined; /** * Get the most recent message from the thread */ last(): Message | undefined; /** * Get the number of messages in the thread */ size(): number; /** * Get the current token usage for the thread (cumulative) */ getTokenUsage(): TokenUsage; /** * Get the token usage for the last message/operation only (per-message tracking) * This gives you the cost of just the most recent interaction, not cumulative */ getLastMessageTokenUsage(): TokenUsage; /** * Update token usage (internal method for drivers) * Tracks both cumulative and per-message token usage */ updateTokenUsage(usage: Partial<TokenUsage>): void; /** * Set up event listeners to track per-tool token usage */ private setupToolTokenTracking; /** * Get per-tool token breakdown for all tools used in this thread */ getToolTokenBreakdown(): ToolTokenBreakdown[]; /** * Get token usage for a specific tool call */ getToolTokenUsage(toolUseId: string): ToolTokenBreakdown | undefined; /** * Get aggregated token analytics for all tools */ getToolTokenAnalytics(): { totalToolTokens: number; toolCount: number; averageTokensPerTool: number; tools: { [toolName: string]: { calls: number; totalTokens: number; avgTokens: number; }; }; }; /** * Remove all messages from the thread that match the filter * If no filter is provided, remove all messages */ clear(filter?: (message: Message) => boolean): void; /** * Get all messages from the thread */ all(): Message[]; /** * Check if the thread has reached its maximum number of steps * @returns true if max steps reached, false otherwise */ private checkMaxSteps; /** * Increment the step counter when assistant makes tool calls */ private incrementStepCounter; /** * Execute a tool call and return its result messages * @param toolCall The tool call to execute * @param tool The tool to execute with */ private executeToolCall; /** * Process a message containing tool calls * @param message The message containing tool calls */ private processToolCalls; /** * Process an assistant message without tool calls * @param message The assistant message to process */ private processAssistantMessage; /** * Handle a message from the LLM * This includes processing tool calls and triggering events */ processLastMessage(): Promise<Thread>; /** * Handle aborted execution by cleaning up any orphaned tool_use blocks */ private handleAbortedExecution; /** * Send a message or the current thread state to the LLM */ send(message?: Message, toolResponse?: boolean): Promise<this>; /** * Check if an assistant message appears incomplete * (promises action but has no tool calls) */ private isIncompleteResponse; /** * Wraps a stream with completion handling */ private wrapStream; /** * Stream a message or the current thread state to the LLM */ stream(message?: Message, toolResponse?: boolean): Promise<AsyncGenerator<string, void>>; /** * Handle the completion of a stream */ private streamComplete; /** * Validates that the thread has a user message as the first non-system message * @throws Error if validation fails */ private validateThread; /** * Get all available tools in the thread */ getTools(): Tool[]; /** * Add a new tool to the thread * @param tool The tool to add * @returns The tool that was added */ addTool(tool: Tool | ToolInterface): Tool; /** * Generate a unique thread ID using UUID v4 */ private generateThreadId; /** * Create a new thread with the same message history and tools */ fork(): Thread; /** * Calculate delay for retry attempts */ private calculateDelay; /** * Sleep for a specified amount of time */ private sleep; } /** * Implement a class that represents a message in the thread. * - content - the content of the message * - json - the json content of the message (if any) - support a number of ways of finding it * - role - the role of the message (user, assistant, tool) * - toolCalls - the tool calls that were made in the message * - toolResults - the results of the tool calls * - attachments - array of attachments (documents, images, videos) * - cache: boolean - whether the message is cached */ type MessageRole = "system" | "user" | "assistant" | "tool"; type AttachmentType = "document" | "image" | "video"; type DocumentFormat = "pdf" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html" | "txt" | "md"; type ImageFormat = "png" | "jpeg" | "gif" | "webp"; type VideoFormat = "mkv" | "mov" | "mp4" | "webm" | "flv" | "mpeg" | "mpg" | "wmv" | "three_gp"; interface AttachmentSource { bytes?: Uint8Array; uri?: string; bucketOwner?: string; } interface BaseAttachment { type: AttachmentType; source: AttachmentSource; } interface DocumentAttachment extends BaseAttachment { type: "document"; format: DocumentFormat; name: string; } interface ImageAttachment extends BaseAttachment { type: "image"; format: ImageFormat; } interface VideoAttachment extends BaseAttachment { type: "video"; format: VideoFormat; } type Attachment = DocumentAttachment | ImageAttachment | VideoAttachment; interface ToolCall { name: string; toolUseId: string; arguments: Record<string, any>; thread?: Thread; message?: Message; tool?: Tool; } interface Reasoning { text: string; type: "text"; signature: string; } interface ToolResult { name: string; toolUseId: string; result: any; error?: string; } declare class Message { private _content?; private _role; private _reasoning; private _toolCalls; private _toolResults; private _attachments; private _cache; constructor({ content, role, toolCalls, toolResults, attachments, reasoning, cache, }: { content?: string | undefined; role?: MessageRole; reasoning?: Reasoning[]; toolCalls?: ToolCall[]; toolResults?: ToolResult[]; attachments?: Attachment[]; cache?: boolean; }); get content(): string | undefined; get role(): MessageRole; get toolCalls(): ToolCall[]; get toolResults(): ToolResult[]; get attachments(): Attachment[]; get cache(): boolean; get reasoning(): Reasoning[]; isToolResponse(): boolean; isAssistantMessage(): boolean; isUserMessage(): boolean; isSystemMessage(): boolean; isToolCall(): boolean; /** * Get attachments of a specific type */ getAttachmentsByType<T extends Attachment>(type: AttachmentType): T[]; /** * Add an attachment to the message */ addAttachment(attachment: Attachment): void; /** * Remove an attachment from the message */ removeAttachment(index: number): void; /** * Attempts to parse and return JSON content from the message * Supports multiple formats: * 1. Direct JSON string * 2. JSON within markdown code blocks * 3. JSON within specific delimiters */ get json(): any | null; } /** * Implement a class that represents a tool. * - name - the name of the tool * - description - the description of the tool * - parameters - the parameters of the tool * - execute(): Promise<any> - the function that executes the tool * - setFollowUp() - add a follow-up message to the tool call * - toMessages(): Message[] - convert the tool to a list of messages include tool response messages and any follow-up messages */ interface ToolParameter { name: string; type: "string" | "number" | "boolean" | "object" | "array"; description: string; required?: boolean; } interface ToolCallResult { success: boolean; result: any; toolCall: ToolCall; error?: string; } interface ToolInterface extends EventSourceInterface { name: string; description: string; parameters: z.ZodTypeAny; executeTool: (args: any, toolCall: ToolCall, tokenUsage?: { inputTokens: number; outputTokens: number; totalTokens: number; }) => Promise<any>; } declare function makeToolFromInterface(t: ToolInterface): Tool; interface ToolOptions { name: string; description: string; parameters: z.ZodTypeAny; followUpMessage?: string; executeFn: (args: Record<string, any>, toolCall: ToolCall, tokenUsage?: { inputTokens: number; outputTokens: number; totalTokens: number; }) => Promise<any>; id?: string; trace?: boolean; traceMetadata?: Record<string, unknown>; } declare class Tool extends EventSource implements ToolInterface { readonly id: string; followUpMessage?: string; name: string; description: string; parameters: z.ZodTypeAny; private readonly executeFn; private readonly trace; private readonly traceMetadata?; constructor(options: ToolOptions); /** * Get the name of the tool */ getName(): string; /** * Get the description of the tool */ getDescription(): string; /** * Get the parameters of the tool */ getParameters(): z.ZodTypeAny; /** * Execute the tool with the given arguments */ executeTool(args: any, toolCall: ToolCall, tokenUsage?: { inputTokens: number; outputTokens: number; totalTokens: number; }): Promise<ToolCallResult>; /** * Execute the tool with tracing if enabled * @param args Tool arguments * @param toolCall The tool call object * @returns Result of the tool execution */ private executeWithTracing; /** * Format tool inputs for better tracing display * @param args Raw tool arguments * @returns Formatted input object */ private formatToolInput; /** * Set a follow-up message to be included after the tool response */ setFollowUp(message: string): void; /** * Convert the tool to a list of messages including tool response messages * and any follow-up messages */ toMessages(result: ToolCallResult): Message[]; /** * Extract attachments embedded in the tool result payload and return a copy without them */ private extractAttachmentsFromResult; /** * Type guard to ensure attachment candidates meet the expected shape */ private isAttachment; /** * Report token usage for a specific tool execution (called by drivers) */ reportTokenUsage(toolUseId: string, tokenUsage: { inputTokens: number; outputTokens: number; totalTokens: number; }, executionTimeMs?: number, startTime?: number, endTime?: number): void; } /** * An evaluator will run after an agent completes. It has the ability to request that the agent * continue working, reset the agent, or invoke other agents. * * Evaluators are triggered on the first non-tool-call message. * Evaluators can be chained, but each evaluator must be resolved first. * Evaluators may continue the thread, but if so, must not resolve a promise until they do. * Evaluators cannot be triggered twice on the same thread. * * * Evaluators must resolve before the agent will resolve. */ declare function makeEvaluator(fn: Evaluator): Evaluator; type Evaluator = (props: { thread: Thread; agent: Agent; complete: (result: boolean) => void; }) => (() => void) | undefined; /** * AI Agent, capable of running many threads in parallel, self-reflection, and evaluation. * - knowledge (array of facts) * - instructions (array of instructions) * - memory (array of serialized threads) * - system prompt * - tools * - evaluator(s) * - driver(s) */ interface AgentKnowledge { fact: string; source?: string; timestamp?: Date; } interface AgentInstruction { instruction: string; priority: number; context?: string; } interface AgentMemory { threadId: string; messages: Message[]; timestamp: Date; metadata?: Record<string, any>; } interface AgentThreadResponse<TOutput = any> { /** * The parsed output (if schema provided) or raw content */ output: TOutput; /** * Raw response content from the LLM */ rawContent: string; /** * The thread that generated the response */ thread: Thread; /** * Parse errors if output schema validation failed */ parseError?: z.ZodError; } interface RetryConfig { /** * Maximum number of retry attempts per driver * @default 3 */ maxAttempts: number; /** * Delay between retries in milliseconds * @default 1000 */ delay: number; /** * Whether to exponentially increase delay * @default true */ exponentialBackoff: boolean; } interface AgentOptions { onComplete?: (agent: Agent) => Promise<void>; knowledge?: AgentKnowledge[]; instructions?: AgentInstruction[]; memory?: AgentMemory[]; systemPrompt?: string; tools?: ToolInterface[]; driver: ThreadDriver; evaluators?: Evaluator[]; trace?: boolean; traceMetadata?: Record<string, unknown>; name?: string; /** * Fallback drivers to use if primary fails * (same as Solo) */ fallbackDrivers?: ThreadDriver[]; /** * Retry configuration for handling failures */ retry?: Partial<RetryConfig>; /** * Callback when switching to fallback driver */ onFallback?: (error: Error, driverIndex: number) => void; } declare class Agent extends EventSource { name?: string; knowledge: AgentKnowledge[]; instructions: AgentInstruction[]; memory: AgentMemory[]; systemPrompt: string; tools: Tool[]; driver: ThreadDriver; activeThreads: Map<string, Thread>; evaluators: Evaluator[]; private readonly trace; private readonly traceMetadata?; private readonly fallbackDrivers; private readonly retry; private readonly onFallback?; constructor({ knowledge, instructions, memory, systemPrompt, tools, driver, evaluators, trace, traceMetadata, fallbackDrivers, retry, onFallback, name, }: AgentOptions); /** * Get tools defined using decorators on the agent class */ private getDecoratedTools; /** * Add a new tool to the agent * @param tool The tool interface to add * @returns The created Tool instance */ addTool(tool: ToolInterface): Tool; /** * Ask a question and get a structured response * @param prompt The prompt to send * @param outputSchema The schema to validate the response against * @param options Additional options for the request * @returns Promise resolving to the structured response */ ask<TOutput>(prompt: string, outputSchema: z.ZodSchema<TOutput>, options?: { systemPrompt?: string; responseFormat?: "json" | "text"; }): Promise<AgentThreadResponse<TOutput>>; /** * Execute the agent with a prompt */ executeAgent(prompt: string): void; /** * Create a new thread with optional messages or prompt */ createThread<TOutput = any>(props: { messages?: Message[]; prompt?: string; systemPrompt?: string; onResponse?: (message: Message) => Promise<void>; outputSchema?: z.ZodSchema<TOutput>; responseFormat?: "json" | "text"; maxSteps?: number; onStructuredResponse?: (response: AgentThreadResponse<TOutput>) => Promise<void>; abortSignal?: AbortSignal; }): Thread; /** * Get an active thread by its ID */ getThread(threadId: string): Thread | undefined; /** * Get all active threads */ getActiveThreads(): Map<string, Thread>; /** * Close a thread and store its messages in memory */ closeThread(threadId: string): void; /** * Add a new piece of knowledge to the agent */ addKnowledge(knowledge: AgentKnowledge): void; /** * Add a new instruction to the agent */ addInstruction(instruction: AgentInstruction): void; /** * Get all tools available to the agent */ getTools(): Tool[]; /** * Get all knowledge available to the agent */ getKnowledge(): AgentKnowledge[]; /** * Get all instructions available to the agent */ getInstructions(): AgentInstruction[]; /** * Get all memory entries available to the agent */ getMemory(): AgentMemory[]; setSystemPrompt(systemPrompt: string): void; /** * Build the system prompt combining knowledge, instructions, and base prompt */ private buildSystemPrompt; /** * Execute a task with tracing enabled * @param name The name of the trace * @param task The task to execute * @param metadata Additional metadata for tracing (e.g. runId, tags) */ protected executeTask<T>(name: string, task: () => Promise<T>, metadata?: Record<string, unknown>): Promise<T>; /** * Configure a driver to use structured output if supported */ private configureDriverForStructuredOutput; /** * Parse a structured response from the LLM */ private parseStructuredResponse; /** * Get a human-readable description of a schema for prompt hints */ private getSchemaDescription; /** * Get a description of a Zod type */ private getZodTypeDescription; } declare abstract class AgentTool extends Agent implements ToolInterface { abstract readonly name: string; abstract readonly description: string; abstract readonly parameters: z.ZodTypeAny; abstract executeTool(args: any, toolCall: ToolCall): Promise<any>; } /** * Decorator to mark a method as a tool with a name * Optional - if not provided, the method name will be used */ declare const ToolName: (name: string) => (target: any, propertyKey: string, _descriptor: PropertyDescriptor) => void; /** * Decorator to add a description to a tool method */ declare const ToolDescription: (description: string) => (target: any, propertyKey: string, _descriptor: PropertyDescriptor) => void; /** * Decorator to define a parameter for a tool method * @param name Parameter name * @param description Parameter description * @param type Zod schema for parameter validation */ declare const ToolParam: (name: string, description: string, type: z.ZodTypeAny) => (target: any, propertyKey: string, _parameterIndex: number) => void; /** * Get all tool methods from a class instance */ declare const getToolMethods: (instance: any) => ToolInterface[]; /** * Solo - Simple interface for one-off LLM calls with structured output support * * Use Solo when you need: * - Classification tasks * - Data extraction with specific schemas * - Simple transformations (text → structured data) * - Retry/fallback for reliability * * Use Agent when you need: * - Tool calling and execution * - Multi-step workflows * - Complex reasoning with loops * - Conversation management */ interface SoloOptions<TOutput = string> { /** * The model driver to use for LLM calls */ driver: ThreadDriver; /** * System prompt to use for the conversation */ systemPrompt?: string; /** * Optional Zod schema for structured output * When provided, the response will be parsed and validated */ outputSchema?: z.ZodSchema<TOutput>; /** * Retry configuration for handling failures */ retry?: { /** * Maximum number of retry attempts * @default 3 */ maxAttempts?: number; /** * Delay between retries in milliseconds * @default 1000 */ delay?: number; /** * Whether to exponentially increase delay * @default true */ exponentialBackoff?: boolean; }; /** * Fallback drivers to use if primary fails */ fallbackDrivers?: ThreadDriver[]; /** * Callback when switching to fallback driver */ onFallback?: (error: Error, driverIndex: number) => void; /** * Temperature override for more consistent outputs */ temperature?: number; /** * Response format hint for the model */ responseFormat?: "json" | "text"; } interface SoloResponse<TOutput = string> { /** * The parsed output (if schema provided) or raw content */ output: TOutput; /** * Raw response content from the LLM */ rawContent: string; /** * All messages in the conversation */ messages: Message[]; /** * Which driver was used (primary or fallback index) */ driverUsed: "primary" | number; /** * Number of retry attempts made */ retryAttempts: number; /** * Parse errors if output schema validation failed */ parseError?: z.ZodError; } /** * Solo class for one-off LLM calls with structured output * * @example * ```typescript * // Simple classification * const classifier = new Solo({ * driver: new OpenAIThreadDriver({ apiKey: 'key' }), * systemPrompt: 'Classify the sentiment of the text', * outputSchema: z.enum(['positive', 'negative', 'neutral']) * }); * * const result = await classifier.ask('I love this product!'); * console.log(result.output); // 'positive' * ``` * * @example * ```typescript * // Structured extraction * const extractor = new Solo({ * driver: new OpenAIThreadDriver({ apiKey: 'key' }), * outputSchema: z.object({ * name: z.string(), * age: z.number(), * interests: z.array(z.string()) * }) * }); * * const result = await extractor.ask('John is 25 and likes coding and hiking'); * console.log(result.output); // { name: 'John', age: 25, interests: ['coding', 'hiking'] } * ``` */ declare class Solo<TOutput = string> { private readonly driver; private readonly systemPrompt?; private readonly outputSchema?; private readonly retry; private readonly fallbackDrivers; private readonly responseFormat?; private readonly onFallback?; constructor(options: SoloOptions<TOutput>); /** * Make a one-off LLM call with automatic parsing and validation * @param prompt The prompt to send to the LLM * @param options Optional overrides for this specific call * @returns Promise resolving to the structured response */ ask(prompt: string, options?: { systemPrompt?: string; temperature?: number; parseOnly?: boolean; }): Promise<SoloResponse<TOutput>>; /** * Make a streaming LLM call (note: structured output not supported in streaming) * @param prompt The prompt to send * @returns AsyncGenerator yielding response chunks */ stream(prompt: string, options?: { systemPrompt?: string; temperature?: number; }): AsyncGenerator<string, SoloResponse<string>>; /** * Parse a raw LLM response using the configured schema * Useful for testing or manual parsing */ parse(content: string): TOutput | undefined; private executeCall; private buildMessages; private getSchemaDescription; private getZodTypeDescription; private calculateDelay; private sleep; /** * Configure a driver to use structured output if supported */ private configureDriverForStructuredOutput; } /** * Convenience function for quick classification tasks * @param prompt The text to classify * @param categories Array of possible categories * @param driver The model driver to use * @param systemPrompt Optional system prompt for context * @returns Promise resolving to the selected category * * @example * ```typescript * const sentiment = await classify( * "I love this product!", * ['positive', 'negative', 'neutral'], * driver * ); * console.log(sentiment); // 'positive' * ``` */ declare function classify<T extends string>(prompt: string, categories: readonly T[], driver: ThreadDriver, systemPrompt?: string): Promise<T>; /** * Convenience function for structured data extraction * @param prompt The text to extract data from * @param schema Zod schema defining the expected structure * @param driver The model driver to use * @param systemPrompt Optional system prompt for context * @returns Promise resolving to the extracted data * * @example * ```typescript * const PersonSchema = z.object({ * name: z.string(), * age: z.number(), * email: z.string().email().optional() * }); * * const person = await extract( * "John Doe is 30 years old and his email is john@example.com", * PersonSchema, * driver * ); * console.log(person); // { name: 'John Doe', age: 30, email: 'john@example.com' } * ``` */ declare function extract<T>(prompt: string, schema: z.ZodSchema<T>, driver: ThreadDriver, systemPrompt?: string): Promise<T>; /** * Base type for a reusable Gig piece */ interface PieceDefinition<TOutput = any> { /** * The name of the piece (used in groove.recall) */ name: string; /** * The function that generates the prompt or executes custom logic */ play: ((groove: Groove) => string | Promise<any>) | string; /** * Configuration for the piece */ config?: { outputSchema?: z.ZodSchema<TOutput>; responseFormat?: "json" | "text"; temperature?: number; systemPrompt?: string; tools?: Tool[]; instructions?: Array<{ instruction: string; priority: number; }>; driver?: ThreadDriver; fallbackDrivers?: ThreadDriver[]; retry?: { maxAttempts?: number; delay?: number; exponentialBackoff?: boolean; }; onFallback?: (error: Error, driverIndex: number) => void; after?: string | string[]; when?: (groove: Groove) => boolean | Promise<boolean>; onError?: "stop" | "continue" | "retry"; retries?: number; }; /** * Metadata about the piece */ meta?: { version?: string; description?: string; author?: string; tags?: string[]; }; } /** * Helper to create a Solo instance from a piece definition */ declare function createSoloFromPiece<TOutput = any>(piece: PieceDefinition<TOutput>, driver: ThreadDriver): Solo<TOutput>; /** * Helper to create an Agent instance from a piece definition */ declare function createAgentFromPiece(piece: PieceDefinition, driver: ThreadDriver): Agent; /** * Gig - A jazz performance where musicians play together * * In jazz, a gig is fluid and improvisational: * - Musicians can play solo or together * - The setlist evolves during performance * - Players respond to each other and the audience * - Every performance is unique * * The Gig's "driver" provides the default driver for all pieces, * but individual pieces can override with their own drivers * for specialized tasks or fallback scenarios. */ interface Groove { recordings: Map<string, any>; vibe: Map<string, any>; recall<T = any>(piece: string): T | undefined; recallAll<T = any>(...pieces: string[]): Record<string, T>; setVibe(key: string, value: any): void; feelVibe<T = any>(key: string): T | undefined; } interface Piece { play: ((groove: Groove) => any) | ((groove: Groove) => Promise<any>); with?: { outputSchema?: z.ZodSchema<any>; responseFormat?: "json" | "text"; temperature?: number; systemPrompt?: string; tools?: Tool[]; instructions?: Array<{ instruction: string; priority: number; }>; driver?: ThreadDriver; fallbackDrivers?: ThreadDriver[]; retry?: { maxAttempts?: number; delay?: number; exponentialBackoff?: boolean; }; onFallback?: (error: Error, driverIndex: number) => void; after?: string | string[]; when?: (groove: Groove) => boolean | Promise<boolean>; onError?: "stop" | "continue" | "retry"; retries?: number; }; } interface GigOptions { label: string; driver: ThreadDriver; fallbackDrivers?: ThreadDriver[]; retry?: { maxAttempts?: number; delay?: number; exponentialBackoff?: boolean; }; onFallback?: (error: Error, driverIndex: number) => void; onProgress?: (piece: string, groove: Groove) => void; onError?: (error: Error, piece: string) => void; timeLimit?: number; } interface GigResults { success: boolean; recordings: Map<string, any>; errors: Map<string, Error>; vibe: Map<string, any>; duration: number; performed: string[]; skipped: string[]; failed: string[]; } /** * Gig - Orchestrate a jazz performance * * @example * ```typescript * const gig = new Gig({ * label: "Customer Service Club", * driver: driver // Default driver for all pieces * }); * * // Add pieces to the setlist - freeform style * gig * .add("classify", groove => * `Classify this: ${groove.feelVibe("request")}`, { * solo: { * outputSchema: z.enum(["urgent", "normal", "low"]) * }, * driver: fastDriver // Override with faster model * }) * .add("process", groove => * `Handle ${groove.recall("classify")} priority request`, { * tools: [databaseTool, emailTool], * after: "classify" * // Uses default driver * }) * .add("search", async groove => { * // Custom async performance * const category = groove.recall("classify"); * return await searchKnowledgeBase(category); * }, { * after: "classify" * }) * .add("respond", groove => { * const results = groove.recallAll("process", "search"); * return `Generate response using: ${JSON.stringify(results)}`; * }, { * after: ["process", "search"], * driver: creativeDriver // Override for creative response * }); * * const results = await gig.perform({ request: "Password reset" }); * ``` */ declare class Gig extends EventSource { private label; private driver; private pieces; private options; private dependencies; private pieceOrder; private parallelGroups; private lastPieceName; private lastParallelGroup; constructor(options: GigOptions); /** * Add a piece to the setlist (runs sequentially by default) * * The beauty of jazz is its flexibility - a piece can be: * - A simple string prompt (becomes a solo) * - A function returning a prompt (dynamic solo) * - An async function (custom performance) * - Anything that uses tools (becomes ensemble playing) * * @example * // Pattern 1: Explicit format * gig.add("classify", () => "Classify this", { outputSchema: schema }) * * // Pattern 2: Object format * gig.add("classify", classifyPiece) * * // Pattern 3: Self-contained piece * gig.add(classifyPiece) */ add(nameOrPiece: string | PieceDefinition, playOrPiece?: Piece["play"] | string | PieceDefinition, config?: Piece["with"]): this; /** * Add pieces that run in parallel * * @example * gig * .add("setup", "Prepare the data") * .parallel([ * ["analyze_sentiment", "Analyze sentiment"], * ["extract_entities", "Extract entities"], * ["classify_intent", "Classify intent"] * ]) * .add("summarize", "Combine all analyses") */ parallel(pieces: Array<[string, Piece["play"] | string, Piece["with"]?]>): this; private isInCurrentParallelGroup; /** * Add multiple pieces at once */ setlist(pieces: Record<string, Piece["play"] | Piece>): this; /** * Perform the gig */ perform(initialVibe?: Record<string, any>): Promise<GigResults>; private performGig; private performPiece; private playPiece; private getReadyPieces; private validateNoCycles; private hasCycle; private createGroove; private rest; private timeout; /** * Get a visualization of the setlist */ getSetlist(): string; } type ThreeKeyedLockEvaluatorProps = { evalPrompt?: string; exitPrompt?: string; }; declare const threeKeyedLockEvaluator: ({ evalPrompt, exitPrompt, }: ThreeKeyedLockEvaluatorProps) => Evaluator; type AgentToolEvaluatorProps = { agent: Agent; toolName: string; description: string; promptTemplate?: string; parameters?: z.ZodTypeAny; followUpMessage?: string; }; /** * Creates an evaluator that allows one agent to use another agent as a tool. * This is useful for delegating specific tasks to specialized agents. * * @param props Configuration for the agent tool evaluator * @returns An evaluator function that integrates an agent as a tool */ declare const agentToolEvaluator: ({ agent, toolName, description, promptTemplate, parameters, followUpMessage, }: AgentToolEvaluatorProps) => Evaluator; /** * Abstract base class for model drivers. * Implements common functionality and defines the interface for model-specific implementations. */ declare abstract class BaseModelDriver extends EventSource implements ThreadDriver { /** * Check if the abort signal has been triggered and throw an error if so */ protected checkAbortSignal(abortSignal?: AbortSignal): void; /** * Execute an async operation with abort signal checking before and after */ protected withAbortCheck<T>(abortSignal: AbortSignal | undefined, operation: () => Promise<T>): Promise<T>; /** * Wrap an async generator with abort signal checking on each iteration */ protected wrapStreamWithAbort<T>(abortSignal: AbortSignal | undefined, stream: AsyncGenerator<T>): AsyncGenerator<T>; /** * Process and send a thread to the model and return the updated thread */ abstract sendThread(thread: Thread, abortSignal?: AbortSignal): Promise<Thread>; /** * Stream a response from the model */ abstract streamThread(thread: Thread, abortSignal?: AbortSignal): AsyncGenerator<{ stream: AsyncGenerator<string, void>; message: Message; }, Thread>; } /** * Implement a class that represents a Google Gemini AI client * and provides a set of tools for converting messages to and from the LLM. * - sendThread(thread: Thread): Thread - send a message to the LLM * - streamThread(thread: Thread): AsyncGenerator<string, Thread> - stream a message to the LLM */ /** * Available Gemini model options */ type GeminiModel = "gemini-2.5-flash" | "gemini-2.5-pro-exp-03-25" | "gemini-2.0-flash-lite" | "gemini-2.0-flash-thinking-exp-01-21" | "gemini-1.5-flash" | "gemini-1.5-pro" | "gemini-1.0-pro"; /** * Schema for structured output */ interface GeminiResponseSchema { type: "string" | "integer" | "number" | "boolean" | "array" | "object"; format?: string; description?: string; nullable?: boolean; enum?: string[]; maxItems?: string; minItems?: string; properties?: Record<string, GeminiResponseSchema>; required?: string[]; propertyOrdering?: string[]; items?: GeminiResponseSchema; } interface GeminiConfig { model?: GeminiModel | string; temperature?: number; maxTokens?: number; apiKey?: string; cache?: boolean; /** * Whether to enable tracing * @default false */ trace?: boolean; /** * Additional metadata for tracing */ traceMetadata?: Record<string, unknown>; /** * Schema for structured output */ responseSchema?: GeminiResponseSchema; } declare class GeminiThreadDriver extends BaseModelDriver { private readonly genAI; private readonly model; private readonly temperature; private readonly maxTokens; private readonly cache; private readonly responseSchema?; /** * Get list of available model names for easy reference */ static getAvailableModels(): GeminiModel[]; constructor(config?: GeminiConfig); /** * Convert messages to Gemini API format */ private convertMessagesToGeminiFormat; /** * Parse tool calls from the Gemini API response */ private parseToolCalls; /** * Send the thread state to the LLM and process the response */ sendThread(thread: Thread, abortSignal?: AbortSignal): Promise<Thread>; /** * Create tool config for Gemini API */ private createToolConfig; /** * Stream the thread state to the LLM and process the response chunk by chunk */ streamThread(thread: Thread, abortSignal?: AbortSignal): AsyncGenerator<{ stream: AsyncGenerator<string, void>; message: Message; }, Thread>; /** * Create a stream processor for handling Gemini API streaming responses */ private createStreamProcessor; } /** * Implement a class that represents a Cohere AI client * and provides a set of tools for converting messages to and from the LLM. * - sendThread(thread: Thread): Thread - send a message to the LLM * - streamThread(thread: Thread): AsyncGenerator<string, Thread> - stream a message to the LLM */ /** * Available Cohere models */ type CohereModel = "command-a-03-2025" | "command-r7b-12-2024" | "command-r-plus-08-2024" | "command-r-plus-04-2024" | "command-r-plus" | "command-r-08-2024" | "command-r-03-2024" | "command-r" | "command" | "command-light" | "command-light-nightly"; /** * Configuration options for the Cohere driver */ interface CohereConfig { /** * Cohere API Key */ apiKey?: string; /** * Model to use * @default "command-r-plus" */ model?: CohereModel; /** * Temperature for response generation * @default 0.7 */ temperature?: number; /** * Maximum number of tokens to generate */ maxTokens?: number; /** * Whether to cache responses * @default false */ cache?: boolean; /** * Enable tracing for this driver * @default false */ trace?: boolean; /** * Additional metadata for tracing */ traceMetadata?: Record<string, unknown>; } /** * Represents a Cohere AI client and provides tools for converting messages to/from the LLM */ declare class CohereThreadDriver extends BaseModelDriver { /** * Cohere API Client */ private client; /** * Cohere model to use */ private model; /** * Temperature for response generation */ private temperature; /** * Maximum number of tokens to generate */ private maxTokens?; /** * Whether to cache responses */ private cache; /** * Returns a list of available Cohere models */ static getAvailableModels(): CohereModel[]; /** * Create a new Cohere driver * @param config Configuration options */ constructor(config?: CohereConfig); /** * Send a thread to the LLM and get a response * @param thread Thread to send * @returns Updated thread with LLM response */ sendThread(thread: Thread, abortSignal?: AbortSignal): Promise<Thread>; /** * Stream a thread to the LLM and get a streaming response * @param thread Thread to send * @returns AsyncGenerator yielding the stream and updated thread */ streamThread(thread: Thread, abortSignal?: AbortSignal): AsyncGenerator<{ stream: AsyncGenerator<string, void>; message: Message; }, Thread>; /** * Create a stream generator for handling streaming responses */ private createStreamGenerator; /** * Convert a thread's messages to Cohere's API format * @param thread Thread to convert * @returns Object containing messages array and optional system message */ private convertMessagesToCohereFormat; /** * Create tool definitions from thread tools * @param tools Tools to convert * @returns Array of tool definitions for Cohere API */ private createToolDefinitions; /** * Parse tool calls from Cohere API response * @param response Cohere API response * @returns Array of tool calls if present */ private parseToolCalls; } /** * Implement a class that represents an OpenAI client * and provides a set of tools for converting messages to and from the LLM. * - sendThread(thread: Thread): Thread - send a message to the LLM * - streamThread(thread: Thread): AsyncGenerator<string, Thread> - stream a message to the LLM */ /** * Available OpenAI models */ type OpenAIModel = "gpt-4o" | "gpt-4o-mini" | "gpt-4o-mini-audio-preview" | "gpt-4" | "gpt-4-turbo" | "gpt-3.5-turbo" | "gpt-4-vision-preview" | "gpt-4.1" | "gpt-5" | "o1" | "o1-mini" | "o1-preview" | "o3" | "o3-mini" | "o3-pro" | "o4-mini"; /** * Configuration options for the OpenAI driver */ /** * Schema for structured output */ interface OpenAIResponseSchema { type: "string" | "integer" | "number" | "boolean" | "array" | "object"; format?: string; description?: string; nullable?: boolean; enum?: string[]; maxItems?: string; minItems?: string; properties?: Record<string, OpenAIResponseSchema>; required?: string[]; propertyOrdering?: string[]; items?: OpenAIResponseSchema; } /** * Advanced provider-specific options for OpenAI API */ interface OpenAIProviderOptions { /** * Reasoning effort level for reasoning models (o1, o3, etc.) * Controls how thoroughly the model thinks before responding */ reasoning?: { effort?: "low" | "medium" | "high"; }; /** * Direct reasoning effort parameter for GPT-5 (alternative to reasoning.effort) * @example 'high' | 'medium' | 'low' */ reasoning_effort?: "low" | "medium" | "high"; /** * Verbosity level for GPT-5 responses * Controls how detailed or concise the model's responses should be */ verbosity?: "low" | "medium" | "high"; /** * Maximum completion tokens for reasoning models * (different from standard max_tokens parameter) */ max_completion_tokens?: number; /** * Context-Free Grammar (CFG) configuration for GPT-5 * Strictly constrains model output to match predefined syntax */ cfg?: { grammar?: string; }; /** * Freeform tool calling configuration for GPT-5 * Enables raw text payloads without JSON wrapping */ freeform_tools?: boolean; /** * MCP (Model Context Protocol) server configuration */ mcp?: { server?: string; headers?: Record<string, string>; approval_required?: boolean; }; /** * Modality configuration for multi-modal models */ modality?: "text" | "audio" | "image" | "multimodal"; /** * Prediction configuration for speculative decoding */ prediction?: { type?: "content"; content?: string; }; /** * Store conversation for future reference */ store?: boolean; /** * Additional OpenAI-specific parameters that should be passed directly to the API * This allows for future GPT-5 parameters without needing code changes */ [key: string]: any; } /** * Enhanced token usage with OpenAI-specific breakdown */ interface OpenAIEnhancedTokenUsage { inputTokens: number; outputTokens: number; totalTokens: number; cachedTokens?: number; reasoningTokens?: number; newContentTokens?: number; toolTokenBreakdown?: ToolTokenBreakdown[]; } interface OpenAIConfig { /** * OpenAI API Key */ apiKey?: string; /** * Model to use * @default "gpt-4o" */ model?: OpenAIModel; /** * Temperature for response generation * @default 0.7 */ temperature?: number; /** * Maximum number of tokens to generate */ maxTokens?: number; /** * Whether to cache responses * @default false */ cache?: boolean; /** * Whether to enable tracing * @default false */ trace?: boolean; /** * Additional metadata for tracing */ traceMetadata?: Record<string, unknown>; /** * Schema for structured output */ responseSchema?: OpenAIResponseSchema; /** * Advanced provider-specific options (e.g., reasoning effort, CFG, MCP configuration) * @example { reasoning: { effort: 'high' } } // For reasoning models * @example { cfg: { grammar: 'python-like' } } // For GPT-5 constrained output * @example { max_completion_tokens: 4000 } // For reasoning models */ providerOptions?: OpenAIProviderOptions; /** * Maximum character limit for input validation * @default 1_000_000 (1M characters ≈ 200K-500K tokens) * Set to undefined to disable character-based validation entirely */ maxPromptCharacters?: number; } /** * Represents an OpenAI client and provides tools for converting messages to/from the LLM */ declare class OpenAIThreadDriver extends BaseModelDriver { /** * OpenAI API Client */ private client; /** * OpenAI model to use */ private model; /** * Temperature for response generation */ private temperature; /** * Maximum number of tokens to generate */ private maxT