ai-functions
Version:
Core AI primitives for building intelligent applications
368 lines • 11.9 kB
TypeScript
/**
* 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