agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
66 lines (65 loc) • 3.21 kB
TypeScript
/**
* withFallback — provider decorator that falls back to a secondary
* on error.
*
* Pattern: Decorator (GoF) — composes two `LLMProvider`s into one.
* Role: Outer ring (Hexagonal). Stacks with `withRetry`:
* `withRetry(withFallback(primary, fallback))` first retries
* the primary, then on exhaustion falls back to the secondary.
*
* Common pairings:
* • Anthropic primary, OpenAI fallback (vendor outage tolerance)
* • Real provider primary, Mock fallback (degrade gracefully in dev)
* • Premium model primary, cheaper model fallback (cost ceiling)
*
* `stream()` falls back too — if the primary's stream errors before
* yielding any chunks, we restart on the fallback. Once the primary
* has yielded chunks the stream is committed — fallback would
* duplicate the partial output.
*
* **Status: contract-shaped and tested — independently reproduced against a
* local harness, 2026-08-13.** Somebody who is not this library's author called
* a failed primary once and a healthy fallback once, with
* `agentfootprint.fallback.triggered` on the typed stream; a stream that failed
* BEFORE its first chunk moved to the fallback, and one that failed AFTER a
* chunk did not — the stream-pinning law, measured rather than asserted, and
* the reason no output was duplicated. Both providers were SCRIPTED doubles and
* the run was deterministic and local (the trial's own words: it *"consumed no
* GCP credit"*). **No failover between two live providers has been exercised
* from this repository**, so this is not the field-validated rung.
*/
import type { LLMProvider } from '../provider-contract/types.js';
export interface WithFallbackOptions {
/**
* Predicate to decide whether an error from the primary should
* trigger fallback. Default: every error except an AbortError and an
* `UnsupportedThinkingError`, `UnsupportedToolChoiceError` or
* `UnsafeThinkingReplayError` (a request the
* primary refused before sending).
* Override to gate on specific status codes or error types.
*/
readonly shouldFallback?: (error: unknown) => boolean;
/**
* Hook invoked when the primary fails and we're about to call the
* fallback. Useful for logging in standalone (non-agentfootprint) use.
*
* You do NOT need this to get fallback telemetry inside a run: since
* v7.8 the in-run LLM call sites hand this decorator an `LLMCallHooks`
* and translate its reports into `agentfootprint.fallback.triggered`
* events, stamped with the real `runId`/`runtimeStageId`. This hook is
* the consumer-owned escape hatch and its contract is unchanged.
*/
readonly onFallback?: (error: unknown) => void;
}
/**
* Wrap a primary provider with a fallback. Tries primary first; on
* error matching the policy, calls the fallback.
*
* @example
* const provider = withFallback(
* anthropic({ apiKey: A }),
* openai({ apiKey: O }),
* { onFallback: (err) => console.warn('primary failed, falling back:', err) },
* );
*/
export declare function withFallback(primary: LLMProvider, fallback: LLMProvider, options?: WithFallbackOptions): LLMProvider;