UNPKG

ai-functions

Version:

Core AI primitives for building intelligent applications

368 lines 11.9 kB
/** * Retry and fallback patterns for AI function calls * * Provides: * - Exponential backoff with configurable base delay and multiplier * - Jitter to prevent thundering herd (equal, full, decorrelated strategies) * - Circuit breaker for fail-fast behavior after repeated failures * - Fallback chains for model failover (sonnet -> opus -> gpt-4o) * - Error classification for intelligent retry decisions * - Partial retry for batch operations * * Per-model policy data (which models retry how, who falls back to whom, * which batch tiers each model supports) lives in `language-models`'s * `policyFor()`. The classes in this file are the *machinery* that reads * that policy. See `RetryPolicy.forModel`, `CircuitBreaker.forModel`, * `FallbackChain.forModel`. * * @packageDocumentation */ import { type ModelPolicy } from 'language-models'; /** * Error categories for retry decision making */ export declare enum ErrorCategory { /** Network connectivity issues (retryable) */ Network = "network", /** Rate limiting / quota exceeded (retryable with backoff) */ RateLimit = "rate_limit", /** Invalid input / bad request (not retryable) */ InvalidInput = "invalid_input", /** Authentication / authorization errors (not retryable) */ Authentication = "authentication", /** Server errors (retryable) */ Server = "server", /** Context length exceeded (not retryable without modification) */ ContextLength = "context_length", /** Unknown error type */ Unknown = "unknown" } /** * Base class for retryable errors */ export declare class RetryableError extends Error { readonly retryable = true; readonly category: ErrorCategory; constructor(message: string, category?: ErrorCategory); } /** * Base class for non-retryable errors */ export declare class NonRetryableError extends Error { readonly retryable = false; readonly category: ErrorCategory; constructor(message: string, category?: ErrorCategory); } /** * Network-related errors (connection issues, timeouts) */ export declare class NetworkError extends RetryableError { constructor(message: string); } /** * Rate limit errors with optional retry-after */ export declare class RateLimitError extends RetryableError { readonly retryAfter?: number; constructor(message: string, options?: { retryAfter?: number; }); /** * Create RateLimitError from HTTP response */ static fromResponse(response: { status: number; headers?: Record<string, string>; }): RateLimitError; } /** * Error thrown when circuit breaker is open */ export declare class CircuitOpenError extends Error { readonly retryable = false; constructor(message?: string); } /** * Classify an error into a category for retry decisions */ export declare function classifyError(error: unknown): ErrorCategory; /** * Jitter strategy for backoff calculation */ export type JitterStrategy = 'equal' | 'full' | 'decorrelated'; /** * Options for backoff calculation */ export interface BackoffOptions { /** Base delay in milliseconds (default: 1000) */ baseDelay?: number; /** Maximum delay cap in milliseconds (default: 30000) */ maxDelay?: number; /** Exponential multiplier (default: 2) */ multiplier?: number; /** Jitter factor 0-1 for equal jitter (default: 0) */ jitter?: number; /** Jitter strategy (default: 'equal') */ jitterStrategy?: JitterStrategy; /** Previous delay for decorrelated jitter */ previousDelay?: number; } /** * Calculate backoff delay with exponential increase and optional jitter * * @param attempt - Current attempt number (0-indexed) * @param options - Backoff configuration * @returns Delay in milliseconds */ export declare function calculateBackoff(attempt: number, options?: BackoffOptions): number; /** * Options for retry policy */ export interface RetryOptions { /** Maximum number of retries (default: 3) */ maxRetries?: number; /** Base delay in milliseconds (default: 1000) */ baseDelay?: number; /** Maximum delay cap in milliseconds (default: 30000) */ maxDelay?: number; /** Exponential multiplier (default: 2) */ multiplier?: number; /** Jitter factor 0-1 (default: 0) */ jitter?: number; /** Jitter strategy (default: 'equal') */ jitterStrategy?: JitterStrategy; /** Respect retry-after headers from rate limit errors (default: true) */ respectRetryAfter?: boolean; /** Custom function to determine if error is retryable */ shouldRetry?: (error: unknown) => boolean; } /** * Info passed to operations during retry */ export interface RetryInfo { attempt: number; maxRetries: number; } /** * Result of a batch item operation */ export interface BatchItemResult<T, R> { success: boolean; item: T; result?: R; error?: Error; } /** * Retry policy for executing operations with exponential backoff * * @deprecated Phase C Week 3 — `RetryPolicy` has 1 real production caller * (audited 2026-05-06; see `bd show aip-ibid`): * `ai-database/src/cascade-orchestrator.ts:1235` (loose coupling — dynamic * import + graceful try/catch fallback when ai-functions not available). * AI SDK 6's `customProvider({ retryPolicy })` and `wrapLanguageModel(model, * retryMiddleware)` cover the same surface. Migration tracked in aip-ibid; * the one callsite can move on a separate commit. Will be removed in the * Phase C semver bump. */ export declare class RetryPolicy { private readonly options; constructor(options?: RetryOptions); /** * Build a RetryPolicy from a model's `ModelPolicy` (loaded via * `language-models`). Per-call `overrides` win over policy data. * * @example * ```ts * const policy = RetryPolicy.forModel('sonnet') * // Uses retry settings derived for anthropic/claude-sonnet-4.5 * ``` */ static forModel(alias: string, overrides?: RetryOptions): RetryPolicy; /** * Build a RetryPolicy directly from a `ModelPolicy`. Useful when the policy * is already in hand (e.g. from a request context). */ static fromPolicy(policy: ModelPolicy, overrides?: RetryOptions): RetryPolicy; /** * Execute an operation with retry logic */ execute<T>(operation: (info: RetryInfo) => Promise<T>): Promise<T>; /** * Execute a batch operation with partial retry for failed items */ executeBatch<T, R>(items: T[], batchProcessor: (items: T[]) => Promise<BatchItemResult<T, R>[]>): Promise<BatchItemResult<T, R>[]>; private isRetryable; private sleep; } /** * Circuit breaker state */ export type CircuitState = 'closed' | 'open' | 'half-open'; /** * Options for circuit breaker */ export interface CircuitBreakerOptions { /** Number of failures before opening circuit (default: 5) */ failureThreshold?: number; /** Time in ms before transitioning to half-open (default: 30000) */ resetTimeout?: number; /** Number of successful calls to close circuit (default: 1) */ successThreshold?: number; } /** * Circuit breaker metrics */ export interface CircuitBreakerMetrics { state: CircuitState; failureCount: number; successCount: number; lastFailure: Date | null; lastSuccess: Date | null; totalFailures: number; totalSuccesses: number; } /** * Circuit breaker for fail-fast behavior * * States: * - CLOSED: Normal operation, failures tracked * - OPEN: Fail fast, reject all requests * - HALF-OPEN: Allow single test request * * @deprecated Phase C Week 3 — `CircuitBreaker` has zero real callers in * primitives.org.ai (audited 2026-05-06; only comment-only references in * `language-models/src/index.ts`; see `bd show aip-ibid`). AI SDK 6's * `wrapLanguageModel(model, circuitMiddleware)` replacement is the going- * forward primitive. Will be removed in the Phase C semver bump. */ export declare class CircuitBreaker { private _state; private _failureCount; private _successCount; private _lastFailure; private _lastSuccess; private _totalFailures; private _totalSuccesses; private _openedAt; private readonly options; constructor(options?: CircuitBreakerOptions); /** * Build a CircuitBreaker for a specific model, using its `ModelPolicy`. * Per-call overrides win over policy data. */ static forModel(alias: string, overrides?: CircuitBreakerOptions): CircuitBreaker; /** * Current circuit state */ get state(): CircuitState; /** * Current failure count */ get failureCount(): number; /** * Execute an operation through the circuit breaker */ execute<T>(operation: () => Promise<T>): Promise<T>; /** * Record a successful operation */ private recordSuccess; /** * Record a failed operation */ private recordFailure; /** * Get circuit breaker metrics */ getMetrics(): CircuitBreakerMetrics; /** * Manually reset the circuit breaker */ reset(): void; } /** * A model in the fallback chain */ export interface FallbackModel<T = unknown, P = unknown> { name: string; execute: (params?: P) => Promise<T>; } /** * Options for fallback chain */ export interface FallbackOptions { /** Custom function to determine if fallback should be attempted */ shouldFallback?: (error: unknown) => boolean; } /** * Metrics from fallback chain execution */ export interface FallbackMetrics { attempts: number; successfulModel: string | null; failedModels: string[]; totalDuration: number; errors: Array<{ model: string; error: Error; }>; } /** * Fallback chain for model failover * * Tries models in order until one succeeds: * sonnet -> opus -> gpt-4o -> gemini * * @deprecated Phase C Week 3 — `FallbackChain` (LLM model failover) has * zero real callers in primitives.org.ai (audited 2026-05-06; the * `human-in-the-loop` package's `FallbackChain` is a different class for * HITL fallback resolution, not LLM failover). AI SDK 4.3+ ships native * `customProvider({ fallbackProvider })` which is the going-forward * primitive. See `bd show aip-ibid`. Will be removed in the Phase C * semver bump. */ export declare class FallbackChain<T = unknown, P = unknown> { private readonly models; private readonly options; private lastMetrics; constructor(models: FallbackModel<T, P>[], options?: FallbackOptions); /** * Build a FallbackChain from a model's `ModelPolicy`. The caller supplies * an `executor` that takes a model id and returns a promise — the chain * applies it to the primary model first, then to each fallback in order. * * @example * ```ts * const chain = FallbackChain.forModel('sonnet', (modelId, params) => * ai({ model: modelId, prompt: params!.prompt }) * ) * await chain.execute({ prompt: 'Hello' }) * ``` */ static forModel<T = unknown, P = unknown>(alias: string, executor: (modelId: string, params?: P) => Promise<T>, options?: FallbackOptions): FallbackChain<T, P>; /** * Execute the fallback chain */ execute(params?: P): Promise<T>; /** * Get metrics from the last execution */ getMetrics(): FallbackMetrics; } /** * Wrap an async function with retry logic * * @example * ```ts * const reliableFetch = withRetry(fetch, { * maxRetries: 3, * baseDelay: 1000, * jitter: 0.2, * }) * * const response = await reliableFetch('https://api.example.com') * ``` */ export declare function withRetry<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, options?: RetryOptions): (...args: TArgs) => Promise<TResult>; export type { RetryOptions as RetryPolicyOptions }; //# sourceMappingURL=retry.d.ts.map