@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
TypeScript
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