//#region src/types/errors.d.ts type LLMErrorType = 'timeout' | 'api' | 'network' | 'parse' | 'validation' | 'invalid_params' | 'rate_limited' | 'quota_exceeded' | 'circuit_open' | 'fallback_exhausted' | 'aborted' | 'unknown'; /** * Machine readable discriminator within a `type`, for cases where `type` * alone is too coarse to act on. Optional and additive: errors thrown * before a given code existed simply omit it. Not owned by a single type; * e.g. `authentication`/`authorization` apply the same way regardless of * which type wraps them. */ type LLMErrorCode = 'unknown_tool' | 'duplicate_tool_call_id' | 'tool_choice_none_violated' | 'unexpected_tool_calls' | 'unsupported_capability' | 'duplicate_tool_names' | 'unknown_tool_choice' | 'duplicate_tool_result_ids' | 'unknown_tool_result_ids' | 'missing_tool_results' | 'middleware_threw' | 'rate_limit_queue_full' | 'rate_limit_queue_timeout' | 'rate_limit_capacity_exceeded' | 'provider_rate_limited' | 'retry_budget_exhausted' | 'request_timeout' | 'idle_timeout' | 'middleware_timeout' | 'deadline_exceeded' | 'authentication' | 'authorization' | 'not_found' | 'payload_too_large' | 'server_error' | 'empty_response' | 'connection_failed' | 'circuit_cooling_down' | 'circuit_trial_in_flight' | 'fallback_exhausted' | 'tool_arguments_parse_failed' | 'stream_frame_invalid' | 'soft_failure_detected'; /** One tool call's contract failure, used to report every bad call in a response at once. */ interface ToolIssue { name: string; toolCallId: string; code: LLMErrorCode; detail?: unknown; } /** * The specific values behind a `duplicate_tool_names` failure: the * offending call's `tools` array had more than one entry sharing a name. */ interface DuplicateToolNamesIssue { names: string[]; } /** * The specific values behind an `unknown_tool_choice` failure: `toolChoice` * named a tool that wasn't in the call's own `tools` array. */ interface UnknownToolChoiceIssue { requested: string; available: string[]; } /** * The specific values behind a `duplicate_tool_result_ids` / * `unknown_tool_result_ids` / `missing_tool_results` failure: which * `history` turn was affected, and which `toolCallId`s were the problem. */ interface HistoryToolResultIssue { historyIndex: number; ids: string[]; } /** * The specific values behind an `unsupported_capability` failure: which * capability the current adapter/client/model doesn't support. */ interface UnsupportedCapabilityIssue { capability: string; } /** * Maps each `LLMErrorCode` that carries structured `issues` to that * payload's exact shape. Not every code appears here: most `invalid_params` * failures are a single deterministic fact the `message` already states in * full, so adding a typed `issues` entry for them would only duplicate the * message into a field, the same near-duplicate-code problem `code` itself * avoids. Codes that repeat here are exactly the ones whose `message` * already string-joins a list a caller might want to consume directly * rather than re-parse out of prose, or that otherwise want a place to * report the exact captured values of a failure. * * Deliberately not a mapped type over the whole `LLMErrorCode` union: a * schema-validation failure's `issues` (the caller's own Zod-compatible * validator's error object) has no code and no shape VernLLM could know in * advance, so it stays untyped on `LLMError.issues` itself rather than * forcing every code into this table. */ interface LLMErrorIssuesByCode { unknown_tool: ToolIssue[]; duplicate_tool_call_id: ToolIssue[]; duplicate_tool_names: DuplicateToolNamesIssue; unknown_tool_choice: UnknownToolChoiceIssue; duplicate_tool_result_ids: HistoryToolResultIssue; unknown_tool_result_ids: HistoryToolResultIssue; missing_tool_results: HistoryToolResultIssue; unsupported_capability: UnsupportedCapabilityIssue; } /** * Point-in-time copy of an `LLMError`'s fields, produced by * `LLMError.toSnapshot()`. This is what `RetryAttempt.error` holds * instead of a live `LLMError`. * * A past attempt only needs to be describable (message, type, code, * whether it was retryable), never thrown again. So it skips `Error`'s * behavior, `instanceof` identity, and any live getter. Using the full * `LLMError` class here would also make the type self referential * through its own `attempts` field. * * Has no `cause`. `cause` is `unknown` and never validated by VernLLM, * and it is meant to be read directly on the live error you just * caught, not carried indefinitely inside history. `type`, `code`, * `status`, and `issues` are the structured fields a snapshot carries * instead. * * `attempts` is still present, since a recorded attempt can itself be * the terminal failure of an inner retry loop with its own history (see * `FallbackAttempt`). That's a tree of past data, not a cycle. */ interface LLMErrorSnapshot { message: string; type: LLMErrorType; status?: number; issues?: unknown; retryAfterMs?: number; code?: LLMErrorCode; /** Computed once, at snapshot time, since a snapshot has no live getter. */ retryable: boolean; /** This attempt's own prior attempts, if it was itself the terminal failure of a retry loop. */ attempts?: RetryAttempt[]; } /** * Point-in-time copy of the request an attempt sent, produced by * `toRequestSnapshot()`. This is what `RetryAttempt.request` holds. * Mirrors `LLMErrorSnapshot`: plain data, never thrown or dispatched * again, safe to serialize and store. */ interface LLMRequestSnapshot { /** Provider id this attempt targeted, e.g. "openai". */ provider: string; /** Model id this attempt targeted. */ model: string; /** The payload as actually sent for this attempt, after any transform/repair. Passed through `safeBody`. */ body: unknown; /** Non sensitive request headers. Auth headers are stripped before the snapshot is built, never included. */ headers?: Record; /** Wall clock time the attempt started, ms since epoch. */ startedAt: number; } /** * One failed attempt on the way to a terminal error: which attempt index * it was, and a snapshot of the error it failed with. The base shape * every richer attempt record (e.g. `FallbackAttempt`) extends, rather * than duplicates. */ interface RetryAttempt { index: number; error: LLMErrorSnapshot; /** What was sent for this attempt. Optional: absent for attempts predating this field. */ request?: LLMRequestSnapshot; } /** Optional fields for constructing an {@link LLMError}. `message` and `type` stay positional since every throw site sets both. */ interface LLMErrorOptions { status?: number; issues?: unknown; cause?: unknown; retryAfterMs?: number; /** Stable discriminator within `type`. Absent on errors predating it. */ code?: LLMErrorCode; /** Every attempt made before this error was thrown, in order. Absent when nothing was retried. */ attempts?: RetryAttempt[]; } export declare class LLMError extends Error { type: LLMErrorType; status?: number; issues?: unknown; cause?: unknown; retryAfterMs?: number; /** Stable discriminator within `type`. Absent on errors predating it. */ code?: LLMErrorCode; /** Every attempt made before this error was thrown, in order. Absent when nothing was retried. */ attempts?: RetryAttempt[]; constructor(message: string, type: LLMErrorType, options?: LLMErrorOptions); /** * Computed purely from `type`/`code`, independent of any specific call's * `nonRetryableStatus` list. False for `parse`/`validation`/ * `invalid_params`/`aborted` types (the caller's own input, the model's * own response, or intentional cancellation, none of which are the * provider being unhealthy), the tool contract codes, the local * rate limit codes, and the middleware timeout code. * Subclasses (see `FallbackExhaustedError`) may override this when `type` * alone carries no retry signal. */ get retryable(): boolean; /** * Whether this failure should count toward the circuit breaker's * failure threshold. Not the same question as `retryable`: * `quota_exceeded` is retryable but says nothing about provider * health, so it's excluded here even though `retryable` is true for * it. Always false whenever `retryable` is false. */ get countsTowardBreaker(): boolean; /** * Copies this error's fields into an {@link LLMErrorSnapshot}, for * recording as a `RetryAttempt`/`FallbackAttempt`. `retryable` is * captured here since a snapshot has no getter of its own. `cause` is * not copied, see `LLMErrorSnapshot`'s own doc. `issues` and every * nested `attempts` entry's own `issues` go through `safeAttempts`, * since a schema validation failure's `issues` is a caller supplied * value, not controlled by VernLLM, and `attempts` is itself a public * constructor option a caller can hand build. */ toSnapshot(): LLMErrorSnapshot; /** * Controls what `JSON.stringify(err)` produces. Omits `cause` for the * same reason `toSnapshot()` does: `cause` is `unknown` and never * validated by VernLLM, and some SDK errors carry circular structures * `JSON.stringify` cannot serialize at all. Read `err.cause` directly * instead. `issues`, including every nested `attempts` entry's own * `issues`, goes through `safeAttempts` for the same reason: a schema * validation failure's `issues` is caller supplied and not guaranteed * circular free. Also includes `message` and `retryable`, which a * plain property walk would otherwise miss: `message` is * non-enumerable on `Error`, and `retryable` is a getter, not an own * property. */ toJSON(): Record; } export declare function isLLMError(err: unknown): err is LLMError; /** * Narrows `err.issues` to the exact shape {@link LLMErrorIssuesByCode} maps * `code` to, for any code listed there. `code` stays the only discriminator * VernLLM uses; this just gives that existing check a typed return instead * of requiring a manual cast of `issues`: * * ```ts * if (isLLMError(err) && hasIssues(err, 'duplicate_tool_names')) { * console.log(err.issues.names); // string[], no cast needed * } * ``` */ export declare function hasIssues(err: LLMError, code: C): err is LLMError & { code: C; issues: LLMErrorIssuesByCode[C]; }; //#endregion //#region src/types/cache.d.ts interface CacheAdapter { get(key: string): Promise<{ hit: boolean; value: T | null; }>; set(key: string, value: T, ttl: number): Promise; delete?(key: string): Promise; resolveKey?(key: string): Promise; } /** * Which entry `InMemoryCacheAdapter` evicts once `maxSize` is exceeded. * `'fifo'` (default) drops the oldest inserted entry. `'lru'` drops the * least recently read or written entry. */ type EvictionOption = 'fifo' | 'lru'; /** * Trivial default so the package works out of the box with no external deps. * Not shared across processes, swap in Redis/Upstash/etc for production. */ export declare class InMemoryCacheAdapter implements CacheAdapter { private readonly maxSize; private store; private readonly eviction; constructor(maxSize?: number, eviction?: EvictionOption); get(key: string): Promise<{ hit: boolean; value: T | null; }>; set(key: string, value: T, ttl: number): Promise; delete(key: string): Promise; private cleanupExpiredEntries; private enforceSizeLimit; } /** * Normalizes keys before caching to avoid duplicate entries from formatting differences. */ export declare class NormalizedCacheAdapter implements CacheAdapter { private readonly inner; constructor(inner?: CacheAdapter); private normalize; resolveKey(key: string): Promise; get(key: string): Promise<{ hit: boolean; value: T | null; }>; set(key: string, value: T, ttl: number): Promise; delete(key: string): Promise; } /** * Two-tier cache with fast local L1 and shared L2. * L2 hits are promoted back to L1. */ export declare class TieredCacheAdapter implements CacheAdapter { private readonly l1; private readonly l2; private readonly l1Ttl?; constructor(l1: CacheAdapter, l2: CacheAdapter, l1Ttl?: number | undefined); /** * Forwards to L1's `resolveKey` if it has one, otherwise L2's. L1 is * preferred since `get()` checks L1 first, so its notion of "the same * key" is the one that determines whether a lookup can skip L2 entirely. */ resolveKey(key: string): Promise; get(key: string): Promise<{ hit: boolean; value: T | null; }>; set(key: string, value: T, ttl: number): Promise; delete(key: string): Promise; } //#endregion //#region src/logger.d.ts interface Logger { debug(message: string): void; warn(message: string): void; error(message: string, meta?: Record): void; } /** * Default logger. `debug` is gated by the `debug` option on VernLLM * warn/error always fire since they indicate real problems (retries, cache failures) */ export declare class ConsoleLogger implements Logger { private debugEnabled; constructor(debugEnabled: boolean); debug(message: string): void; warn(message: string): void; error(message: string, meta?: Record): void; } //#endregion //#region src/types/usage.d.ts type ReserveUsage = (params: { coalesced: boolean; signal?: AbortSignal; }) => Promise; type RefundUsage = (params: { coalesced: boolean; signal?: AbortSignal; }) => Promise; /** * The reserve/refund usage hooks shared by `CallParams`, `CachedCallParams`, * and `VernLLM`'s internal `withReservedUsage`. Centralized here so the pair * has one definition instead of being redeclared at each use site. */ interface UsageHooks { /** * Reserves usage before the request. Failures become * LLMError('quota_exceeded'). */ reserveUsage?: ReserveUsage; /** * Refunds usage after a failed call if reservation succeeded. */ refundUsage?: RefundUsage; } interface TokenUsage { promptTokens: number; completionTokens: number; totalTokens: number; /** * Tokens spent on internal reasoning, a subset of `completionTokens`, * never added on top of it. Undefined when the provider's response * doesn't report a separate reasoning figure, e.g. Bedrock Converse * without an explicit `additionalModelResponseFieldPaths` request. */ reasoningTokens?: number; requestId: string; model: string; /** * The provider target that produced this usage. See `VernLLMOptions['name']`, * default `'primary'`. Optional so consumers constructing a `TokenUsage` * themselves (e.g. in tests) aren't forced to supply it; `VernLLM` always * populates it. Absent means the same as `'primary'` if you need a value. */ provider?: string; /** * Whether this usage came from a fallback target rather than the * primary. Optional for the same reason `provider` is: `VernLLM` * always populates it, a hand-constructed `TokenUsage` (e.g. in tests) * isn't forced to. */ usedFallback?: boolean; } type OnUsage = (usage: TokenUsage) => void; /** * Called when a provider response arrives but VernLLM's own post-processing * then fails, after usage data was already present in that response. Covers * any error thrown after usage extraction, not just parse/validation, since * everything in that path only runs once a response, and real spend, has * already arrived. Fires once per failed attempt with extractable usage, * never for transport failures, where no response means no honest number * to report. */ type OnUsageFailure = (usage: TokenUsage, error: LLMError) => void; //#endregion //#region src/types/events.d.ts /** * Reports what happened during a call. Fire and forget, mirroring * `onUsage`: the return value is never read and a throwing handler cannot * change what the call does, only what gets reported about it. */ type VernLLMEvent = { kind: 'retry'; requestId: string; provider: string; /** The model actually resolved for this call (honors a per-call `model` override). */ model: string; /** The 1-based retry ordinal (the 1st retry is `1`, not the overall attempt count). */ attempt: number; maxRetries: number; delayMs: number; retryAfterHonored: boolean; error: LLMError; } | { kind: 'circuit_state'; provider: string; /** * The model of the call that triggered this specific transition * (whatever was passed to the `assertClosed`/`recordSuccess`/ * `recordFailure` call that caused it), not a property of the * circuit itself: the breaker still counts failures across every * model together, so a threshold crossing can be the sum of * several different models' failures even though only the * triggering call's `model` is reported here. */ model: string; from: CircuitState; to: CircuitState; consecutiveFailures: number; } | { kind: 'fallback'; requestId: string; /** Provider name of the target that just failed. */ from: string; /** Provider name of the target about to be tried next. */ to: string; /** `-1` for the primary target, otherwise the index into `fallback`. */ fromIndex: number; toIndex: number; /** The normalized error that caused `from` to be abandoned. */ error: LLMError; /** Time spent on `from`, including its own retries, before giving up. */ elapsedMs: number; } | { kind: 'rate_limited'; requestId: string; provider: string; /** The model actually resolved for this call (honors a per-call `model` override). */ model: string; /** How long this attempt sat queued for capacity before it was let through. */ waitedMs: number; /** Which configured bucket was blocking this attempt just before it cleared. */ reason: 'concurrency' | 'rpm' | 'tpm'; } | { kind: 'middleware'; requestId: string; /** This middleware's `name`, or its array position if unnamed. */ middleware: string; hook: 'transform' | 'wrap_short_circuit' | 'enabled_skip'; /** For `hook: 'transform'` only: which top-level fields the merged patch touched. */ patchedFields?: string[]; } | { /** * Reported once a call fully succeeds. Same data `VernLLMOptions.onUsage` * receives; that option is sugar over this event, not a second * reporting path, see `makeEventReporter`. */ kind: 'usage'; requestId: string; usage: TokenUsage; } | { /** * A provider response arrived, carrying real usage, and VernLLM's own * post-processing then failed. Fires once per failed attempt with * extractable usage, matching `VernLLMOptions.onUsageFailure`'s own * granularity, which this event is sugar over, not a second path. */ kind: 'usage_failure'; requestId: string; usage: TokenUsage; error: LLMError; }; type OnEvent = (event: VernLLMEvent) => void; //#endregion //#region src/types/middleware.d.ts /** Capabilities of the target a middleware hook is currently looking at. */ interface MiddlewareCapabilities { /** * Whether this target honors `response_format: { type: 'json_object' }` * as a real constraint. Mirrors `LLMClient.supportsJsonObjectMode`. * `false` for `fromAnthropic` and `fromBedrock`. */ supportsJsonObjectMode: boolean; } /** * Not exported. Distinguishes `MiddlewareStateKey` from * `MiddlewareRef` and from a plain `{ debugName }` object literal at * the type level, even though all three have the identical runtime * shape. Without this, `MiddlewareStateKey`/`MiddlewareRef` are * structurally just `{ debugName: string }`, so TypeScript would treat * a state key as a valid middleware ref (or vice versa), and would let * anyone hand-write `{ debugName: 'auth' }` in place of a real * `createMiddlewareRef` result. Neither is possible once this brand is * required: only `createStateKey`, which alone has access to this * symbol, can produce a value satisfying `MiddlewareStateKey`. */ declare const stateKeyBrand: unique symbol; /** * A typed reference to one slot in `ctx.state`. Create one with * `createStateKey`, export it, and import the same reference wherever * another middleware needs to read or write the same value. There's no * string key anywhere in this path, so a typo becomes a missing import * or an undefined variable, a compile error, instead of a silently * created new property. */ interface MiddlewareStateKey { readonly debugName: string; readonly [stateKeyBrand]: true; /** * Never set at runtime; exists purely so `T` is actually used * somewhere in this interface's shape (a phantom type), which is what * lets `MiddlewareStateBag.get`/`set` infer the right type for a given * key instead of two `MiddlewareStateKey` and * `MiddlewareStateKey` keys being structurally identical. */ readonly __phantom?: T; } /** Creates a new, distinct `MiddlewareStateKey`. `debugName` is used only in log lines and the `'middleware'` event; it never affects equality. */ export declare function createStateKey(debugName: string): MiddlewareStateKey; /** Not exported. See `stateKeyBrand`; same reasoning, distinct symbol, so the two token types can't be cross-assigned either. */ declare const middlewareRefBrand: unique symbol; /** * A typed reference to one middleware's identity, for `runsAfter`/ * `runsBefore` to target. Purely an ordering concern: unlike `name`, * `ref` is never used as a display label anywhere (`name` still covers * that), only as a `runsAfter`/`runsBefore` match target. Create one * with `createMiddlewareRef`, export it from the package that owns the * middleware, and have any dependent import the same reference instead * of typing a matching `name` string. Same reasoning as * `MiddlewareStateKey`: a typo becomes a missing import, a compile * error, instead of a silently unresolved (or worse, silently * colliding) string. */ interface MiddlewareRef { readonly debugName: string; readonly [middlewareRefBrand]: true; } /** Creates a new, distinct `MiddlewareRef`. `debugName` is used only in error messages when a reference doesn't resolve; it never affects equality, so two refs with the same `debugName` never collide. */ export declare function createMiddlewareRef(debugName: string): MiddlewareRef; /** * A `runsAfter`/`runsBefore` entry that escalates an unresolved * reference from a warning to a construction-time throw. Wrap a * `MiddlewareRef` with `requireRef` when the dependency isn't optional: * a bare `MiddlewareRef` in `runsAfter`/`runsBefore` means "order * relative to this if it's registered," which is the right default for * a dependency a third party may reasonably not have installed. A * `RequiredMiddlewareRef` means "this middleware must not run without * that dependency having already run". The app should fail to start * rather than run with a silently-missing ordering guarantee. */ interface RequiredMiddlewareRef { readonly ref: MiddlewareRef; } /** Wraps `ref` so `runsAfter`/`runsBefore` throws at `VernLLM` construction time if it doesn't resolve, instead of warning and continuing. */ export declare function requireRef(ref: MiddlewareRef): RequiredMiddlewareRef; /** * Typed, per-logical-call storage two middleware can deliberately share a * value through (a span ID one sets, another reads). Backed by a plain * `Map` internally, created once per logical call and never read or * written by VernLLM itself. */ interface MiddlewareStateBag { get(key: MiddlewareStateKey): T | undefined; set(key: MiddlewareStateKey, value: T): void; } /** A plain, `Map`-backed `MiddlewareStateBag`. */ export declare function createMiddlewareStateBag(): MiddlewareStateBag; /** Fields every `MiddlewareContext` variant carries, regardless of `stage`. */ interface MiddlewareContextBase { requestId: string; /** Capabilities of the target this stage's identity fields describe. */ capabilities: MiddlewareCapabilities; signal?: AbortSignal; /** Shared, collision-proof state for two middleware to deliberately coordinate through. See `MiddlewareStateBag`. */ state: MiddlewareStateBag; /** Simple, string-keyed scratch space, pre-namespaced to this one middleware so two middleware can never collide here even by accident. */ own: Record; /** * Every registered middleware's resolved label, in `transformOrder`, * frozen. Lets a middleware make an informed call, like skipping a * duplicate action when it detects another known middleware by name * already handles it, without needing to know anything else about * that middleware's own configuration. */ registeredMiddlewareNames: readonly string[]; } /** * The `ctx` `transform` receives, and every attempt-scoped event context * (`'retry'`, `'fallback'`, `'circuit_state'`, `'middleware'`). Built once * a specific target has actually been selected for this attempt, so every * field describes the real target, not a placeholder. */ interface AttemptContext extends MiddlewareContextBase { stage: 'attempt'; /** The target this attempt is actually dispatched to. */ requestedProvider: string; requestedModel: string; isFallbackAttempt: boolean; /** * The real, current attempt number for this dispatch. * * Exception: on a `'circuit_state'` event triggered by a pre-dispatch * check (`assertClosed`, before any attempt has been made), this is * `1` regardless of which attempt is about to run, since no attempt * exists yet to report. Every other `'circuit_state'` event, and every * other attempt-scoped event, reports the real attempt number. */ attempt: number; } /** * The `ctx` `wrap` receives before `next()` resolves (and `onError`'s own * `ctx`, built the same way under the hood). Built once, before any * fallback target is chosen, so it only ever describes the primary * target. There is no real "requested" target yet, and no attempt count, * fallback flag, or per-attempt capability to report. Read `next()`'s * resolved `CallResult.meta` once you need to know what actually * happened. */ interface PreDispatchContext extends MiddlewareContextBase { stage: 'pre-dispatch'; /** The primary target only, not necessarily who ends up answering. */ primaryProvider: string; primaryModel: string; } /** * `enabled` and `onEvent` are called from both stages (gating/observing * `transform` as well as `wrap`), so they receive this union and must * narrow on `ctx.stage` before reading stage-specific fields. * `transform` and `wrap` themselves receive the single variant that's * always accurate for them (`AttemptContext`/`PreDispatchContext` * respectively). See `VernLLMMiddleware`. */ type MiddlewareContext = AttemptContext | PreDispatchContext; /** The `response_format` shape `RequestBuilder` can put on the wire. */ type WireResponseFormat = { type: 'json_object'; } | { type: 'json_schema'; json_schema: { name: string; schema: Record; strict?: boolean; description?: string; }; }; /** A tool as it appears on the wire, OpenAI's `function`-wrapped shape. */ interface WireTool { type: 'function'; function: { name: string; description: string; parameters: Record; }; } /** * The wire-shaped request `RequestBuilder.build()` produces for one call * attempt, before dispatch. Read only inside `transform`; return a patch * of the fields you want to change instead of the whole object. */ interface WireCallRequest { model: string; temperature?: number; max_tokens: number; response_format?: WireResponseFormat; reasoning_effort?: 'minimal' | 'low' | 'medium' | 'high'; budget_tokens?: number; tools?: WireTool[]; tool_choice?: WireToolChoice; messages: WireMessage[]; } /** * What `transform` returns: a patch merged onto the request that * `RequestBuilder.build()` (plus every earlier middleware's own patch) * already produced, not a replacement for it. `model` and * `response_format` can't be expressed here at all, since everything * downstream that attributes a call to a target keys off the values * `RequestBuilder` already resolved for those two fields, not off * whatever ends up on the wire request. `messages`/`tools` are joined by * a separate `add*` field, appended rather than replaced, so two * independently written middleware can each add to the list without one * silently clobbering what the other already added. */ interface WireCallRequestPatch { temperature?: number; max_tokens?: number; reasoning_effort?: 'minimal' | 'low' | 'medium' | 'high'; budget_tokens?: number; tool_choice?: WireToolChoice; /** Replaces the whole message list. Prefer `addMessages` unless a full replace is genuinely the intent. */ messages?: WireMessage[]; /** Appended after whatever earlier middleware already added. Never clobbers a prior addition. */ addMessages?: WireMessage[]; /** Replaces the whole tool list. Prefer `addTools`, same reasoning as `messages`/`addMessages`. */ tools?: WireTool[]; /** Appended after whatever earlier middleware already added. Never clobbers a prior addition. */ addTools?: WireTool[]; } /** * The settled outcome of one logical call, passed to `wrap`'s `next()`. * `meta` is populated once a target has actually answered, for both * streaming and non-streaming calls (`undefined` only on a cache hit, * where nothing was actually spent). */ interface CallResult { value: T; meta?: CallMeta; } /** * One entry in `VernLLMOptions.middleware`. All four hooks are optional; * an entry that sets none of them is inert. See the middleware docs for * how `transform`, `wrap`, `onEvent`, and `enabled` compose across * several entries. */ interface VernLLMMiddleware { /** Used in log lines and the `'middleware'` event. Defaults to this entry's array position when omitted. */ name?: string; /** * This entry's own identity, purely for another middleware's * `runsAfter`/`runsBefore` to target. Create with `createMiddlewareRef`, * export it, and have a dependent import the same reference. Optional: * only needed if something else must be able to depend on this * specific entry. Unrelated to `name`: `ref` is never shown in logs, * `name` is never matched against for ordering. */ ref?: MiddlewareRef; /** Sort key for composition order, ascending, ties broken by array order. See the middleware docs for what "lower runs first" means for `wrap`. */ priority?: number; /** * Other middleware this entry must run after, breaking ties * `priority` alone can't express. Matched by `ref` identity, so a * typo or a stale copy simply fails to resolve instead of silently * matching the wrong entry. A bare `MiddlewareRef` that doesn't * resolve is dropped, not an error, since a third party may * reasonably reference a well known middleware that isn't installed * everywhere; wrap it with `requireRef` to make that same target * mandatory instead, throwing at `VernLLM` construction time if it's * missing. A cycle across `runsAfter`/`runsBefore` always throws, * regardless of whether any individual entry is required. */ runsAfter?: (MiddlewareRef | RequiredMiddlewareRef)[]; /** * Other middleware this entry must run before. See `runsAfter`; a * bare reference is dropped if unresolved, a `requireRef`-wrapped one * throws. */ runsBefore?: (MiddlewareRef | RequiredMiddlewareRef)[]; /** * Pins this entry's slot in `wrap` nesting only, independent of * `priority`/`runsAfter`/`runsBefore`, which still govern * `transform`/`onEvent` order. `'outermost'` sees the net * `CallResult` of every retry, fallback, and other middleware's * `wrap`; `'innermost'` sits closest to the real dispatch. A numeric * value behaves like `priority`, but only for `wrap` nesting. */ position?: 'outermost' | 'innermost' | number; /** * Boolean for a static on/off switch, or a predicate evaluated per * call. A throwing, rejecting, or timed-out predicate is logged and * treated as `false` for that call. */ enabled?: boolean | ((ctx: MiddlewareContext) => boolean | Promise); /** Per-middleware override of the instance-level `middlewareTimeoutMs`, applied to this entry's `transform` and function `enabled`. `<= 0` means unbounded (no timer at all). */ timeoutMs?: number; /** Transforms the outgoing wire request for one attempt. Runs once per attempt, including retries. `ctx` is always accurate to the real target for this attempt. */ transform?: (request: Readonly, ctx: AttemptContext) => WireCallRequestPatch | Promise; /** * Wraps one whole logical call, exactly once, regardless of how many * retries or fallback targets ran underneath it. `ctx` is built once, * before any fallback target is chosen, so it only describes the * primary target. There is no `requestedProvider`/`isFallbackAttempt`/ * `attempt` to read here. Read `next()`'s resolved `CallResult.meta` * for what actually happened. */ wrap?: (request: Readonly, next: () => Promise, ctx: PreDispatchContext) => Promise; /** Observes the same events reported on `VernLLMOptions.onEvent`, filtered by this middleware's own `enabled`. Called from both stages; narrow on `ctx.stage` before reading stage-specific fields. */ onEvent?: (event: VernLLMEvent, ctx: MiddlewareContext) => void; } //#endregion //#region src/circuitBreaker.d.ts /** The call this mutation happened as part of, forwarded to `onStateChange` untouched. */ interface CircuitBreakerCallContext { requestId: string; state: MiddlewareStateBag; signal?: AbortSignal; /** Omitted for calls before any attempt exists, like `assertClosed`'s pre-dispatch check. */ attempt?: number; } /** * Fires after every real state change, never a no-op transition. `model` * is the resolved model of whichever call triggered it. With * `isolateByModel` off, failures are still counted across every model. * Shared by `CircuitBreakerOptions` and `CircuitBreakerAdapter`, so a * custom adapter reports state changes the same way the built in * `CircuitBreaker` does. */ type CircuitBreakerStateChangeHandler = (from: CircuitState, to: CircuitState, consecutiveFailures: number, model?: string, context?: CircuitBreakerCallContext) => void; interface CircuitBreakerOptions { /** Consecutive failures before the circuit opens, default 5 */ threshold?: number; /** How long the circuit stays open before allowing a trial request, in ms. Default 30000 */ cooldownMs?: number; onStateChange?: CircuitBreakerStateChangeHandler; /** * Track a separate circuit per resolved model instead of one shared * circuit. Default false. A call that omits `model` falls into one * shared bucket alongside every other call that also omits it. */ isolateByModel?: boolean; /** Trial calls allowed through per half-open cycle. Default 1, clamped to at least 1. */ halfOpenProbes?: number; /** Fraction of `halfOpenProbes` that must succeed to close the circuit. Default 1, clamped to `[0, 1]`. */ halfOpenSuccessRatio?: number; /** * Grows `cooldownMs` on each repeat open instead of a fixed wait. * `{ multiplier, maxMs }` covers exponential growth; a `CooldownBackoff` * function covers anything else. Omitted means `cooldownMs` stays fixed. */ cooldownBackoff?: ExponentialBackoffOptions | CooldownBackoff; /** * Decides when a bucket's failures should open the circuit. * `{ kind: 'consecutive', threshold }` (the default) opens after that * many failures in a row. `{ kind: 'rolling', windowMs, minCalls, * failureRatio }` opens once at least `minCalls` calls have landed in * the trailing `windowMs` and the failure ratio reaches `failureRatio`. * `minCalls` must be a non-negative integer; `failureRatio` must be * finite and within `[0, 1]`. Both are validated at construction, * thrown as `RangeError`. A `TrippingPolicy` covers anything else, one * instance shared across every model automatically under * `isolateByModel`, since it tracks its own state per key rather than * owning one flat counter. */ tripping?: TrippingOption; } /** Computes the cooldown for a bucket's `reopenCount`-th repeat open. */ type CooldownBackoff = (reopenCount: number, baseCooldownMs: number) => number; interface ExponentialBackoffOptions { /** Growth factor applied per repeat open, e.g. 2 doubles each time. */ multiplier: number; /** Upper bound on the computed cooldown, in ms. Default `Infinity`. */ maxMs?: number; } /** * Decides when a bucket's failures should open the circuit. Keyed by * `key` (a resolved model, or the shared bucket's key when * `isolateByModel` is off) rather than holding one flat counter, so a * single `TrippingPolicy` instance is always safe to share across every * bucket: `CircuitBreaker` never needs to clone or construct a fresh one * per model, `isolateByModel` isolation falls out of `key` alone. */ interface TrippingPolicy { onSuccess(key: string): void; /** Returns true if this failure should open the circuit for `key`. */ onFailure(key: string): boolean; reset(key: string): void; /** * Called when `key`'s bucket is discarded (closed and idle, under * `isolateByModel`), so a keyed policy can release that key's state. * Optional: omit if there's nothing to release. */ forget?(key: string): void; } export declare class ConsecutiveTripping implements TrippingPolicy { private readonly threshold; private failuresByKey; constructor(threshold: number); onSuccess(key: string): void; onFailure(key: string): boolean; reset(key: string): void; forget(key: string): void; } export declare class RollingTripping implements TrippingPolicy { private readonly windowMs; private readonly minCalls; private readonly failureRatio; private ratiosByKey; constructor(windowMs: number, minCalls: number, failureRatio: number); private ratioFor; onSuccess(key: string): void; onFailure(key: string): boolean; reset(key: string): void; forget(key: string): void; } /** Not exported. Internal shorthand union for `CircuitBreakerOptions.tripping`. */ type TrippingOption = { kind: 'consecutive'; threshold: number; } | { kind: 'rolling'; windowMs: number; minCalls: number; failureRatio: number; } | TrippingPolicy; type CircuitState = 'closed' | 'open' | 'half-open'; /** * What VernLLM's dispatch layer needs from a breaker. `CircuitBreaker` * implements this; a caller wanting cross process coordination can hand * over their own instance instead. * * `assertClosed`, `recordSuccess`, `recordFailure`, and `onStateChange` * are required, mirroring `RateLimiterAdapter`'s four required methods. * `onStateChange` is required so `circuit_state` events can't go * silently missing; a no-op `() => {}` is fine if you don't care. * * `getState`, `getFailureBreakdown`, `isolateByModel`, `open`, and * `close` are optional. Omitting one makes the matching call a no-op * or return `undefined`/`false`, same as no breaker configured. * `open`/`close` are optional since they let VernLLM force a * transition, control a distributed adapter may not want to grant. */ interface CircuitBreakerAdapter { /** Throws when the circuit is open (or half open with no trial slot free) for `model`. */ assertClosed(model?: string, context?: CircuitBreakerCallContext): void; recordSuccess(model?: string, context?: CircuitBreakerCallContext): void; /** `code`, when present, is the failing call's `LLMErrorCode`. */ recordFailure(model?: string, context?: CircuitBreakerCallContext, code?: LLMErrorCode): void; getState?(model?: string): CircuitState; /** Failure counts by `LLMErrorCode` for `model`'s bucket, `'unknown'` for one that carried no code. */ getFailureBreakdown?(model?: string): Partial>; /** Whether this adapter tracks failures per model, mirroring `CircuitBreakerOptions.isolateByModel`. Read by `warnIfModelUnsupported`'s diagnostic warning and by `VernLLM.getCircuitStates()`'s public output; omit if the notion doesn't apply to your adapter, `false` is assumed. */ isolateByModel?: boolean; /** Manually opens the circuit, as if enough consecutive failures had just happened. Optional: an adapter that doesn't want external callers forcing a transition can omit it. */ open?(model?: string, context?: CircuitBreakerCallContext): void; /** Manually closes the circuit, without requiring a real success first. Same opt-in reasoning as `open`. */ close?(model?: string, context?: CircuitBreakerCallContext): void; /** Gives back a half-open trial slot when a call ends without `recordSuccess` or `recordFailure`. Idempotent, and a no-op for a call that holds no slot. */ releaseTrial?(model?: string, context?: CircuitBreakerCallContext): void; /** Awaited right before `assertClosed` to refresh local state. Never blocks or fails a call: a rejection or `prepareTimeoutMs` is logged and the call carries on. */ prepare?(model?: string, context?: CircuitBreakerCallContext): Promise; /** How long to wait for `prepare`, in ms. Default 1000. */ prepareTimeoutMs?: number; /** Live counterpart of `getState`, read by `VernLLM.readCircuitStates()`. */ readState?(model?: string): Promise; /** Receives the instance's `Logger` once, when `VernLLM` wires this adapter in. */ setLogger?(logger: Logger): void; /** * Called after every real state change, never a no-op transition. VernLLM * wraps it the same way it wraps the built in `CircuitBreaker`'s * `onStateChange`: every call still reports a `circuit_state` event * first, then this hook is chained after that, wrapped so a throw here * can't break the call that triggered it. */ onStateChange: CircuitBreakerStateChangeHandler; } /** * Per retry VernLLM-instance circuit breaker. Tracks consecutive failures * across calls. Once the threshold is hit, short-circuits new calls with * LLMError('circuit_open') until the cooldown elapses and a trial succeeds. */ export declare class CircuitBreaker implements CircuitBreakerAdapter { private readonly cooldownMs; /** Satisfies `CircuitBreakerAdapter.onStateChange`, required there. Defaults to a no-op when `options.onStateChange` is omitted. */ readonly onStateChange: CircuitBreakerStateChangeHandler; /** Whether this breaker tracks failures per model instead of one shared circuit. */ readonly isolateByModel: boolean; private readonly halfOpenProbes; private readonly halfOpenSuccessRatio; private readonly cooldownBackoff?; /** One instance, keyed per model internally. See `TrippingPolicy`. */ private readonly tripping; private readonly sharedBucket; private readonly bucketsByModel; constructor(options?: CircuitBreakerOptions); /** * Throws if the circuit is open and the cooldown hasn't elapsed, or if * half-open with every trial slot claimed. Otherwise claims a trial slot. */ assertClosed(model?: string, context?: CircuitBreakerCallContext): void; recordSuccess(model?: string, context?: CircuitBreakerCallContext): void; /** `code`, when present, is the failing `LLMError`'s `code`. Missing attributes to `'unknown'`. */ recordFailure(model?: string, context?: CircuitBreakerCallContext, code?: LLMErrorCode): void; /** * Gives back the half-open trial slot `context`'s call claimed, when * that call ended without recording an outcome. No-op without a * `context`, when the bucket isn't half-open, or when the call's permit * is stale or already spent (an outcome was recorded), so calling it * defensively on every failure path is safe. */ releaseTrial(model?: string, context?: CircuitBreakerCallContext): void; /** With `isolateByModel` off, `model` is ignored and the shared circuit's state is returned. */ getState(model?: string): CircuitState; /** Failure counts by `LLMErrorCode` for `model`'s bucket. Returned as a plain object copy. */ getFailureBreakdown(model?: string): Partial>; /** Manually opens the circuit, as if `threshold` consecutive failures had just happened. */ open(model?: string, context?: CircuitBreakerCallContext): void; /** Manually closes the circuit and resets its failure count, without requiring a real success first. */ close(model?: string, context?: CircuitBreakerCallContext): void; /** * Opens `bucket`: stamps `openedAt`/`cooldownMsForOpen` and transitions * to `open`. Shared by `recordFailure`'s trip, `settleTrialIfComplete`'s * reopen, and the manual `open()`, all of which reach this with * `bucket.trial` already `null`. */ private openBucket; /** Computes and clamps the cooldown for `bucket`'s current `reopenCount`. Called once, on open. */ private computeCooldown; /** Returns the bucket for a model if one already exists, without allocating. */ private lookupBucket; /** * The key `tripping` is called with. Real per-model isolation under * `isolateByModel`, matching `ensureBucketFor`/`lookupBucket`'s own * per-model key. Otherwise one fixed shared key regardless of what * `model` was passed, matching `sharedBucket` being the one and only * bucket in that mode: `model` is never allowed to split tripping state * when `isolateByModel` is off, the same way it never splits which * bucket a call lands in. */ private trippingKeyFor; /** Creates and stores a bucket for a model when the first mutation needs one. */ private ensureBucketFor; /** Drops an idle model's bucket and lets `tripping` release that key's state too. */ private forgetModel; /** Every state mutation routes through here, so `onStateChange` fires exactly once per real change. */ private transition; /** Once every admitted trial has reported in, closes or reopens based on `halfOpenSuccessRatio`. */ private settleTrialIfComplete; } //#endregion //#region src/internal/retryBudget.d.ts /** * Tunables for a `RetryBudget`. `windowMs`/`minCalls` behave the same as * `RollingTripping`'s (see `circuitBreaker.ts`): `minCalls` gates the * check so a cold start with too little traffic to judge doesn't trip. * `retryRatio` is the max fraction of calls in the window allowed to be * retries before the budget stops allowing more. `minCalls` must be a * non-negative integer; `retryRatio` must be finite and within `[0, 1]`. * Both are validated at construction, thrown as `RangeError`. */ interface RetryBudgetOptions { windowMs: number; minCalls: number; retryRatio: number; } /** * Caps how much of a target's recent traffic is allowed to be retries, * independent of the circuit breaker. The breaker asks whether the * provider is healthy; this asks whether retrying is still worth the * capacity it costs, regardless of provider health. Reuses `RollingRatio`, * the same primitive `RollingTripping` is built on, rather than a second * hand rolled window. */ export declare class RetryBudget { private readonly options; private readonly ratio; constructor(options: RetryBudgetOptions); /** * Throws `LLMError('retry_budget_exhausted')` once at least `minCalls` * calls have landed in the trailing `windowMs` and the retry ratio * among them has reached `retryRatio`. A no-op otherwise. */ assertAvailable(): void; /** Records one attempt. `isRetry` is false for a call's first attempt, true for every attempt after it. */ recordAttempt(isRetry: boolean): void; /** Current traffic and retry ratio in the trailing window. */ getSnapshot(): { attempts: number; retryRatio: number; }; } //#endregion //#region src/internal/utils/rate-limit/rateLimitHint.utils.d.ts /** A normalized read of a provider's rate limit headers. */ interface ProviderRateLimitHint { remainingRequests?: number; limitRequests?: number; resetAfterMs?: number; } //#endregion //#region src/rateLimit.d.ts /** The request shape sent to `LLMClient['chat']['completions']['create']`, used for token estimation. */ type WireRequest = Parameters[0]; /** Which configured bucket is currently blocking a call. */ type RateLimitReason = 'concurrency' | 'rpm' | 'tpm'; interface RateLimitOptions { /** Max requests per minute. Omit for unlimited. */ requestsPerMinute?: number; /** * Max tokens per minute. Enforced against a pre-flight estimate, then * reconciled against reported usage once the call completes. Omit for * unlimited. */ tokensPerMinute?: number; /** Max requests in flight at once. Default 0, meaning unlimited. */ maxConcurrent?: number; /** * Max time a call may sit queued waiting for capacity, in ms. Exceeding * it throws rather than hanging forever. Default 30000. Pass 0 to wait * indefinitely. */ maxQueueMs?: number; /** Max queued calls before new ones reject immediately instead of queueing. Default 0, unbounded. */ maxQueueSize?: number; /** * Pre-flight token estimate for `tokensPerMinute`. Defaults to a * chars/4 heuristic over message content plus `max_tokens`. */ estimateTokens?: (request: WireRequest) => number; /** * Scales the pre-flight estimate down before it's reserved against * `tokensPerMinute`, since most calls don't use their full `max_tokens` * budget. Applied after `estimateTokens`, as rate-limiter bookkeeping * only; never changes the `max_tokens` sent to the provider. * `release`'s `actualTokens` still reconciles against real usage * afterward. Default `1` (today's behavior, no scaling). Must be a * finite number greater than `0`; values above `1` are clamped to `1`. */ estimateFraction?: number; /** * AIMD against the `requestsPerMinute` bucket. Omit for a fixed * ceiling, today's behavior. Requires `requestsPerMinute`. */ aimd?: AimdOptions; } interface AimdOptions { /** Added to the requests-per-minute ceiling on every clean release. */ increaseBy: number; /** Multiplied against the ceiling on a rate-limit signal. Must be greater than `0` and at most `1`; clamped otherwise. */ decreaseFactor: number; /** Floor the ceiling never shrinks below. */ minCapacity: number; /** Ceiling the bucket never grows above. */ maxCapacity: number; /** * Shrink proactively once a provider hint reports `remainingRequests` * at or below this, before a real 429 happens. Default 0, meaning * off. */ proactiveFloor?: number; } interface RateLimitState { /** Requests still available this window, or `undefined` if `requestsPerMinute` isn't configured. */ requestsRemaining?: number; /** Tokens still available this window, or `undefined` if `tokensPerMinute` isn't configured. */ tokensRemaining?: number; /** Concurrency slots currently in use, or `undefined` if `maxConcurrent` isn't configured. */ concurrentInFlight?: number; } interface RateLimitAcquireResult { /** * Releases the concurrency slot this attempt held and reconciles the * token bucket against real usage, when `actualTokens` is supplied. * Idempotent: only the first call does anything. Must run in a * `finally` block so a slot is never leaked on a failed attempt. */ release: (actualTokens?: number, success?: boolean) => void; /** How long this attempt waited in queue before capacity was available. */ waitedMs: number; /** Which bucket was blocking this attempt just before it cleared, if any wait happened. */ reason?: RateLimitReason; } /** Default `estimateTokens`: chars/4 over every message's content, plus the requested `max_tokens`. */ export declare function defaultEstimateTokens(request: WireRequest): number; /** * What VernLLM's dispatch layer needs from a limiter. `RateLimiter` * implements this; a caller wanting cross-process coordination can hand * over their own instance instead, see `buildRateLimit`. Every method is * required, `RateLimiter` itself already no-ops the AIMD methods when * `aimd` isn't configured, so a custom limiter follows the same pattern. */ interface RateLimiterAdapter { estimate(request: WireRequest): number; acquire(estimatedTokens: number, signal?: AbortSignal): Promise; signalRateLimit(): void; reactToRateLimitHint(hint: ProviderRateLimitHint | undefined): void; /** Optional: current bucket levels, for introspection. Omit if the adapter has no state worth reporting. */ getState?(): RateLimitState; /** Optional: live bucket levels for `VernLLM.readRateLimitState()`, the async counterpart of `getState`. */ readState?(): Promise; /** Optional: receives the instance's `Logger` once, when `VernLLM` wires this adapter in. */ setLogger?(logger: Logger): void; } /** * Per-target rate limiter. Up to three buckets (requests/min, tokens/min, * concurrency) behind one FIFO queue, so a large call isn't starved by a * stream of small ones. Any bucket omitted from `options` has infinite * capacity and never blocks. */ export declare class RateLimiter implements RateLimiterAdapter { private readonly requests?; private readonly tokens?; private readonly concurrency?; /** Buckets in acquire precedence order (concurrency, rpm, tpm), omitted ones filtered out. Built once so order can't drift between `tryAcquireBuckets` and `scheduleWake`. */ private readonly buckets; private readonly maxQueueMs; private readonly maxQueueSize; private readonly estimateTokensFn; private readonly estimateFraction; private readonly aimd?; private readonly queue; /** * A single scheduled re-check for the head of the queue when it's * blocked on a bucket that refills on its own clock (rpm/tpm), so a * queue that nobody calls `acquire`/`release` on again isn't stuck * forever waiting for an external trigger to re-drain it. Not needed * for a concurrency block, which only clears via `release`. */ private wakeTimer?; constructor(options: RateLimitOptions); /** * Pre-flight token estimate for a request, per the configured (or * default) heuristic, scaled by `estimateFraction`. This is the sole * value reserved against `tokensPerMinute` and later reconciled in * `release`; the provider-facing `max_tokens` on the request itself is * never touched. */ estimate(request: WireRequest): number; /** * Waits for capacity in every configured bucket, then takes from each. * The returned `release` gives the concurrency slot back and reconciles * the token bucket against real usage; it must run in a `finally` block. */ acquire(estimatedTokens: number, signal?: AbortSignal): Promise; private queueFullError; private enqueue; /** Takes from every configured bucket as one atomic unit, in `this.buckets`' order. Rolls back whatever was already taken if any bucket lacks capacity. */ private tryAcquireBuckets; /** Drains the queue head first. Stops at the first waiter that still can't proceed, so no one is starved out of turn. */ private drain; /** * Schedules a one-shot re-check of the queue for whenever the bucket * that's currently blocking the head waiter should next have enough * capacity. A no-op for a concurrency block (only `release` can clear * that) or while a wake is already pending. */ private scheduleWake; /** * Builds the one-shot release closure for an acquired slot. Only the * concurrency bucket is given back on release; the requests-per-minute * bucket is a real spend that only recovers via its own refill, and the * tokens bucket is reconciled against `actualTokens` rather than fully * refunded, since real tokens really were spent. * * `success` defaults to `false`: the AIMD ceiling only grows when the * caller explicitly confirms a successful attempt. A failed or * rate-limited attempt still releases its slot (so nothing leaks), but * must not also grow the ceiling right back up after * `signalRateLimit()` just shrank it. */ private makeRelease; /** Shared guard and resize call behind both AIMD halves below; only the arithmetic differs. */ private resizeRequestsCeiling; /** AIMD's additive-increase half: grows the ceiling by `aimd.increaseBy` on a clean release. No-op without `aimd`/`requestsPerMinute`. */ private growOnSuccess; /** * AIMD's multiplicative-decrease half. Called on a real 429, and, * where an adapter can produce a hint, proactively via * `reactToRateLimitHint`. Never throws or blocks a call itself, only * adjusts the ceiling as a side effect. */ signalRateLimit(): void; /** * AIMD's proactive entry point: shrinks via `signalRateLimit()` if * `hint.remainingRequests` is at or below `aimd.proactiveFloor`. */ reactToRateLimitHint(hint: ProviderRateLimitHint | undefined): void; /** * Current bucket levels, read live rather than cached. `concurrency` * tracks free slots internally, so `concurrentInFlight` is reported as * `capacity - available`, the inverse of what the bucket itself holds. */ getState(): RateLimitState; } //#endregion //#region src/internal/utils/rate-limit/rateLimitAdapter.utils.d.ts /** Not exported. Internal shorthand only, so this union isn't duplicated between the public option fields and `buildRateLimit`'s own signature. */ type RateLimitOption = RateLimitOptions | RateLimiterAdapter; //#endregion //#region src/types/fallback.d.ts /** * One provider to try after the primary (or after an earlier fallback * target) fails. Order is the policy: VernLLM never reorders, scores, or * selects a target, it only walks the list as given. * * Most per-target overrides fall back to the parent `VernLLM` instance's * own option when omitted, so a target only needs to specify what's * actually different about it (a different client/model is the common * case). `circuitBreaker`, `rateLimit`, and `retryBudget` are the * exception: they are never inherited from the parent, since a breaker, * limiter, or budget tuned for the primary provider's limits is rarely * right for a fallback's. Leave them unset on a target to run it without * one, even if the parent has one configured. */ interface FallbackTarget { client: LLMClient; model: string; /** Label for events, errors, and `TokenUsage.provider`. Default `` `fallback[${index}]` ``. */ name?: string; maxRetries?: number; timeoutMs?: number; chunkIdleTimeoutMs?: number; baseDelayMs?: number; defaultMaxTokens?: number; defaultTemperature?: number | null; defaultReasoningEffort?: 'minimal' | 'low' | 'medium' | 'high'; defaultBudgetTokens?: number; nonRetryableStatus?: number[]; /** This target's own circuit breaker, independent of every other target's. Not inherited from the parent's `circuitBreaker`. */ circuitBreaker?: boolean | CircuitBreakerOptions; /** This target's own rate limiter, independent of every other target's. Not inherited from the parent's `rateLimit`. */ rateLimit?: RateLimitOption; /** This target's own retry budget, independent of every other target's. Not inherited from the parent's `retryBudget`. */ retryBudget?: RetryBudgetOptions; /** * Reclassifies an otherwise-successful result from this target as a * failure. Falls back to the parent `VernLLM` instance's own * `detectSoftFailure` when omitted, same as most other per-target * options (unlike `circuitBreaker`/`rateLimit`, which never inherit). */ detectSoftFailure?: DetectSoftFailure; } /** * Written into `CallParams['meta']` once `call()` resolves, so a caller * who wants provider identity on the same line as the result doesn't need * to read it back out of `onUsage`. */ interface CallMeta { provider: string; model: string; /** `-1` if the primary target answered, otherwise the index into `fallback`. */ fallbackIndex: number; usedFallback: boolean; /** Attempts made against the target that ultimately answered, including the successful one. */ attempts: number; } /** One target's circuit state, as returned by `VernLLM.getCircuitStates()`. */ interface TargetCircuitState { provider: string; /** Position in the chain: `0` for the primary, `1`+ for fallback targets. */ index: number; isFallback: boolean; /** Whether this target tracks failures per model. `false` means `model` on `getCircuitStates` had no effect on this entry. */ isolateByModel: boolean; /** `undefined` if that target has no circuit breaker configured. */ state: CircuitState | undefined; } /** Which target/model `VernLLM.getCircuitState`, `openCircuit`, and `closeCircuit` act on. */ interface CircuitTarget { /** Which target to act on. `0` is the primary, `1`+ are fallbacks. Defaults to `0`. */ index?: number; /** Which model bucket to act on, if the resolved target isolates by model. */ model?: string; } /** * One target's failure, recorded on the way to either the next target or * `FallbackExhaustedError`. Extends `RetryAttempt`: `index` is `-1` for * the primary target here (rather than a plain retry count), and * `provider`/`model` identify which target failed. */ interface FallbackAttempt extends RetryAttempt { provider: string; model: string; } /** * Decides what happens after a target's own retries are exhausted or * abandoned early. Called once per failed target. `'retry'` is not a * valid return here: retrying already happened inside the target, this * only decides whether to move on to the next one or stop. */ type FallbackOn = (error: LLMError, context: { isLastTarget: boolean; }) => 'next' | 'stop'; /** * The default `fallbackOn` policy. Exported so a caller can wrap rather * than replace it, e.g. `fallbackOn: (e, ctx) => myCheck(e) ? 'stop' : defaultFallbackOn(e, ctx)`. */ export declare const defaultFallbackOn: FallbackOn; /** * Thrown when the chain gives up, whether because the last target failed * or `fallbackOn` chose to stop early. Carries each attempt in order so * an outage across providers stays debuggable without reproducing it. * Extends `LLMError` so `isLLMError` and any `instanceof LLMError` check * still passes, inheriting the last failure's `type`/`status`/`retryAfterMs` * so existing type-based handling, including reading `retryAfterMs` on an * `'api'`-typed error, keeps working on a fallback-exhausted error too. */ export declare class FallbackExhaustedError extends LLMError { readonly attempts: FallbackAttempt[]; constructor(attempts: FallbackAttempt[]); /** * `type: 'fallback_exhausted'` by itself says nothing about whether * retrying could help; the reason the last target failed does. Defers to * that attempt's own `retryable` instead of anything about this class's * own type. */ get retryable(): boolean; } /** Narrows `err` to {@link FallbackExhaustedError}, for direct access to its `attempts` (`provider`/`model` per failed target) without a manual `instanceof` check. */ export declare function isFallbackExhaustedError(err: unknown): err is FallbackExhaustedError; /** * Creates an empty ref box to pass as `CallParams['meta']`, so a caller can * read the `CallMeta` written by `call()` on the same line as the result * instead of pre-declaring a `{ current?: CallMeta }` by hand. * * @example * const meta = metaRef(); * const result = await vern.call({ userContent: '...', meta }); * meta.current?.provider; */ export declare function metaRef(): { current?: CallMeta; }; //#endregion //#region src/types/schema.d.ts /** * Minimal structural type for a Zod-like schema, so this package doesnt need * a hard dependency on a specific Zod major version. Any object exposing * `safeParse` (Zod v3/v4, and most Zod-compatible validators) should satisfy this */ interface SchemaLike { safeParse(data: unknown): { success: true; data: T; } | { success: false; error: unknown; }; } /** * A provider-native JSON Schema for structured outputs (OpenAI/Groq * `response_format: { type: 'json_schema' }`) This is the wire-format * schema the model is constrained to generate against, distinct from * `schema`, which is a client-side Zod validator run on the parsed result * You can use one, both, or neither; using both gets you provider-level * constraint plus client-side type inference/validation as a safety net */ interface JsonSchemaSpec { name: string; schema: Record; /** Enforces the schema strictly (OpenAI-specific), default true when supported */ strict?: boolean; description?: string; } //#endregion //#region src/types/tools.d.ts /** * Describes a capability the model may request, not the capability * itself. VernLLM transports this to the provider and parses what comes * back; it never executes anything. */ interface ToolDefinition { name: Name; description: string; /** JSON Schema for the tool's input. */ parameters: Record; /** * Optional client-side validator run on the parsed `arguments` before * they're handed back to the caller, mirroring the `schema: SchemaLike` * pattern already used for response validation (see `types/schema.ts`). * Reuses that zero-dependency, `safeParse`-compatible shape instead of * requiring a JSON Schema validator (e.g. ajv) as a new dependency. * Failed validation throws `LLMError('validation')`. If omitted, VernLLM * parses arguments as JSON but does not validate them further. * * When set, `Args` (and therefore `Name`) flow into the `ToolCall`s * returned by `call()`/`cachedCall()`, provided the tool was declared * with `defineTool()` or otherwise has a literal `name`; see * `defineTool()` below for why a plain object literal often doesn't. */ argumentsSchema?: SchemaLike; } /** * Preserves a tool definition's literal `name` (and its `argumentsSchema`'s * inferred `Args`) so it can discriminate a `ToolCall` union later. * * A plain object literal like `{ name: 'get_weather', ... }` widens `name` * to `string` unless annotated `as const`, which silently defeats * `ToolCall` narrowing the moment a second tool is added to the same * `tools: [...]` array (single-tool arrays still narrow fine even without * this, since there's nothing to discriminate against but that stops * being true as soon as a second tool shows up). Wrapping the same object * in `defineTool()` preserves the literal `name` type without requiring * `as const` at every call site. */ export declare function defineTool(tool: ToolDefinition): ToolDefinition; /** Maps a single `ToolDefinition` to its matching `ToolCall` shape. */ type ToolCallFor = T extends ToolDefinition ? { id: string; name: N; arguments: A; } : never; /** * A single tool invocation requested by the model. * * When `Tools` is a literal tuple (e.g. inferred from `tools: [getWeather, * cancelOrder]` at a `call()`/`cachedCall()` site), this is a discriminated * union keyed by `name`. Checking `call.name === 'get_weather'` narrows * `call.arguments` to that tool's `Args` with no cast needed. Without a * literal `Tools` (the default), this collapses back to today's * `{ id: string; name: string; arguments: unknown }`. */ type ToolCall = ToolCallFor; /** The application's result of executing a `ToolCall`, sent back to the model. */ interface ToolResult { toolCallId: string; content: unknown; /** * Signals a failed tool execution back to the model (matches Anthropic's * native `is_error` on tool_result blocks). Only `fromAnthropic` honors * this today, Gemini and Bedrock have no equivalent wire concept, so * other adapters ignore it silently. */ isError?: boolean; } /** `call()` result when `tools` was set and the model produced a normal answer. */ interface ContentResult { type: 'content'; content: T; } /** `call()` result when `tools` was set and the model requested one or more tools. */ interface ToolCallResult { type: 'tool_calls'; toolCalls: ToolCall[]; /** Any text the model produced alongside the tool request, if present. */ content?: string; } type CallWithToolsResult = ContentResult | ToolCallResult; /** Recovers `Tools` from a `result` already typed `ContentResult | ToolCallResult`. Falls back to `ToolDefinition[]`. */ type ExtractTools = Extract> extends ToolCallResult ? Tools : ToolDefinition[]; /** Explicit `Tools` type argument if given, otherwise inferred via `ExtractTools`. `never` marks "unset". */ type ResolvedTools = [Tools] extends [never] ? ExtractTools : Tools; /** * Runtime check for whether a `call()` result is a `tool_calls` result. Use * this instead of trusting static narrowing whenever `tools` was set * conditionally, see `ConditionalToolCallParams`. * * ```ts * const result = await llm.call({ userContent: '...', tools: someCondition ? [myTool] : undefined }); * if (isToolCallResult(result)) { * // result.toolCalls[number].arguments typed per tool, inferred automatically * } * ``` * * Pass `Tools` explicitly to override inference, e.g. `isToolCallResult(result)`. */ export declare function isToolCallResult(result: R): result is R & ToolCallResult>>; /** What the model should do about tools on a given call. */ type ToolChoice = 'auto' | 'none' | 'required' | { name: string; }; //#endregion //#region src/types/call.d.ts /** * Any valid JSON value: a primitive, `null`, or a JSON array/object made * of the same. This is what `call()` returns when `jsonMode: true`. */ type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue; }; /** * Content for an `assistant` turn in `history`. Accepts a string or a * parsed `JsonValue`, so a prior `jsonMode: true` response can be pushed * straight back into history. Request construction stringifies non-string * content before it's sent to the provider. */ type AssistantContent = string | JsonValue; /** * A single prior turn in a multi-turn conversation, passed via `history`. * * Supports normal user/assistant messages and tool continuations: an assistant * turn may include `toolCalls`, and a tool turn carries the matching * `toolResults`. A tool turn must immediately follow an assistant tool call * turn, and every requested tool call must have a result. */ type ConversationTurn = { role: 'user'; content: string; } | { role: 'assistant'; content?: AssistantContent; toolCalls?: ToolCall[]; } | { role: 'tool'; toolResults: ToolResult[]; }; /** A plain text segment of a multimodal `userContent` array. */ interface TextBlock { type: 'text'; text: string; } /** * An inline image segment of a multimodal `userContent` array. * * `data` is the raw base64-encoded image bytes, with no `data:` URL prefix * (adapters that need a data URL, e.g. OpenAI-compatible `image_url`, build * it themselves from `mimeType` + `data`; adapters that need raw bytes, e.g. * Bedrock, decode the base64 themselves). */ interface ImageBlock { type: 'image'; /** Base64-encoded image bytes, no `data:` prefix */ data: string; /** e.g. 'image/png', 'image/jpeg', 'image/webp', 'image/gif' */ mimeType: string; } /** A single segment of multimodal `userContent`. */ type ContentBlock = TextBlock | ImageBlock; /** * Every field of a call request except the `reserveUsage`/`refundUsage` * hooks from `UsageHooks`. `CallParams` is this plus `UsageHooks`; the * `Cached*` param types below are call sites that want the request shape * without those two hooks (usage is metered once, at the `cachedCall` * level, not per-request), and use this directly instead of re-deriving * it with `Omit, 'reserveUsage' | 'refundUsage'>` each time. */ interface LLMRequestShape { systemPrompt?: string; /** Current user message, as text or multimodal content blocks. */ userContent: string | ContentBlock[]; /** * Previous conversation turns. Must alternate roles; tool turns must follow * assistant tool calls. Invalid history throws LLMError('invalid_params'). */ history?: ConversationTurn[]; /** * Generation temperature. Default 0.2, not the provider's own default. * Pass `null` to omit `temperature` from the request entirely, so the * provider applies its own default instead. */ temperature?: number | null; jsonMode?: boolean; maxTokens?: number; requestId?: string; signal?: AbortSignal; /** * Total time budget in ms for this whole call, across every retry and * every fallback target. Unlike timeoutMs, which resets on each attempt, * this is a single clock starting when call is invoked. The call is * aborted once this elapses, even mid retry or mid fallback, the same * way an aborted signal is today. Omit for no overall deadline, only * the existing per attempt timeoutMs applies. * * Only bounds getting to a final result: choosing a target, retrying, * and opening a stream. It does not extend to the time spent reading a * stream after it has opened. Use chunkIdleTimeoutMs for gaps between * chunks once a stream is open. */ deadlineMs?: number; /** * Per-call override for the instance's `chunkIdleTimeoutMs` (max gap * between stream chunks once opened). Only applies when `stream: true`. * Useful for routes using reasoning-heavy models with documented long * silent gaps mid-stream. Pass 0 to disable the idle timeout for this * call. */ chunkIdleTimeoutMs?: number; /** Overrides the instance model for this call. */ model?: string; /** * Reasoning effort for supported reasoning models. Pass `null` to * explicitly skip an instance-level `defaultReasoningEffort` for this * one call (e.g. a call using a forced `toolChoice`, which Anthropic * rejects alongside any reasoning at all), the same way `temperature: * null` opts a call out of `defaultTemperature`. Omitting the field * entirely (`undefined`) defers to the instance default instead. */ reasoningEffort?: 'minimal' | 'low' | 'medium' | 'high' | null; /** * Token budget for internal reasoning, for models with a native numeric * budget (Anthropic's `budget_tokens`, Gemini's `thinkingBudget`). On a * provider that only understands `reasoningEffort` tiers (OpenAI- * compatible), this is converted to the nearest tier instead of sent as * a raw number. When both `budgetTokens` and `reasoningEffort` are set, * each adapter prefers whichever field it natively understands and * ignores the other. See the reasoning budget docs for the conversion * table used in each direction. Pass `null` to explicitly skip an * instance-level `defaultBudgetTokens` for this one call, mirroring * `reasoningEffort: null` above; omitting the field entirely defers to * the instance default. */ budgetTokens?: number | null; /** * Provider-native JSON Schema output constraint. Implies jsonMode: true. */ jsonSchema?: JsonSchemaSpec; /** * Validates parsed JSON output. Failure throws LLMError('validation'). * Implies jsonMode: true. */ schema?: SchemaLike; /** * Tools the model may call. When set, `call()` returns a * `CallWithToolsResult` union instead of `T` directly. Combining with * `jsonSchema` is provider-dependent; see the Tool Calling docs. * * Passed as a literal array (or via `defineTool()`-wrapped entries, see * `types/tools.ts`), this also drives the `Tools` type parameter, which * narrows `CallWithToolsResult`'s `toolCalls[number].arguments` per tool. */ tools?: Tools; /** Defaults to `'auto'` when `tools` is set. */ toolChoice?: ToolChoice; /** * Streams the response incrementally instead of resolving once. Default: * false. Requires a client/adapter that implements `createStream`. * Retry/timeout/circuit-breaker guarantees apply only to opening the * stream (through the first chunk); a failure after that point rejects * `finalResult` directly and is not retried, since a mid-stream failure * isn't connection-time evidence for the circuit breaker, the attempt * already counted as a success once the first chunk arrived. Once the * stream opens successfully, `finalResult` still resolves to the same * validated `T`/`CallWithToolsResult` shape `call()` would have * returned for the same params with `stream` omitted. See * `StreamCallResult`. */ stream?: boolean; /** * Optional out-parameter for provider identity. Pass `{}` (or any object * with a mutable `current` property) and `call()` writes a `CallMeta` * into `meta.current` before returning, alongside whatever `onUsage` * already reports. This includes `stream: true`: the target is chosen * once the stream opens, which is also the point `call()` itself * returns `{ chunks, finalResult }`, so `meta.current` is already set * by then. `TokenUsage.provider`/`usedFallback` from `onUsage` reports * the same information asynchronously, for both streaming and * non-streaming calls. * * `meta.current` is only written once execution actually reaches and * selects a provider target. A `wrap` middleware that short-circuits * without calling `next()` never reaches that point, so `meta.current` * is left untouched; if the same holder object is reused across calls, * it can still hold a prior call's target. */ meta?: { current?: CallMeta; }; } interface CallParams extends LLMRequestShape, UsageHooks {} /** * A `CallParams` variant where tool calling is explicitly enabled. * * Requiring `tools` to be present allows TypeScript to select the * tool-aware `call()` overload and return `CallWithToolsResult` instead * of the normal `T` response type. */ type ToolEnabledCallParams = CallParams & { tools: NonNullable['tools']>; }; /** * A `CallParams` variant for tools set conditionally, e.g. `tools: * someCondition ? [myTool] : undefined`. Selects the `call()` overload * returning the honest union `T | CallWithToolsResult` instead of * falling through to plain `T` (which is what happened before this type * existed, since `ToolDefinition[] | undefined` matched neither * `ToolEnabledCallParams` nor `ToolsDisabledCallParams`). Forces an * `isToolCallResult()` check before treating the result as plain * content. Omitting `tools` entirely still resolves to plain `T`, since * tools genuinely cannot have run there. * * `Tools` still can't reliably infer a literal tuple here the way * `ToolEnabledCallParams` does for an inline array (a ternary/variable * expression doesn't carry the same `const`-literal preservation), so * getting typed `arguments` out of a conditional-tools result also needs * an explicit `Tools` type argument on `isToolCallResult()` when * narrowing, see its docs. */ type ConditionalToolCallParams = CallParams & { tools: Tools | undefined; }; /** Conditional tool-call parameters whose non-tool result is plain text. */ type ConditionalStringToolCallParams = ConditionalToolCallParams & { jsonMode: false; }; /** * A `CallParams` variant where tools are offered but the model is barred * from calling one. `toolChoice: 'none'` guarantees the response can never * be a `tool_calls` result, so `call()` can narrow straight to * `ContentResult` instead of the full `CallWithToolsResult` union. * A call site that already knows it forced `'none'` no longer needs a * runtime `isToolCallResult` check, or to remember that `String(result)` * on the wrapper object silently produces `"[object Object]"` instead of * throwing. The type itself rules that shape out. */ type ToolsDisabledCallParams = CallParams & { tools: NonNullable['tools']>; toolChoice: 'none'; }; /** * `CallParams` with `jsonMode: false`. Selects the `call()` overload * that returns a plain `string`. `jsonSchema` is typed `never` here: a * truthy `jsonSchema` forces JSON parsing at runtime regardless of * `jsonMode` (see `RequestBuilder.build()`), so `jsonMode: false` + * `jsonSchema` together would otherwise still match this overload and * falsely promise a `string`. */ type JsonModeDisabledCallParams = Omit, 'jsonSchema'> & { jsonMode: false; jsonSchema?: never; }; /** * `CallParams` with `jsonMode: true` and no `schema`. Selects the * `call()` overload that returns a `JsonValue`. * * `schema` is explicitly typed `never` here, not just omitted: `CallParams['schema']` * would be `SchemaLike | undefined`, and a schema whose inferred result type is * itself structurally assignable to `JsonValue` (e.g. a schema for `string[]` or * `Record`) would still satisfy that shape, incorrectly selecting this * overload over the schema-aware generic one and widening the result to `JsonValue`. Forcing * `schema?: never` makes any call that sets `schema` fail this overload's structural check * regardless of the schema's result type, so it always falls through to the generic * `CallParams` overload and infers `T` from the schema instead. */ type JsonModeEnabledCallParams = Omit, 'schema'> & { jsonMode: true; schema?: never; }; /** Shared cache-configuration fields, minus the internal `fn` primitive. */ interface CachedCallInput extends UsageHooks { cacheKey: string; ttl: number; signal?: AbortSignal; } /** * Parameters for a cached LLM call without tool calling: cache config * plus the `CallParams` passed to `call()`. `reserveUsage`/`refundUsage` * belong at the top level (`CachedCallInput`), not nested in `call`; see * the caching docs for why. */ type CachedCallParams = CachedCallInput & { call: LLMRequestShape; }; /** * Parameters for a cached LLM call with tool calling enabled. * * The cached value includes the full `CallWithToolsResult`, meaning * tool requests and normal content responses are cached exactly as returned * by the model. * * See `CachedCallParams` for why `reserveUsage`/`refundUsage` are omitted * from `call`'s type here too. */ type CachedToolCallParams = CachedCallInput & { call: LLMRequestShape & { tools: NonNullable['tools']>; }; }; /** * Parameters for a cached LLM call with `call.tools` set conditionally. * Selects the `cachedCall()` overload that returns the honest union * `T | CallWithToolsResult` instead of narrowing to plain `T`. See * `ConditionalToolCallParams` for why this overload exists. */ type CachedConditionalToolCallParams = CachedCallInput & { call: LLMRequestShape & { tools: Tools | undefined; }; }; /** Cached conditional tool-call parameters whose non-tool result is plain text. */ type CachedConditionalStringToolCallParams = CachedConditionalToolCallParams & { call: { jsonMode: false; }; }; /** * Parameters for a cached LLM call with `jsonMode: false`. Selects the * `cachedCall()` overload that returns a plain `string`. */ type CachedJsonModeDisabledCallParams = CachedCallInput & { call: Omit, 'jsonSchema'> & { jsonMode: false; jsonSchema?: never; }; }; /** * Parameters for a cached LLM call with `jsonMode: true` and no `schema`. * Selects the `cachedCall()` overload that returns a `JsonValue`. */ type CachedJsonModeEnabledCallParams = CachedCallInput & { call: Omit, 'schema'> & { jsonMode: true; schema?: never; }; }; /** Context handed to `DetectSoftFailure` alongside the response it's inspecting. */ interface SoftFailureMeta { requestId: string; model: string; providerName: string; isFallback: boolean; /** 1-based, matching `CallMeta.attempts`. */ attempt: number; /** * Token usage for this attempt, if the provider reported it on this * response. `undefined` when the provider omitted usage, not when * usage was zero, so a cost check should treat a missing value as * unknown rather than as free. */ usage?: TokenUsage; } /** * Inspects an otherwise-successful result and optionally reclassifies it * as a failure. Returning `undefined` leaves the result as a success; * returning an `LLMErrorCode` fails the attempt with that code, feeding * the same retry and circuit-breaker paths a thrown error would. A * result that parses fine but is empty, truncated, or a low-confidence * refusal is otherwise invisible to both. */ type DetectSoftFailure = (result: T | CallWithToolsResult, meta: SoftFailureMeta) => LLMErrorCode | undefined; //#endregion //#region src/types/stream.d.ts /** One incremental unit of a streaming response, as delivered to the caller. */ type StreamChunk = { type: 'text-delta'; delta: string; } | { type: 'tool_call_delta'; index: number; id?: string; name?: string; argsDelta?: string; /** * True when `argsDelta` is the whole set of arguments, not a * fragment. Set for Gemini (its API returns function-call args * whole in one chunk) and for cache/replay chunks, which are * one-shot too. Omitted or `false` for a genuine fragment from * providers that do stream incrementally (OpenAI-compatible, * Anthropic, Bedrock). */ complete?: boolean; } | { type: 'usage'; usage: TokenUsage; }; /** * What `call()` returns when `stream: true`. `finalResult` resolves to * the same shape `call()` would have returned with `stream` omitted. * `chunks` is single-use and buffered; see the streaming docs for the * full consumption/backpressure semantics. */ interface StreamCallResult { chunks: AsyncIterable; finalResult: Promise; } /** * A `CallParams` variant where streaming is explicitly enabled. * * Requiring `stream: true` to be statically present allows TypeScript to * select the streaming `call()` overload and return `StreamCallResult<...>` * instead of the normal, single-shot response type. */ type StreamEnabledCallParams = CallParams & { stream: true; }; /** Streaming conditional tool-call parameters whose non-tool result is text. */ type StreamConditionalStringToolCallParams = StreamEnabledCallParams & ConditionalStringToolCallParams; /** Recovers `T` from a `result` already typed `T | StreamCallResult`. Falls back to `unknown`. */ type ExtractStreamValue = Extract> extends StreamCallResult ? V : unknown; /** * Runtime check for whether a `call()` result is a `StreamCallResult` * (`{ chunks, finalResult }`) rather than the resolved value directly. * Useful when `stream` was computed conditionally and cast/narrowed * manually, since TypeScript's `call()` overloads only pick the streaming * shape for a literal `stream: true` at the call site. * * ```ts * const params = someCondition ? { userContent: '...', stream: true } : { userContent: '...' }; * const result = await llm.call(params as CallParams | (CallParams & { stream: true })); * if (isStreamResult(result)) { * for await (const chunk of result.chunks) { ... } * } * ``` */ export declare function isStreamResult(result: R): result is R & StreamCallResult>; /** * `StreamEnabledCallParams` with `jsonMode: false`. Selects the streaming * `call()` overload whose `finalResult` resolves to a plain `string`. * `jsonSchema` is typed `never` for the same reason as * `JsonModeDisabledCallParams`. */ type StreamJsonModeDisabledCallParams = Omit, 'jsonSchema'> & { jsonMode: false; jsonSchema?: never; }; /** * `StreamEnabledCallParams` with `jsonMode: true` and no `schema`. Selects * the streaming `call()` overload whose `finalResult` resolves to a * `JsonValue`. * * `schema` is explicitly `never` here for the same reason as * `JsonModeEnabledCallParams`: a schema whose result type is itself * structurally assignable to `JsonValue` would otherwise still satisfy this * overload's shape and incorrectly widen the result to `JsonValue` instead * of the schema's real type. */ type StreamJsonModeEnabledCallParams = Omit, 'schema'> & { jsonMode: true; schema?: never; }; /** * The adapter-facing, pre-normalization shape a `createStream` client * implementation emits, analogous to how `WireMessage`/`WireToolCall` * already sit between `CallParams` and each provider's own wire format. */ type WireStreamChunk = { type: 'text-delta'; delta: string; } | { type: 'tool_call_delta'; index: number; id?: string; name?: string; argumentsDelta?: string; /** Same meaning as `StreamChunk`'s `tool_call_delta.complete`. */ complete?: boolean; } | { type: 'usage'; usage: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number; completion_tokens_details?: { reasoning_tokens?: number; }; }; } | { /** * A provider keep-alive signal with no content of its own (e.g. * Anthropic's `ping` events, an SSE comment-line heartbeat). * Adapters yield this so the stream loop resets its idle timeout. * Never surfaced to callers as a `StreamChunk`. */ type: 'ping'; } | { /** * AIMD's proactive rate-limit hint, read off the stream's * response headers (where the adapter/SDK can get at them) and * yielded once, as early as possible. Mirrors `attachRateLimitHint` * for the non-streaming path, just carried as a chunk instead of a * hidden property on a response object, since a stream has no * single response value to attach one to. Never surfaced to * callers as a `StreamChunk`. */ type: 'rate_limit_hint'; hint: ProviderRateLimitHint; }; /** * Parameters for a cached, streaming LLM call without tool calling. * * The cached value is `T`, same as `CachedCallParams`, but a miss * relays live `chunks` to the caller while the result is being generated, * and a hit synthesizes a one-shot `chunks` replay from the cached value * (see `VernLLM.cachedCall`'s docs for exactly what that replay looks * like). * * `reserveUsage`/`refundUsage` are omitted from `call`'s type; see * `CachedCallParams` for why they belong at the top level here too. */ type CachedStreamCallParams = CachedCallInput & { call: LLMRequestShape & { stream: true; }; }; /** * Parameters for a cached, streaming LLM call with tool calling enabled. * * The cached value is the full `CallWithToolsResult`, same as * `CachedToolCallParams`, with the same live-chunks-on-miss, * replayed-chunks-on-hit behavior as `CachedStreamCallParams`. */ type CachedStreamToolCallParams = CachedCallInput & { call: LLMRequestShape & { stream: true; tools: NonNullable['tools']>; }; }; /** * Parameters for a cached, streaming LLM call with `call.tools` set * conditionally. Selects the `cachedCall()` overload whose `finalResult` * (on a miss) or cached value (on a hit) is the honest union * `T | CallWithToolsResult` instead of narrowing to plain `T`. See * `ConditionalToolCallParams` for why this overload exists. */ type CachedStreamConditionalToolCallParams = CachedCallInput & { call: LLMRequestShape & { stream: true; tools: Tools | undefined; }; }; /** Cached streaming conditional tool-call parameters whose non-tool result is text. */ type CachedStreamConditionalStringToolCallParams = CachedStreamConditionalToolCallParams & { call: { jsonMode: false; }; }; /** * Parameters for a cached, streaming LLM call with `jsonMode: false`. * Selects the `cachedCall()` overload whose `finalResult` (on a miss) or * cached value (on a hit) is a plain `string`. */ type CachedStreamJsonModeDisabledCallParams = CachedCallInput & { call: Omit, 'jsonSchema'> & { stream: true; jsonMode: false; jsonSchema?: never; }; }; /** * Parameters for a cached, streaming LLM call with `jsonMode: true` and no * `schema`. Selects the `cachedCall()` overload whose `finalResult` (on a * miss) or cached value (on a hit) is a `JsonValue`. */ type CachedStreamJsonModeEnabledCallParams = CachedCallInput & { call: Omit, 'schema'> & { stream: true; jsonMode: true; schema?: never; }; }; //#endregion //#region src/types/client.d.ts /** A tool call as it appears on the wire, OpenAI's `function`-wrapped shape. */ interface WireToolCall { id: string; type: 'function'; function: { name: string; /** JSON-encoded arguments, matching every OpenAI-compatible provider's wire format. */ arguments: string; }; } /** One entry of `LLMClient`'s `messages` array, named so callers building it can annotate against it. */ type WireMessage = { role: 'system'; content: string; } | { role: 'user'; content: string | ContentBlock[]; } | { role: 'assistant'; /** Optional: an assistant turn that only requested tools has no text. */ content?: string; tool_calls?: WireToolCall[]; } | { role: 'tool'; tool_call_id: string; content: string; /** Honored by `fromAnthropic` (maps to `tool_result.is_error`) and `fromBedrock` (maps to `toolResult.status`); other adapters ignore it. */ is_error?: boolean; }; /** The OpenAI-shaped wire `tool_choice`. */ type WireToolChoice = 'auto' | 'none' | 'required' | { type: 'function'; function: { name: string; }; }; /** * Minimal shape similar to the OpenAI SDK's chat.completions.create API, * `response_format.json_schema` and `reasoning_effort` are optional on the wire * providers that don't support them will just ignore fields they don't recognize, * but not every SDKs TS types accept them, hence this being a structural type * rather than importing the SDKs own params type */ interface LLMClient { /** * Whether this client supports OpenAI's `response_format: { type: * 'json_object' }` as a real, API-level constraint. Defaults to `true` * when omitted (every OpenAI-compatible client and `fromGemini` map it to * a real field). `fromAnthropic` and `fromBedrock` set this to `false`: * neither provider has a field that mechanically guarantees JSON output * for this mode, so `RequestBuilder` downgrades a *default* (unset) * `jsonMode` to plain text for these clients instead of requesting * `json_object` and getting an unenforced, provider-side no-op back. An * *explicit* `jsonMode: true` still throws for such clients, since that's * a caller deliberately asking for a guarantee the client can't provide. */ supportsJsonObjectMode?: boolean; chat: { completions: { create(params: { model: string; temperature?: number; max_tokens: number; response_format?: { type: 'json_object'; } | { type: 'json_schema'; json_schema: { name: string; schema: Record; strict?: boolean; description?: string; }; }; /** OpenAI reasoning-model param (o-series, gpt-5), ignored by providers that don't support it */ reasoning_effort?: 'minimal' | 'low' | 'medium' | 'high'; /** * Numeric reasoning token budget, for providers with a native * budget field (Anthropic, Gemini). Ignored by clients that only * understand `reasoning_effort` tiers, use that field instead for * those. */ budget_tokens?: number; /** Tools the model may call, OpenAI's `function`-wrapped shape. */ tools?: Array<{ type: 'function'; function: { name: string; description: string; parameters: Record; }; }>; tool_choice?: WireToolChoice; /** * Wire-format messages. Breaking change for custom adapters: * implementations must handle tool messages and assistant tool_calls. * Exhaustive switches over only system/user/assistant roles may no longer compile. */ messages: WireMessage[]; }, options: { signal: AbortSignal; }): Promise<{ choices?: Array<{ message?: { content?: string | null; tool_calls?: WireToolCall[]; }; }>; usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number; completion_tokens_details?: { reasoning_tokens?: number; }; }; }>; /** * Optional. Required only for `stream: true` calls. Adapters/clients * that don't implement this make `stream: true` throw a clear * `LLMError('validation')` rather than a confusing runtime failure. * Takes the same request shape as `create`, minus the response type. */ createStream?(params: Parameters[0], options: { signal: AbortSignal; }): AsyncIterable; }; }; } //#endregion //#region src/internal/utils/cache/cacheAdapter.utils.d.ts /** * Not exported. Internal shorthand for `VernLLMOptions.cache`, so the * union isn't duplicated between that field and `buildCache`'s own * signature. A caller never writes this type by name, either a config * object literal or a real `CacheAdapter`. */ type CacheOption = { maxSize?: number; eviction?: EvictionOption; } | CacheAdapter; //#endregion //#region src/internal/utils/circuit-breaker/circuitBreakerAdapter.utils.d.ts /** * Not re-exported from the package root, imported directly from this * internal module by `VernLLMOptions.circuitBreaker`'s own type (see * options.ts) so that union isn't duplicated between the public option * field and `buildCircuitBreaker`'s own signature below, same pattern * `CacheOption` and `RateLimitOption` already use for their own options. */ type CircuitBreakerOption = boolean | CircuitBreakerOptions | CircuitBreakerAdapter; //#endregion //#region src/types/options.d.ts interface VernLLMOptions { client: LLMClient; model: string; /** * Label for this provider in usage (`TokenUsage.provider`) and events. * Default `'primary'`. */ name?: string; /** Max retries after the first attempt. Default 1 (2 attempts total) */ maxRetries?: number; /** Per-attempt timeout in ms. Default 25000 */ timeoutMs?: number; /** * For `stream: true` calls: max gap allowed between chunks once the * stream has opened, in ms. Resets on every chunk, including keep-alive * pings. `timeoutMs` only covers opening the stream and its first * chunk; this covers every gap after that. Also counts as a * circuit-breaker failure, unlike other mid-stream errors, since a * provider that streams one chunk then stalls should still trip it. * Default 30000. Pass 0 or negative to disable. */ chunkIdleTimeoutMs?: number; /** Base delay for exponential backoff in ms. Default 500 */ baseDelayMs?: number; /** Default max_tokens for calls that don't override it. Default 1000 */ defaultMaxTokens?: number; /** * Default temperature for calls that don't override it. Default 0.2, not * the provider's own default. Pass `null` to omit `temperature` from the * request entirely, so the provider applies its own default instead. */ defaultTemperature?: number | null; /** * Default reasoning effort for calls that don't override it. Not sent * when omitted, same as leaving `reasoningEffort` unset on a call. See * `budgetTokens`/`reasoningEffort` on `CallParams` for how the two * relate and how each adapter converts between them. */ defaultReasoningEffort?: 'minimal' | 'low' | 'medium' | 'high'; /** * Default reasoning token budget for calls that don't override it. Not * sent when omitted. If both this and `defaultReasoningEffort` are set, * each adapter still prefers whichever field it natively understands, * same as at the per-call level. */ defaultBudgetTokens?: number; /** * Enables debug logging of raw model output (logs up to 800 chars of each * response) and provider errors. Off by default. Only controls the * default `ConsoleLogger`: when a custom `logger` is supplied instead, * that logger's own `debug()` implementation decides whether messages * are emitted, and this option has no effect on it. */ debug?: boolean; /** * Applied before every internal `logger.debug()` call: the raw output * logged on success, and the provider error logged on a failed call or * a failed stream open. This is the one piece of logging an app can't * intercept itself, since it's a direct call into `logger.debug` * rather than something routed through `onEvent`/`onUsage`; anything * caught elsewhere (events, `LLMError.cause`) already passes through * the app's own callback and can be redacted there instead. Runs * before `logger.debug()` regardless of whether that call ends up * emitting anything, so with a custom `logger`, `redact` still applies * even without `debug: true`; see `debug` for why. Default: identity * (no redaction). */ redact?: (text: string) => string; /** * Cache for cachedCall. `{ maxSize, eviction }` configures the * built-in in-memory adapter (`eviction` default `'fifo'`). Pass a * `CacheAdapter` directly for a real backend. Default: in-memory, * maxSize 1000, fifo. */ cache?: CacheOption; /** * Reclassifies an otherwise-successful result as a failure, e.g. a * response that parsed fine but came back empty or truncated. Runs * once per attempt, right after a response is validated. Returning * `undefined` leaves the result untouched; returning an * `LLMErrorCode` fails that attempt with it, feeding the same retry * and circuit-breaker paths a thrown error would. A throwing hook is * caught, logged, and treated as no soft failure, so a broken hook * degrades safely instead of failing every call. */ detectSoftFailure?: DetectSoftFailure; /** HTTP status codes that should fail fast without retrying. Default [400, 401, 403, 404, 422] */ nonRetryableStatus?: number[]; /** Custom JSON parser. Must return undefined/null on failure. Default: JSON.parse wrapped in try/catch */ parseJson?: (content: string) => unknown; /** Called after every successful call with token usage, if the provider reports it */ onUsage?: OnUsage; /** * Called when a provider response arrives but VernLLM's own post-processing * then fails, after usage data was already present in that response. * Separate from `onUsage`, which only fires on full success. * * For non-streaming calls, never fires for transport failures (timeout, * network error, non-retryable status), since no response means no usage * to report. For streaming calls, this is not guaranteed: a stream can * deliver a usage chunk and then fail later (e.g. an idle timeout waiting * for the final close), in which case this does fire. */ onUsageFailure?: OnUsageFailure; /** * Injectable logger. Defaults to a console-based logger gated by `debug`. * Pass `'silent'` to discard all log output without stubbing a Logger. */ logger?: Logger | 'silent'; /** * Enables a circuit breaker that short-circuits calls after repeated * consecutive failures, instead of continuing to hammer a down provider * Pass `true` for defaults, or an options object to tune threshold/cooldown. * Pass a `CircuitBreakerAdapter` instead for cross-process coordination, * the same pattern `cache` and `rateLimit` already support. */ circuitBreaker?: CircuitBreakerOption; /** * Reports retries and circuit-breaker state transitions as they happen. * Fire and forget: a throwing handler is caught and logged, and its * return value is never read, so it cannot influence the call. */ onEvent?: OnEvent; /** * Client-side rate limiting. Queues calls locally to stay under the * configured requests/tokens-per-minute or concurrency caps, instead of * letting the provider reject them. Independent of the `Retry-After` * handling already applied to a provider 429: this avoids tripping the * limit in the first place. Omit for unlimited (the default). * * A plain config object builds an in-process limiter. Pass a * `RateLimiterAdapter` instead for cross-process coordination. */ rateLimit?: RateLimitOption; /** * Caps how much of this target's recent traffic is allowed to be * retries, independent of `circuitBreaker`. Once at least `minCalls` * calls have landed in the trailing `windowMs` and the retry ratio * among them reaches `retryRatio`, further retries against this target * throw `LLMError('retry_budget_exhausted')` instead of retrying, * protecting the target's real capacity even while its breaker is * still closed. Omit for no budget (the default). Never inherited by * `fallback` targets, same as `circuitBreaker`/`rateLimit`. */ retryBudget?: RetryBudgetOptions; /** * Ordered targets tried after the primary, in order, once it (and its * own retries) is exhausted or abandoned. Order is the policy: VernLLM * never reorders, scores, or selects between targets. Each target keeps * its own retry state, circuit breaker, and rate limiter, independent * of every other target's. A single `FallbackTarget` is equivalent to * `[target]`. */ fallback?: FallbackTarget | FallbackTarget[]; /** * Decides what happens after a target fails: `'next'` to move on to * the following target (or throw, if it was the last one), `'stop'` to * give up immediately without trying any remaining targets. Called * once per failed target, after that target's own retries are * exhausted or abandoned early, so `'retry'` is never a valid return * here. Defaults to `defaultFallbackOn`, which stops on * parse/validation/aborted/quota errors and on tool-contract failures * (the model ignoring the request, not the provider being unhealthy), * and moves on for everything else. */ fallbackOn?: FallbackOn; /** * Transforms outgoing requests and/or wraps whole logical calls, * without touching retry, circuit breaker, or fallback internals. * Defaults to an empty array. See `VernLLMMiddleware` for the four * available hooks (`transform`, `wrap`, `onEvent`, `enabled`). */ middleware?: VernLLMMiddleware[]; /** * Bounds `transform` and a function `enabled`, the same way every * other blocking operation in the package is already bounded. * Overridable per middleware via that entry's own `timeoutMs`. * `<= 0` means unbounded (no timer at all). Default 5000. */ middlewareTimeoutMs?: number; } //#endregion //#region src/types/createMiddleware.d.ts /** * `VernLLMMiddleware` plus `onError`, a convenience for the common "I * only care about failures" case. Everything else is passed through to * the resulting `VernLLMMiddleware` unchanged; setting `wrap` directly * alongside `onError` is an error, since `onError` builds its own `wrap` * under the hood, and building it around a `wrap` you also supplied * would silently drop one of the two. */ type CreateMiddlewareOptions = Omit & { wrap?: undefined; /** * Called with this call's terminal error, if it fails: the same error * `wrap`'s own `next()` would reject with. Never called on success, * and never called for a failure some *other* middleware's `wrap` * already swallowed by short-circuiting with its own `CallResult`. * The original error is always rethrown afterward, `onError` only * observes it, exactly like `onUsage`/`onEvent` elsewhere: a throwing * `onError` is discarded (not logged, this helper has no `Logger` of * its own to log through) and otherwise has no effect on the call. * `ctx` is `wrap`'s own pre-dispatch context (`onError` builds a `wrap` * under the hood), so it only describes the primary target. */ onError?: (error: LLMError, ctx: PreDispatchContext) => void | Promise; }; /** * Builds a `VernLLMMiddleware` entry. Plain pass-through when `onError` * is omitted; when it's set, wraps it in a `wrap` that calls `next()`, * reports `onError` on a rejection, and always rethrows the original * error afterward, so `onError` never changes what the call itself * returns or throws, only what gets observed about it. */ export declare function createMiddleware(options: CreateMiddlewareOptions): VernLLMMiddleware; //#endregion //#region src/vernLLM.d.ts /** * A LLM call framework for resilience, observability and control. This is VernLLM! * * Adds retry with backoff and jitter, per-attempt timeouts, an optional * circuit breaker, JSON parsing with optional schema validation, usage * tracking, and an optional response cache. All configurable, all opt-in * beyond sensible defaults. */ export declare class VernLLM { private readonly logger; /** * One `CallExecutor` per provider target: index 0 is the primary, * everything after it is a `fallback` target, in the order declared. * Walked by `runFallbackChain`, moving to the next entry only when * `fallbackOn` says to. */ private readonly executors; /** Decides whether a failed target is followed by the next one or the chain stops. See `VernLLMOptions['fallbackOn']`. */ private readonly fallbackOn; /** Reports a `'fallback'` event when the chain moves to the next target. Shared `onEvent` plumbing, same as every executor's. */ private readonly reportEvent; /** Owns cache reads/writes and in-flight coalescing for `cachedCall()`. Only calls back into `this.call()` as an opaque function. */ private readonly cacheOrchestrator; /** * See `VernLLMOptions.middleware`. Every resolved view of composition * order, built once here at construction time by * `buildMiddlewarePipeline`. Nothing downstream computes order * itself; each consumer reads `transformOrder`, `wrapOrder`, or * `names`, whichever it actually needs. */ private readonly pipeline; /** See `VernLLMOptions.middlewareTimeoutMs`. Bounds `transform` and a function `enabled`; `wrap` itself is never bounded by this. */ private readonly middlewareTimeoutMs; /** * Maps `cachedCall()`'s inner `this.call(...)` params to its own * `middlewareState`, so that call's own `runOperation` skips wrapping * again and reuses the same state bag `wrap` just ran with (so a * value `wrap` sets is visible to `transform`, same as a direct * call). Keyed by object identity, not `requestId`, since two * concurrent `cachedCall()`s can share an explicit `requestId`. */ private readonly cachedCallInnerParams; /** * Shares one `CallMeta` holder across every `cachedCall()` in flight * for the same resolved cache key, so a joining invocation (never * calls `call()` itself) reports the trigger's real metadata instead * of `undefined`. A true cache hit never creates an entry, so it * still reports no metadata correctly. */ private readonly cachedCallMeta; /** * @param options Client, model, and tunables. Defaults: `maxRetries` 1, * `timeoutMs` 25000, `baseDelayMs` 500, `defaultMaxTokens` 1000, * `defaultTemperature` 0.2, `cache` an in-memory adapter, * `nonRetryableStatus` `[400, 401, 403, 404, 422]`, `debug` false. */ constructor(options: VernLLMOptions); /** Logs a failed refundUsage attempt via the configured logger. */ private logRefundError; /** Everything `executeLogicalCall`/`executeLogicalStreamCall` (in `logicalCall.ts`) need from this instance, gathered once so `call()` doesn't rebuild it per invocation. */ private get logicalCallDependencies(); /** Everything `runOperation` (in `runOperation.ts`) needs from this instance, gathered once so `call()`/`cachedCall()` don't rebuild it per invocation. */ private get runOperationDependencies(); /** * Makes a single logical LLM call, retrying on failure per the configured * policy. Fails fast if the breaker is open or the signal is already * aborted. Rejects with a normalized `LLMError` on exhausted retries. * * Supports `tools`, `stream`, and JSON mode/schema, in any combination. * See the Tool Calling and Streaming docs for return-shape details and * the TypeScript overloads that select between them. * * @param params System/user content plus per-call overrides. See `CallParams`. * @returns The parsed response (or raw string if `jsonMode` is false), a * `CallWithToolsResult` when `tools` is set, or a `{ chunks, * finalResult }` `StreamCallResult` when `stream: true`. See `StreamCallResult`. */ call(params: StreamEnabledCallParams & ToolsDisabledCallParams): Promise>>; call(params: StreamEnabledCallParams & ToolEnabledCallParams): Promise>>; call(params: StreamConditionalStringToolCallParams): Promise>>; call(params: StreamEnabledCallParams & ConditionalToolCallParams): Promise>>; call(params: StreamJsonModeDisabledCallParams): Promise>; call(params: StreamJsonModeEnabledCallParams): Promise>; call(params: StreamEnabledCallParams): Promise>; call(params: ToolsDisabledCallParams): Promise>; call(params: ToolEnabledCallParams): Promise>; call(params: ConditionalStringToolCallParams): Promise>; call(params: ConditionalToolCallParams): Promise>; call(params: JsonModeDisabledCallParams): Promise; call(params: JsonModeEnabledCallParams): Promise; call(params: CallParams): Promise; /** * Thin delegator kept private on `VernLLM` (rather than only existing on * `CacheOrchestrator`) since it's the one caching primitive exercised * directly by white-box tests, independent of the public `cachedCall()` * surface. */ private runCached; /** * Removes a cached response by key when the configured cache adapter * supports deletion. Cache invalidation is the caller's responsibility; * only the application knows when cached data is stale. * * @param key The raw cache key (resolved through the adapter's * `resolveKey`, if any, before deletion). */ deleteCache(key: string): Promise; /** * Cache wrapper composing `call` + caching, so cached LLM calls * automatically get retry/timeout/circuit-breaker behavior. Concurrent * misses for the same `cacheKey` share a single in-flight call, avoiding * cache stampedes. Supports `stream: true` and `tools` in any combination. * * When `call.tools` is set, this caches the whole result including any * `tool_calls` decision, not just final answers; use a short `ttl` or a * separate `cacheKey` if a tool's result shouldn't be reused across calls. * * @param params `cacheKey`, `ttl`, optional * `reserveUsage`/`refundUsage`/`signal`, plus `call`, the `CallParams` * to pass through to `this.call(...)`. The top-level `signal` governs * the cached operation and its usage hooks only; to also abort the * underlying provider request, set `signal` inside `call`. * @returns The cached value on a hit, or the freshly-called result on a miss. */ cachedCall(params: CachedStreamToolCallParams): Promise>>; cachedCall(params: CachedStreamConditionalStringToolCallParams): Promise>>; cachedCall(params: CachedStreamConditionalToolCallParams): Promise>>; cachedCall(params: CachedStreamJsonModeDisabledCallParams): Promise>; cachedCall(params: CachedStreamJsonModeEnabledCallParams): Promise>; cachedCall(params: CachedStreamCallParams): Promise>; cachedCall(params: CachedToolCallParams): Promise>; cachedCall(params: CachedConditionalStringToolCallParams): Promise>; cachedCall(params: CachedConditionalToolCallParams): Promise>; cachedCall(params: CachedJsonModeDisabledCallParams): Promise; cachedCall(params: CachedJsonModeEnabledCallParams): Promise; cachedCall(params: CachedCallParams): Promise; /** * @param target.index Which target to read. Defaults to the primary. * @param target.model Which model bucket to read, if the target isolates by model. * @returns The breaker state, or `undefined` if that target has no breaker. * @throws {RangeError} If `target.index` names no target. Lets a real * target with no breaker (`undefined`) stay distinguishable from a * target that doesn't exist. */ getCircuitState(target?: CircuitTarget): CircuitState | undefined; /** * @param target.index Which target to read. Defaults to the primary. * @param target.model Which model bucket to read, if the target isolates by model. * @returns Failure counts by `LLMErrorCode`, `'unknown'` for a missing * code, or `undefined` if that target has no breaker. * @throws {RangeError} If `target.index` names no target. */ getFailureBreakdown(target?: CircuitTarget): Partial> | undefined; /** * @param target.index Which target to read. Defaults to the primary. * @returns This target's current retry traffic/ratio in the trailing * window, or `undefined` if that target has no retry budget * configured. A budget is target-scoped, not model-scoped, so unlike * `getFailureBreakdown` there's no `target.model` to pass. * @throws {RangeError} If `target.index` names no target. */ getRetryBudgetState(target?: Pick): { attempts: number; retryRatio: number; } | undefined; /** * @param target.index Which target to read. Defaults to the primary. * @returns This target's current rate limit levels, or `undefined` if * that target has no limiter configured. A limiter is target-scoped, * not model-scoped, so unlike `getFailureBreakdown` there's no * `target.model` to pass. * @throws {RangeError} If `target.index` names no target. */ getRateLimitState(target?: Pick): RateLimitState | undefined; /** * @param model Which model bucket to read, for targets that isolate by model. * @returns Every target's state, in chain order. */ getCircuitStates(model?: string): TargetCircuitState[]; /** * The live counterpart of `getRateLimitState`, asking the limiter for its current levels. * * @param target.index Which target to read. Defaults to the primary. * @returns This target's live rate limit levels, or `undefined` if that target has no limiter * configured. * @throws {RangeError} If `target.index` names no target. */ readRateLimitState(target?: Pick): Promise; /** * The live counterpart of `getCircuitStates`, asking each breaker for its current state. * * @param model Which model bucket to read, for targets that isolate by model. * @returns Every target's state, in chain order. */ readCircuitStates(model?: string): Promise; /** * Manually opens a target's breaker, e.g. to pull a provider out of * rotation ahead of known maintenance instead of waiting for it to fail. * * @param target.index Which target to open. Defaults to the primary. * @param target.model Which model bucket to open, if the target isolates by model. * @throws {RangeError} If `target.index` names no target. */ openCircuit(target?: CircuitTarget): void; /** * Manually closes a target's breaker, e.g. once a provider is confirmed * healthy again without waiting out the cooldown. * * @param target.index Which target to close. Defaults to the primary. * @param target.model Which model bucket to close, if the target isolates by model. * @throws {RangeError} If `target.index` names no target. */ closeCircuit(target?: CircuitTarget): void; } //#endregion //#region src/paramsHelpers.d.ts /** * Identity function preserving `params`'s own precise type, unlike a `: * CallParams` annotation, which would widen `tools` away and break the * `ConditionalToolCallParams` overload for `tools: someCondition ? * [tool] : undefined`. Use it when you need `call()` params in a named, * reusable variable; skip it when you can pass the object inline. * * ```ts * const params = defineCallParams({ * userContent: 'What is the weather?', * tools: someCondition ? [weatherTool] : undefined, * }); * const result = await llm.call(params); * // result: unknown | CallWithToolsResult, same as inline * ``` * * `T` isn't a parameter here; pin it via `llm.call(params)` as usual. * `defineCachedCallParams` is the `cachedCall()` counterpart. */ export declare function defineCallParams

