ai-functions
Version:
Core AI primitives for building intelligent applications
453 lines • 12.9 kB
TypeScript
/**
* Agentic Tool Orchestration
*
* Provides multi-turn model→tools→model loop orchestration for complex AI workflows.
*
* Key components:
* - AgenticLoop: Orchestrates multi-turn conversations with tool execution
* - ToolRouter: Routes tool calls to registered handlers
* - ToolValidator: Validates tool arguments before execution
*
* @packageDocumentation
*/
import { z, type ZodTypeAny } from 'zod';
/**
* A tool that can be executed by the agentic loop
*/
export interface Tool<TParams extends ZodTypeAny = ZodTypeAny, TResult = unknown> {
/** Unique name for the tool */
name: string;
/** Human-readable description */
description: string;
/** Zod schema for parameters */
parameters: TParams;
/** Execute the tool with validated parameters */
execute: (params: z.infer<TParams>) => Promise<TResult>;
}
/**
* A tool call from the model
*/
export interface ToolCall {
/** Name of the tool to call */
name: string;
/** Arguments for the tool */
arguments: Record<string, unknown>;
/** Optional ID for tracking */
id?: string;
}
/**
* Result of a tool execution
*/
export interface ToolResult<T = unknown> {
/** Whether execution succeeded */
success: boolean;
/** The result value if successful */
result?: T;
/** Error message if failed */
error?: string;
/** The original tool call */
toolCall?: ToolCall;
/** Number of retries attempted */
retryCount?: number;
}
/**
* Formatted tool result for model consumption
*/
export interface FormattedToolResult {
/** Role is always 'tool' */
role: 'tool';
/** String content of the result */
content: string;
/** Tool call ID for correlation */
tool_call_id?: string;
/** Whether this is an error result */
isError?: boolean;
}
/**
* Validation result for tool arguments
*/
export interface ValidationResult {
/** Whether validation passed */
valid: boolean;
/** Validation errors if any */
errors?: string[];
/** Validated and parsed arguments */
parsedArgs?: unknown;
}
/**
* Model response from a generation
*/
export interface ModelResponse {
/** Generated text (if no tool calls) */
text?: string;
/** Tool calls requested by the model */
toolCalls?: ToolCall[];
/** Why generation stopped */
finishReason: 'stop' | 'tool_call' | 'length' | 'content_filter' | 'error';
/** Token usage */
usage?: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
};
}
/**
* Message in the conversation
*/
export interface Message {
role: 'user' | 'assistant' | 'system' | 'tool';
content: string;
tool_calls?: ToolCall[];
tool_call_id?: string;
isError?: boolean;
}
/**
* Step information for callbacks
*/
export interface StepInfo {
/** Step number (1-indexed) */
stepNumber: number;
/** Tool calls in this step */
toolCalls: Array<ToolCall & {
result?: unknown;
error?: string;
}>;
/** Model response */
response: ModelResponse;
/** Current messages */
messages: Message[];
}
/**
* Options for creating an AgenticLoop
*/
export interface LoopOptions {
/** Available tools */
tools: Tool[];
/** Maximum number of steps before stopping */
maxSteps: number;
/** Whether to throw error when maxSteps is exceeded */
strictMaxSteps?: boolean;
/** Whether to execute tool calls in parallel */
parallelExecution?: boolean;
/** Maximum concurrent tool calls when parallel execution is enabled */
maxParallelCalls?: number;
/** Whether to retry failed tool calls */
retryFailedTools?: boolean;
/** Maximum retries per tool call */
maxToolRetries?: number;
/** Whether to continue when a tool fails */
continueOnError?: boolean;
/** Timeout for individual tool execution (ms) */
toolTimeout?: number;
/** Track token usage across steps */
trackUsage?: boolean;
/** Callback for each step */
onStep?: (step: StepInfo) => void;
}
/**
* Model generation options passed to the model
*/
export interface ModelGenerationOptions {
/** Messages for the conversation */
messages: Message[];
/** Tools available for use */
tools: Record<string, {
description: string;
parameters: unknown;
execute: (args: unknown) => Promise<unknown>;
}>;
}
/**
* Options for running the loop
*/
export interface RunOptions {
/** Model to use for generation */
model: {
generate: (options: ModelGenerationOptions) => Promise<ModelResponse>;
};
/** Initial prompt */
prompt: string;
/** System message */
system?: string;
/** Abort signal */
abortSignal?: AbortSignal;
}
/**
* Extended tool call result with metadata
*/
export interface ToolCallResult {
/** Tool name */
name: string;
/** Arguments passed */
arguments: Record<string, unknown>;
/** Result if successful */
result?: unknown;
/** Error if failed */
error?: string;
/** Number of retries */
retryCount?: number;
}
/**
* Tool result for SDK compatibility
*/
export interface SDKToolResult {
/** Tool name */
toolName: string;
/** Tool call ID */
toolCallId?: string;
/** Result value */
result: unknown;
}
/**
* Result of running the agentic loop
*/
export interface LoopResult {
/** Final text output */
text: string;
/** Number of steps executed */
steps: number;
/** All tool calls made */
toolCalls: ToolCallResult[];
/** Tool results in SDK format */
toolResults: SDKToolResult[];
/** Why the loop stopped */
stopReason: 'stop' | 'max_steps' | 'error' | 'aborted';
/** Token usage if tracked */
usage?: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
};
/** Conversation messages */
messages: Message[];
}
/**
* Validates tool arguments before execution
*/
export declare class ToolValidator {
private tools;
/**
* Register a tool for validation
*/
register(tool: Tool): void;
/**
* Validate arguments for a tool
*/
validate(toolName: string, args: unknown): ValidationResult;
/**
* Validate multiple tool calls at once
*/
validateAll(calls: ToolCall[]): ValidationResult[];
}
/**
* Routes tool calls to registered handlers
*
* @deprecated Phase C Week 2 — `ToolRouter` has zero production callers in
* primitives.org.ai (audited 2026-05-06; see `bd show aip-ibid`). Only the
* `ai-primitives` umbrella re-export tests reference it. AI SDK 6's native
* tool-routing under `generateText({ tools })` and `Agent` / `ToolLoopAgent`
* cover the same surface. Will be removed in the Phase C semver bump
* alongside `AgenticLoop` and `createAgenticLoop`.
*/
export declare class ToolRouter {
private tools;
private validator;
/**
* Register a tool
*/
register(tool: Tool): void;
/**
* Route a single tool call
*/
route(call: ToolCall): Promise<ToolResult>;
/**
* Route multiple tool calls sequentially
*/
routeAll(calls: ToolCall[]): Promise<ToolResult[]>;
/**
* Route multiple tool calls in parallel
*/
routeAllParallel(calls: ToolCall[]): Promise<ToolResult[]>;
/**
* Format a tool result for model consumption
*/
formatResult(result: ToolResult): FormattedToolResult;
}
/**
* Orchestrates multi-turn model→tools→model loops
*
* @deprecated Phase C Week 2 — `AgenticLoop` has zero production callers in
* primitives.org.ai (audited 2026-05-06; see `bd show aip-ibid`). Only the
* `ai-primitives` umbrella re-export tests reference it. The production
* cascade walker (`services-as-software/v3/invoke/cascade-walker.ts:178`)
* already uses AI SDK 6's `generateText({ tools, maxSteps: 10 })` directly
* for agentic steps — no consumer code paths through this class. AI SDK 6's
* `Agent` / `ToolLoopAgent` (`stopWhen: stepCountIs(N)`) are the going-
* forward primitives. Will be removed in the Phase C semver bump.
*/
export declare class AgenticLoop {
private options;
private router;
private validator;
constructor(options: LoopOptions);
/**
* Get tools in AI SDK format
*/
getToolsForSDK(): Record<string, {
description: string;
parameters: unknown;
execute: (args: unknown) => Promise<unknown>;
}>;
/**
* Execute a tool call with timeout and retry support
*/
private executeToolCall;
/**
* Execute multiple tool calls
*/
private executeToolCalls;
/**
* Build messages for the next model call
*/
private buildMessages;
/**
* Run the agentic loop
*/
run(runOptions: RunOptions): Promise<LoopResult>;
/**
* Run the agentic loop with streaming support
*
* Returns an async generator that yields step events as they occur.
*/
stream(runOptions: RunOptions): AsyncGenerator<LoopStreamEvent, LoopResult>;
}
/**
* Events emitted during streaming loop execution
*/
export type LoopStreamEvent = {
type: 'start';
prompt: string;
timestamp: number;
} | {
type: 'step_start';
stepNumber: number;
timestamp: number;
} | {
type: 'step_end';
stepNumber: number;
hasToolCalls: boolean;
timestamp: number;
} | {
type: 'text';
text: string;
stepNumber: number;
timestamp: number;
} | {
type: 'tool_calls';
toolCalls: ToolCall[];
stepNumber: number;
timestamp: number;
} | {
type: 'tool_result';
toolName: string;
result?: unknown;
error?: string;
stepNumber: number;
timestamp: number;
} | {
type: 'max_steps';
steps: number;
timestamp: number;
} | {
type: 'aborted';
steps: number;
timestamp: number;
} | {
type: 'error';
error: string;
timestamp: number;
} | {
type: 'end';
steps: number;
stopReason: LoopResult['stopReason'];
timestamp: number;
};
/**
* Create a tool from a simple function
*/
export declare function createTool<TParams extends z.ZodRawShape, TResult>(config: {
name: string;
description: string;
parameters: TParams;
execute: (params: z.infer<z.ZodObject<TParams>>) => Promise<TResult>;
}): Tool<z.ZodObject<TParams>, TResult>;
/**
* Compose multiple tools into a single toolset
*/
export declare function createToolset(...tools: Tool[]): Tool[];
/**
* Create a tool that wraps another tool with middleware
*/
export declare function wrapTool<T extends Tool>(tool: T, middleware: {
before?: (params: unknown) => Promise<unknown> | unknown;
after?: (result: unknown) => Promise<unknown> | unknown;
onError?: (error: Error) => Promise<unknown> | unknown;
}): Tool;
/**
* Options for cachedTool
*/
export interface CachedToolOptions {
/** Time-to-live in milliseconds (default: 60000) */
ttl?: number;
/** Function to generate cache key from params (default: JSON.stringify) */
keyFn?: (params: unknown) => string;
/** Interval in ms for automatic cleanup of expired entries (default: 0 = disabled) */
cleanupIntervalMs?: number;
/** Maximum cache size before LRU eviction kicks in (default: 0 = unlimited) */
maxSize?: number;
}
/**
* Extended tool interface with cache management methods
*/
export interface CachedTool extends Tool {
/** Get the current number of entries in the cache */
cacheSize(): number;
/** Clear all cache entries */
clearCache(): void;
/** Stop cleanup timer and clear cache */
destroy(): void;
}
/**
* Create a tool with caching support
*
* Features:
* - TTL-based expiration
* - Optional periodic cleanup of expired entries (prevents memory leaks)
* - Optional max size with LRU eviction
* - Manual cache control (clear, destroy)
*/
export declare function cachedTool<T extends Tool>(tool: T, options?: CachedToolOptions): CachedTool;
/**
* Create a tool with rate limiting
*/
export declare function rateLimitedTool<T extends Tool>(tool: T, options: {
maxCalls: number;
windowMs: number;
}): Tool;
/**
* Create a tool that times out after a specified duration
*/
export declare function timeoutTool<T extends Tool>(tool: T, timeoutMs: number): Tool;
/**
* Create an agentic loop with sensible defaults
*
* @deprecated Phase C Week 2 — `createAgenticLoop` has zero production
* callers in primitives.org.ai (only `ai-primitives` umbrella re-export
* tests). Use AI SDK 6's `Agent` / `ToolLoopAgent` with
* `stopWhen: stepCountIs(N)` instead. Will be removed alongside
* `AgenticLoop` in the Phase C semver bump. See `bd show aip-ibid`.
*/
export declare function createAgenticLoop(options: Partial<LoopOptions> & {
tools: Tool[];
}): AgenticLoop;
//# sourceMappingURL=tool-orchestration.d.ts.map