agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
82 lines • 4.45 kB
TypeScript
/**
* backoff — builders of a `RetryPolicy.backoffMs` function: an exponential
* schedule, and a wait the server stated winning over it.
*
* STAND-IN, TO BE DELETED. footprintjs is adding these two helpers to its
* attempt runner (`attempt/backoff.ts`): `exponentialBackoff({ baseDelayMs,
* multiplier, jitter, maxDelayMs, random })` and
* `statedWaitFirst(readStatedWaitMs, inner)`, with these names, signatures and
* laws. Once a footprintjs release
* ships them, import them in `throttleRetry.ts` and delete this file; nothing
* else changes.
*
* Pattern: Two pure builders. Each returns `(attempt, error?) => ms`.
* Role: The wait `throttleRetry.ts` plans for each retry.
* Emits: N/A.
*
* ── THE LAW ──────────────────────────────────────────────────────────────
* `exponentialBackoff`: after failed attempt `n` (1-based) the ceiling is
*
* c = min(maxDelayMs, baseDelayMs × multiplier^(n − 1))
* (saturating at MAX_SAFE_INTEGER)
*
* and, for `r = random()` in [0, 1]:
*
* 'none' c `random` is never called
* 'equal' min(c, c/2 + r × c/2) never below half the ceiling
* 'full' r × c anywhere from 0 up to it
*
* Waits are exact milliseconds — nothing is rounded. `random` defaults to
* `Math.random`, taken when the function is BUILT; a fixed source makes a test
* exact. A `random()` answering outside [0, 1] throws when asked.
*
* `statedWaitFirst`: when `readStatedWaitMs(error)` answers a finite number
* ≥ 0, that is the wait; otherwise `inner` decides. A reader that throws or
* answers anything else states nothing, so a malformed `Retry-After` falls back
* to the schedule rather than to a wrong wait.
*
* `maxDelayMs` caps the CEILING, before the draw, so capped waits keep their
* jitter: at the cap, equal jitter still spreads between c/2 and c. Capped
* after the draw instead, every wait past twice the cap would BE the cap, and
* clients refused together would retry in lockstep. A stated wait is never
* capped: `statedWaitFirst` hands it back whole.
*/
/** How much of an exponential wait is random: none of it, half of it, or all of it. */
export type BackoffJitter = 'none' | 'equal' | 'full';
/** The schedule {@link exponentialBackoff} builds a backoff function from. */
export interface BackoffSpec {
/** The ceiling of the first wait (after attempt 1), in milliseconds — a finite number ≥ 0. */
readonly baseDelayMs: number;
/** How much each ceiling grows over the one before — a finite number ≥ 1. Default `2`. */
readonly multiplier?: number;
/** How much of each wait is random. Default `'none'`: the wait IS the ceiling. */
readonly jitter?: BackoffJitter;
/**
* The cap on every ceiling, in milliseconds — a number ≥ 0, applied BEFORE
* the draw, so capped waits keep their jitter. Default: none.
*/
readonly maxDelayMs?: number;
/** The jitter's random source, answering in [0, 1]. Default `Math.random`, taken when built. */
readonly random?: () => number;
}
/** A `RetryPolicy.backoffMs` function: the wait in milliseconds after failed attempt `attempt` (1-based). */
export type BackoffFunction = (attempt: number, error?: unknown) => number;
/** Reads the wait an error states (a parsed `Retry-After`), in milliseconds, or `undefined` when it states none. */
export type StatedWaitReader = (error: unknown) => number | undefined;
/**
* An exponential schedule: the wait after failed attempt `n` is drawn under
* `baseDelayMs × multiplier^(n − 1)` by the declared jitter.
*
* @throws when built: a `TypeError` for an option of the wrong type, a
* `RangeError` for one out of range — each naming the option.
*/
export declare function exponentialBackoff(spec: BackoffSpec): BackoffFunction;
/**
* A wait the outside system stated wins; otherwise `inner` — a backoff
* function, or a fixed number of milliseconds — decides.
*
* @throws when built: a `TypeError` for a reader that is not a function, or an
* `inner` that is neither a function nor a finite number ≥ 0.
*/
export declare function statedWaitFirst(readStatedWaitMs: StatedWaitReader, inner: BackoffFunction | number): BackoffFunction;
//# sourceMappingURL=backoff.d.ts.map