>(params: P): P; /** * The `cachedCall()` counterpart to `defineCallParams`: preserves the * whole `{ cacheKey, ttl, call }` object, `call.tools` included, in one * named variable. * * ```ts * const params = defineCachedCallParams({ * cacheKey: 'weather-ny', * ttl: 60, * call: { userContent: 'What is the weather?', tools: someCondition ? [weatherTool] : undefined }, * }); * const result = await llm.cachedCall(params); * ``` */ export declare function defineCachedCallParams

>(params: P): P; //#endregion //#region src/adapters/internal/sse.d.ts /** * Parses a Server-Sent-Events byte/text stream into the JSON payload of * each `data:` frame, in arrival order. Generic over transport: works with * anything that hands back progressively-arriving `Uint8Array` or `string` * chunks via async iteration: native `fetch`'s `response.body` (wrapped * to be iterable, see `webStreamToAsyncIterable` in `fetch.ts`), axios's * Node `Readable` (already async-iterable, no wrapping needed), etc, so * this framing layer doesn't care which transport produced the bytes. * * Follows the SSE spec's frame-delimiting rules closely enough for LLM * streaming responses: frames are separated by a blank line, each frame * may carry one or more `data:` lines (joined with `\n` per spec when * there's more than one), `:`-prefixed lines are comments and ignored, and * other SSE fields (`event:`, `id:`, `retry:`) are ignored since VernLLM * only needs the payload. A frame whose data is exactly `[DONE]` (the * sentinel several providers, notably OpenAI, send to mark stream end) * ends iteration without yielding it. * * Line endings: `\r\n` and bare `\r` (both legal per the SSE spec, alongside `\n`) are normalized * to `\n` before frame splitting. A `\r` at the very end of the currently-buffered text is left * alone until either more text arrives (in case it's the first half of a split `\r\n` pair) or the * stream ends, so a `\r\n` pair split across two transport chunks is never misread as two blank * lines. * * Malformed JSON in a frame throws `LLMError('parse')`, consistent with * how malformed JSON is handled elsewhere in VernLLM. */ export declare function parseSseStream(source: AsyncIterable): AsyncGenerator; /** * Sentinel yielded by `parseSseStream` for a comment-only frame (no * `data:` payload), the mechanism providers use for SSE keep-alive * pings. Exported so a consumer (e.g. `fromFetch`) can react to "still * alive" separately from a genuinely empty frame (`NO_DATA`, kept internal). */ export declare const SSE_PING: unique symbol; //#endregion //#region src/adapters/internal/imageFormat.d.ts /** * MIME types accepted for `ImageBlock.mimeType` across all adapters. This is * the intersection of what Anthropic, Gemini, OpenAI-compatible, and Bedrock * Converse all natively support, so a `ContentBlock[]` that validates for * one provider validates for all of them. */ declare const SUPPORTED_IMAGE_MIME_TYPES: readonly ["image/png", "image/jpeg", "image/gif", "image/webp"]; type SupportedImageMimeType = (typeof SUPPORTED_IMAGE_MIME_TYPES)[number]; //#endregion //#region src/adapters/internal/nativeStructuredOutput.d.ts /** * A static allow-list or predicate naming which models support native, * schema-constrained output as its own request field. Anthropic's * `output_config.format` and Bedrock's `outputConfig.textFormat` separate * from `tools`/`tool_choice`, so it can be combined with real, * caller-supplied `tools` in the same request. * * There is no built-in default list here. Which models support this is * Anthropic's and Bedrock's call to make, not this package's, and it * changes over time; hardcoding a guessed list would risk silently * routing a request onto a field a given model doesn't actually support, * trading a clear `LLMError('invalid_params')` with * `code: 'unsupported_capability'` for a confusing error from the provider * instead. So this is opt-in: pass the model IDs you've verified against the * provider's own docs (or a predicate). Left unset, no model is treated as * native-capable, `jsonSchema` keeps using the older forced-single-tool-call * emulation, and combining it with `tools` throws the coded capability error, * exactly this package's behavior before native support was added. */ type ModelCapabilityOverride = string[] | ((model: string) => boolean); //#endregion //#region src/adapters/internal/reasoningBudget.utils.d.ts /** * Shared conversion between the two reasoning controls VernLLM exposes: * `reasoningEffort` (a tier string, OpenAI's native shape) and * `budgetTokens` (a raw integer, Anthropic's and Gemini's native shape). * * Every adapter prefers its own native field when the caller set it, and * only calls into this table when the caller set the other one instead. * The numbers here are a guess, not a provider guarantee, callers who * need a precise budget on a specific model should set `budgetTokens` * directly rather than relying on this table's `reasoningEffort` mapping. * * The table itself is overridable per adapter instance, via * `reasoningEffortTokens` on each `from*` adapter's options (see * `AnthropicAdapterOptions`, `GeminiAdapterOptions`, * `OpenAICompatibleAdapterOptions`, `BedrockAdapterOptions`), for callers * who want `reasoningEffort` tiers to map onto different token counts * than the defaults below, e.g. a model whose useful reasoning range * doesn't match these numbers. */ type EffortTokenTable = Record<'minimal' | 'low' | 'medium' | 'high', number>; //#endregion //#region src/adapters/anthropic.d.ts /** Anthropic's native per-block content shape for a message. */ type AnthropicContentBlock = { type: 'text'; text: string; } | { type: 'image'; source: { type: 'base64'; media_type: SupportedImageMimeType; data: string; }; } | { type: 'tool_use'; id: string; name: string; input: unknown; } | { type: 'tool_result'; tool_use_id: string; content: string; is_error?: boolean; }; /** Minimal structural type for the Anthropic SDK's `messages.create` */ interface AnthropicClient { messages: { create(params: { model: string; max_tokens: number; temperature?: number; system?: string; messages: Array<{ role: 'user' | 'assistant'; content: string | AnthropicContentBlock[]; }>; tools?: Array<{ name: string; description?: string; input_schema: { type: 'object'; [key: string]: unknown; }; strict?: boolean; }>; tool_choice?: { type: 'auto'; } | { type: 'any'; } | { type: 'none'; } | { type: 'tool'; name: string; }; /** * Native, schema-constrained output: a separate request field from * `tools`/`tool_choice`, so it can be sent alongside real tool * calls. Only built by this adapter for models covered by * `nativeStructuredOutputModels` (opt-in, see * `AnthropicAdapterOptions`); other models keep getting * `jsonSchema` emulated as a forced single tool call, the * pre-existing behavior. * * Matches the real Anthropic API's `output_config.format` shape * exactly: just `type` and `schema`, no `name`/`description`/ * `strict`. Those three exist on VernLLM's own `jsonSchema` API * (and are still forwarded on the legacy forced-tool-call path, * where they're real `Tool` fields), but the native structured- * output endpoint has no equivalent for any of them. */ output_config?: { format?: { type: 'json_schema'; schema: Record; }; /** * Effort control for adaptive thinking, on models where manual * `budget_tokens` thinking is no longer accepted (see * `supportsManualThinkingBudget` in * `adapters/internal/reasoningBudget.utils.ts`). Sibling to * `format`, either or both may be present independently. */ effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max'; }; /** * Native reasoning control. `{ type: 'enabled', budget_tokens }` * is built directly from `CallParams.budgetTokens`, or converted * from `reasoningEffort`, on models that still accept a manual * token budget. `{ type: 'adaptive' }` is sent instead, paired * with `output_config.effort`, on models that only support * adaptive thinking. See * `adapters/internal/reasoningBudget.utils.ts`. */ thinking?: { type: 'enabled'; budget_tokens: number; } | { type: 'adaptive'; }; }, options: { signal: AbortSignal; }): Promise<{ content: Array<{ type: string; text?: string; id?: string; name?: string; input?: unknown; }>; usage?: { input_tokens?: number; output_tokens?: number; output_tokens_details?: { thinking_tokens?: number; } | null; }; }>; }; } /** Optional configuration for `fromAnthropic`. */ interface AnthropicAdapterOptions { /** * Which models support native, schema-constrained output * (`output_config.format`), independent of `tools`/`tool_choice`, so it * can be combined with real `tools` in one request. Pass a static list * of model IDs (verified against Anthropic's own docs) or a predicate. * * There is no built-in default here (see `supportsNativeStructuredOutput` * for why). Left unset, every model uses the older forced-single-tool- * call emulation, and `tools` + `jsonSchema` together is rejected, * exactly this adapter's behavior before native support was added. */ nativeStructuredOutputModels?: ModelCapabilityOverride; /** * Overrides the token count `reasoningEffort` tiers map onto when the * caller sets `reasoningEffort` but not `budgetTokens` (Claude has no * tier concept of its own, see `adapters/internal/reasoningBudget.utils.ts`). * Only the tiers listed are changed; any omitted tier keeps the * built-in default. Has no effect when `budgetTokens` is set directly. */ reasoningEffortTokens?: Partial; /** * Marks additional models as adaptive-only, on top of this package's * own built-in rule (Claude Opus 4.7 and later, every Claude 5 tier * model, see `isAdaptiveOnlyModel` in * `adapters/internal/reasoningBudget.utils.ts`). Additive, not a * replacement: it can correct a false negative (a newer model this * package doesn't know about yet), it can't un-mark a model the * built-in rule already caught. Pass a static list of model IDs or a * predicate. */ adaptiveOnlyModels?: ModelCapabilityOverride; /** * Whether the client's `messages.create` supports `.withResponse()` * (needed for AIMD's proactive path). Default `false`, since * `AnthropicClient` is structural and a test fake or thin wrapper * won't implement it. */ supportsWithResponse?: boolean; } /** * Wraps an Anthropic SDK client so it satisfies the same `LLMClient` * interface VernLLM uses for OpenAI/Groq. * * `response_format: json_schema`, on a model covered by * `options.nativeStructuredOutputModels`, is sent as `output_config.format`, * its own request field, independent of `tools`/`tool_choice`, so it can be * combined with real, caller-supplied `tools` in the same request. Only * `type` and `schema` are sent on this path, the real Anthropic API's * `output_config.format` has no `name`/`description`/`strict` fields. * * On any other model (the default, since `nativeStructuredOutputModels` is * opt-in), `response_format: json_schema` is mapped to Anthropic's forced * tool-use instead: a single tool is defined with `input_schema` set to * the caller's schema, `description` forwarded when provided, and `strict` * forwarded when set, and `tool_choice` forces the model to call it. This * legacy path cannot be combined with real `tools` (both would need the * same `tools`/`tool_choice` field), and a call that tries throws * `LLMError('invalid_params')` with `code: 'unsupported_capability'` and * `issues: { capability: 'tools_with_json_schema' }` before reaching the API. Provider-constrained * schema matching applies only when `strict: true` is forwarded and * supported. * * `response_format: json_object` throws `LLMError('validation')`. Anthropic * has no API-level field that mechanically guarantees JSON output the way * OpenAI's `json_object` mode does; the only way to emulate it was a * system-prompt instruction with no actual enforcement behind it, a * guarantee this adapter no longer pretends to make. Use `jsonSchema` * instead, which maps to a real constraint either way (native * `output_config.format` or a forced tool call). */ export declare function fromAnthropic(anthropicClient: AnthropicClient, options?: AnthropicAdapterOptions): LLMClient; //#endregion //#region src/adapters/gemini.d.ts /** * Gemini's native per-part content shape for a `contents` entry. * `functionCall.args` and `functionResponse.response` are typed as * `Record` (not `unknown`) to match the real SDK's * `FunctionCall.args` / `FunctionResponse.response`, see the doc comment * on {@link GeminiClient}. */ type GeminiPart = { text: string; } | { inlineData: { mimeType: string; data: string; }; } | { functionCall: { id?: string; name: string; args: Record; }; } | { functionResponse: { id?: string; name: string; response: Record; }; }; /** * Structural type matching the real `@google/genai` SDK, in either shape * it's commonly held in: the callable model methods directly (`ai.models`), * or the complete top-level client (`ai`, via the optional `models` field * below). Both work with `fromGemini` directly, with no cast: * * ```ts * import { GoogleGenAI } from '@google/genai'; * const ai = new GoogleGenAI({ apiKey: '...' }); * const llm = new VernLLM({ client: fromGemini(ai), model: 'gemini-2.5-flash' }); * ``` * * `generateContent` is optional so a `{ models: ... }`-shaped value is * still a structural `GeminiClient`; `fromGemini` resolves `models` at * runtime and throws if nothing callable results. * * Every field is shaped to be structurally assignable from the real SDK's * generated types without importing them, so provider SDKs stay optional: * `model` is required (the real SDK requires it), `functionCall.args` / * `functionResponse.response` are `Record` (matching the * real SDK, not `unknown`), `toolConfig...mode` is `any` (TypeScript never * treats a string-literal union as assignable to the real SDK's string * enum), and response-side `functionCall.name` is optional (matching the * real SDK). */ interface GeminiClient { /** Present when this is the whole top-level SDK client, not `ai.models`. `fromGemini` unwraps it at runtime. */ models?: GeminiClient; generateContent?(params: { model: string; contents: Array<{ role: 'user' | 'model'; parts: GeminiPart[]; }>; config?: { systemInstruction?: { parts: Array<{ text: string; }>; }; temperature?: number; maxOutputTokens?: number; responseMimeType?: string; responseSchema?: Record; tools?: Array<{ functionDeclarations: Array<{ name: string; description?: string; parameters: Record; }>; }>; toolConfig?: { functionCallingConfig: { mode: any; allowedFunctionNames?: string[]; }; }; /** * Native reasoning control. `thinkingBudget` is built from * `CallParams.budgetTokens` directly when set (0 disables thinking, * -1 requests automatic budgeting, both passed through unchanged), * or converted from `reasoningEffort`, on Gemini 2.5 and earlier * models. `thinkingLevel` is used instead on Gemini 3 and later, * which use a level-based control rather than a numeric budget. * `any`, same reason as `toolConfig...mode` above, see class doc * comment. See `usesGeminiThinkingLevel` in * `adapters/internal/reasoningBudget.utils.ts`. */ thinkingConfig?: { thinkingBudget?: number; thinkingLevel?: any; }; abortSignal?: AbortSignal; }; }): Promise<{ candidates?: Array<{ content?: { parts?: Array<{ text?: string; functionCall?: { id?: string; name?: string; args?: unknown; }; }>; }; }>; usageMetadata?: { promptTokenCount?: number; candidatesTokenCount?: number; totalTokenCount?: number; thoughtsTokenCount?: number; }; }>; /** * Optional. Required only for `stream: true` calls. Takes the same * request shape as `generateContent`. Matching the real SDK's own * `generateContentStream`, this resolves to an `AsyncIterable` (rather * than returning one synchronously) of partial responses, each chunk * holding the same `candidates[].content.parts[]` structure as * `generateContent`'s response, just incremental. */ generateContentStream?(params: Parameters>[0]): Promise; }; }>; usageMetadata?: { promptTokenCount?: number; candidatesTokenCount?: number; totalTokenCount?: number; thoughtsTokenCount?: number; }; }>>; } /** * Wraps a Gemini client so it satisfies the `LLMClient` interface VernLLM * uses for OpenAI-compatible APIs. Gemini's shape differs on nearly every * axis: a `contents` array instead of `messages`, a separate * `systemInstruction` field instead of a `system` role message, * `generationConfig` instead of top-level `temperature`/`max_tokens`, and * native JSON Schema support via `responseMimeType: 'application/json'` + * `responseSchema`. `reasoning_effort` has no native Gemini equivalent, so * it's converted to a `thinkingConfig.thinkingBudget` token count; `budget_tokens` * maps to `thinkingBudget` directly, Gemini's native reasoning control. See * `adapters/internal/reasoningBudget.utils.ts`. * * `tools` maps to Gemini's native `functionDeclarations`/`functionCall`; * `tool_choice` maps to `toolConfig.functionCallingConfig`. Gemini accepts * `responseSchema` and `tools` in the same request natively, so both are * set independently here and no special-casing is needed for the * combination, unlike `fromAnthropic`/`fromBedrock`. * * `createStream` calls `generateContentStream` (optional on `GeminiClient` *, required only if the caller sets `stream: true`) and translates each * partial response into `WireStreamChunk`s. Unlike OpenAI/Anthropic, * Gemini's own function-calling API doesn't stream tool-call arguments * incrementally: a `functionCall` part always arrives whole in one chunk, * so each one is emitted as a single, complete `tool_call_delta` (a * one-shot "delta" containing the full arguments) rather than accumulated * fragments, that's a real difference in the underlying API, not * something this adapter can smooth over. `usageMetadata` is (per Gemini's * own behavior) only reliably present on the last chunk, so the `usage` * `WireStreamChunk` is emitted once, after the stream completes, from * whichever chunk's `usageMetadata` was seen last. * * Accepts a `GeminiClient` in either shape it structurally covers: the * callable model methods directly (`ai.models`), or the complete * top-level client (`ai`), unwrapping `.models` internally when present. * Both work with no cast: `fromGemini(ai.models)` and `fromGemini(ai)`. * Throws `LLMError('invalid_params')` up front if nothing callable * results. */ interface GeminiAdapterOptions { /** * Overrides the token count `reasoningEffort` tiers map onto when the * caller sets `reasoningEffort` but not `budgetTokens` (Gemini has no * tier string of its own, see `adapters/internal/reasoningBudget.utils.ts`). * Only the tiers listed are changed; any omitted tier keeps the * built-in default. Has no effect when `budgetTokens` is set directly. */ reasoningEffortTokens?: Partial; /** * Marks additional models as using `thinkingLevel` instead of * `thinkingBudget`, on top of this package's own built-in rule (every * Gemini 3 series model and later, see `usesGeminiThinkingLevel` in * `adapters/internal/reasoningBudget.utils.ts`). Additive, not a * replacement: it can correct a false negative (a newer model this * package doesn't know about yet), it can't un-mark a model the * built-in rule already caught. Pass a static list of model IDs or a * predicate. */ thinkingLevelModels?: ModelCapabilityOverride; } export declare function fromGemini(client: GeminiClient, options?: GeminiAdapterOptions): LLMClient; //#endregion //#region src/adapters/bedrock.d.ts /** Bedrock Converse's supported inline image formats. */ type BedrockImageFormat = 'png' | 'jpeg' | 'gif' | 'webp'; /** Bedrock Converse's native per-block content shape for a message. */ type BedrockContentBlock = { text: string; } | { image: { format: BedrockImageFormat; source: { bytes: Uint8Array; }; }; } | { toolUse: { toolUseId: string; name: string; input: unknown; }; } | { toolResult: { toolUseId: string; content: Array<{ text: string; }>; status?: 'success' | 'error'; }; }; /** * Minimal structural type matching AWS Bedrock's Converse API. This is * intentionally NOT `BedrockRuntimeClient` itself, the AWS SDK v3 client * exposes `.send(command)`, not a direct `.converse()` method, and pulling * in `@aws-sdk/client-bedrock-runtime` as a dependency just for its types * isn't worth it for a structural adapter. Wrap your client, e.g: * * ```ts * import { BedrockRuntimeClient, ConverseCommand } from '@aws-sdk/client-bedrock-runtime'; * const client = new BedrockRuntimeClient({ region: 'us-east-1' }); * const converseClient = { * converse: (params, options) => * client.send(new ConverseCommand(params), { abortSignal: options.signal }), * }; * ``` */ interface BedrockConverseClient { converse(params: { modelId: string; messages: Array<{ role: 'user' | 'assistant'; content: BedrockContentBlock[]; }>; system?: Array<{ text: string; }>; inferenceConfig?: { temperature?: number; maxTokens?: number; }; toolConfig?: { tools: Array<{ toolSpec: { name: string; description?: string; inputSchema: { json: Record; }; strict?: boolean; }; }>; toolChoice?: { tool: { name: string; }; } | { auto: Record; } | { any: Record; }; }; /** * Native, schema-constrained output: a separate request field from * `toolConfig`, so it can be sent alongside real tool calls. Only * built by this adapter for models covered by * `nativeStructuredOutputModels` (opt-in, see * `BedrockAdapterOptions`); other models keep getting `jsonSchema` * emulated as a forced single tool call via `toolConfig`, the * pre-existing behavior. * * Matches the real Bedrock Converse API's `outputConfig.textFormat` * shape exactly: the schema itself is nested one level deeper, under * `structure.jsonSchema`, not flat on `textFormat`, and `schema` is * a JSON-encoded *string*, not a parsed object, unlike every other * schema field this adapter builds (`toolSpec.inputSchema.json` * included). There is no `strict` field here, unlike `toolSpec`. */ outputConfig?: { textFormat?: { type: 'json_schema'; structure: { jsonSchema: { schema: string; name?: string; description?: string; }; }; }; /** * Effort control for adaptive thinking, on Claude models where * manual `budget_tokens` thinking is no longer accepted (see * `supportsManualThinkingBudget` in * `adapters/internal/reasoningBudget.utils.ts`). Sibling to * `textFormat`, either or both may be present independently. */ effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max'; }; /** * Model-specific passthrough. Converse has no reasoning-budget field * of its own, so a token budget for a Claude model on Bedrock is * forwarded here under Anthropic's own key, `{ thinking: { type: * 'enabled', budget_tokens } }`. Non-Claude models get nothing here, * there is no equivalent field to reach for. */ additionalModelRequestFields?: Record; }, options: { signal: AbortSignal; }): Promise<{ output?: { message?: { content?: Array<{ text?: string; toolUse?: { toolUseId?: string; name?: string; input?: unknown; }; }>; }; }; usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number; }; }>; /** * Optional. Required only for `stream: true` calls. Takes the same * request shape `converse` does, returning `{ stream }`, matching * `ConverseStreamCommand`'s real AWS SDK v3 output shape, an * `AsyncIterable` of incremental events under a `stream` property, * rather than the whole response being the iterable directly. */ converseStream?(params: Parameters[0], options: { signal: AbortSignal; }): Promise<{ stream: AsyncIterable; }>; } /** * One event of a Bedrock `ConverseStreamCommand` response's `stream`. * Content blocks (text or toolUse) are identified by `contentBlockIndex`, * Converse's own convention for correlating start/delta/stop events across * possibly-interleaved blocks, mirrored directly by VernLLM's * `tool_call_delta.index`. */ type BedrockConverseStreamEvent = { messageStart: { role: 'assistant'; }; } | { contentBlockStart: { contentBlockIndex: number; start?: { toolUse?: { toolUseId?: string; name?: string; }; }; }; } | { contentBlockDelta: { contentBlockIndex: number; delta?: { text?: string; } | { toolUse?: { input?: string; }; }; }; } | { contentBlockStop: { contentBlockIndex: number; }; } | { messageStop: { stopReason?: string; }; } | { metadata: { usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number; }; }; } | { internalServerException: { message?: string; }; } | { modelStreamErrorException: { message?: string; originalStatusCode?: number; }; } | { validationException: { message?: string; }; } | { throttlingException: { message?: string; }; } | { serviceUnavailableException: { message?: string; }; }; /** * Optional configuration for `fromBedrock`. */ interface BedrockAdapterOptions { /** * Optional preflight check for tool-use support, needed whenever a * `jsonSchema` call ends up sending Converse `toolConfig`, either the * legacy forced-single-tool-call emulation, or real `tools` sent * alongside native structured output (`outputConfig`). VernLLM never * guesses capability from a failed call's error message (AWS's error * text isn't a documented, stable contract), so this is opt-in: pass * either a static list of tool-use-capable model IDs, or a predicate * function, and VernLLM will reject unsupported models with a clear * `LLMError('validation')` *before* dispatching the request, instead of * on the wire. * * Left unset (default), no preflight check runs, and a `jsonSchema` call * to an unsupported model surfaces Bedrock's raw `converse` error as-is. */ toolUseSupportedModels?: string[] | ((modelId: string) => boolean); /** * Which models support native, schema-constrained output * (`outputConfig.textFormat`), independent of `toolConfig`, so it can be * combined with real `tools` in one request. Pass a static list of * model IDs (verified against Bedrock's own docs) or a predicate. * * There is no built-in default here (see `supportsNativeStructuredOutput` * for why). Left unset, every model uses the older forced-single-tool- * call emulation via `toolConfig`, and `tools` + `jsonSchema` together is * rejected, exactly this adapter's behavior before native support was * added. */ nativeStructuredOutputModels?: ModelCapabilityOverride; /** * Overrides the token count `reasoningEffort` tiers map onto when the * caller sets `reasoningEffort` but not `budgetTokens` (Converse has no * tier string of its own, see `adapters/internal/reasoningBudget.utils.ts`). * Only the tiers listed are changed; any omitted tier keeps the * built-in default. Has no effect when `budgetTokens` is set directly, * or when the target model isn't a Claude model. */ reasoningEffortTokens?: Partial; /** * Marks additional models as adaptive-only, on top of this package's * own built-in rule (Claude Opus 4.7 and later, every Claude 5 tier * model, see `isAdaptiveOnlyModel` in * `adapters/internal/reasoningBudget.utils.ts`). Additive, not a * replacement: it can correct a false negative (a newer model this * package doesn't know about yet), it can't un-mark a model the * built-in rule already caught. Pass a static list of model IDs or a * predicate. */ adaptiveOnlyModels?: ModelCapabilityOverride; } /** * Minimal structural shape of an AWS SDK v3 client that exposes `.send()`, * matching `BedrockRuntimeClient` (and its abort-signal-aware call * convention). Avoids importing `@aws-sdk/client-bedrock-runtime` for the * type. */ interface AwsSendClient { send(command: unknown, options?: { abortSignal?: AbortSignal; }): Promise; } /** * Wraps a Bedrock Converse-API client so it satisfies the `LLMClient` * interface VernLLM uses for OpenAI/Groq. The Converse API is unified * across Bedrock's model families (Anthropic, Titan, Llama, Mistral, etc.), * so unlike raw per-model Bedrock invocation, this one adapter works * regardless of which underlying model `modelId` points at, as long as * that model supports Converse (most current-generation ones do) * * `bedrockClient` accepts either a hand-written `BedrockConverseClient` * (a `.converse()`/`.converseStream()` wrapper you provide) or a real AWS * SDK v3 client (anything with `.send()`, matching `BedrockRuntimeClient`) * directly, detected structurally. Passing a raw AWS client skips the * hand-written wrapper entirely, internally doing what it would * (`send(new ConverseCommand(...))`, `send(new * ConverseStreamCommand(...))`). See `wrapAwsSendClient` for how that path * is implemented, including why `@aws-sdk/client-bedrock-runtime` stays * out of this package's dependencies either way. * * `response_format: json_schema`, on a model covered by * `options.nativeStructuredOutputModels` (opt-in, unset by default), is * sent as `outputConfig.textFormat`, its own request field, independent of * `toolConfig`, so it can be combined with real, caller-supplied `tools` * in the same request. Matches the real Converse API's shape exactly: the * schema is nested under `structure.jsonSchema` and JSON-encoded as a * string, not the parsed object `toolConfig`'s tool schemas use, and there * is no `strict` field on this path. * * On any other model (the default), `response_format: json_schema` is * mapped to Converse's `toolConfig` instead: a single tool is defined from * the schema, description, and strictness settings, and `toolChoice` * forces the model to call it. This legacy path cannot be combined with * real `tools` (both would need the same `toolConfig`), and a call that * tries throws `LLMError('invalid_params')` with `code: 'unsupported_capability'` * and `issues: { capability: 'tools_with_json_schema' }` before reaching the API. * Provider-constrained schema matching applies only when `strict: true` is * forwarded and supported. Native tool support varies by model family; * pass `toolUseSupportedModels` to preflight-check it (see * `BedrockAdapterOptions`), otherwise a `jsonSchema` call to an * unsupported model surfaces Bedrock's raw error unchanged. * * `response_format: json_object` throws `LLMError('validation')`: Converse * has no field that mechanically guarantees JSON output, and the only way * to emulate it was an unenforced system-prompt instruction, a guarantee * this adapter no longer pretends to make. Use `jsonSchema` instead. * `reasoning_effort` (no Converse equivalent) is converted to a token * budget and forwarded via `additionalModelRequestFields` for Claude * models only; `budget_tokens` is forwarded the same way directly. Both * are silently dropped for non-Claude models, which have no equivalent * field to reach for. See `adapters/internal/reasoningBudget.utils.ts`. * * `tools` alone maps to Converse's native `toolConfig`/`toolUse`/ * `toolResult`; `tool_choice` maps to `toolConfig.toolChoice`. * * `createStream` calls `converseStream` (optional on `BedrockConverseClient` *, required only if the caller sets `stream: true`) and translates its * `contentBlockStart`/`contentBlockDelta`/`metadata` events into * `WireStreamChunk`s. Content blocks are tracked by `contentBlockIndex`, * same as `fromAnthropic`'s block-index tracking (Converse's streaming * shape is structurally close to Anthropic's own, both being tool-use-aware * content-block streams), including the same `json-tool` unwrapping: a * `jsonSchema`-forced tool's `toolUse.input` deltas are re-emitted as * `text-delta`, not `tool_call_delta`, so the accumulated result lands in * `finalizeResponse`'s `content` path exactly like the non-streaming * `create` branch above unwraps it. */ export declare function fromBedrock(bedrockClient: BedrockConverseClient | AwsSendClient, options?: BedrockAdapterOptions): LLMClient; //#endregion //#region src/adapters/fetch.d.ts /** The chat-completion-shaped request VernLLM builds internally */ type ChatRequest = Parameters[0]; /** * The minimal shape the fetch adapter needs from a response object. * Native `fetch`'s `Response` satisfies this, but so do wrappers around * `axios`, `node-fetch`, `undici`, etc, which makes `request` swappable * without forcing consumers to polyfill the full `Response` interface */ interface ResponseLike { ok: boolean; status: number; headers: { get(name: string): string | null; }; text(): Promise; json(): Promise; } /** A fetch-compatible request function; defaults to native `fetch` */ type RequestLike = (url: string, init: { method: string; headers: Record; body?: string; signal?: AbortSignal; }) => Promise; /** * A streaming-capable request function. Unlike `RequestLike`, which returns * a fully-buffered `ResponseLike`, this resolves to an `AsyncIterable` of * progressively-arriving chunks, the common ground across transports: * native `fetch`'s `response.body` (wrapped to be iterable; see * `webStreamToAsyncIterable` below), axios's Node `Readable` in * `responseType: 'stream'` mode (already async-iterable, no wrapping * needed), `node-fetch`, `undici`, etc, all satisfy this with little or no * glue code. Defaults to native `fetch`. */ type StreamRequestLike = (url: string, init: { method: string; headers: Record; body?: string; signal?: AbortSignal; }) => Promise>; interface FetchAdapterConfig { /** Endpoint URL, or a function of the request in case it depends on model/params */ url: string | ((params: ChatRequest) => string); /** Static headers, or a function (sync or async) for things like refreshed auth tokens */ headers?: Record | (() => Record | Promise>); /** HTTP method. Default 'POST' */ method?: string; /** * The function used to make the HTTP request. Defaults to native `fetch`. * Swap in `axios`, `node-fetch`, or any other transport, as long as it * resolves to a `ResponseLike` object */ request?: RequestLike; /** Maps VernLLMs internal chat-completion request into the providers raw request body */ mapRequest: (params: ChatRequest) => unknown; /** * Maps the providers raw JSON response into `{ content, usage?, toolCalls? }` * `content` is the assistants text (JSON string when JSON mode was requested). * `content` may be empty/omitted when the model responded with only tool * calls and no text. * * `toolCalls`, when the model requested one or more tools, is the list of * calls as flat `{ id, name, arguments }` entries (matching this config's * own `toolCalls?: Array<{ id: string; name: string; arguments: string }>` * return type below), each entry's `arguments` already JSON-*encoded* as a * string (not the parsed object), mirroring the wire format every * OpenAI-compatible provider uses. `fromFetch` itself converts these into * `WireToolCall`'s `type`/`function`-wrapped shape before returning them * from `create`. VernLLM parses (and validates, if `argumentsSchema` was * set) the arguments string internally, mapResponse doesn't need to do * that itself. */ mapResponse: (json: unknown) => { content?: string; usage?: { promptTokens?: number; completionTokens?: number; totalTokens?: number; }; toolCalls?: Array<{ id: string; name: string; arguments: string; }>; }; /** * Optional. Required only for `stream: true` calls. The function used to * open a streaming HTTP request. Takes the same request shape as * `request`, but resolves to an `AsyncIterable` of progressively-arriving * `Uint8Array` or `string` chunks instead of a buffered `ResponseLike`. * Defaults to native `fetch`. */ requestStream?: StreamRequestLike; /** * Optional. How the raw stream bytes are split into individual event * payloads. Defaults to Server-Sent Events framing (`data: ...` blocks * separated by a blank line, `[DONE]` sentinel honored, see * `parseSseStream`), which covers the large majority of LLM providers' * streaming HTTP endpoints. Override this for a provider that frames its * stream differently, e.g. newline-delimited JSON (NDJSON) with no SSE * envelope. */ parseStreamFrames?: (chunks: AsyncIterable) => AsyncIterable; /** * Optional. Required only for `stream: true` calls. Maps one parsed * stream event (already extracted from its frame by `parseStreamFrames`) * into zero, one, or more `WireStreamChunk`s, mirrors `mapResponse`'s * role for the non-streaming path, just per-event instead of once for * the whole body. Return `undefined` to skip an event that carries * nothing VernLLM needs (e.g. a provider's keep-alive ping). Configs * that don't implement this make `stream: true` throw a clear * `LLMError('validation')` rather than a confusing runtime failure or a * silently empty stream. */ mapStreamEvent?: (event: unknown) => WireStreamChunk | WireStreamChunk[] | undefined; /** * Optional. How to read AIMD's proactive rate limit hint off a * successful response. Defaults to OpenAI's header set. */ parseRateLimitHint?: (headers: ResponseLike['headers']) => ProviderRateLimitHint; } /** * A fetch-based escape hatch for providers with no SDK, or where pulling one * in isnt worth it. You supply the URL, headers, and two small mapping * functions; this handles the HTTP call and slots the result into the same * `LLMClient` shape every other adapter produces, so retries, timeouts, * the circuit breaker, and JSON/schema handling all still work unmodified * * Non-2xx responses throw an error with `.status` set to the HTTP status * code, so VernLLMs `nonRetryableStatus` handling (e.g. failing fast on * 401/403) applies here too * * Tool calling works the same way as every other adapter: `mapRequest` * receives the full `ChatRequest`, including `tools`/`toolChoice`, so it can * translate them into whatever shape the provider's wire format expects * (typically an OpenAI-`function`-wrapped `tools` array plus a `tool_choice` * field). On the way back, `mapResponse` may return a `toolCalls` array * (id/name/JSON-encoded-arguments-string per call) alongside or instead of * `content`; VernLLM parses and (if `argumentsSchema` was set) validates * those arguments the same way it does for every other adapter. For * `stream: true`, tool-call deltas go through the existing * `mapStreamEvent` seam via `WireStreamChunk`'s `tool_call_delta` variant, * no separate config is needed for streaming vs non-streaming tool calls. * * `createStream` requires `mapStreamEvent` (there's no non-streaming * response to fall back on, unlike the other three optional streaming * seams). It opens the request via `requestStream` (defaults to native * `fetch`), splits the raw bytes into individual events via * `parseStreamFrames` (defaults to SSE framing, see `parseSseStream`), * and translates each event into `WireStreamChunk`(s) via * `mapStreamEvent`. Both seams are overridable per-config for providers * that don't fit the SSE-over-fetch default. If a custom `request` * transport is configured, `requestStream` must be configured too, * `requestStream` never silently falls back to `request` (see * `createStream`'s own comment for why), so a `stream: true` call with * `request` set but no `requestStream` throws a clear * `LLMError('validation')` instead of quietly using unrelated native * `fetch`. */ export declare function fromFetch(config: FetchAdapterConfig): LLMClient; //#endregion //#region src/adapters/openaiCompatible.d.ts /** * Adapter for any SDK/client whose `chat.completions.create` already * matches the OpenAI wire format: this covers most hosted inference * providers, since "OpenAI-compatible" is a de facto standard for chat * completion APIs. Almost everything passes straight through untouched, * this exists purely so call sites read clearly (`fromMistral(client)` vs * handing a Mistral client to something typed for OpenAI) and so a real * transformation could be added later, per-provider, without a breaking * change. * * The one thing that isn't a pure passthrough: a `ContentBlock[]` * `userContent` is translated into OpenAI's native `image_url` content-part * shape, since VernLLM's `ContentBlock` is intentionally provider-agnostic * rather than a copy of any one provider's wire format. * * Not every SDKs own TypeScript types line up exactly with `LLMClient` * (extra fields, stricter unions, etc.), so this takes `unknown` and casts: * the actual compatibility contract is the JSON each provider sends and * receives over the wire, not the SDKs TS types. * * `createStream` is implemented by calling the same underlying * `chat.completions.create` with `stream: true` (and, for providers that * support it, `stream_options: { include_usage: true }`, so a final usage * block arrives), the OpenAI SDK, and every OpenAI-compatible client * modeled on it, returns an `AsyncIterable` of SSE chunks instead of a * single completion object when `stream: true` is set. Each chunk is * translated into `WireStreamChunk`(s) via `toWireStreamChunks`. * * Note on long-running reasoning models: this adapter consumes the * underlying SDK's already-parsed stream rather than raw SSE bytes, so * unlike `fromFetch`/`fromAnthropic` it cannot see comment-only keep-alive * ping frames. Combined with `chunkIdleTimeoutMs`'s 30 second default and * `reasoningEffort` (documented to have long silent gaps for o-series and * similar models), a long-running reasoning call on this adapter can trip * the idle timeout even though the provider is still working. Raise or * disable `chunkIdleTimeoutMs` per call for those routes, see `CallParams`. */ interface OpenAICompatibleAdapterOptions { /** * Whether the provider supports `stream_options.include_usage`. Not * every "OpenAI-compatible" provider is guaranteed to, so this defaults * to `true` (matching OpenAI, Groq, Mistral, and most others observed) * and should be set to `false` for a provider verified not to support * it. When `false`, `stream_options` is omitted entirely and no usage * block will arrive on the stream; callers relying on streamed `usage` * with such a provider won't get one. */ supportsStreamUsage?: boolean; /** * Overrides the token count `budgetTokens` buckets into when the caller * sets `budgetTokens` but not `reasoningEffort` (OpenAI-compatible * clients have no numeric budget field of their own, see * `adapters/internal/reasoningBudget.utils.ts`). Only the tiers listed * are changed; any omitted tier keeps the built-in default. Has no * effect when `reasoningEffort` is set directly. */ reasoningEffortTokens?: Partial; /** * Whether the client's request builder supports `.withResponse()` * (needed for AIMD's proactive path). Default `false`, since not * every "OpenAI-compatible" client is confirmed to support it. */ supportsWithResponse?: boolean; } export declare function fromOpenAICompatible(client: unknown, options?: OpenAICompatibleAdapterOptions): LLMClient; /** * Named alias for the OpenAI SDK itself. A raw `new OpenAI(...)` instance * structurally matches most of `LLMClient`, but newer `openai` SDK major * versions have widened `ChatCompletionContentPart` (e.g. adding a `file` * variant) in ways that no longer structurally satisfy VernLLM's * provider-agnostic `ContentBlock[]` on `userContent`, so passing the SDK * instance directly can fail to typecheck depending on the installed * `openai` version. Wrapping with `fromOpenAI()` sidesteps that by * translating through `unknown` at the boundary, and also picks up * multimodal image translation and `createStream` wiring that a raw * client doesn't have. See Migration Notes for details. * * `supportsWithResponse` defaults to `false` here too: * `client` is `unknown`, so there's no way to verify it's really the * official `openai` package's client versus a fake or a test double. * Pass `supportsWithResponse: true` once you've confirmed it. */ export declare const fromOpenAI: typeof fromOpenAICompatible; /** Groqs SDK matches the OpenAI wire format */ export declare const fromGroq: typeof fromOpenAICompatible; /** * Mistrals `chat.completions`-shaped client (or their OpenAI-compat * endpoint). Mistral supports `stream_options.include_usage` (added after * an earlier period where it returned a 422 for unrecognized fields, per * Mistral's changelog and streaming docs), so this is a plain alias like * the others, `supportsStreamUsage` defaults to `true`. */ export declare const fromMistral: typeof fromOpenAICompatible; /** DeepSeeks API is OpenAI-compatible */ export declare const fromDeepSeek: typeof fromOpenAICompatible; /** Cerebras inference API is OpenAI-compatible */ export declare const fromCerebras: typeof fromOpenAICompatible; /** Together AIs API is OpenAI-compatible */ export declare const fromTogether: typeof fromOpenAICompatible; /** Fireworks AIs API is OpenAI-compatible */ export declare const fromFireworks: typeof fromOpenAICompatible; /** * Ollama exposes an OpenAI-compatible endpoint at `/v1/chat/completions` * (as opposed to its native `/api/chat` format, which differs). Point an * OpenAI SDK instances `baseURL` at your Ollama server and pass it here: * this does not talk to Ollamas native API directly. */ export declare const fromOllama: typeof fromOpenAICompatible; /** OpenRouter's API is OpenAI-compatible */ export declare const fromOpenRouter: typeof fromOpenAICompatible; /** Perplexity's API is OpenAI-compatible */ export declare const fromPerplexity: typeof fromOpenAICompatible; /** DeepInfra's API is OpenAI-compatible */ export declare const fromDeepInfra: typeof fromOpenAICompatible; /** Novita's API is OpenAI-compatible */ export declare const fromNovita: typeof fromOpenAICompatible; /** Hyperbolic's API is OpenAI-compatible */ export declare const fromHyperbolic: typeof fromOpenAICompatible; /** Moonshot's (Kimi) API is OpenAI-compatible */ export declare const fromMoonshot: typeof fromOpenAICompatible; /** Zhipu's (GLM) API is OpenAI-compatible */ export declare const fromZhipu: typeof fromOpenAICompatible; /** * LM Studio exposes an OpenAI-compatible endpoint at `/v1/chat/completions`. * Point an OpenAI SDK instance's `baseURL` at your local LM Studio server. */ export declare const fromLMStudio: typeof fromOpenAICompatible; /** * vLLM's OpenAI-compatible server mode exposes `/v1/chat/completions`. * Point an OpenAI SDK instance's `baseURL` at your vLLM server. */ export declare const fromVLLM: typeof fromOpenAICompatible; /** xAI's Grok API is OpenAI-compatible */ export declare const fromXAI: typeof fromOpenAICompatible; /** NVIDIA NIM's hosted and self-hosted endpoints are OpenAI-compatible */ export declare const fromNvidiaNIM: typeof fromOpenAICompatible; /** Vercel AI Gateway is OpenAI-compatible */ export declare const fromVercelAIGateway: typeof fromOpenAICompatible; /** Cloudflare Workers AI exposes an OpenAI-compatible endpoint */ export declare const fromCloudflareWorkersAI: typeof fromOpenAICompatible; /** Nebius AI Studio is OpenAI-compatible */ export declare const fromNebius: typeof fromOpenAICompatible; /** SambaNova Cloud's API is OpenAI-compatible */ export declare const fromSambaNova: typeof fromOpenAICompatible; /** Baseten's model hosting exposes an OpenAI-compatible endpoint */ export declare const fromBaseten: typeof fromOpenAICompatible; /** Featherless AI's API is OpenAI-compatible */ export declare const fromFeatherless: typeof fromOpenAICompatible; /** Friendli AI's serving endpoint is OpenAI-compatible */ export declare const fromFriendli: typeof fromOpenAICompatible; /** SiliconFlow's API is OpenAI-compatible */ export declare const fromSiliconFlow: typeof fromOpenAICompatible; /** Parasail's inference API is OpenAI-compatible */ export declare const fromParasail: typeof fromOpenAICompatible; /** StepFun's API is OpenAI-compatible */ export declare const fromStepFun: typeof fromOpenAICompatible; /** MiniMax's API is OpenAI-compatible */ export declare const fromMiniMax: typeof fromOpenAICompatible; /** Lambda Labs' Inference API is OpenAI-compatible */ export declare const fromLambdaLabs: typeof fromOpenAICompatible; /** Snowflake Cortex's LLM endpoint is OpenAI-compatible */ export declare const fromSnowflakeCortex: typeof fromOpenAICompatible; /** Anyscale Endpoints' API is OpenAI-compatible */ export declare const fromAnyscale: typeof fromOpenAICompatible; /** Lepton AI's inference API is OpenAI-compatible */ export declare const fromLepton: typeof fromOpenAICompatible; /** Inference.net's API is OpenAI-compatible */ export declare const fromInferenceNet: typeof fromOpenAICompatible; /** Infermatic's API is OpenAI-compatible */ export declare const fromInfermatic: typeof fromOpenAICompatible; /** AtlasCloud's inference API is OpenAI-compatible */ export declare const fromAtlasCloud: typeof fromOpenAICompatible; /** 01.AI's (Yi models) API is OpenAI-compatible */ export declare const from01AI: typeof fromOpenAICompatible; //#endregion export type { AnthropicClient, AssistantContent, AttemptContext, BedrockConverseClient, CacheAdapter, CachedCallParams, CachedConditionalToolCallParams, CachedJsonModeDisabledCallParams, CachedJsonModeEnabledCallParams, CachedStreamCallParams, CachedStreamConditionalToolCallParams, CachedStreamJsonModeDisabledCallParams, CachedStreamJsonModeEnabledCallParams, CachedStreamToolCallParams, CachedToolCallParams, CallMeta, CallParams, CallResult, CallWithToolsResult, CircuitBreakerAdapter, CircuitBreakerCallContext, CircuitBreakerOptions, CircuitBreakerStateChangeHandler, CircuitState, CircuitTarget, ConditionalToolCallParams, ContentBlock, ContentResult, ConversationTurn, CooldownBackoff, CreateMiddlewareOptions, DuplicateToolNamesIssue, EvictionOption, ExponentialBackoffOptions, FallbackAttempt, FallbackOn, FallbackTarget, FetchAdapterConfig, GeminiClient, HistoryToolResultIssue, ImageBlock, JsonModeDisabledCallParams, JsonModeEnabledCallParams, JsonSchemaSpec, JsonValue, LLMClient, LLMErrorCode, LLMErrorIssuesByCode, LLMErrorSnapshot, LLMErrorType, LLMRequestShape, LLMRequestSnapshot, Logger, MiddlewareCapabilities, MiddlewareContext, MiddlewareContextBase, MiddlewareRef, MiddlewareStateBag, MiddlewareStateKey, OnEvent, OnUsage, PreDispatchContext, RateLimitAcquireResult, RateLimitOptions, RateLimitReason, RateLimitState, RateLimiterAdapter, RefundUsage, RequiredMiddlewareRef, ReserveUsage, RetryAttempt, RetryBudgetOptions, SchemaLike, StreamCallResult, StreamChunk, StreamEnabledCallParams, StreamJsonModeDisabledCallParams, StreamJsonModeEnabledCallParams, TargetCircuitState, TextBlock, TokenUsage, ToolCall, ToolCallResult, ToolChoice, ToolDefinition, ToolEnabledCallParams, ToolIssue, ToolResult, ToolsDisabledCallParams, TrippingPolicy, UnknownToolChoiceIssue, UnsupportedCapabilityIssue, VernLLMEvent, VernLLMMiddleware, VernLLMOptions, WireCallRequest, WireCallRequestPatch, WireMessage, WireRequest, WireResponseFormat, WireStreamChunk, WireTool, WireToolCall, WireToolChoice }; //# sourceMappingURL=index.d.cts.map