vern-llm 2.4.2 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -7,16 +7,7 @@ type LLMErrorType = 'timeout' | 'api' | 'network' | 'parse' | 'validation' | 'in
7
7
  * e.g. `authentication`/`authorization` apply the same way regardless of
8
8
  * which type wraps them.
9
9
  */
10
- 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' | 'rate_limit_queue_full' | 'rate_limit_queue_timeout' | 'rate_limit_capacity_exceeded' | 'provider_rate_limited' | 'request_timeout' | 'idle_timeout' | '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';
11
- /**
12
- * Tool contract codes: a model or provider response defect, not a
13
- * transient provider fault. Deterministic on the wire request, so
14
- * retrying can't change the outcome and it shouldn't count toward the
15
- * circuit breaker either. Shared by `LLMError.retryable` below and by
16
- * `CallExecutor`'s own retry/breaker accounting, so the two can't drift
17
- * apart.
18
- */
19
-
10
+ 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';
20
11
  /** One tool call's contract failure, used to report every bad call in a response at once. */
21
12
  interface ToolIssue {
22
13
  name: string;
@@ -133,21 +124,6 @@ interface LLMRequestSnapshot {
133
124
  /** Wall clock time the attempt started, ms since epoch. */
134
125
  startedAt: number;
135
126
  }
136
- /**
137
- * Builds a point-in-time, plain data copy of one attempt's outgoing
138
- * request. Mirrors `LLMError.toSnapshot()`: never thrown or dispatched
139
- * again, safe to serialize and store. A plain function rather than a
140
- * method, since unlike `LLMError` a request has no throwable identity or
141
- * derived state worth wrapping in a class.
142
- *
143
- * `startedAt` is optional so existing call sites (and tests) that don't
144
- * care about exact timing keep working, but a caller that has a real
145
- * capture time should always pass it: this function may run well after
146
- * the request was actually dispatched (e.g. `callExecutor` only builds
147
- * the snapshot once an attempt has failed), so defaulting to `Date.now()`
148
- * here would record failure-handling time, not request-start time.
149
- */
150
-
151
127
  /**
152
128
  * One failed attempt on the way to a terminal error: which attempt index
153
129
  * it was, and a snapshot of the error it failed with. The base shape
@@ -187,11 +163,20 @@ declare class LLMError extends Error {
187
163
  * `nonRetryableStatus` list. False for `parse`/`validation`/
188
164
  * `invalid_params`/`aborted` types (the caller's own input, the model's
189
165
  * own response, or intentional cancellation, none of which are the
190
- * provider being unhealthy), the tool contract codes, and the local
191
- * rate limit codes. Subclasses (see `FallbackExhaustedError`) may
192
- * override this when `type` alone carries no retry signal.
166
+ * provider being unhealthy), the tool contract codes, the local
167
+ * rate limit codes, and the middleware timeout code.
168
+ * Subclasses (see `FallbackExhaustedError`) may override this when `type`
169
+ * alone carries no retry signal.
193
170
  */
194
171
  get retryable(): boolean;
172
+ /**
173
+ * Whether this failure should count toward the circuit breaker's
174
+ * failure threshold. Not the same question as `retryable`:
175
+ * `quota_exceeded` is retryable but says nothing about provider
176
+ * health, so it's excluded here even though `retryable` is true for
177
+ * it. Always false whenever `retryable` is false.
178
+ */
179
+ get countsTowardBreaker(): boolean;
195
180
  /**
196
181
  * Copies this error's fields into an {@link LLMErrorSnapshot}, for
197
182
  * recording as a `RetryAttempt`/`FallbackAttempt`. `retryable` is
@@ -234,10 +219,9 @@ declare function isLLMError(err: unknown): err is LLMError;
234
219
  declare function hasIssues<C extends keyof LLMErrorIssuesByCode>(err: LLMError, code: C): err is LLMError & {
235
220
  code: C;
236
221
  issues: LLMErrorIssuesByCode[C];
237
- }; //#endregion
222
+ };
223
+ //#endregion
238
224
  //#region src/types/cache.d.ts
239
-
240
- //# sourceMappingURL=errors.d.ts.map
241
225
  interface CacheAdapter<T = unknown> {
242
226
  get(key: string): Promise<{
243
227
  hit: boolean;
@@ -248,13 +232,20 @@ interface CacheAdapter<T = unknown> {
248
232
  resolveKey?(key: string): Promise<string>;
249
233
  }
250
234
  /**
251
- * Trivial default so the package works out of the box with no external deps
252
- * Not shared across processes, swap in Redis/Upstash/etc for production
235
+ * Which entry `InMemoryCacheAdapter` evicts once `maxSize` is exceeded.
236
+ * `'fifo'` (default) drops the oldest inserted entry. `'lru'` drops the
237
+ * least recently read or written entry.
238
+ */
239
+ type EvictionOption = 'fifo' | 'lru';
240
+ /**
241
+ * Trivial default so the package works out of the box with no external deps.
242
+ * Not shared across processes, swap in Redis/Upstash/etc for production.
253
243
  */
254
244
  declare class InMemoryCacheAdapter<T = unknown> implements CacheAdapter<T> {
255
245
  private readonly maxSize;
256
246
  private store;
257
- constructor(maxSize?: number);
247
+ private readonly eviction;
248
+ constructor(maxSize?: number, eviction?: EvictionOption);
258
249
  get(key: string): Promise<{
259
250
  hit: boolean;
260
251
  value: T | null;
@@ -301,104 +292,512 @@ declare class TieredCacheAdapter<T = unknown> implements CacheAdapter<T> {
301
292
  set(key: string, value: T, ttl: number): Promise<void>;
302
293
  delete(key: string): Promise<void>;
303
294
  }
304
-
295
+ //#endregion
296
+ //#region src/types/events.d.ts
297
+ /**
298
+ * Reports what happened during a call. Fire and forget, mirroring
299
+ * `onUsage`: the return value is never read and a throwing handler cannot
300
+ * change what the call does, only what gets reported about it.
301
+ */
302
+ type VernLLMEvent = {
303
+ kind: 'retry';
304
+ requestId: string;
305
+ provider: string;
306
+ /** The model actually resolved for this call (honors a per-call `model` override). */
307
+ model: string;
308
+ /** The 1-based retry ordinal (the 1st retry is `1`, not the overall attempt count). */
309
+ attempt: number;
310
+ maxRetries: number;
311
+ delayMs: number;
312
+ retryAfterHonored: boolean;
313
+ error: LLMError;
314
+ } | {
315
+ kind: 'circuit_state';
316
+ provider: string;
317
+ /**
318
+ * The model of the call that triggered this specific transition
319
+ * (whatever was passed to the `assertClosed`/`recordSuccess`/
320
+ * `recordFailure` call that caused it), not a property of the
321
+ * circuit itself: the breaker still counts failures across every
322
+ * model together, so a threshold crossing can be the sum of
323
+ * several different models' failures even though only the
324
+ * triggering call's `model` is reported here.
325
+ */
326
+ model: string;
327
+ from: CircuitState;
328
+ to: CircuitState;
329
+ consecutiveFailures: number;
330
+ } | {
331
+ kind: 'fallback';
332
+ requestId: string;
333
+ /** Provider name of the target that just failed. */
334
+ from: string;
335
+ /** Provider name of the target about to be tried next. */
336
+ to: string;
337
+ /** `-1` for the primary target, otherwise the index into `fallback`. */
338
+ fromIndex: number;
339
+ toIndex: number;
340
+ /** The normalized error that caused `from` to be abandoned. */
341
+ error: LLMError;
342
+ /** Time spent on `from`, including its own retries, before giving up. */
343
+ elapsedMs: number;
344
+ } | {
345
+ kind: 'rate_limited';
346
+ requestId: string;
347
+ provider: string;
348
+ /** The model actually resolved for this call (honors a per-call `model` override). */
349
+ model: string;
350
+ /** How long this attempt sat queued for capacity before it was let through. */
351
+ waitedMs: number;
352
+ /** Which configured bucket was blocking this attempt just before it cleared. */
353
+ reason: 'concurrency' | 'rpm' | 'tpm';
354
+ } | {
355
+ kind: 'middleware';
356
+ requestId: string;
357
+ /** This middleware's `name`, or its array position if unnamed. */
358
+ middleware: string;
359
+ hook: 'transform' | 'wrap_short_circuit' | 'enabled_skip';
360
+ /** For `hook: 'transform'` only: which top-level fields the merged patch touched. */
361
+ patchedFields?: string[];
362
+ };
363
+ type OnEvent = (event: VernLLMEvent) => void;
364
+ //#endregion
365
+ //#region src/types/middleware.d.ts
366
+ /** Capabilities of the target a middleware hook is currently looking at. */
367
+ interface MiddlewareCapabilities {
368
+ /**
369
+ * Whether this target honors `response_format: { type: 'json_object' }`
370
+ * as a real constraint. Mirrors `LLMClient.supportsJsonObjectMode`.
371
+ * `false` for `fromAnthropic` and `fromBedrock`.
372
+ */
373
+ supportsJsonObjectMode: boolean;
374
+ }
375
+ /**
376
+ * A typed reference to one slot in `ctx.state`. Create one with
377
+ * `createStateKey`, export it, and import the same reference wherever
378
+ * another middleware needs to read or write the same value. There's no
379
+ * string key anywhere in this path, so a typo becomes a missing import
380
+ * or an undefined variable, a compile error, instead of a silently
381
+ * created new property.
382
+ */
383
+ interface MiddlewareStateKey<T> {
384
+ readonly debugName: string;
385
+ /**
386
+ * Never set at runtime; exists purely so `T` is actually used
387
+ * somewhere in this interface's shape (a phantom type), which is what
388
+ * lets `MiddlewareStateBag.get`/`set` infer the right type for a given
389
+ * key instead of two `MiddlewareStateKey<string>` and
390
+ * `MiddlewareStateKey<number>` keys being structurally identical.
391
+ */
392
+ readonly __phantom?: T;
393
+ }
394
+ /** Creates a new, distinct `MiddlewareStateKey`. `debugName` is used only in log lines and the `'middleware'` event; it never affects equality. */
395
+ declare function createStateKey<T>(debugName: string): MiddlewareStateKey<T>;
396
+ /**
397
+ * Typed, per-logical-call storage two middleware can deliberately share a
398
+ * value through (a span ID one sets, another reads). Backed by a plain
399
+ * `Map` internally, created once per logical call and never read or
400
+ * written by VernLLM itself.
401
+ */
402
+ interface MiddlewareStateBag {
403
+ get<T>(key: MiddlewareStateKey<T>): T | undefined;
404
+ set<T>(key: MiddlewareStateKey<T>, value: T): void;
405
+ }
406
+ /** A plain, `Map`-backed `MiddlewareStateBag`. */
407
+ declare function createMiddlewareStateBag(): MiddlewareStateBag;
408
+ /** Fields every `MiddlewareContext` variant carries, regardless of `stage`. */
409
+ interface MiddlewareContextBase {
410
+ requestId: string;
411
+ /** Capabilities of the target this stage's identity fields describe. */
412
+ capabilities: MiddlewareCapabilities;
413
+ signal?: AbortSignal;
414
+ /** Shared, collision-proof state for two middleware to deliberately coordinate through. See `MiddlewareStateBag`. */
415
+ state: MiddlewareStateBag;
416
+ /** Simple, string-keyed scratch space, pre-namespaced to this one middleware so two middleware can never collide here even by accident. */
417
+ own: Record<string, unknown>;
418
+ }
419
+ /**
420
+ * The `ctx` `transform` receives, and every attempt-scoped event context
421
+ * (`'retry'`, `'fallback'`, `'circuit_state'`, `'middleware'`). Built once
422
+ * a specific target has actually been selected for this attempt, so every
423
+ * field describes the real target, not a placeholder.
424
+ */
425
+ interface AttemptContext extends MiddlewareContextBase {
426
+ stage: 'attempt';
427
+ /** The target this attempt is actually dispatched to. */
428
+ requestedProvider: string;
429
+ requestedModel: string;
430
+ isFallbackAttempt: boolean;
431
+ /**
432
+ * The real, current attempt number for this dispatch.
433
+ *
434
+ * Exception: on a `'circuit_state'` event triggered by a pre-dispatch
435
+ * check (`assertClosed`, before any attempt has been made), this is
436
+ * `1` regardless of which attempt is about to run, since no attempt
437
+ * exists yet to report. Every other `'circuit_state'` event, and every
438
+ * other attempt-scoped event, reports the real attempt number.
439
+ */
440
+ attempt: number;
441
+ }
442
+ /**
443
+ * The `ctx` `wrap` receives before `next()` resolves (and `onError`'s own
444
+ * `ctx`, built the same way under the hood). Built once, before any
445
+ * fallback target is chosen, so it only ever describes the primary
446
+ * target. There is no real "requested" target yet, and no attempt count,
447
+ * fallback flag, or per-attempt capability to report. Read `next()`'s
448
+ * resolved `CallResult.meta` once you need to know what actually
449
+ * happened.
450
+ */
451
+ interface PreDispatchContext extends MiddlewareContextBase {
452
+ stage: 'pre-dispatch';
453
+ /** The primary target only, not necessarily who ends up answering. */
454
+ primaryProvider: string;
455
+ primaryModel: string;
456
+ }
457
+ /**
458
+ * `enabled` and `onEvent` are called from both stages (gating/observing
459
+ * `transform` as well as `wrap`), so they receive this union and must
460
+ * narrow on `ctx.stage` before reading stage-specific fields.
461
+ * `transform` and `wrap` themselves receive the single variant that's
462
+ * always accurate for them (`AttemptContext`/`PreDispatchContext`
463
+ * respectively). See `VernLLMMiddleware`.
464
+ */
465
+ type MiddlewareContext = AttemptContext | PreDispatchContext;
466
+ /** The `response_format` shape `RequestBuilder` can put on the wire. */
467
+ type WireResponseFormat = {
468
+ type: 'json_object';
469
+ } | {
470
+ type: 'json_schema';
471
+ json_schema: {
472
+ name: string;
473
+ schema: Record<string, unknown>;
474
+ strict?: boolean;
475
+ description?: string;
476
+ };
477
+ };
478
+ /** A tool as it appears on the wire, OpenAI's `function`-wrapped shape. */
479
+ interface WireTool {
480
+ type: 'function';
481
+ function: {
482
+ name: string;
483
+ description: string;
484
+ parameters: Record<string, unknown>;
485
+ };
486
+ }
487
+ /**
488
+ * The wire-shaped request `RequestBuilder.build()` produces for one call
489
+ * attempt, before dispatch. Read only inside `transform`; return a patch
490
+ * of the fields you want to change instead of the whole object.
491
+ */
492
+ interface WireCallRequest {
493
+ model: string;
494
+ temperature?: number;
495
+ max_tokens: number;
496
+ response_format?: WireResponseFormat;
497
+ reasoning_effort?: 'minimal' | 'low' | 'medium' | 'high';
498
+ budget_tokens?: number;
499
+ tools?: WireTool[];
500
+ tool_choice?: WireToolChoice;
501
+ messages: WireMessage[];
502
+ }
503
+ /**
504
+ * What `transform` returns: a patch merged onto the request that
505
+ * `RequestBuilder.build()` (plus every earlier middleware's own patch)
506
+ * already produced, not a replacement for it. `model` and
507
+ * `response_format` can't be expressed here at all, since everything
508
+ * downstream that attributes a call to a target keys off the values
509
+ * `RequestBuilder` already resolved for those two fields, not off
510
+ * whatever ends up on the wire request. `messages`/`tools` are joined by
511
+ * a separate `add*` field, appended rather than replaced, so two
512
+ * independently written middleware can each add to the list without one
513
+ * silently clobbering what the other already added.
514
+ */
515
+ interface WireCallRequestPatch {
516
+ temperature?: number;
517
+ max_tokens?: number;
518
+ reasoning_effort?: 'minimal' | 'low' | 'medium' | 'high';
519
+ budget_tokens?: number;
520
+ tool_choice?: WireToolChoice;
521
+ /** Replaces the whole message list. Prefer `addMessages` unless a full replace is genuinely the intent. */
522
+ messages?: WireMessage[];
523
+ /** Appended after whatever earlier middleware already added. Never clobbers a prior addition. */
524
+ addMessages?: WireMessage[];
525
+ /** Replaces the whole tool list. Prefer `addTools`, same reasoning as `messages`/`addMessages`. */
526
+ tools?: WireTool[];
527
+ /** Appended after whatever earlier middleware already added. Never clobbers a prior addition. */
528
+ addTools?: WireTool[];
529
+ }
530
+ /**
531
+ * The settled outcome of one logical call, passed to `wrap`'s `next()`.
532
+ * `meta` is populated once a target has actually answered, for both
533
+ * streaming and non-streaming calls (`undefined` only on a cache hit,
534
+ * where nothing was actually spent).
535
+ */
536
+ interface CallResult<T = unknown> {
537
+ value: T;
538
+ meta?: CallMeta;
539
+ }
540
+ /**
541
+ * One entry in `VernLLMOptions.middleware`. All four hooks are optional;
542
+ * an entry that sets none of them is inert. See the middleware docs for
543
+ * how `transform`, `wrap`, `onEvent`, and `enabled` compose across
544
+ * several entries.
545
+ */
546
+ interface VernLLMMiddleware {
547
+ /** Used in log lines and the `'middleware'` event. Defaults to this entry's array position when omitted. */
548
+ name?: string;
549
+ /** Sort key for composition order, ascending, ties broken by array order. See the middleware docs for what "lower runs first" means for `wrap`. */
550
+ priority?: number;
551
+ /**
552
+ * Boolean for a static on/off switch, or a predicate evaluated per
553
+ * call. A throwing, rejecting, or timed-out predicate is logged and
554
+ * treated as `false` for that call.
555
+ */
556
+ enabled?: boolean | ((ctx: MiddlewareContext) => boolean | Promise<boolean>);
557
+ /** Per-middleware override of the instance-level `middlewareTimeoutMs`, applied to this entry's `transform` and function `enabled`. `<= 0` means unbounded (no timer at all). */
558
+ timeoutMs?: number;
559
+ /** 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. */
560
+ transform?: (request: Readonly<WireCallRequest>, ctx: AttemptContext) => WireCallRequestPatch | Promise<WireCallRequestPatch>;
561
+ /**
562
+ * Wraps one whole logical call, exactly once, regardless of how many
563
+ * retries or fallback targets ran underneath it. `ctx` is built once,
564
+ * before any fallback target is chosen, so it only describes the
565
+ * primary target. There is no `requestedProvider`/`isFallbackAttempt`/
566
+ * `attempt` to read here. Read `next()`'s resolved `CallResult.meta`
567
+ * for what actually happened.
568
+ */
569
+ wrap?: (request: Readonly<WireCallRequest>, next: () => Promise<CallResult>, ctx: PreDispatchContext) => Promise<CallResult>;
570
+ /** 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. */
571
+ onEvent?: (event: VernLLMEvent, ctx: MiddlewareContext) => void;
572
+ }
305
573
  //#endregion
306
574
  //#region src/circuitBreaker.d.ts
307
- //# sourceMappingURL=cache.d.ts.map
575
+ /** The call this mutation happened as part of, forwarded to `onStateChange` untouched. */
576
+ interface CircuitBreakerCallContext {
577
+ requestId: string;
578
+ state: MiddlewareStateBag;
579
+ signal?: AbortSignal;
580
+ /** Omitted for calls before any attempt exists, like `assertClosed`'s pre-dispatch check. */
581
+ attempt?: number;
582
+ }
308
583
  interface CircuitBreakerOptions {
309
584
  /** Consecutive failures before the circuit opens, default 5 */
310
585
  threshold?: number;
311
586
  /** How long the circuit stays open before allowing a trial request, in ms. Default 30000 */
312
587
  cooldownMs?: number;
313
588
  /**
314
- * Called after every real state change, never for a no-op transition
315
- * (e.g. open to open). `model` is the resolved model of whichever call
316
- * triggered this specific transition (the `model` passed to whichever
317
- * of `assertClosed`/`recordSuccess`/`recordFailure` caused it).
318
- *
319
- * With `isolateByModel` off (the default), this is a label only: the
320
- * breaker still counts failures across every model together, so a
321
- * threshold crossing can be the sum of several different models'
322
- * failures even though only the triggering call's `model` is reported
323
- * here. With `isolateByModel` on, it's exact: each model has its own
324
- * counter, so the transition really was caused solely by that model.
589
+ * Fires after every real state change, never a no-op transition. `model`
590
+ * is the resolved model of whichever call triggered it. With
591
+ * `isolateByModel` off, failures are still counted across every model.
325
592
  */
326
- onStateChange?: (from: CircuitState, to: CircuitState, consecutiveFailures: number, model?: string) => void;
593
+ onStateChange?: (from: CircuitState, to: CircuitState, consecutiveFailures: number, model?: string, context?: CircuitBreakerCallContext) => void;
327
594
  /**
328
595
  * Track a separate circuit per resolved model instead of one shared
329
- * circuit for the whole instance. A failure on one model then never
330
- * opens another model's circuit, at the cost of slower detection for
331
- * an outage spread across many distinct models (each model's counter
332
- * must independently cross `threshold`). Default false: one shared
333
- * circuit, matching every version before this option existed.
334
- *
335
- * A call that omits `model` (only possible calling `CircuitBreaker`
336
- * directly, `VernLLM` always passes one) falls into one shared bucket
337
- * alongside every other call that also omits it.
596
+ * circuit. Default false. A call that omits `model` falls into one
597
+ * shared bucket alongside every other call that also omits it.
338
598
  */
339
599
  isolateByModel?: boolean;
600
+ /** Trial calls allowed through per half-open cycle. Default 1, clamped to at least 1. */
601
+ halfOpenProbes?: number;
602
+ /** Fraction of `halfOpenProbes` that must succeed to close the circuit. Default 1, clamped to `[0, 1]`. */
603
+ halfOpenSuccessRatio?: number;
604
+ /**
605
+ * Grows `cooldownMs` on each repeat open instead of a fixed wait.
606
+ * `{ multiplier, maxMs }` covers exponential growth; a `CooldownBackoff`
607
+ * function covers anything else. Omitted means `cooldownMs` stays fixed.
608
+ */
609
+ cooldownBackoff?: ExponentialBackoffOptions | CooldownBackoff;
610
+ /**
611
+ * Decides when a bucket's failures should open the circuit.
612
+ * `{ kind: 'consecutive', threshold }` (the default) opens after that
613
+ * many failures in a row. `{ kind: 'rolling', windowMs, minCalls,
614
+ * failureRatio }` opens once at least `minCalls` calls have landed in
615
+ * the trailing `windowMs` and the failure ratio reaches `failureRatio`.
616
+ * `minCalls` must be a non-negative integer; `failureRatio` must be
617
+ * finite and within `[0, 1]`. Both are validated at construction,
618
+ * thrown as `RangeError`. A `TrippingPolicy` covers anything else, one
619
+ * instance shared across every model automatically under
620
+ * `isolateByModel`, since it tracks its own state per key rather than
621
+ * owning one flat counter.
622
+ */
623
+ tripping?: TrippingOption;
624
+ }
625
+ /** Computes the cooldown for a bucket's `reopenCount`-th repeat open. */
626
+ type CooldownBackoff = (reopenCount: number, baseCooldownMs: number) => number;
627
+ interface ExponentialBackoffOptions {
628
+ /** Growth factor applied per repeat open, e.g. 2 doubles each time. */
629
+ multiplier: number;
630
+ /** Upper bound on the computed cooldown, in ms. Default `Infinity`. */
631
+ maxMs?: number;
632
+ }
633
+ /**
634
+ * Decides when a bucket's failures should open the circuit. Keyed by
635
+ * `key` (a resolved model, or the shared bucket's key when
636
+ * `isolateByModel` is off) rather than holding one flat counter, so a
637
+ * single `TrippingPolicy` instance is always safe to share across every
638
+ * bucket: `CircuitBreaker` never needs to clone or construct a fresh one
639
+ * per model, `isolateByModel` isolation falls out of `key` alone.
640
+ */
641
+ interface TrippingPolicy {
642
+ onSuccess(key: string): void;
643
+ /** Returns true if this failure should open the circuit for `key`. */
644
+ onFailure(key: string): boolean;
645
+ reset(key: string): void;
646
+ /**
647
+ * Called when `key`'s bucket is discarded (closed and idle, under
648
+ * `isolateByModel`), so a keyed policy can release that key's state.
649
+ * Optional: omit if there's nothing to release.
650
+ */
651
+ forget?(key: string): void;
652
+ }
653
+ declare class ConsecutiveTripping implements TrippingPolicy {
654
+ private readonly threshold;
655
+ private failuresByKey;
656
+ constructor(threshold: number);
657
+ onSuccess(key: string): void;
658
+ onFailure(key: string): boolean;
659
+ reset(key: string): void;
660
+ forget(key: string): void;
661
+ }
662
+ declare class RollingTripping implements TrippingPolicy {
663
+ private readonly windowMs;
664
+ private readonly minCalls;
665
+ private readonly failureRatio;
666
+ private ratiosByKey;
667
+ constructor(windowMs: number, minCalls: number, failureRatio: number);
668
+ private ratioFor;
669
+ onSuccess(key: string): void;
670
+ onFailure(key: string): boolean;
671
+ reset(key: string): void;
672
+ forget(key: string): void;
340
673
  }
674
+ /** Not exported. Internal shorthand union for `CircuitBreakerOptions.tripping`. */
675
+ type TrippingOption = {
676
+ kind: 'consecutive';
677
+ threshold: number;
678
+ } | {
679
+ kind: 'rolling';
680
+ windowMs: number;
681
+ minCalls: number;
682
+ failureRatio: number;
683
+ } | TrippingPolicy;
341
684
  type CircuitState = 'closed' | 'open' | 'half-open';
342
685
  /**
343
- * Per retry VernLLM-instance circuit breaker. Tracks consecutive failures across
344
- * calls. Once the threshold is hit, short-circuits new calls with an
345
- * LLMError('circuit_open') instead of hitting the provider, until the
346
- * cooldown elapses and a single trial call is allowed through
686
+ * Per retry VernLLM-instance circuit breaker. Tracks consecutive failures
687
+ * across calls. Once the threshold is hit, short-circuits new calls with
688
+ * LLMError('circuit_open') until the cooldown elapses and a trial succeeds.
347
689
  */
348
690
  declare class CircuitBreaker {
349
- private readonly threshold;
350
691
  private readonly cooldownMs;
351
692
  private readonly onStateChange?;
352
- /** Whether this breaker tracks failures per model instead of one shared circuit. Read by `CallExecutor`/`VernLLM` to report per-target in `getCircuitStates`. */
693
+ /** Whether this breaker tracks failures per model instead of one shared circuit. */
353
694
  readonly isolateByModel: boolean;
695
+ private readonly halfOpenProbes;
696
+ private readonly halfOpenSuccessRatio;
697
+ private readonly cooldownBackoff?;
698
+ /** One instance, keyed per model internally. See `TrippingPolicy`. */
699
+ private readonly tripping;
354
700
  private readonly sharedBucket;
355
701
  private readonly bucketsByModel;
356
702
  constructor(options?: CircuitBreakerOptions);
357
- /** Returns the bucket for a model if one already exists, without allocating. */
358
- private lookupBucket;
359
- /** Creates and stores a bucket for a model when the first mutation needs one. */
360
- private ensureBucketFor;
361
- /** Every state mutation routes through here, so `onStateChange` fires exactly once per real change. */
362
- private transition;
363
703
  /**
364
704
  * Throws if the circuit is open and the cooldown hasn't elapsed, or if
365
- * the circuit is half-open and a trial call is already in flight.
366
- * Otherwise, if the circuit just became eligible for a trial (cooldown
367
- * elapsed, or half-open with no trial currently running), this call
368
- * becomes that trial
369
- */
370
- assertClosed(model?: string): void;
371
- recordSuccess(model?: string): void;
372
- recordFailure(model?: string): void;
373
- /**
374
- * With `isolateByModel` off (the default), `model` is ignored and the
375
- * one shared circuit's state is returned, unchanged from every version
376
- * before this option existed. With `isolateByModel` on, returns that
377
- * model's own state, `'closed'` for a model never seen yet, same as a
378
- * fresh breaker.
705
+ * half-open with every trial slot claimed. Otherwise claims a trial slot.
379
706
  */
707
+ assertClosed(model?: string, context?: CircuitBreakerCallContext): void;
708
+ recordSuccess(model?: string, context?: CircuitBreakerCallContext): void;
709
+ /** `code`, when present, is the failing `LLMError`'s `code`. Missing attributes to `'unknown'`. */
710
+ recordFailure(model?: string, context?: CircuitBreakerCallContext, code?: LLMErrorCode): void;
711
+ /** With `isolateByModel` off, `model` is ignored and the shared circuit's state is returned. */
380
712
  getState(model?: string): CircuitState;
713
+ /** Failure counts by `LLMErrorCode` for `model`'s bucket. Returned as a plain object copy. */
714
+ getFailureBreakdown(model?: string): Partial<Record<LLMErrorCode | 'unknown', number>>;
715
+ /** Manually opens the circuit, as if `threshold` consecutive failures had just happened. */
716
+ open(model?: string, context?: CircuitBreakerCallContext): void;
717
+ /** Manually closes the circuit and resets its failure count, without requiring a real success first. */
718
+ close(model?: string, context?: CircuitBreakerCallContext): void;
719
+ /**
720
+ * Opens `bucket`: stamps `openedAt`/`cooldownMsForOpen` and transitions
721
+ * to `open`. Shared by `recordFailure`'s trip, `settleTrialIfComplete`'s
722
+ * reopen, and the manual `open()`, all of which reach this with
723
+ * `bucket.trial` already `null`.
724
+ */
725
+ private openBucket;
726
+ /** Computes and clamps the cooldown for `bucket`'s current `reopenCount`. Called once, on open. */
727
+ private computeCooldown;
728
+ /** Returns the bucket for a model if one already exists, without allocating. */
729
+ private lookupBucket;
381
730
  /**
382
- * Manually opens the circuit, as if `threshold` consecutive failures had
383
- * just happened, e.g. to pull a provider out of rotation ahead of known
384
- * maintenance. Resets the cooldown window from now, same as a real
385
- * threshold-crossing failure would, and clears any in-flight half-open
386
- * trial since it no longer applies once the circuit is (re)opened.
387
- */
388
- open(model?: string): void;
389
- /**
390
- * Manually closes the circuit and resets its failure count, e.g. once a
391
- * provider is confirmed healthy again without waiting out the cooldown.
392
- * Mirrors `recordSuccess`'s bookkeeping (including dropping the
393
- * per-model bucket under `isolateByModel`, once idle) but without
394
- * requiring an actual successful call first.
731
+ * The key `tripping` is called with. Real per-model isolation under
732
+ * `isolateByModel`, matching `ensureBucketFor`/`lookupBucket`'s own
733
+ * per-model key. Otherwise one fixed shared key regardless of what
734
+ * `model` was passed, matching `sharedBucket` being the one and only
735
+ * bucket in that mode: `model` is never allowed to split tripping state
736
+ * when `isolateByModel` is off, the same way it never splits which
737
+ * bucket a call lands in.
395
738
  */
396
- close(model?: string): void;
739
+ private trippingKeyFor;
740
+ /** Creates and stores a bucket for a model when the first mutation needs one. */
741
+ private ensureBucketFor;
742
+ /** Drops an idle model's bucket and lets `tripping` release that key's state too. */
743
+ private forgetModel;
744
+ /** Every state mutation routes through here, so `onStateChange` fires exactly once per real change. */
745
+ private transition;
746
+ /** Once every admitted trial has reported in, closes or reopens based on `halfOpenSuccessRatio`. */
747
+ private settleTrialIfComplete;
748
+ }
749
+ //#endregion
750
+ //#region src/internal/retryBudget.d.ts
751
+ /**
752
+ * Tunables for a `RetryBudget`. `windowMs`/`minCalls` behave the same as
753
+ * `RollingTripping`'s (see `circuitBreaker.ts`): `minCalls` gates the
754
+ * check so a cold start with too little traffic to judge doesn't trip.
755
+ * `retryRatio` is the max fraction of calls in the window allowed to be
756
+ * retries before the budget stops allowing more. `minCalls` must be a
757
+ * non-negative integer; `retryRatio` must be finite and within `[0, 1]`.
758
+ * Both are validated at construction, thrown as `RangeError`.
759
+ */
760
+ interface RetryBudgetOptions {
761
+ windowMs: number;
762
+ minCalls: number;
763
+ retryRatio: number;
764
+ }
765
+ /**
766
+ * Caps how much of a target's recent traffic is allowed to be retries,
767
+ * independent of the circuit breaker. The breaker asks whether the
768
+ * provider is healthy; this asks whether retrying is still worth the
769
+ * capacity it costs, regardless of provider health. Reuses `RollingRatio`,
770
+ * the same primitive `RollingTripping` is built on, rather than a second
771
+ * hand rolled window.
772
+ */
773
+ declare class RetryBudget {
774
+ private readonly options;
775
+ private readonly ratio;
776
+ constructor(options: RetryBudgetOptions);
777
+ /**
778
+ * Throws `LLMError('retry_budget_exhausted')` once at least `minCalls`
779
+ * calls have landed in the trailing `windowMs` and the retry ratio
780
+ * among them has reached `retryRatio`. A no-op otherwise.
781
+ */
782
+ assertAvailable(): void;
783
+ /** Records one attempt. `isRetry` is false for a call's first attempt, true for every attempt after it. */
784
+ recordAttempt(isRetry: boolean): void;
785
+ /** Current traffic and retry ratio in the trailing window. */
786
+ getSnapshot(): {
787
+ attempts: number;
788
+ retryRatio: number;
789
+ };
790
+ }
791
+ //#endregion
792
+ //#region src/internal/utils/rateLimitHint.utils.d.ts
793
+ /** A normalized read of a provider's rate limit headers. */
794
+ interface ProviderRateLimitHint {
795
+ remainingRequests?: number;
796
+ limitRequests?: number;
797
+ resetAfterMs?: number;
397
798
  }
398
-
399
799
  //#endregion
400
800
  //#region src/rateLimit.d.ts
401
- //# sourceMappingURL=circuitBreaker.d.ts.map
402
801
  /** The request shape sent to `LLMClient['chat']['completions']['create']`, used for token estimation. */
403
802
  type WireRequest = Parameters<LLMClient['chat']['completions']['create']>[0];
404
803
  /** Which configured bucket is currently blocking a call. */
@@ -427,6 +826,27 @@ interface RateLimitOptions {
427
826
  * chars/4 heuristic over message content plus `max_tokens`.
428
827
  */
429
828
  estimateTokens?: (request: WireRequest) => number;
829
+ /**
830
+ * AIMD against the `requestsPerMinute` bucket. Omit for a fixed
831
+ * ceiling, today's behavior. Requires `requestsPerMinute`.
832
+ */
833
+ aimd?: AimdOptions;
834
+ }
835
+ interface AimdOptions {
836
+ /** Added to the requests-per-minute ceiling on every clean release. */
837
+ increaseBy: number;
838
+ /** Multiplied against the ceiling on a rate-limit signal. Must be in `(0, 1]`; clamped otherwise. */
839
+ decreaseFactor: number;
840
+ /** Floor the ceiling never shrinks below. */
841
+ minCapacity: number;
842
+ /** Ceiling the bucket never grows above. */
843
+ maxCapacity: number;
844
+ /**
845
+ * Shrink proactively once a provider hint reports `remainingRequests`
846
+ * at or below this, before a real 429 happens. Default 0, meaning
847
+ * off.
848
+ */
849
+ proactiveFloor?: number;
430
850
  }
431
851
  interface RateLimitAcquireResult {
432
852
  /**
@@ -435,7 +855,7 @@ interface RateLimitAcquireResult {
435
855
  * Idempotent: only the first call does anything. Must run in a
436
856
  * `finally` block so a slot is never leaked on a failed attempt.
437
857
  */
438
- release: (actualTokens?: number) => void;
858
+ release: (actualTokens?: number, success?: boolean) => void;
439
859
  /** How long this attempt waited in queue before capacity was available. */
440
860
  waitedMs: number;
441
861
  /** Which bucket was blocking this attempt just before it cleared, if any wait happened. */
@@ -443,19 +863,35 @@ interface RateLimitAcquireResult {
443
863
  }
444
864
  /** Default `estimateTokens`: chars/4 over every message's content, plus the requested `max_tokens`. */
445
865
  declare function defaultEstimateTokens(request: WireRequest): number;
866
+ /**
867
+ * What VernLLM's dispatch layer needs from a limiter. `RateLimiter`
868
+ * implements this; a caller wanting cross-process coordination can hand
869
+ * over their own instance instead, see `buildRateLimit`. Every method is
870
+ * required, `RateLimiter` itself already no-ops the AIMD methods when
871
+ * `aimd` isn't configured, so a custom limiter follows the same pattern.
872
+ */
873
+ interface RateLimiterAdapter {
874
+ estimate(request: WireRequest): number;
875
+ acquire(estimatedTokens: number, signal?: AbortSignal): Promise<RateLimitAcquireResult>;
876
+ signalRateLimit(): void;
877
+ reactToRateLimitHint(hint: ProviderRateLimitHint | undefined): void;
878
+ }
446
879
  /**
447
880
  * Per-target rate limiter. Up to three buckets (requests/min, tokens/min,
448
881
  * concurrency) behind one FIFO queue, so a large call isn't starved by a
449
882
  * stream of small ones. Any bucket omitted from `options` has infinite
450
883
  * capacity and never blocks.
451
884
  */
452
- declare class RateLimiter {
885
+ declare class RateLimiter implements RateLimiterAdapter {
453
886
  private readonly requests?;
454
887
  private readonly tokens?;
455
888
  private readonly concurrency?;
889
+ /** Buckets in acquire precedence order (concurrency, rpm, tpm), omitted ones filtered out. Built once so order can't drift between `tryAcquireBuckets` and `scheduleWake`. */
890
+ private readonly buckets;
456
891
  private readonly maxQueueMs;
457
892
  private readonly maxQueueSize;
458
893
  private readonly estimateTokensFn;
894
+ private readonly aimd?;
459
895
  private readonly queue;
460
896
  /**
461
897
  * A single scheduled re-check for the head of the queue when it's
@@ -476,12 +912,7 @@ declare class RateLimiter {
476
912
  acquire(estimatedTokens: number, signal?: AbortSignal): Promise<RateLimitAcquireResult>;
477
913
  private queueFullError;
478
914
  private enqueue;
479
- /**
480
- * Checks and takes from every configured bucket as one atomic unit: if
481
- * any bucket lacks capacity, whatever was already taken from the
482
- * earlier ones in this attempt is rolled back before reporting which
483
- * bucket blocked.
484
- */
915
+ /** Takes from every configured bucket as one atomic unit, in `this.buckets`' order. Rolls back whatever was already taken if any bucket lacks capacity. */
485
916
  private tryAcquireBuckets;
486
917
  /** Drains the queue head first. Stops at the first waiter that still can't proceed, so no one is starved out of turn. */
487
918
  private drain;
@@ -498,13 +929,37 @@ declare class RateLimiter {
498
929
  * bucket is a real spend that only recovers via its own refill, and the
499
930
  * tokens bucket is reconciled against `actualTokens` rather than fully
500
931
  * refunded, since real tokens really were spent.
932
+ *
933
+ * `success` defaults to `false`: the AIMD ceiling only grows when the
934
+ * caller explicitly confirms a successful attempt. A failed or
935
+ * rate-limited attempt still releases its slot (so nothing leaks), but
936
+ * must not also grow the ceiling right back up after
937
+ * `signalRateLimit()` just shrank it.
501
938
  */
502
939
  private makeRelease;
940
+ /** Shared guard and resize call behind both AIMD halves below; only the arithmetic differs. */
941
+ private resizeRequestsCeiling;
942
+ /** AIMD's additive-increase half: grows the ceiling by `aimd.increaseBy` on a clean release. No-op without `aimd`/`requestsPerMinute`. */
943
+ private growOnSuccess;
944
+ /**
945
+ * AIMD's multiplicative-decrease half. Called on a real 429, and,
946
+ * where an adapter can produce a hint, proactively via
947
+ * `reactToRateLimitHint`. Never throws or blocks a call itself, only
948
+ * adjusts the ceiling as a side effect.
949
+ */
950
+ signalRateLimit(): void;
951
+ /**
952
+ * AIMD's proactive entry point: shrinks via `signalRateLimit()` if
953
+ * `hint.remainingRequests` is at or below `aimd.proactiveFloor`.
954
+ */
955
+ reactToRateLimitHint(hint: ProviderRateLimitHint | undefined): void;
503
956
  }
504
-
957
+ //#endregion
958
+ //#region src/internal/utils/rateLimitAdapter.utils.d.ts
959
+ /** Not exported. Internal shorthand only, so this union isn't duplicated between the public option fields and `buildRateLimit`'s own signature. */
960
+ type RateLimitOption = RateLimitOptions | RateLimiterAdapter;
505
961
  //#endregion
506
962
  //#region src/types/fallback.d.ts
507
- //# sourceMappingURL=rateLimit.d.ts.map
508
963
  /**
509
964
  * One provider to try after the primary (or after an earlier fallback
510
965
  * target) fails. Order is the policy: VernLLM never reorders, scores, or
@@ -513,11 +968,11 @@ declare class RateLimiter {
513
968
  * Most per-target overrides fall back to the parent `VernLLM` instance's
514
969
  * own option when omitted, so a target only needs to specify what's
515
970
  * actually different about it (a different client/model is the common
516
- * case). `circuitBreaker` and `rateLimit` are the exception: they are
517
- * never inherited from the parent, since a breaker or limiter tuned for
518
- * the primary provider's limits is rarely right for a fallback's. Leave
519
- * them unset on a target to run it without one, even if the parent has
520
- * one configured.
971
+ * case). `circuitBreaker`, `rateLimit`, and `retryBudget` are the
972
+ * exception: they are never inherited from the parent, since a breaker,
973
+ * limiter, or budget tuned for the primary provider's limits is rarely
974
+ * right for a fallback's. Leave them unset on a target to run it without
975
+ * one, even if the parent has one configured.
521
976
  */
522
977
  interface FallbackTarget {
523
978
  client: LLMClient;
@@ -536,7 +991,16 @@ interface FallbackTarget {
536
991
  /** This target's own circuit breaker, independent of every other target's. Not inherited from the parent's `circuitBreaker`. */
537
992
  circuitBreaker?: boolean | CircuitBreakerOptions;
538
993
  /** This target's own rate limiter, independent of every other target's. Not inherited from the parent's `rateLimit`. */
539
- rateLimit?: RateLimitOptions;
994
+ rateLimit?: RateLimitOption;
995
+ /** This target's own retry budget, independent of every other target's. Not inherited from the parent's `retryBudget`. */
996
+ retryBudget?: RetryBudgetOptions;
997
+ /**
998
+ * Reclassifies an otherwise-successful result from this target as a
999
+ * failure. Falls back to the parent `VernLLM` instance's own
1000
+ * `detectSoftFailure` when omitted, same as most other per-target
1001
+ * options (unlike `circuitBreaker`/`rateLimit`, which never inherit).
1002
+ */
1003
+ detectSoftFailure?: DetectSoftFailure;
540
1004
  }
541
1005
  /**
542
1006
  * Written into `CallParams['meta']` once `call()` resolves, so a caller
@@ -616,10 +1080,21 @@ declare class FallbackExhaustedError extends LLMError {
616
1080
  }
617
1081
  /** Narrows `err` to {@link FallbackExhaustedError}, for direct access to its `attempts` (`provider`/`model` per failed target) without a manual `instanceof` check. */
618
1082
  declare function isFallbackExhaustedError(err: unknown): err is FallbackExhaustedError;
619
-
1083
+ /**
1084
+ * Creates an empty ref box to pass as `CallParams['meta']`, so a caller can
1085
+ * read the `CallMeta` written by `call()` on the same line as the result
1086
+ * instead of pre-declaring a `{ current?: CallMeta }` by hand.
1087
+ *
1088
+ * @example
1089
+ * const meta = metaRef();
1090
+ * const result = await vern.call({ userContent: '...', meta });
1091
+ * meta.current?.provider;
1092
+ */
1093
+ declare function metaRef(): {
1094
+ current?: CallMeta;
1095
+ };
620
1096
  //#endregion
621
1097
  //#region src/types/schema.d.ts
622
- //# sourceMappingURL=fallback.d.ts.map
623
1098
  /**
624
1099
  * Minimal structural type for a Zod-like schema, so this package doesnt need
625
1100
  * a hard dependency on a specific Zod major version. Any object exposing
@@ -649,10 +1124,8 @@ interface JsonSchemaSpec {
649
1124
  strict?: boolean;
650
1125
  description?: string;
651
1126
  }
652
-
653
1127
  //#endregion
654
1128
  //#region src/types/tools.d.ts
655
- //# sourceMappingURL=schema.d.ts.map
656
1129
  /**
657
1130
  * Describes a capability the model may request, not the capability
658
1131
  * itself. VernLLM transports this to the provider and parses what comes
@@ -687,7 +1160,7 @@ interface ToolDefinition<Name extends string = string, Args = unknown> {
687
1160
  * to `string` unless annotated `as const`, which silently defeats
688
1161
  * `ToolCall` narrowing the moment a second tool is added to the same
689
1162
  * `tools: [...]` array (single-tool arrays still narrow fine even without
690
- * this, since there's nothing to discriminate against but that stops
1163
+ * this, since there's nothing to discriminate against but that stops
691
1164
  * being true as soon as a second tool shows up). Wrapping the same object
692
1165
  * in `defineTool()` preserves the literal `name` type without requiring
693
1166
  * `as const` at every call site.
@@ -704,7 +1177,7 @@ type ToolCallFor<T> = T extends ToolDefinition<infer N, infer A> ? {
704
1177
  *
705
1178
  * When `Tools` is a literal tuple (e.g. inferred from `tools: [getWeather,
706
1179
  * cancelOrder]` at a `call()`/`cachedCall()` site), this is a discriminated
707
- * union keyed by `name` checking `call.name === 'get_weather'` narrows
1180
+ * union keyed by `name`. Checking `call.name === 'get_weather'` narrows
708
1181
  * `call.arguments` to that tool's `Args` with no cast needed. Without a
709
1182
  * literal `Tools` (the default), this collapses back to today's
710
1183
  * `{ id: string; name: string; arguments: unknown }`.
@@ -803,7 +1276,6 @@ declare function isToolCallResult<Tools extends readonly ToolDefinition[] | unde
803
1276
  type ToolChoice = 'auto' | 'none' | 'required' | {
804
1277
  name: string;
805
1278
  };
806
-
807
1279
  //#endregion
808
1280
  //#region src/types/usage.d.ts
809
1281
  type ReserveUsage = (params: {
@@ -869,10 +1341,8 @@ type OnUsage = (usage: TokenUsage) => void;
869
1341
  * to report.
870
1342
  */
871
1343
  type OnUsageFailure = (usage: TokenUsage, error: LLMError) => void;
872
-
873
1344
  //#endregion
874
1345
  //#region src/types/call.d.ts
875
- //# sourceMappingURL=usage.d.ts.map
876
1346
  /**
877
1347
  * Any valid JSON value: a primitive, `null`, or a JSON array/object made
878
1348
  * of the same. This is what `call()` returns when `jsonMode: true`.
@@ -928,13 +1398,21 @@ interface ImageBlock {
928
1398
  }
929
1399
  /** A single segment of multimodal `userContent`. */
930
1400
  type ContentBlock = TextBlock | ImageBlock;
931
- interface CallParams<T = unknown, Tools extends readonly ToolDefinition[] = ToolDefinition[]> extends UsageHooks {
1401
+ /**
1402
+ * Every field of a call request except the `reserveUsage`/`refundUsage`
1403
+ * hooks from `UsageHooks`. `CallParams` is this plus `UsageHooks`; the
1404
+ * `Cached*` param types below are call sites that want the request shape
1405
+ * without those two hooks (usage is metered once, at the `cachedCall`
1406
+ * level, not per-request), and use this directly instead of re-deriving
1407
+ * it with `Omit<CallParams<T>, 'reserveUsage' | 'refundUsage'>` each time.
1408
+ */
1409
+ interface LLMRequestShape<T = unknown, Tools extends readonly ToolDefinition[] = ToolDefinition[]> {
932
1410
  systemPrompt?: string;
933
1411
  /** Current user message, as text or multimodal content blocks. */
934
1412
  userContent: string | ContentBlock[];
935
1413
  /**
936
1414
  * Previous conversation turns. Must alternate roles; tool turns must follow
937
- * assistant tool calls. Invalid history throws LLMError('validation').
1415
+ * assistant tool calls. Invalid history throws LLMError('invalid_params').
938
1416
  */
939
1417
  history?: ConversationTurn[];
940
1418
  /**
@@ -947,6 +1425,20 @@ interface CallParams<T = unknown, Tools extends readonly ToolDefinition[] = Tool
947
1425
  maxTokens?: number;
948
1426
  requestId?: string;
949
1427
  signal?: AbortSignal;
1428
+ /**
1429
+ * Total time budget in ms for this whole call, across every retry and
1430
+ * every fallback target. Unlike timeoutMs, which resets on each attempt,
1431
+ * this is a single clock starting when call is invoked. The call is
1432
+ * aborted once this elapses, even mid retry or mid fallback, the same
1433
+ * way an aborted signal is today. Omit for no overall deadline, only
1434
+ * the existing per attempt timeoutMs applies.
1435
+ *
1436
+ * Only bounds getting to a final result: choosing a target, retrying,
1437
+ * and opening a stream. It does not extend to the time spent reading a
1438
+ * stream after it has opened. Use chunkIdleTimeoutMs for gaps between
1439
+ * chunks once a stream is open.
1440
+ */
1441
+ deadlineMs?: number;
950
1442
  /**
951
1443
  * Per-call override for the instance's `chunkIdleTimeoutMs` (max gap
952
1444
  * between stream chunks once opened). Only applies when `stream: true`.
@@ -1019,15 +1511,24 @@ interface CallParams<T = unknown, Tools extends readonly ToolDefinition[] = Tool
1019
1511
  * Optional out-parameter for provider identity. Pass `{}` (or any object
1020
1512
  * with a mutable `current` property) and `call()` writes a `CallMeta`
1021
1513
  * into `meta.current` before returning, alongside whatever `onUsage`
1022
- * already reports. Ignored for `stream: true`, since `call()` returns
1023
- * before the outcome (and so the target that answered) is known; read
1024
- * `TokenUsage.provider`/`usedFallback` from `onUsage` for streaming
1025
- * calls instead.
1514
+ * already reports. This includes `stream: true`: the target is chosen
1515
+ * once the stream opens, which is also the point `call()` itself
1516
+ * returns `{ chunks, finalResult }`, so `meta.current` is already set
1517
+ * by then. `TokenUsage.provider`/`usedFallback` from `onUsage` reports
1518
+ * the same information asynchronously, for both streaming and
1519
+ * non-streaming calls.
1520
+ *
1521
+ * `meta.current` is only written once execution actually reaches and
1522
+ * selects a provider target. A `wrap` middleware that short-circuits
1523
+ * without calling `next()` never reaches that point, so `meta.current`
1524
+ * is left untouched; if the same holder object is reused across calls,
1525
+ * it can still hold a prior call's target.
1026
1526
  */
1027
1527
  meta?: {
1028
1528
  current?: CallMeta;
1029
1529
  };
1030
1530
  }
1531
+ interface CallParams<T = unknown, Tools extends readonly ToolDefinition[] = ToolDefinition[]> extends LLMRequestShape<T, Tools>, UsageHooks {}
1031
1532
  /**
1032
1533
  * A `CallParams` variant where tool calling is explicitly enabled.
1033
1534
  *
@@ -1119,7 +1620,7 @@ interface CachedCallInput extends UsageHooks {
1119
1620
  * the caching docs for why.
1120
1621
  */
1121
1622
  type CachedCallParams<T> = CachedCallInput & {
1122
- call: Omit<CallParams<T>, 'reserveUsage' | 'refundUsage'>;
1623
+ call: LLMRequestShape<T>;
1123
1624
  };
1124
1625
  /**
1125
1626
  * Parameters for a cached LLM call with tool calling enabled.
@@ -1132,7 +1633,9 @@ type CachedCallParams<T> = CachedCallInput & {
1132
1633
  * from `call`'s type here too.
1133
1634
  */
1134
1635
  type CachedToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedCallInput & {
1135
- call: Omit<ToolEnabledCallParams<T, Tools>, 'reserveUsage' | 'refundUsage'>;
1636
+ call: LLMRequestShape<T, Tools> & {
1637
+ tools: NonNullable<LLMRequestShape<T, Tools>['tools']>;
1638
+ };
1136
1639
  };
1137
1640
  /**
1138
1641
  * Parameters for a cached LLM call with `call.tools` set conditionally.
@@ -1141,7 +1644,9 @@ type CachedToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefin
1141
1644
  * `ConditionalToolCallParams` for why this overload exists.
1142
1645
  */
1143
1646
  type CachedConditionalToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedCallInput & {
1144
- call: Omit<ConditionalToolCallParams<T, Tools>, 'reserveUsage' | 'refundUsage'>;
1647
+ call: LLMRequestShape<T, Tools> & {
1648
+ tools: Tools | undefined;
1649
+ };
1145
1650
  };
1146
1651
  /** Cached conditional tool-call parameters whose non-tool result is plain text. */
1147
1652
  type CachedConditionalStringToolCallParams<Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedConditionalToolCallParams<string, Tools> & {
@@ -1154,19 +1659,41 @@ type CachedConditionalStringToolCallParams<Tools extends readonly ToolDefinition
1154
1659
  * `cachedCall()` overload that returns a plain `string`.
1155
1660
  */
1156
1661
  type CachedJsonModeDisabledCallParams = CachedCallInput & {
1157
- call: Omit<JsonModeDisabledCallParams, 'reserveUsage' | 'refundUsage'>;
1662
+ call: Omit<LLMRequestShape<unknown>, 'jsonSchema'> & {
1663
+ jsonMode: false;
1664
+ jsonSchema?: never;
1665
+ };
1158
1666
  };
1159
1667
  /**
1160
1668
  * Parameters for a cached LLM call with `jsonMode: true` and no `schema`.
1161
1669
  * Selects the `cachedCall()` overload that returns a `JsonValue`.
1162
1670
  */
1163
1671
  type CachedJsonModeEnabledCallParams = CachedCallInput & {
1164
- call: Omit<JsonModeEnabledCallParams, 'reserveUsage' | 'refundUsage'>;
1672
+ call: Omit<LLMRequestShape<JsonValue>, 'schema'> & {
1673
+ jsonMode: true;
1674
+ schema?: never;
1675
+ };
1165
1676
  };
1166
-
1677
+ /** Context handed to `DetectSoftFailure` alongside the response it's inspecting. */
1678
+ interface SoftFailureMeta {
1679
+ requestId: string;
1680
+ model: string;
1681
+ providerName: string;
1682
+ isFallback: boolean;
1683
+ /** 1-based, matching `CallMeta.attempts`. */
1684
+ attempt: number;
1685
+ }
1686
+ /**
1687
+ * Inspects an otherwise-successful result and optionally reclassifies it
1688
+ * as a failure. Returning `undefined` leaves the result as a success;
1689
+ * returning an `LLMErrorCode` fails the attempt with that code, feeding
1690
+ * the same retry and circuit-breaker paths a thrown error would. A
1691
+ * result that parses fine but is empty, truncated, or a low-confidence
1692
+ * refusal is otherwise invisible to both.
1693
+ */
1694
+ type DetectSoftFailure<T = unknown> = (result: T | CallWithToolsResult<T>, meta: SoftFailureMeta) => LLMErrorCode | undefined;
1167
1695
  //#endregion
1168
1696
  //#region src/types/stream.d.ts
1169
- //# sourceMappingURL=call.d.ts.map
1170
1697
  /** One incremental unit of a streaming response, as delivered to the caller. */
1171
1698
  type StreamChunk = {
1172
1699
  type: 'text-delta';
@@ -1271,6 +1798,18 @@ type WireStreamChunk = {
1271
1798
  * Never surfaced to callers as a `StreamChunk`.
1272
1799
  */
1273
1800
  type: 'ping';
1801
+ } | {
1802
+ /**
1803
+ * AIMD's proactive rate-limit hint, read off the stream's
1804
+ * response headers (where the adapter/SDK can get at them) and
1805
+ * yielded once, as early as possible. Mirrors `attachRateLimitHint`
1806
+ * for the non-streaming path, just carried as a chunk instead of a
1807
+ * hidden property on a response object, since a stream has no
1808
+ * single response value to attach one to. Never surfaced to
1809
+ * callers as a `StreamChunk`.
1810
+ */
1811
+ type: 'rate_limit_hint';
1812
+ hint: ProviderRateLimitHint;
1274
1813
  };
1275
1814
  /**
1276
1815
  * Parameters for a cached, streaming LLM call without tool calling.
@@ -1282,10 +1821,12 @@ type WireStreamChunk = {
1282
1821
  * like).
1283
1822
  *
1284
1823
  * `reserveUsage`/`refundUsage` are omitted from `call`'s type; see
1285
- * `CachedCallParams` for why they belong at the top level here too.
1824
+ * `CachedCallParams` for why they belong at the top level here too.
1286
1825
  */
1287
1826
  type CachedStreamCallParams<T> = CachedCallInput & {
1288
- call: Omit<StreamEnabledCallParams<T>, 'reserveUsage' | 'refundUsage'>;
1827
+ call: LLMRequestShape<T> & {
1828
+ stream: true;
1829
+ };
1289
1830
  };
1290
1831
  /**
1291
1832
  * Parameters for a cached, streaming LLM call with tool calling enabled.
@@ -1295,7 +1836,10 @@ type CachedStreamCallParams<T> = CachedCallInput & {
1295
1836
  * replayed-chunks-on-hit behavior as `CachedStreamCallParams<T>`.
1296
1837
  */
1297
1838
  type CachedStreamToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedCallInput & {
1298
- call: Omit<StreamEnabledCallParams<T, Tools> & ToolEnabledCallParams<T, Tools>, 'reserveUsage' | 'refundUsage'>;
1839
+ call: LLMRequestShape<T, Tools> & {
1840
+ stream: true;
1841
+ tools: NonNullable<LLMRequestShape<T, Tools>['tools']>;
1842
+ };
1299
1843
  };
1300
1844
  /**
1301
1845
  * Parameters for a cached, streaming LLM call with `call.tools` set
@@ -1305,7 +1849,10 @@ type CachedStreamToolCallParams<T, Tools extends readonly ToolDefinition[] = Too
1305
1849
  * `ConditionalToolCallParams` for why this overload exists.
1306
1850
  */
1307
1851
  type CachedStreamConditionalToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedCallInput & {
1308
- call: Omit<StreamEnabledCallParams<T, Tools> & ConditionalToolCallParams<T, Tools>, 'reserveUsage' | 'refundUsage'>;
1852
+ call: LLMRequestShape<T, Tools> & {
1853
+ stream: true;
1854
+ tools: Tools | undefined;
1855
+ };
1309
1856
  };
1310
1857
  /** Cached streaming conditional tool-call parameters whose non-tool result is text. */
1311
1858
  type CachedStreamConditionalStringToolCallParams<Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedStreamConditionalToolCallParams<string, Tools> & {
@@ -1319,7 +1866,11 @@ type CachedStreamConditionalStringToolCallParams<Tools extends readonly ToolDefi
1319
1866
  * cached value (on a hit) is a plain `string`.
1320
1867
  */
1321
1868
  type CachedStreamJsonModeDisabledCallParams = CachedCallInput & {
1322
- call: Omit<StreamJsonModeDisabledCallParams, 'reserveUsage' | 'refundUsage'>;
1869
+ call: Omit<LLMRequestShape<unknown>, 'jsonSchema'> & {
1870
+ stream: true;
1871
+ jsonMode: false;
1872
+ jsonSchema?: never;
1873
+ };
1323
1874
  };
1324
1875
  /**
1325
1876
  * Parameters for a cached, streaming LLM call with `jsonMode: true` and no
@@ -1327,12 +1878,14 @@ type CachedStreamJsonModeDisabledCallParams = CachedCallInput & {
1327
1878
  * miss) or cached value (on a hit) is a `JsonValue`.
1328
1879
  */
1329
1880
  type CachedStreamJsonModeEnabledCallParams = CachedCallInput & {
1330
- call: Omit<StreamJsonModeEnabledCallParams, 'reserveUsage' | 'refundUsage'>;
1881
+ call: Omit<LLMRequestShape<JsonValue>, 'schema'> & {
1882
+ stream: true;
1883
+ jsonMode: true;
1884
+ schema?: never;
1885
+ };
1331
1886
  };
1332
-
1333
1887
  //#endregion
1334
1888
  //#region src/types/client.d.ts
1335
- //# sourceMappingURL=stream.d.ts.map
1336
1889
  /** A tool call as it appears on the wire, OpenAI's `function`-wrapped shape. */
1337
1890
  interface WireToolCall {
1338
1891
  id: string;
@@ -1462,10 +2015,20 @@ interface LLMClient {
1462
2015
  };
1463
2016
  };
1464
2017
  }
1465
-
2018
+ //#endregion
2019
+ //#region src/internal/utils/cacheAdapter.utils.d.ts
2020
+ /**
2021
+ * Not exported. Internal shorthand for `VernLLMOptions.cache`, so the
2022
+ * union isn't duplicated between that field and `buildCache`'s own
2023
+ * signature. A caller never writes this type by name, either a config
2024
+ * object literal or a real `CacheAdapter`.
2025
+ */
2026
+ type CacheOption = {
2027
+ maxSize?: number;
2028
+ eviction?: EvictionOption;
2029
+ } | CacheAdapter;
1466
2030
  //#endregion
1467
2031
  //#region src/logger.d.ts
1468
- //# sourceMappingURL=client.d.ts.map
1469
2032
  interface Logger {
1470
2033
  debug(message: string): void;
1471
2034
  warn(message: string): void;
@@ -1482,73 +2045,8 @@ declare class ConsoleLogger implements Logger {
1482
2045
  warn(message: string): void;
1483
2046
  error(message: string, meta?: Record<string, unknown>): void;
1484
2047
  }
1485
-
1486
- //#endregion
1487
- //#region src/types/events.d.ts
1488
- //# sourceMappingURL=logger.d.ts.map
1489
- /**
1490
- * Reports what happened during a call. Fire and forget, mirroring
1491
- * `onUsage`: the return value is never read and a throwing handler cannot
1492
- * change what the call does, only what gets reported about it.
1493
- */
1494
- type VernLLMEvent = {
1495
- kind: 'retry';
1496
- requestId: string;
1497
- provider: string;
1498
- /** The model actually resolved for this call (honors a per-call `model` override). */
1499
- model: string;
1500
- /** The 1-based retry ordinal (the 1st retry is `1`, not the overall attempt count). */
1501
- attempt: number;
1502
- maxRetries: number;
1503
- delayMs: number;
1504
- retryAfterHonored: boolean;
1505
- error: LLMError;
1506
- } | {
1507
- kind: 'circuit_state';
1508
- provider: string;
1509
- /**
1510
- * The model of the call that triggered this specific transition
1511
- * (whatever was passed to the `assertClosed`/`recordSuccess`/
1512
- * `recordFailure` call that caused it), not a property of the
1513
- * circuit itself: the breaker still counts failures across every
1514
- * model together, so a threshold crossing can be the sum of
1515
- * several different models' failures even though only the
1516
- * triggering call's `model` is reported here.
1517
- */
1518
- model: string;
1519
- from: CircuitState;
1520
- to: CircuitState;
1521
- consecutiveFailures: number;
1522
- } | {
1523
- kind: 'fallback';
1524
- requestId: string;
1525
- /** Provider name of the target that just failed. */
1526
- from: string;
1527
- /** Provider name of the target about to be tried next. */
1528
- to: string;
1529
- /** `-1` for the primary target, otherwise the index into `fallback`. */
1530
- fromIndex: number;
1531
- toIndex: number;
1532
- /** The normalized error that caused `from` to be abandoned. */
1533
- error: LLMError;
1534
- /** Time spent on `from`, including its own retries, before giving up. */
1535
- elapsedMs: number;
1536
- } | {
1537
- kind: 'rate_limited';
1538
- requestId: string;
1539
- provider: string;
1540
- /** The model actually resolved for this call (honors a per-call `model` override). */
1541
- model: string;
1542
- /** How long this attempt sat queued for capacity before it was let through. */
1543
- waitedMs: number;
1544
- /** Which configured bucket was blocking this attempt just before it cleared. */
1545
- reason: 'concurrency' | 'rpm' | 'tpm';
1546
- };
1547
- type OnEvent = (event: VernLLMEvent) => void;
1548
-
1549
2048
  //#endregion
1550
2049
  //#region src/types/options.d.ts
1551
- //# sourceMappingURL=events.d.ts.map
1552
2050
  interface VernLLMOptions {
1553
2051
  client: LLMClient;
1554
2052
  model: string;
@@ -1617,8 +2115,24 @@ interface VernLLMOptions {
1617
2115
  * (no redaction).
1618
2116
  */
1619
2117
  redact?: (text: string) => string;
1620
- /** Cache adapter for cachedCall. Defaults to an in-memory adapter */
1621
- cache?: CacheAdapter;
2118
+ /**
2119
+ * Cache for cachedCall. `{ maxSize, eviction }` configures the
2120
+ * built-in in-memory adapter (`eviction` default `'fifo'`). Pass a
2121
+ * `CacheAdapter` directly for a real backend. Default: in-memory,
2122
+ * maxSize 1000, fifo.
2123
+ */
2124
+ cache?: CacheOption;
2125
+ /**
2126
+ * Reclassifies an otherwise-successful result as a failure, e.g. a
2127
+ * response that parsed fine but came back empty or truncated. Runs
2128
+ * once per attempt, right after a response is validated. Returning
2129
+ * `undefined` leaves the result untouched; returning an
2130
+ * `LLMErrorCode` fails that attempt with it, feeding the same retry
2131
+ * and circuit-breaker paths a thrown error would. A throwing hook is
2132
+ * caught, logged, and treated as no soft failure, so a broken hook
2133
+ * degrades safely instead of failing every call.
2134
+ */
2135
+ detectSoftFailure?: DetectSoftFailure;
1622
2136
  /** HTTP status codes that should fail fast without retrying. Default [400, 401, 403, 404, 422] */
1623
2137
  nonRetryableStatus?: number[];
1624
2138
  /** Custom JSON parser. Must return undefined/null on failure. Default: JSON.parse wrapped in try/catch */
@@ -1657,8 +2171,22 @@ interface VernLLMOptions {
1657
2171
  * letting the provider reject them. Independent of the `Retry-After`
1658
2172
  * handling already applied to a provider 429: this avoids tripping the
1659
2173
  * limit in the first place. Omit for unlimited (the default).
2174
+ *
2175
+ * A plain config object builds an in-process limiter. Pass a
2176
+ * `RateLimiterAdapter` instead for cross-process coordination.
2177
+ */
2178
+ rateLimit?: RateLimitOption;
2179
+ /**
2180
+ * Caps how much of this target's recent traffic is allowed to be
2181
+ * retries, independent of `circuitBreaker`. Once at least `minCalls`
2182
+ * calls have landed in the trailing `windowMs` and the retry ratio
2183
+ * among them reaches `retryRatio`, further retries against this target
2184
+ * throw `LLMError('retry_budget_exhausted')` instead of retrying,
2185
+ * protecting the target's real capacity even while its breaker is
2186
+ * still closed. Omit for no budget (the default). Never inherited by
2187
+ * `fallback` targets, same as `circuitBreaker`/`rateLimit`.
1660
2188
  */
1661
- rateLimit?: RateLimitOptions;
2189
+ retryBudget?: RetryBudgetOptions;
1662
2190
  /**
1663
2191
  * Ordered targets tried after the primary, in order, once it (and its
1664
2192
  * own retries) is exhausted or abandoned. Order is the policy: VernLLM
@@ -1680,11 +2208,57 @@ interface VernLLMOptions {
1680
2208
  * and moves on for everything else.
1681
2209
  */
1682
2210
  fallbackOn?: FallbackOn;
2211
+ /**
2212
+ * Transforms outgoing requests and/or wraps whole logical calls,
2213
+ * without touching retry, circuit breaker, or fallback internals.
2214
+ * Defaults to an empty array. See `VernLLMMiddleware` for the four
2215
+ * available hooks (`transform`, `wrap`, `onEvent`, `enabled`).
2216
+ */
2217
+ middleware?: VernLLMMiddleware[];
2218
+ /**
2219
+ * Bounds `transform` and a function `enabled`, the same way every
2220
+ * other blocking operation in the package is already bounded.
2221
+ * Overridable per middleware via that entry's own `timeoutMs`.
2222
+ * `<= 0` means unbounded (no timer at all). Default 5000.
2223
+ */
2224
+ middlewareTimeoutMs?: number;
1683
2225
  }
1684
-
2226
+ //#endregion
2227
+ //#region src/types/createMiddleware.d.ts
2228
+ /**
2229
+ * `VernLLMMiddleware` plus `onError`, a convenience for the common "I
2230
+ * only care about failures" case. Everything else is passed through to
2231
+ * the resulting `VernLLMMiddleware` unchanged; setting `wrap` directly
2232
+ * alongside `onError` is an error, since `onError` builds its own `wrap`
2233
+ * under the hood, and building it around a `wrap` you also supplied
2234
+ * would silently drop one of the two.
2235
+ */
2236
+ type CreateMiddlewareOptions = Omit<VernLLMMiddleware, 'wrap'> & {
2237
+ wrap?: undefined;
2238
+ /**
2239
+ * Called with this call's terminal error, if it fails: the same error
2240
+ * `wrap`'s own `next()` would reject with. Never called on success,
2241
+ * and never called for a failure some *other* middleware's `wrap`
2242
+ * already swallowed by short-circuiting with its own `CallResult`.
2243
+ * The original error is always rethrown afterward, `onError` only
2244
+ * observes it, exactly like `onUsage`/`onEvent` elsewhere: a throwing
2245
+ * `onError` is discarded (not logged, this helper has no `Logger` of
2246
+ * its own to log through) and otherwise has no effect on the call.
2247
+ * `ctx` is `wrap`'s own pre-dispatch context (`onError` builds a `wrap`
2248
+ * under the hood), so it only describes the primary target.
2249
+ */
2250
+ onError?: (error: LLMError, ctx: PreDispatchContext) => void | Promise<void>;
2251
+ };
2252
+ /**
2253
+ * Builds a `VernLLMMiddleware` entry. Plain pass-through when `onError`
2254
+ * is omitted; when it's set, wraps it in a `wrap` that calls `next()`,
2255
+ * reports `onError` on a rejection, and always rethrows the original
2256
+ * error afterward, so `onError` never changes what the call itself
2257
+ * returns or throws, only what gets observed about it.
2258
+ */
2259
+ declare function createMiddleware(options: CreateMiddlewareOptions): VernLLMMiddleware;
1685
2260
  //#endregion
1686
2261
  //#region src/vernLLM.d.ts
1687
- //# sourceMappingURL=options.d.ts.map
1688
2262
  /**
1689
2263
  * A LLM call framework for resilience, observability and control. This is VernLLM!
1690
2264
  *
@@ -1698,21 +2272,37 @@ declare class VernLLM {
1698
2272
  /**
1699
2273
  * One `CallExecutor` per provider target: index 0 is the primary,
1700
2274
  * everything after it is a `fallback` target, in the order declared.
1701
- * Each owns its own request building, retry/timeout, circuit breaker,
1702
- * and rate limiter. `call()` walks this array in `runFallbackChain`,
1703
- * moving to the next entry only when `fallbackOn` says to.
2275
+ * Walked by `runFallbackChain`, moving to the next entry only when
2276
+ * `fallbackOn` says to.
1704
2277
  */
1705
2278
  private readonly executors;
1706
2279
  /** Decides whether a failed target is followed by the next one or the chain stops. See `VernLLMOptions['fallbackOn']`. */
1707
2280
  private readonly fallbackOn;
1708
2281
  /** Reports a `'fallback'` event when the chain moves to the next target. Shared `onEvent` plumbing, same as every executor's. */
1709
2282
  private readonly reportEvent;
2283
+ /** Owns cache reads/writes and in-flight coalescing for `cachedCall()`. Only calls back into `this.call()` as an opaque function. */
2284
+ private readonly cacheOrchestrator;
2285
+ /** See `VernLLMOptions.middleware`. Sorted once here by `priority`, ascending, ties broken by original array order. */
2286
+ private readonly middleware;
2287
+ /** See `VernLLMOptions.middlewareTimeoutMs`. Bounds `transform` and a function `enabled`; `wrap` itself is never bounded by this. */
2288
+ private readonly middlewareTimeoutMs;
1710
2289
  /**
1711
- * Owns cache key resolution, cache reads/writes, and in-flight
1712
- * coalescing for `cachedCall()`. Independent of `executor`: it only
1713
- * ever calls back into `this.call()` as an opaque function.
2290
+ * Maps `cachedCall()`'s inner `this.call(...)` params to its own
2291
+ * `middlewareState`, so that call's own `runOperation` skips wrapping
2292
+ * again and reuses the same state bag `wrap` just ran with (so a
2293
+ * value `wrap` sets is visible to `transform`, same as a direct
2294
+ * call). Keyed by object identity, not `requestId`, since two
2295
+ * concurrent `cachedCall()`s can share an explicit `requestId`.
1714
2296
  */
1715
- private readonly cacheOrchestrator;
2297
+ private readonly cachedCallInnerParams;
2298
+ /**
2299
+ * Shares one `CallMeta` holder across every `cachedCall()` in flight
2300
+ * for the same resolved cache key, so a joining invocation (never
2301
+ * calls `call()` itself) reports the trigger's real metadata instead
2302
+ * of `undefined`. A true cache hit never creates an entry, so it
2303
+ * still reports no metadata correctly.
2304
+ */
2305
+ private readonly cachedCallMeta;
1716
2306
  /**
1717
2307
  * @param options Client, model, and tunables. Defaults: `maxRetries` 1,
1718
2308
  * `timeoutMs` 25000, `baseDelayMs` 500, `defaultMaxTokens` 1000,
@@ -1722,72 +2312,23 @@ declare class VernLLM {
1722
2312
  constructor(options: VernLLMOptions);
1723
2313
  /** Logs a failed refundUsage attempt via the configured logger. */
1724
2314
  private logRefundError;
1725
- /**
1726
- * Walks `this.executors` in order, running `attempt` against each until
1727
- * one succeeds or every target has failed. `run` on a lone target
1728
- * (no `fallback` configured) throws exactly what it throws today: the
1729
- * loop's single iteration path is unchanged from pre-fallback behavior.
1730
- *
1731
- * For streaming, `attempt` is `executor.runStream`, whose own retries
1732
- * only cover *opening* the stream (see `CallExecutor.runStream`). A
1733
- * mid-stream failure surfaces through `finalResult` after this function
1734
- * has already returned, so it's never seen here and never falls over,
1735
- * per the streaming limitation: splicing a second model's output into a
1736
- * response the consumer has already partially rendered would corrupt
1737
- * it.
1738
- */
1739
- private runFallbackChain;
2315
+ /** Everything `executeLogicalCall`/`executeLogicalStreamCall` (in `logicalCall.ts`) need from this instance, gathered once so `call()` doesn't rebuild it per invocation. */
2316
+ private get logicalCallDependencies();
2317
+ /** Everything `runOperation` (in `runOperation.ts`) needs from this instance, gathered once so `call()`/`cachedCall()` don't rebuild it per invocation. */
2318
+ private get runOperationDependencies();
1740
2319
  /**
1741
2320
  * Makes a single logical LLM call, retrying on failure per the configured
1742
2321
  * policy. Fails fast if the breaker is open or the signal is already
1743
- * aborted. Rejects with a normalized LLMError on exhausted retries.
1744
- *
1745
- * When `tools` is set, returns a `CallWithToolsResult<T>` instead of `T`:
1746
- * `{ type: 'content', content }` or `{ type: 'tool_calls', toolCalls,
1747
- * content? }`. VernLLM never executes tools; run them yourself and
1748
- * continue via `history` (see `ConversationTurn`). Mutually exclusive
1749
- * with `jsonSchema`/`schema`.
1750
- *
1751
- * TypeScript picks the tools-aware overload (`CallWithToolsResult<T>`)
1752
- * when `tools` is a literal array on `params`, and the conditional-tools
1753
- * overload (`T | CallWithToolsResult<T>`, see `ConditionalToolCallParams`)
1754
- * when `tools` is present but statically `ToolDefinition[] | undefined`,
1755
- * e.g. `const tools = condition ? [myTool] : undefined`. Either way, use
1756
- * `isToolCallResult()` to narrow the result once `tools` isn't a literal
1757
- * array: TypeScript's static type can't know from the `ConditionalToolCallParams`
1758
- * shape alone whether tools actually ran on a given call. Only omitting
1759
- * `tools` entirely resolves to the plain `T` overload, since then tools
1760
- * genuinely cannot have run. See the Tool Calling docs for details.
2322
+ * aborted. Rejects with a normalized `LLMError` on exhausted retries.
1761
2323
  *
1762
- * The same static-vs-dynamic caveat applies to `stream`: TypeScript only
1763
- * selects the streaming overload (returning `StreamCallResult<...>`) when
1764
- * `stream: true` is statically present on `params`. A `stream` value set
1765
- * conditionally on a plain `CallParams<T>` still resolves to `Promise<T>`
1766
- * (or `Promise<CallWithToolsResult<T>>`) at the type level even though
1767
- * the actual runtime result is the `{ chunks, finalResult }` streaming
1768
- * shape whenever `stream` evaluates to `true`, callers doing this should
1769
- * narrow/cast accordingly rather than relying on the static return type.
1770
- *
1771
- * Pinning `T` explicitly (`call<string>(...)`) alongside a literal
1772
- * `tools` array loses per-tool `arguments` typing: TypeScript's own
1773
- * generic inference rules mean providing *any* explicit type argument
1774
- * suppresses inference for every subsequent type parameter in that call,
1775
- * `Tools` included, regardless of its `const` modifier or default. This
1776
- * isn't specific to this overload, it's true of all TypeScript generic
1777
- * calls with a partial explicit type argument list. Pass `Tools`
1778
- * explicitly too when pinning `T` this way, e.g. `call<string, typeof
1779
- * myTools>(...)`, or prefer inferring `T` from `schema`/`jsonSchema`
1780
- * instead (which doesn't touch the type argument list, so `Tools` still
1781
- * infers normally).
2324
+ * Supports `tools`, `stream`, and JSON mode/schema, in any combination.
2325
+ * See the Tool Calling and Streaming docs for return-shape details and
2326
+ * the TypeScript overloads that select between them.
1782
2327
  *
1783
2328
  * @param params System/user content plus per-call overrides. See `CallParams`.
1784
- * @returns Without `tools` or `stream`: the parsed response, or raw
1785
- * string if `jsonMode` is false. With `tools`: a `CallWithToolsResult<T>`,
1786
- * narrowed to `ContentResult<T>` when `toolChoice: 'none'` is set, since
1787
- * the model is then structurally barred from returning a `tool_calls`
1788
- * result. With `stream: true` (statically): a `{ chunks, finalResult }`
1789
- * `StreamCallResult`, `finalResult` resolving to whichever of the above
1790
- * shapes applies once the stream completes. See `StreamCallResult`.
2329
+ * @returns The parsed response (or raw string if `jsonMode` is false), a
2330
+ * `CallWithToolsResult<T>` when `tools` is set, or a `{ chunks,
2331
+ * finalResult }` `StreamCallResult` when `stream: true`. See `StreamCallResult`.
1791
2332
  */
1792
2333
  call<T = unknown>(params: StreamEnabledCallParams<T> & ToolsDisabledCallParams<T>): Promise<StreamCallResult<ContentResult<T>>>;
1793
2334
  call<T = unknown, const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: StreamEnabledCallParams<T, Tools> & ToolEnabledCallParams<T, Tools>): Promise<StreamCallResult<CallWithToolsResult<T, Tools>>>;
@@ -1821,35 +2362,19 @@ declare class VernLLM {
1821
2362
  deleteCache(key: string): Promise<void>;
1822
2363
  /**
1823
2364
  * Cache wrapper composing `call` + caching, so cached LLM calls
1824
- * automatically get retry/timeout/circuit-breaker behavior. `reserveUsage`/
1825
- * `refundUsage` are read from the top-level params only. Concurrent misses
1826
- * for the same `cacheKey` share a single in-flight call, avoiding cache
1827
- * stampedes. Supports `stream: true` and `tools` in any combination.
1828
- *
1829
- * When `call.tools` is set, this caches the whole `CallWithToolsResult`,
1830
- * including `tool_calls` results, not just final answers. Whether
1831
- * that's appropriate depends on the tool: caching "the model decided to
1832
- * call get_weather" is usually fine to reuse briefly, but caching a
1833
- * decision made under permissions or account state that can change
1834
- * between calls is not. Use a short `ttl` or a separate `cacheKey` for
1835
- * such tools if this distinction matters.
2365
+ * automatically get retry/timeout/circuit-breaker behavior. Concurrent
2366
+ * misses for the same `cacheKey` share a single in-flight call, avoiding
2367
+ * cache stampedes. Supports `stream: true` and `tools` in any combination.
1836
2368
  *
1837
- * There is no public way to cache an arbitrary non-LLM function through
1838
- * `VernLLM`. This method always composes with `call()`. For
1839
- * general-purpose caching unrelated to an LLM call, use a dedicated
1840
- * caching library at the application level instead.
2369
+ * When `call.tools` is set, this caches the whole result including any
2370
+ * `tool_calls` decision, not just final answers; use a short `ttl` or a
2371
+ * separate `cacheKey` if a tool's result shouldn't be reused across calls.
1841
2372
  *
1842
- * The same `T`-vs-`Tools` inference caveat documented on `call()` applies
1843
- * here too: pinning `T` explicitly (`cachedCall<string>(...)`) alongside
1844
- * a literal `call.tools` array loses per-tool `arguments` typing, pass
1845
- * `Tools` explicitly too in that case.
1846
- *
1847
- * @param params `cacheKey`, `ttl`, and optional
2373
+ * @param params `cacheKey`, `ttl`, optional
1848
2374
  * `reserveUsage`/`refundUsage`/`signal`, plus `call`, the `CallParams`
1849
- * (optionally with `tools` and/or `stream`) to pass through to
1850
- * `this.call(...)`. The top-level `signal` governs the cached operation
1851
- * and its usage hooks only; to also abort the underlying provider
1852
- * request, set `signal` inside `call`.
2375
+ * to pass through to `this.call(...)`. The top-level `signal` governs
2376
+ * the cached operation and its usage hooks only; to also abort the
2377
+ * underlying provider request, set `signal` inside `call`.
1853
2378
  * @returns The cached value on a hit, or the freshly-called result on a miss.
1854
2379
  */
1855
2380
  cachedCall<T, const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: CachedStreamToolCallParams<T, Tools>): Promise<StreamCallResult<CallWithToolsResult<T, Tools>>>;
@@ -1873,6 +2398,26 @@ declare class VernLLM {
1873
2398
  * target that doesn't exist.
1874
2399
  */
1875
2400
  getCircuitState(target?: CircuitTarget): CircuitState | undefined;
2401
+ /**
2402
+ * @param target.index Which target to read. Defaults to the primary.
2403
+ * @param target.model Which model bucket to read, if the target isolates by model.
2404
+ * @returns Failure counts by `LLMErrorCode`, `'unknown'` for a missing
2405
+ * code, or `undefined` if that target has no breaker.
2406
+ * @throws {RangeError} If `target.index` names no target.
2407
+ */
2408
+ getFailureBreakdown(target?: CircuitTarget): Partial<Record<LLMErrorCode | 'unknown', number>> | undefined;
2409
+ /**
2410
+ * @param target.index Which target to read. Defaults to the primary.
2411
+ * @returns This target's current retry traffic/ratio in the trailing
2412
+ * window, or `undefined` if that target has no retry budget
2413
+ * configured. A budget is target-scoped, not model-scoped, so unlike
2414
+ * `getFailureBreakdown` there's no `target.model` to pass.
2415
+ * @throws {RangeError} If `target.index` names no target.
2416
+ */
2417
+ getRetryBudgetState(target?: Pick<CircuitTarget, 'index'>): {
2418
+ attempts: number;
2419
+ retryRatio: number;
2420
+ } | undefined;
1876
2421
  /**
1877
2422
  * @param model Which model bucket to read, for targets that isolate by model.
1878
2423
  * @returns Every target's state, in chain order.
@@ -1896,11 +2441,9 @@ declare class VernLLM {
1896
2441
  * @throws {RangeError} If `target.index` names no target.
1897
2442
  */
1898
2443
  closeCircuit(target?: CircuitTarget): void;
1899
- /** Resolves a target index so every circuit-breaker method agrees on what counts as valid. */
1900
- private resolveExecutor;
1901
- /** Warns when `model` can't do anything on this target, so it's never silently ignored. */
1902
- private warnIfModelUnsupported;
1903
2444
  }
2445
+ //#endregion
2446
+ //#region src/paramsHelpers.d.ts
1904
2447
  /**
1905
2448
  * Identity function preserving `params`'s own precise type, unlike a `:
1906
2449
  * CallParams<T>` annotation, which would widen `tools` away and break the
@@ -1936,10 +2479,8 @@ declare function defineCallParams<P extends CallParams<unknown>>(params: P): P;
1936
2479
  * ```
1937
2480
  */
1938
2481
  declare function defineCachedCallParams<P extends CachedCallParams<unknown>>(params: P): P;
1939
-
1940
2482
  //#endregion
1941
2483
  //#region src/adapters/internal/sse.d.ts
1942
- //# sourceMappingURL=vernLLM.d.ts.map
1943
2484
  /**
1944
2485
  * Parses a Server-Sent-Events byte/text stream into the JSON payload of
1945
2486
  * each `data:` frame, in arrival order. Generic over transport: works with
@@ -1975,33 +2516,22 @@ declare function parseSseStream(source: AsyncIterable<Uint8Array | string>): Asy
1975
2516
  * alive" separately from a genuinely empty frame (`NO_DATA`, kept internal).
1976
2517
  */
1977
2518
  declare const SSE_PING: unique symbol;
1978
-
1979
2519
  //#endregion
1980
2520
  //#region src/adapters/internal/imageFormat.d.ts
1981
- //# sourceMappingURL=sse.d.ts.map
1982
2521
  /**
1983
2522
  * MIME types accepted for `ImageBlock.mimeType` across all adapters. This is
1984
2523
  * the intersection of what Anthropic, Gemini, OpenAI-compatible, and Bedrock
1985
2524
  * Converse all natively support, so a `ContentBlock[]` that validates for
1986
2525
  * one provider validates for all of them.
1987
2526
  */
1988
- declare const SUPPORTED_IMAGE_MIME_TYPES: readonly ["image/png", "image/jpeg", "image/gif", "image/webp"];
2527
+ declare const SUPPORTED_IMAGE_MIME_TYPES: readonly ['image/png', 'image/jpeg', 'image/gif', 'image/webp'];
1989
2528
  type SupportedImageMimeType = (typeof SUPPORTED_IMAGE_MIME_TYPES)[number];
1990
-
1991
2529
  //#endregion
1992
2530
  //#region src/adapters/internal/nativeStructuredOutput.d.ts
1993
- /**
1994
- * Validates an `ImageBlock.mimeType` against the shared supported set.
1995
- * Throws a non-retryable `LLMError('invalid_params')`, since an unsupported
1996
- * mimeType is a bug in the caller's own input, deterministic before any
1997
- * request is built, the same class of failure as every other check in
1998
- * `RequestBuilder`.
1999
- */
2000
-
2001
2531
  /**
2002
2532
  * A static allow-list or predicate naming which models support native,
2003
- * schema-constrained output as its own request field Anthropic's
2004
- * `output_config.format`, Bedrock's `outputConfig.textFormat` separate
2533
+ * schema-constrained output as its own request field. Anthropic's
2534
+ * `output_config.format` and Bedrock's `outputConfig.textFormat` separate
2005
2535
  * from `tools`/`tool_choice`, so it can be combined with real,
2006
2536
  * caller-supplied `tools` in the same request.
2007
2537
  *
@@ -2018,10 +2548,8 @@ type SupportedImageMimeType = (typeof SUPPORTED_IMAGE_MIME_TYPES)[number];
2018
2548
  * exactly this package's behavior before native support was added.
2019
2549
  */
2020
2550
  type ModelCapabilityOverride = string[] | ((model: string) => boolean);
2021
-
2022
2551
  //#endregion
2023
2552
  //#region src/adapters/internal/reasoningBudget.utils.d.ts
2024
- /** Resolves whether `model` is covered by a caller-supplied allow-list/predicate. */
2025
2553
  /**
2026
2554
  * Shared conversion between the two reasoning controls VernLLM exposes:
2027
2555
  * `reasoningEffort` (a tier string, OpenAI's native shape) and
@@ -2042,7 +2570,6 @@ type ModelCapabilityOverride = string[] | ((model: string) => boolean);
2042
2570
  * doesn't match these numbers.
2043
2571
  */
2044
2572
  type EffortTokenTable = Record<'minimal' | 'low' | 'medium' | 'high', number>;
2045
-
2046
2573
  //#endregion
2047
2574
  //#region src/adapters/anthropic.d.ts
2048
2575
  /** Anthropic's native per-block content shape for a message. */
@@ -2196,6 +2723,13 @@ interface AnthropicAdapterOptions {
2196
2723
  * predicate.
2197
2724
  */
2198
2725
  adaptiveOnlyModels?: ModelCapabilityOverride;
2726
+ /**
2727
+ * Whether the client's `messages.create` supports `.withResponse()`
2728
+ * (needed for AIMD's proactive path). Default `false`, since
2729
+ * `AnthropicClient` is structural and a test fake or thin wrapper
2730
+ * won't implement it.
2731
+ */
2732
+ supportsWithResponse?: boolean;
2199
2733
  }
2200
2734
  /**
2201
2735
  * Wraps an Anthropic SDK client so it satisfies the same `LLMClient`
@@ -2229,7 +2763,6 @@ interface AnthropicAdapterOptions {
2229
2763
  * `output_config.format` or a forced tool call).
2230
2764
  */
2231
2765
  declare function fromAnthropic(anthropicClient: AnthropicClient, options?: AnthropicAdapterOptions): LLMClient;
2232
-
2233
2766
  //#endregion
2234
2767
  //#region src/adapters/gemini.d.ts
2235
2768
  /**
@@ -2442,7 +2975,6 @@ interface GeminiAdapterOptions {
2442
2975
  thinkingLevelModels?: ModelCapabilityOverride;
2443
2976
  }
2444
2977
  declare function fromGemini(client: GeminiClient, options?: GeminiAdapterOptions): LLMClient;
2445
-
2446
2978
  //#endregion
2447
2979
  //#region src/adapters/bedrock.d.ts
2448
2980
  /** Bedrock Converse's supported inline image formats. */
@@ -2677,7 +3209,7 @@ type BedrockConverseStreamEvent = {
2677
3209
  interface BedrockAdapterOptions {
2678
3210
  /**
2679
3211
  * Optional preflight check for tool-use support, needed whenever a
2680
- * `jsonSchema` call ends up sending Converse `toolConfig` either the
3212
+ * `jsonSchema` call ends up sending Converse `toolConfig`, either the
2681
3213
  * legacy forced-single-tool-call emulation, or real `tools` sent
2682
3214
  * alongside native structured output (`outputConfig`). VernLLM never
2683
3215
  * guesses capability from a failed call's error message (AWS's error
@@ -2802,7 +3334,6 @@ interface AwsSendClient {
2802
3334
  * `create` branch above unwraps it.
2803
3335
  */
2804
3336
  declare function fromBedrock(bedrockClient: BedrockConverseClient | AwsSendClient, options?: BedrockAdapterOptions): LLMClient;
2805
-
2806
3337
  //#endregion
2807
3338
  //#region src/adapters/fetch.d.ts
2808
3339
  /** The chat-completion-shaped request VernLLM builds internally */
@@ -2920,6 +3451,11 @@ interface FetchAdapterConfig {
2920
3451
  * silently empty stream.
2921
3452
  */
2922
3453
  mapStreamEvent?: (event: unknown) => WireStreamChunk | WireStreamChunk[] | undefined;
3454
+ /**
3455
+ * Optional. How to read AIMD's proactive rate limit hint off a
3456
+ * successful response. Defaults to OpenAI's header set.
3457
+ */
3458
+ parseRateLimitHint?: (headers: ResponseLike['headers']) => ProviderRateLimitHint;
2923
3459
  }
2924
3460
  /**
2925
3461
  * A fetch-based escape hatch for providers with no SDK, or where pulling one
@@ -2944,7 +3480,6 @@ interface FetchAdapterConfig {
2944
3480
  * `mapStreamEvent` seam via `WireStreamChunk`'s `tool_call_delta` variant,
2945
3481
  * no separate config is needed for streaming vs non-streaming tool calls.
2946
3482
  *
2947
-
2948
3483
  * `createStream` requires `mapStreamEvent` (there's no non-streaming
2949
3484
  * response to fall back on, unlike the other three optional streaming
2950
3485
  * seams). It opens the request via `requestStream` (defaults to native
@@ -2961,7 +3496,6 @@ interface FetchAdapterConfig {
2961
3496
  * `fetch`.
2962
3497
  */
2963
3498
  declare function fromFetch(config: FetchAdapterConfig): LLMClient;
2964
-
2965
3499
  //#endregion
2966
3500
  //#region src/adapters/openaiCompatible.d.ts
2967
3501
  /**
@@ -3021,6 +3555,12 @@ interface OpenAICompatibleAdapterOptions {
3021
3555
  * effect when `reasoningEffort` is set directly.
3022
3556
  */
3023
3557
  reasoningEffortTokens?: Partial<EffortTokenTable>;
3558
+ /**
3559
+ * Whether the client's request builder supports `.withResponse()`
3560
+ * (needed for AIMD's proactive path). Default `false`, since not
3561
+ * every "OpenAI-compatible" client is confirmed to support it.
3562
+ */
3563
+ supportsWithResponse?: boolean;
3024
3564
  }
3025
3565
  declare function fromOpenAICompatible(client: unknown, options?: OpenAICompatibleAdapterOptions): LLMClient;
3026
3566
  /**
@@ -3030,11 +3570,15 @@ declare function fromOpenAICompatible(client: unknown, options?: OpenAICompatibl
3030
3570
  * variant) in ways that no longer structurally satisfy VernLLM's
3031
3571
  * provider-agnostic `ContentBlock[]` on `userContent`, so passing the SDK
3032
3572
  * instance directly can fail to typecheck depending on the installed
3033
- * `openai` version. Wrapping with `fromOpenAI()` (a plain alias of
3034
- * `fromOpenAICompatible()`) sidesteps that by translating through
3035
- * `unknown` at the boundary, and also picks up multimodal image
3036
- * translation and `createStream` wiring that a raw client doesn't have.
3037
- * See Migration Notes for details.
3573
+ * `openai` version. Wrapping with `fromOpenAI()` sidesteps that by
3574
+ * translating through `unknown` at the boundary, and also picks up
3575
+ * multimodal image translation and `createStream` wiring that a raw
3576
+ * client doesn't have. See Migration Notes for details.
3577
+ *
3578
+ * `supportsWithResponse` defaults to `false` here too:
3579
+ * `client` is `unknown`, so there's no way to verify it's really the
3580
+ * official `openai` package's client versus a fake or a test double.
3581
+ * Pass `supportsWithResponse: true` once you've confirmed it.
3038
3582
  */
3039
3583
  declare const fromOpenAI: typeof fromOpenAICompatible;
3040
3584
  /** Groqs SDK matches the OpenAI wire format */
@@ -3128,9 +3672,6 @@ declare const fromInfermatic: typeof fromOpenAICompatible;
3128
3672
  declare const fromAtlasCloud: typeof fromOpenAICompatible;
3129
3673
  /** 01.AI's (Yi models) API is OpenAI-compatible */
3130
3674
  declare const from01AI: typeof fromOpenAICompatible;
3131
-
3132
3675
  //#endregion
3133
- //# sourceMappingURL=openaiCompatible.d.ts.map
3134
-
3135
- export { AnthropicClient, AssistantContent, BedrockConverseClient, CacheAdapter, CachedCallParams, CachedConditionalToolCallParams, CachedJsonModeDisabledCallParams, CachedJsonModeEnabledCallParams, CachedStreamCallParams, CachedStreamConditionalToolCallParams, CachedStreamJsonModeDisabledCallParams, CachedStreamJsonModeEnabledCallParams, CachedStreamToolCallParams, CachedToolCallParams, CallMeta, CallParams, CallWithToolsResult, CircuitBreaker, CircuitBreakerOptions, CircuitState, CircuitTarget, ConditionalToolCallParams, ConsoleLogger, ContentBlock, ContentResult, ConversationTurn, DuplicateToolNamesIssue, FallbackAttempt, FallbackExhaustedError, FallbackOn, FallbackTarget, FetchAdapterConfig, GeminiClient, HistoryToolResultIssue, ImageBlock, InMemoryCacheAdapter, JsonModeDisabledCallParams, JsonModeEnabledCallParams, JsonSchemaSpec, JsonValue, LLMClient, LLMError, LLMErrorCode, LLMErrorIssuesByCode, LLMErrorSnapshot, LLMErrorType, LLMRequestSnapshot, Logger, NormalizedCacheAdapter, OnEvent, OnUsage, RateLimitAcquireResult, RateLimitOptions, RateLimitReason, RateLimiter, RefundUsage, ReserveUsage, RetryAttempt, SSE_PING, SchemaLike, StreamCallResult, StreamChunk, StreamEnabledCallParams, StreamJsonModeDisabledCallParams, StreamJsonModeEnabledCallParams, TargetCircuitState, TextBlock, TieredCacheAdapter, TokenUsage, ToolCall, ToolCallResult, ToolChoice, ToolDefinition, ToolEnabledCallParams, ToolIssue, ToolResult, ToolsDisabledCallParams, UnknownToolChoiceIssue, UnsupportedCapabilityIssue, VernLLM, VernLLMEvent, VernLLMOptions, WireMessage, WireRequest, WireStreamChunk, WireToolCall, WireToolChoice, defaultEstimateTokens, defaultFallbackOn, defineCachedCallParams, defineCallParams, defineTool, from01AI, fromAnthropic, fromAnyscale, fromAtlasCloud, fromBaseten, fromBedrock, fromCerebras, fromCloudflareWorkersAI, fromDeepInfra, fromDeepSeek, fromFeatherless, fromFetch, fromFireworks, fromFriendli, fromGemini, fromGroq, fromHyperbolic, fromInferenceNet, fromInfermatic, fromLMStudio, fromLambdaLabs, fromLepton, fromMiniMax, fromMistral, fromMoonshot, fromNebius, fromNovita, fromNvidiaNIM, fromOllama, fromOpenAI, fromOpenAICompatible, fromOpenRouter, fromParasail, fromPerplexity, fromSambaNova, fromSiliconFlow, fromSnowflakeCortex, fromStepFun, fromTogether, fromVLLM, fromVercelAIGateway, fromXAI, fromZhipu, hasIssues, isFallbackExhaustedError, isLLMError, isToolCallResult, parseSseStream };
3676
+ export { type AnthropicClient, type AssistantContent, type AttemptContext, type BedrockConverseClient, type CacheAdapter, type CachedCallParams, type CachedConditionalToolCallParams, type CachedJsonModeDisabledCallParams, type CachedJsonModeEnabledCallParams, type CachedStreamCallParams, type CachedStreamConditionalToolCallParams, type CachedStreamJsonModeDisabledCallParams, type CachedStreamJsonModeEnabledCallParams, type CachedStreamToolCallParams, type CachedToolCallParams, type CallMeta, type CallParams, type CallResult, type CallWithToolsResult, CircuitBreaker, type CircuitBreakerOptions, type CircuitState, type CircuitTarget, type ConditionalToolCallParams, ConsecutiveTripping, ConsoleLogger, type ContentBlock, type ContentResult, type ConversationTurn, type CooldownBackoff, type CreateMiddlewareOptions, type DuplicateToolNamesIssue, type EvictionOption, type ExponentialBackoffOptions, type FallbackAttempt, FallbackExhaustedError, type FallbackOn, type FallbackTarget, type FetchAdapterConfig, type GeminiClient, type HistoryToolResultIssue, type ImageBlock, InMemoryCacheAdapter, type JsonModeDisabledCallParams, type JsonModeEnabledCallParams, type JsonSchemaSpec, type JsonValue, type LLMClient, LLMError, type LLMErrorCode, type LLMErrorIssuesByCode, type LLMErrorSnapshot, type LLMErrorType, type LLMRequestShape, type LLMRequestSnapshot, type Logger, type MiddlewareCapabilities, type MiddlewareContext, type MiddlewareContextBase, type MiddlewareStateBag, type MiddlewareStateKey, NormalizedCacheAdapter, type OnEvent, type OnUsage, type PreDispatchContext, type RateLimitAcquireResult, type RateLimitOptions, type RateLimitReason, RateLimiter, type RateLimiterAdapter, type RefundUsage, type ReserveUsage, type RetryAttempt, RetryBudget, type RetryBudgetOptions, RollingTripping, SSE_PING, type SchemaLike, type StreamCallResult, type StreamChunk, type StreamEnabledCallParams, type StreamJsonModeDisabledCallParams, type StreamJsonModeEnabledCallParams, type TargetCircuitState, type TextBlock, TieredCacheAdapter, type TokenUsage, type ToolCall, type ToolCallResult, type ToolChoice, type ToolDefinition, type ToolEnabledCallParams, type ToolIssue, type ToolResult, type ToolsDisabledCallParams, type TrippingPolicy, type UnknownToolChoiceIssue, type UnsupportedCapabilityIssue, VernLLM, type VernLLMEvent, type VernLLMMiddleware, type VernLLMOptions, type WireCallRequest, type WireCallRequestPatch, type WireMessage, type WireRequest, type WireResponseFormat, type WireStreamChunk, type WireTool, type WireToolCall, type WireToolChoice, createMiddleware, createMiddlewareStateBag, createStateKey, defaultEstimateTokens, defaultFallbackOn, defineCachedCallParams, defineCallParams, defineTool, from01AI, fromAnthropic, fromAnyscale, fromAtlasCloud, fromBaseten, fromBedrock, fromCerebras, fromCloudflareWorkersAI, fromDeepInfra, fromDeepSeek, fromFeatherless, fromFetch, fromFireworks, fromFriendli, fromGemini, fromGroq, fromHyperbolic, fromInferenceNet, fromInfermatic, fromLMStudio, fromLambdaLabs, fromLepton, fromMiniMax, fromMistral, fromMoonshot, fromNebius, fromNovita, fromNvidiaNIM, fromOllama, fromOpenAI, fromOpenAICompatible, fromOpenRouter, fromParasail, fromPerplexity, fromSambaNova, fromSiliconFlow, fromSnowflakeCortex, fromStepFun, fromTogether, fromVLLM, fromVercelAIGateway, fromXAI, fromZhipu, hasIssues, isFallbackExhaustedError, isLLMError, isToolCallResult, metaRef, parseSseStream };
3136
3677
  //# sourceMappingURL=index.d.cts.map