vern-llm 2.4.2 → 2.5.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.ts CHANGED
@@ -7,7 +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';
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' | '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';
11
11
  /**
12
12
  * Tool contract codes: a model or provider response defect, not a
13
13
  * transient provider fault. Deterministic on the wire request, so
@@ -187,9 +187,10 @@ declare class LLMError extends Error {
187
187
  * `nonRetryableStatus` list. False for `parse`/`validation`/
188
188
  * `invalid_params`/`aborted` types (the caller's own input, the model's
189
189
  * 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.
190
+ * provider being unhealthy), the tool contract codes, the local
191
+ * rate limit codes, and the middleware timeout code.
192
+ * Subclasses (see `FallbackExhaustedError`) may override this when `type`
193
+ * alone carries no retry signal.
193
194
  */
194
195
  get retryable(): boolean;
195
196
  /**
@@ -303,8 +304,303 @@ declare class TieredCacheAdapter<T = unknown> implements CacheAdapter<T> {
303
304
  }
304
305
 
305
306
  //#endregion
306
- //#region src/circuitBreaker.d.ts
307
+ //#region src/types/events.d.ts
307
308
  //# sourceMappingURL=cache.d.ts.map
309
+ /**
310
+ * Reports what happened during a call. Fire and forget, mirroring
311
+ * `onUsage`: the return value is never read and a throwing handler cannot
312
+ * change what the call does, only what gets reported about it.
313
+ */
314
+ type VernLLMEvent = {
315
+ kind: 'retry';
316
+ requestId: string;
317
+ provider: string;
318
+ /** The model actually resolved for this call (honors a per-call `model` override). */
319
+ model: string;
320
+ /** The 1-based retry ordinal (the 1st retry is `1`, not the overall attempt count). */
321
+ attempt: number;
322
+ maxRetries: number;
323
+ delayMs: number;
324
+ retryAfterHonored: boolean;
325
+ error: LLMError;
326
+ } | {
327
+ kind: 'circuit_state';
328
+ provider: string;
329
+ /**
330
+ * The model of the call that triggered this specific transition
331
+ * (whatever was passed to the `assertClosed`/`recordSuccess`/
332
+ * `recordFailure` call that caused it), not a property of the
333
+ * circuit itself: the breaker still counts failures across every
334
+ * model together, so a threshold crossing can be the sum of
335
+ * several different models' failures even though only the
336
+ * triggering call's `model` is reported here.
337
+ */
338
+ model: string;
339
+ from: CircuitState;
340
+ to: CircuitState;
341
+ consecutiveFailures: number;
342
+ } | {
343
+ kind: 'fallback';
344
+ requestId: string;
345
+ /** Provider name of the target that just failed. */
346
+ from: string;
347
+ /** Provider name of the target about to be tried next. */
348
+ to: string;
349
+ /** `-1` for the primary target, otherwise the index into `fallback`. */
350
+ fromIndex: number;
351
+ toIndex: number;
352
+ /** The normalized error that caused `from` to be abandoned. */
353
+ error: LLMError;
354
+ /** Time spent on `from`, including its own retries, before giving up. */
355
+ elapsedMs: number;
356
+ } | {
357
+ kind: 'rate_limited';
358
+ requestId: string;
359
+ provider: string;
360
+ /** The model actually resolved for this call (honors a per-call `model` override). */
361
+ model: string;
362
+ /** How long this attempt sat queued for capacity before it was let through. */
363
+ waitedMs: number;
364
+ /** Which configured bucket was blocking this attempt just before it cleared. */
365
+ reason: 'concurrency' | 'rpm' | 'tpm';
366
+ } | {
367
+ kind: 'middleware';
368
+ requestId: string;
369
+ /** This middleware's `name`, or its array position if unnamed. */
370
+ middleware: string;
371
+ hook: 'transform' | 'wrap_short_circuit' | 'enabled_skip';
372
+ /** For `hook: 'transform'` only: which top-level fields the merged patch touched. */
373
+ patchedFields?: string[];
374
+ };
375
+ type OnEvent = (event: VernLLMEvent) => void;
376
+
377
+ //#endregion
378
+ //#region src/types/middleware.d.ts
379
+ //# sourceMappingURL=events.d.ts.map
380
+ /** Capabilities of the target a middleware hook is currently looking at. */
381
+ interface MiddlewareCapabilities {
382
+ /**
383
+ * Whether this target honors `response_format: { type: 'json_object' }`
384
+ * as a real constraint. Mirrors `LLMClient.supportsJsonObjectMode`.
385
+ * `false` for `fromAnthropic` and `fromBedrock`.
386
+ */
387
+ supportsJsonObjectMode: boolean;
388
+ }
389
+ /**
390
+ * A typed reference to one slot in `ctx.state`. Create one with
391
+ * `createStateKey`, export it, and import the same reference wherever
392
+ * another middleware needs to read or write the same value. There's no
393
+ * string key anywhere in this path, so a typo becomes a missing import
394
+ * or an undefined variable, a compile error, instead of a silently
395
+ * created new property.
396
+ */
397
+ interface MiddlewareStateKey<T> {
398
+ readonly debugName: string;
399
+ /**
400
+ * Never set at runtime; exists purely so `T` is actually used
401
+ * somewhere in this interface's shape (a phantom type), which is what
402
+ * lets `MiddlewareStateBag.get`/`set` infer the right type for a given
403
+ * key instead of two `MiddlewareStateKey<string>` and
404
+ * `MiddlewareStateKey<number>` keys being structurally identical.
405
+ */
406
+ readonly __phantom?: T;
407
+ }
408
+ /** Creates a new, distinct `MiddlewareStateKey`. `debugName` is used only in log lines and the `'middleware'` event; it never affects equality. */
409
+ declare function createStateKey<T>(debugName: string): MiddlewareStateKey<T>;
410
+ /**
411
+ * Typed, per-logical-call storage two middleware can deliberately share a
412
+ * value through (a span ID one sets, another reads). Backed by a plain
413
+ * `Map` internally, created once per logical call and never read or
414
+ * written by VernLLM itself.
415
+ */
416
+ interface MiddlewareStateBag {
417
+ get<T>(key: MiddlewareStateKey<T>): T | undefined;
418
+ set<T>(key: MiddlewareStateKey<T>, value: T): void;
419
+ }
420
+ /** A plain, `Map`-backed `MiddlewareStateBag`. */
421
+ declare function createMiddlewareStateBag(): MiddlewareStateBag;
422
+ /** Fields every `MiddlewareContext` variant carries, regardless of `stage`. */
423
+ interface MiddlewareContextBase {
424
+ requestId: string;
425
+ /** Capabilities of the target this stage's identity fields describe. */
426
+ capabilities: MiddlewareCapabilities;
427
+ signal?: AbortSignal;
428
+ /** Shared, collision-proof state for two middleware to deliberately coordinate through. See `MiddlewareStateBag`. */
429
+ state: MiddlewareStateBag;
430
+ /** Simple, string-keyed scratch space, pre-namespaced to this one middleware so two middleware can never collide here even by accident. */
431
+ own: Record<string, unknown>;
432
+ }
433
+ /**
434
+ * The `ctx` `transform` receives, and every attempt-scoped event context
435
+ * (`'retry'`, `'fallback'`, `'circuit_state'`, `'middleware'`). Built once
436
+ * a specific target has actually been selected for this attempt, so every
437
+ * field describes the real target, not a placeholder.
438
+ */
439
+ interface AttemptContext extends MiddlewareContextBase {
440
+ stage: 'attempt';
441
+ /** The target this attempt is actually dispatched to. */
442
+ requestedProvider: string;
443
+ requestedModel: string;
444
+ isFallbackAttempt: boolean;
445
+ /**
446
+ * The real, current attempt number for this dispatch.
447
+ *
448
+ * Exception: on a `'circuit_state'` event triggered by a pre-dispatch
449
+ * check (`assertClosed`, before any attempt has been made), this is
450
+ * `1` regardless of which attempt is about to run, since no attempt
451
+ * exists yet to report. Every other `'circuit_state'` event, and every
452
+ * other attempt-scoped event, reports the real attempt number.
453
+ */
454
+ attempt: number;
455
+ }
456
+ /**
457
+ * The `ctx` `wrap` receives before `next()` resolves (and `onError`'s own
458
+ * `ctx`, built the same way under the hood). Built once, before any
459
+ * fallback target is chosen, so it only ever describes the primary
460
+ * target. There is no real "requested" target yet, and no attempt count,
461
+ * fallback flag, or per-attempt capability to report. Read `next()`'s
462
+ * resolved `CallResult.meta` once you need to know what actually
463
+ * happened.
464
+ */
465
+ interface PreDispatchContext extends MiddlewareContextBase {
466
+ stage: 'pre-dispatch';
467
+ /** The primary target only, not necessarily who ends up answering. */
468
+ primaryProvider: string;
469
+ primaryModel: string;
470
+ }
471
+ /**
472
+ * `enabled` and `onEvent` are called from both stages (gating/observing
473
+ * `transform` as well as `wrap`), so they receive this union and must
474
+ * narrow on `ctx.stage` before reading stage-specific fields.
475
+ * `transform` and `wrap` themselves receive the single variant that's
476
+ * always accurate for them (`AttemptContext`/`PreDispatchContext`
477
+ * respectively). See `VernLLMMiddleware`.
478
+ */
479
+ type MiddlewareContext = AttemptContext | PreDispatchContext;
480
+ /** The `response_format` shape `RequestBuilder` can put on the wire. */
481
+ type WireResponseFormat = {
482
+ type: 'json_object';
483
+ } | {
484
+ type: 'json_schema';
485
+ json_schema: {
486
+ name: string;
487
+ schema: Record<string, unknown>;
488
+ strict?: boolean;
489
+ description?: string;
490
+ };
491
+ };
492
+ /** A tool as it appears on the wire, OpenAI's `function`-wrapped shape. */
493
+ interface WireTool {
494
+ type: 'function';
495
+ function: {
496
+ name: string;
497
+ description: string;
498
+ parameters: Record<string, unknown>;
499
+ };
500
+ }
501
+ /**
502
+ * The wire-shaped request `RequestBuilder.build()` produces for one call
503
+ * attempt, before dispatch. Read only inside `transform`; return a patch
504
+ * of the fields you want to change instead of the whole object.
505
+ */
506
+ interface WireCallRequest {
507
+ model: string;
508
+ temperature?: number;
509
+ max_tokens: number;
510
+ response_format?: WireResponseFormat;
511
+ reasoning_effort?: 'minimal' | 'low' | 'medium' | 'high';
512
+ budget_tokens?: number;
513
+ tools?: WireTool[];
514
+ tool_choice?: WireToolChoice;
515
+ messages: WireMessage[];
516
+ }
517
+ /**
518
+ * What `transform` returns: a patch merged onto the request that
519
+ * `RequestBuilder.build()` (plus every earlier middleware's own patch)
520
+ * already produced, not a replacement for it. `model` and
521
+ * `response_format` can't be expressed here at all, since everything
522
+ * downstream that attributes a call to a target keys off the values
523
+ * `RequestBuilder` already resolved for those two fields, not off
524
+ * whatever ends up on the wire request. `messages`/`tools` are joined by
525
+ * a separate `add*` field, appended rather than replaced, so two
526
+ * independently written middleware can each add to the list without one
527
+ * silently clobbering what the other already added.
528
+ */
529
+ interface WireCallRequestPatch {
530
+ temperature?: number;
531
+ max_tokens?: number;
532
+ reasoning_effort?: 'minimal' | 'low' | 'medium' | 'high';
533
+ budget_tokens?: number;
534
+ tool_choice?: WireToolChoice;
535
+ /** Replaces the whole message list. Prefer `addMessages` unless a full replace is genuinely the intent. */
536
+ messages?: WireMessage[];
537
+ /** Appended after whatever earlier middleware already added. Never clobbers a prior addition. */
538
+ addMessages?: WireMessage[];
539
+ /** Replaces the whole tool list. Prefer `addTools`, same reasoning as `messages`/`addMessages`. */
540
+ tools?: WireTool[];
541
+ /** Appended after whatever earlier middleware already added. Never clobbers a prior addition. */
542
+ addTools?: WireTool[];
543
+ }
544
+ /**
545
+ * The settled outcome of one logical call, passed to `wrap`'s `next()`.
546
+ * `meta` is populated once a target has actually answered, for both
547
+ * streaming and non-streaming calls (`undefined` only on a cache hit,
548
+ * where nothing was actually spent).
549
+ */
550
+ interface CallResult<T = unknown> {
551
+ value: T;
552
+ meta?: CallMeta;
553
+ }
554
+ /**
555
+ * One entry in `VernLLMOptions.middleware`. All four hooks are optional;
556
+ * an entry that sets none of them is inert. See the middleware docs for
557
+ * how `transform`, `wrap`, `onEvent`, and `enabled` compose across
558
+ * several entries.
559
+ */
560
+ interface VernLLMMiddleware {
561
+ /** Used in log lines and the `'middleware'` event. Defaults to this entry's array position when omitted. */
562
+ name?: string;
563
+ /** Sort key for composition order, ascending, ties broken by array order. See the middleware docs for what "lower runs first" means for `wrap`. */
564
+ priority?: number;
565
+ /**
566
+ * Boolean for a static on/off switch, or a predicate evaluated per
567
+ * call. A throwing, rejecting, or timed-out predicate is logged and
568
+ * treated as `false` for that call.
569
+ */
570
+ enabled?: boolean | ((ctx: MiddlewareContext) => boolean | Promise<boolean>);
571
+ /** Per-middleware override of the instance-level `middlewareTimeoutMs`, applied to this entry's `transform` and function `enabled`. `<= 0` means unbounded (no timer at all). */
572
+ timeoutMs?: number;
573
+ /** 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. */
574
+ transform?: (request: Readonly<WireCallRequest>, ctx: AttemptContext) => WireCallRequestPatch | Promise<WireCallRequestPatch>;
575
+ /**
576
+ * Wraps one whole logical call, exactly once, regardless of how many
577
+ * retries or fallback targets ran underneath it. `ctx` is built once,
578
+ * before any fallback target is chosen, so it only describes the
579
+ * primary target. There is no `requestedProvider`/`isFallbackAttempt`/
580
+ * `attempt` to read here. Read `next()`'s resolved `CallResult.meta`
581
+ * for what actually happened.
582
+ */
583
+ wrap?: (request: Readonly<WireCallRequest>, next: () => Promise<CallResult>, ctx: PreDispatchContext) => Promise<CallResult>;
584
+ /** 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. */
585
+ onEvent?: (event: VernLLMEvent, ctx: MiddlewareContext) => void;
586
+ }
587
+
588
+ //#endregion
589
+ //#region src/circuitBreaker.d.ts
590
+ //# sourceMappingURL=middleware.d.ts.map
591
+ /** The call this mutation happened as part of, forwarded to `onStateChange` untouched. `CircuitBreaker` never inspects it. */
592
+ interface CircuitBreakerCallContext {
593
+ requestId: string;
594
+ state: MiddlewareStateBag;
595
+ signal?: AbortSignal;
596
+ /**
597
+ * The real, current attempt number for this dispatch, when the call
598
+ * site actually has one in scope (i.e. after a dispatch was made or
599
+ * failed). Omitted for calls that happen before any attempt exists,
600
+ * like `assertClosed`'s pre-dispatch check.
601
+ */
602
+ attempt?: number;
603
+ }
308
604
  interface CircuitBreakerOptions {
309
605
  /** Consecutive failures before the circuit opens, default 5 */
310
606
  threshold?: number;
@@ -323,7 +619,8 @@ interface CircuitBreakerOptions {
323
619
  * here. With `isolateByModel` on, it's exact: each model has its own
324
620
  * counter, so the transition really was caused solely by that model.
325
621
  */
326
- onStateChange?: (from: CircuitState, to: CircuitState, consecutiveFailures: number, model?: string) => void;
622
+ onStateChange?: (from: CircuitState, to: CircuitState, consecutiveFailures: number, model?: string, /** See `CircuitBreakerCallContext`. */
623
+ context?: CircuitBreakerCallContext) => void;
327
624
  /**
328
625
  * Track a separate circuit per resolved model instead of one shared
329
626
  * circuit for the whole instance. A failure on one model then never
@@ -367,9 +664,9 @@ declare class CircuitBreaker {
367
664
  * elapsed, or half-open with no trial currently running), this call
368
665
  * becomes that trial
369
666
  */
370
- assertClosed(model?: string): void;
371
- recordSuccess(model?: string): void;
372
- recordFailure(model?: string): void;
667
+ assertClosed(model?: string, context?: CircuitBreakerCallContext): void;
668
+ recordSuccess(model?: string, context?: CircuitBreakerCallContext): void;
669
+ recordFailure(model?: string, context?: CircuitBreakerCallContext): void;
373
670
  /**
374
671
  * With `isolateByModel` off (the default), `model` is ignored and the
375
672
  * one shared circuit's state is returned, unchanged from every version
@@ -385,7 +682,7 @@ declare class CircuitBreaker {
385
682
  * threshold-crossing failure would, and clears any in-flight half-open
386
683
  * trial since it no longer applies once the circuit is (re)opened.
387
684
  */
388
- open(model?: string): void;
685
+ open(model?: string, context?: CircuitBreakerCallContext): void;
389
686
  /**
390
687
  * Manually closes the circuit and resets its failure count, e.g. once a
391
688
  * provider is confirmed healthy again without waiting out the cooldown.
@@ -393,7 +690,7 @@ declare class CircuitBreaker {
393
690
  * per-model bucket under `isolateByModel`, once idle) but without
394
691
  * requiring an actual successful call first.
395
692
  */
396
- close(model?: string): void;
693
+ close(model?: string, context?: CircuitBreakerCallContext): void;
397
694
  }
398
695
 
399
696
  //#endregion
@@ -616,6 +913,19 @@ declare class FallbackExhaustedError extends LLMError {
616
913
  }
617
914
  /** Narrows `err` to {@link FallbackExhaustedError}, for direct access to its `attempts` (`provider`/`model` per failed target) without a manual `instanceof` check. */
618
915
  declare function isFallbackExhaustedError(err: unknown): err is FallbackExhaustedError;
916
+ /**
917
+ * Creates an empty ref box to pass as `CallParams['meta']`, so a caller can
918
+ * read the `CallMeta` written by `call()` on the same line as the result
919
+ * instead of pre-declaring a `{ current?: CallMeta }` by hand.
920
+ *
921
+ * @example
922
+ * const meta = metaRef();
923
+ * const result = await vern.call({ userContent: '...', meta });
924
+ * meta.current?.provider;
925
+ */
926
+ declare function metaRef(): {
927
+ current?: CallMeta;
928
+ };
619
929
 
620
930
  //#endregion
621
931
  //#region src/types/schema.d.ts
@@ -687,7 +997,7 @@ interface ToolDefinition<Name extends string = string, Args = unknown> {
687
997
  * to `string` unless annotated `as const`, which silently defeats
688
998
  * `ToolCall` narrowing the moment a second tool is added to the same
689
999
  * `tools: [...]` array (single-tool arrays still narrow fine even without
690
- * this, since there's nothing to discriminate against but that stops
1000
+ * this, since there's nothing to discriminate against but that stops
691
1001
  * being true as soon as a second tool shows up). Wrapping the same object
692
1002
  * in `defineTool()` preserves the literal `name` type without requiring
693
1003
  * `as const` at every call site.
@@ -704,7 +1014,7 @@ type ToolCallFor<T> = T extends ToolDefinition<infer N, infer A> ? {
704
1014
  *
705
1015
  * When `Tools` is a literal tuple (e.g. inferred from `tools: [getWeather,
706
1016
  * cancelOrder]` at a `call()`/`cachedCall()` site), this is a discriminated
707
- * union keyed by `name` checking `call.name === 'get_weather'` narrows
1017
+ * union keyed by `name`. Checking `call.name === 'get_weather'` narrows
708
1018
  * `call.arguments` to that tool's `Args` with no cast needed. Without a
709
1019
  * literal `Tools` (the default), this collapses back to today's
710
1020
  * `{ id: string; name: string; arguments: unknown }`.
@@ -928,13 +1238,21 @@ interface ImageBlock {
928
1238
  }
929
1239
  /** A single segment of multimodal `userContent`. */
930
1240
  type ContentBlock = TextBlock | ImageBlock;
931
- interface CallParams<T = unknown, Tools extends readonly ToolDefinition[] = ToolDefinition[]> extends UsageHooks {
1241
+ /**
1242
+ * Every field of a call request except the `reserveUsage`/`refundUsage`
1243
+ * hooks from `UsageHooks`. `CallParams` is this plus `UsageHooks`; the
1244
+ * `Cached*` param types below are call sites that want the request shape
1245
+ * without those two hooks (usage is metered once, at the `cachedCall`
1246
+ * level, not per-request), and use this directly instead of re-deriving
1247
+ * it with `Omit<CallParams<T>, 'reserveUsage' | 'refundUsage'>` each time.
1248
+ */
1249
+ interface LLMRequestShape<T = unknown, Tools extends readonly ToolDefinition[] = ToolDefinition[]> {
932
1250
  systemPrompt?: string;
933
1251
  /** Current user message, as text or multimodal content blocks. */
934
1252
  userContent: string | ContentBlock[];
935
1253
  /**
936
1254
  * Previous conversation turns. Must alternate roles; tool turns must follow
937
- * assistant tool calls. Invalid history throws LLMError('validation').
1255
+ * assistant tool calls. Invalid history throws LLMError('invalid_params').
938
1256
  */
939
1257
  history?: ConversationTurn[];
940
1258
  /**
@@ -947,6 +1265,20 @@ interface CallParams<T = unknown, Tools extends readonly ToolDefinition[] = Tool
947
1265
  maxTokens?: number;
948
1266
  requestId?: string;
949
1267
  signal?: AbortSignal;
1268
+ /**
1269
+ * Total time budget in ms for this whole call, across every retry and
1270
+ * every fallback target. Unlike timeoutMs, which resets on each attempt,
1271
+ * this is a single clock starting when call is invoked. The call is
1272
+ * aborted once this elapses, even mid retry or mid fallback, the same
1273
+ * way an aborted signal is today. Omit for no overall deadline, only
1274
+ * the existing per attempt timeoutMs applies.
1275
+ *
1276
+ * Only bounds getting to a final result: choosing a target, retrying,
1277
+ * and opening a stream. It does not extend to the time spent reading a
1278
+ * stream after it has opened. Use chunkIdleTimeoutMs for gaps between
1279
+ * chunks once a stream is open.
1280
+ */
1281
+ deadlineMs?: number;
950
1282
  /**
951
1283
  * Per-call override for the instance's `chunkIdleTimeoutMs` (max gap
952
1284
  * between stream chunks once opened). Only applies when `stream: true`.
@@ -1019,15 +1351,24 @@ interface CallParams<T = unknown, Tools extends readonly ToolDefinition[] = Tool
1019
1351
  * Optional out-parameter for provider identity. Pass `{}` (or any object
1020
1352
  * with a mutable `current` property) and `call()` writes a `CallMeta`
1021
1353
  * 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.
1354
+ * already reports. This includes `stream: true`: the target is chosen
1355
+ * once the stream opens, which is also the point `call()` itself
1356
+ * returns `{ chunks, finalResult }`, so `meta.current` is already set
1357
+ * by then. `TokenUsage.provider`/`usedFallback` from `onUsage` reports
1358
+ * the same information asynchronously, for both streaming and
1359
+ * non-streaming calls.
1360
+ *
1361
+ * `meta.current` is only written once execution actually reaches and
1362
+ * selects a provider target. A `wrap` middleware that short-circuits
1363
+ * without calling `next()` never reaches that point, so `meta.current`
1364
+ * is left untouched; if the same holder object is reused across calls,
1365
+ * it can still hold a prior call's target.
1026
1366
  */
1027
1367
  meta?: {
1028
1368
  current?: CallMeta;
1029
1369
  };
1030
1370
  }
1371
+ interface CallParams<T = unknown, Tools extends readonly ToolDefinition[] = ToolDefinition[]> extends LLMRequestShape<T, Tools>, UsageHooks {}
1031
1372
  /**
1032
1373
  * A `CallParams` variant where tool calling is explicitly enabled.
1033
1374
  *
@@ -1119,7 +1460,7 @@ interface CachedCallInput extends UsageHooks {
1119
1460
  * the caching docs for why.
1120
1461
  */
1121
1462
  type CachedCallParams<T> = CachedCallInput & {
1122
- call: Omit<CallParams<T>, 'reserveUsage' | 'refundUsage'>;
1463
+ call: LLMRequestShape<T>;
1123
1464
  };
1124
1465
  /**
1125
1466
  * Parameters for a cached LLM call with tool calling enabled.
@@ -1132,7 +1473,9 @@ type CachedCallParams<T> = CachedCallInput & {
1132
1473
  * from `call`'s type here too.
1133
1474
  */
1134
1475
  type CachedToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedCallInput & {
1135
- call: Omit<ToolEnabledCallParams<T, Tools>, 'reserveUsage' | 'refundUsage'>;
1476
+ call: LLMRequestShape<T, Tools> & {
1477
+ tools: NonNullable<LLMRequestShape<T, Tools>['tools']>;
1478
+ };
1136
1479
  };
1137
1480
  /**
1138
1481
  * Parameters for a cached LLM call with `call.tools` set conditionally.
@@ -1141,7 +1484,9 @@ type CachedToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefin
1141
1484
  * `ConditionalToolCallParams` for why this overload exists.
1142
1485
  */
1143
1486
  type CachedConditionalToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedCallInput & {
1144
- call: Omit<ConditionalToolCallParams<T, Tools>, 'reserveUsage' | 'refundUsage'>;
1487
+ call: LLMRequestShape<T, Tools> & {
1488
+ tools: Tools | undefined;
1489
+ };
1145
1490
  };
1146
1491
  /** Cached conditional tool-call parameters whose non-tool result is plain text. */
1147
1492
  type CachedConditionalStringToolCallParams<Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedConditionalToolCallParams<string, Tools> & {
@@ -1154,14 +1499,20 @@ type CachedConditionalStringToolCallParams<Tools extends readonly ToolDefinition
1154
1499
  * `cachedCall()` overload that returns a plain `string`.
1155
1500
  */
1156
1501
  type CachedJsonModeDisabledCallParams = CachedCallInput & {
1157
- call: Omit<JsonModeDisabledCallParams, 'reserveUsage' | 'refundUsage'>;
1502
+ call: Omit<LLMRequestShape<unknown>, 'jsonSchema'> & {
1503
+ jsonMode: false;
1504
+ jsonSchema?: never;
1505
+ };
1158
1506
  };
1159
1507
  /**
1160
1508
  * Parameters for a cached LLM call with `jsonMode: true` and no `schema`.
1161
1509
  * Selects the `cachedCall()` overload that returns a `JsonValue`.
1162
1510
  */
1163
1511
  type CachedJsonModeEnabledCallParams = CachedCallInput & {
1164
- call: Omit<JsonModeEnabledCallParams, 'reserveUsage' | 'refundUsage'>;
1512
+ call: Omit<LLMRequestShape<JsonValue>, 'schema'> & {
1513
+ jsonMode: true;
1514
+ schema?: never;
1515
+ };
1165
1516
  };
1166
1517
 
1167
1518
  //#endregion
@@ -1282,10 +1633,12 @@ type WireStreamChunk = {
1282
1633
  * like).
1283
1634
  *
1284
1635
  * `reserveUsage`/`refundUsage` are omitted from `call`'s type; see
1285
- * `CachedCallParams` for why they belong at the top level here too.
1636
+ * `CachedCallParams` for why they belong at the top level here too.
1286
1637
  */
1287
1638
  type CachedStreamCallParams<T> = CachedCallInput & {
1288
- call: Omit<StreamEnabledCallParams<T>, 'reserveUsage' | 'refundUsage'>;
1639
+ call: LLMRequestShape<T> & {
1640
+ stream: true;
1641
+ };
1289
1642
  };
1290
1643
  /**
1291
1644
  * Parameters for a cached, streaming LLM call with tool calling enabled.
@@ -1295,7 +1648,10 @@ type CachedStreamCallParams<T> = CachedCallInput & {
1295
1648
  * replayed-chunks-on-hit behavior as `CachedStreamCallParams<T>`.
1296
1649
  */
1297
1650
  type CachedStreamToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedCallInput & {
1298
- call: Omit<StreamEnabledCallParams<T, Tools> & ToolEnabledCallParams<T, Tools>, 'reserveUsage' | 'refundUsage'>;
1651
+ call: LLMRequestShape<T, Tools> & {
1652
+ stream: true;
1653
+ tools: NonNullable<LLMRequestShape<T, Tools>['tools']>;
1654
+ };
1299
1655
  };
1300
1656
  /**
1301
1657
  * Parameters for a cached, streaming LLM call with `call.tools` set
@@ -1305,7 +1661,10 @@ type CachedStreamToolCallParams<T, Tools extends readonly ToolDefinition[] = Too
1305
1661
  * `ConditionalToolCallParams` for why this overload exists.
1306
1662
  */
1307
1663
  type CachedStreamConditionalToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedCallInput & {
1308
- call: Omit<StreamEnabledCallParams<T, Tools> & ConditionalToolCallParams<T, Tools>, 'reserveUsage' | 'refundUsage'>;
1664
+ call: LLMRequestShape<T, Tools> & {
1665
+ stream: true;
1666
+ tools: Tools | undefined;
1667
+ };
1309
1668
  };
1310
1669
  /** Cached streaming conditional tool-call parameters whose non-tool result is text. */
1311
1670
  type CachedStreamConditionalStringToolCallParams<Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedStreamConditionalToolCallParams<string, Tools> & {
@@ -1319,7 +1678,11 @@ type CachedStreamConditionalStringToolCallParams<Tools extends readonly ToolDefi
1319
1678
  * cached value (on a hit) is a plain `string`.
1320
1679
  */
1321
1680
  type CachedStreamJsonModeDisabledCallParams = CachedCallInput & {
1322
- call: Omit<StreamJsonModeDisabledCallParams, 'reserveUsage' | 'refundUsage'>;
1681
+ call: Omit<LLMRequestShape<unknown>, 'jsonSchema'> & {
1682
+ stream: true;
1683
+ jsonMode: false;
1684
+ jsonSchema?: never;
1685
+ };
1323
1686
  };
1324
1687
  /**
1325
1688
  * Parameters for a cached, streaming LLM call with `jsonMode: true` and no
@@ -1327,7 +1690,11 @@ type CachedStreamJsonModeDisabledCallParams = CachedCallInput & {
1327
1690
  * miss) or cached value (on a hit) is a `JsonValue`.
1328
1691
  */
1329
1692
  type CachedStreamJsonModeEnabledCallParams = CachedCallInput & {
1330
- call: Omit<StreamJsonModeEnabledCallParams, 'reserveUsage' | 'refundUsage'>;
1693
+ call: Omit<LLMRequestShape<JsonValue>, 'schema'> & {
1694
+ stream: true;
1695
+ jsonMode: true;
1696
+ schema?: never;
1697
+ };
1331
1698
  };
1332
1699
 
1333
1700
  //#endregion
@@ -1483,72 +1850,9 @@ declare class ConsoleLogger implements Logger {
1483
1850
  error(message: string, meta?: Record<string, unknown>): void;
1484
1851
  }
1485
1852
 
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
1853
  //#endregion
1550
1854
  //#region src/types/options.d.ts
1551
- //# sourceMappingURL=events.d.ts.map
1855
+ //# sourceMappingURL=logger.d.ts.map
1552
1856
  interface VernLLMOptions {
1553
1857
  client: LLMClient;
1554
1858
  model: string;
@@ -1680,11 +1984,61 @@ interface VernLLMOptions {
1680
1984
  * and moves on for everything else.
1681
1985
  */
1682
1986
  fallbackOn?: FallbackOn;
1987
+ /**
1988
+ * Transforms outgoing requests and/or wraps whole logical calls,
1989
+ * without touching retry, circuit breaker, or fallback internals.
1990
+ * Defaults to an empty array. See `VernLLMMiddleware` for the four
1991
+ * available hooks (`transform`, `wrap`, `onEvent`, `enabled`).
1992
+ */
1993
+ middleware?: VernLLMMiddleware[];
1994
+ /**
1995
+ * Bounds `transform` and a function `enabled`, the same way every
1996
+ * other blocking operation in the package is already bounded.
1997
+ * Overridable per middleware via that entry's own `timeoutMs`.
1998
+ * `<= 0` means unbounded (no timer at all). Default 5000.
1999
+ */
2000
+ middlewareTimeoutMs?: number;
1683
2001
  }
1684
2002
 
1685
2003
  //#endregion
1686
- //#region src/vernLLM.d.ts
2004
+ //#region src/types/createMiddleware.d.ts
1687
2005
  //# sourceMappingURL=options.d.ts.map
2006
+ /**
2007
+ * `VernLLMMiddleware` plus `onError`, a convenience for the common "I
2008
+ * only care about failures" case. Everything else is passed through to
2009
+ * the resulting `VernLLMMiddleware` unchanged; setting `wrap` directly
2010
+ * alongside `onError` is an error, since `onError` builds its own `wrap`
2011
+ * under the hood, and building it around a `wrap` you also supplied
2012
+ * would silently drop one of the two.
2013
+ */
2014
+ type CreateMiddlewareOptions = Omit<VernLLMMiddleware, 'wrap'> & {
2015
+ wrap?: undefined;
2016
+ /**
2017
+ * Called with this call's terminal error, if it fails: the same error
2018
+ * `wrap`'s own `next()` would reject with. Never called on success,
2019
+ * and never called for a failure some *other* middleware's `wrap`
2020
+ * already swallowed by short-circuiting with its own `CallResult`.
2021
+ * The original error is always rethrown afterward, `onError` only
2022
+ * observes it, exactly like `onUsage`/`onEvent` elsewhere: a throwing
2023
+ * `onError` is discarded (not logged, this helper has no `Logger` of
2024
+ * its own to log through) and otherwise has no effect on the call.
2025
+ * `ctx` is `wrap`'s own pre-dispatch context (`onError` builds a `wrap`
2026
+ * under the hood), so it only describes the primary target.
2027
+ */
2028
+ onError?: (error: LLMError, ctx: PreDispatchContext) => void | Promise<void>;
2029
+ };
2030
+ /**
2031
+ * Builds a `VernLLMMiddleware` entry. Plain pass-through when `onError`
2032
+ * is omitted; when it's set, wraps it in a `wrap` that calls `next()`,
2033
+ * reports `onError` on a rejection, and always rethrows the original
2034
+ * error afterward, so `onError` never changes what the call itself
2035
+ * returns or throws, only what gets observed about it.
2036
+ */
2037
+ declare function createMiddleware(options: CreateMiddlewareOptions): VernLLMMiddleware;
2038
+
2039
+ //#endregion
2040
+ //#region src/vernLLM.d.ts
2041
+ //# sourceMappingURL=createMiddleware.d.ts.map
1688
2042
  /**
1689
2043
  * A LLM call framework for resilience, observability and control. This is VernLLM!
1690
2044
  *
@@ -1698,21 +2052,37 @@ declare class VernLLM {
1698
2052
  /**
1699
2053
  * One `CallExecutor` per provider target: index 0 is the primary,
1700
2054
  * 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.
2055
+ * Walked by `runFallbackChain`, moving to the next entry only when
2056
+ * `fallbackOn` says to.
1704
2057
  */
1705
2058
  private readonly executors;
1706
2059
  /** Decides whether a failed target is followed by the next one or the chain stops. See `VernLLMOptions['fallbackOn']`. */
1707
2060
  private readonly fallbackOn;
1708
2061
  /** Reports a `'fallback'` event when the chain moves to the next target. Shared `onEvent` plumbing, same as every executor's. */
1709
2062
  private readonly reportEvent;
2063
+ /** Owns cache reads/writes and in-flight coalescing for `cachedCall()`. Only calls back into `this.call()` as an opaque function. */
2064
+ private readonly cacheOrchestrator;
2065
+ /** See `VernLLMOptions.middleware`. Sorted once here by `priority`, ascending, ties broken by original array order. */
2066
+ private readonly middleware;
2067
+ /** See `VernLLMOptions.middlewareTimeoutMs`. Bounds `transform` and a function `enabled`; `wrap` itself is never bounded by this. */
2068
+ private readonly middlewareTimeoutMs;
1710
2069
  /**
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.
2070
+ * Maps `cachedCall()`'s inner `this.call(...)` params to its own
2071
+ * `middlewareState`, so that call's own `runOperation` skips wrapping
2072
+ * again and reuses the same state bag `wrap` just ran with (so a
2073
+ * value `wrap` sets is visible to `transform`, same as a direct
2074
+ * call). Keyed by object identity, not `requestId`, since two
2075
+ * concurrent `cachedCall()`s can share an explicit `requestId`.
1714
2076
  */
1715
- private readonly cacheOrchestrator;
2077
+ private readonly cachedCallInnerParams;
2078
+ /**
2079
+ * Shares one `CallMeta` holder across every `cachedCall()` in flight
2080
+ * for the same resolved cache key, so a joining invocation (never
2081
+ * calls `call()` itself) reports the trigger's real metadata instead
2082
+ * of `undefined`. A true cache hit never creates an entry, so it
2083
+ * still reports no metadata correctly.
2084
+ */
2085
+ private readonly cachedCallMeta;
1716
2086
  /**
1717
2087
  * @param options Client, model, and tunables. Defaults: `maxRetries` 1,
1718
2088
  * `timeoutMs` 25000, `baseDelayMs` 500, `defaultMaxTokens` 1000,
@@ -1722,72 +2092,23 @@ declare class VernLLM {
1722
2092
  constructor(options: VernLLMOptions);
1723
2093
  /** Logs a failed refundUsage attempt via the configured logger. */
1724
2094
  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;
2095
+ /** Everything `executeLogicalCall`/`executeLogicalStreamCall` (in `logicalCall.ts`) need from this instance, gathered once so `call()` doesn't rebuild it per invocation. */
2096
+ private get logicalCallDependencies();
2097
+ /** Everything `runOperation` (in `runOperation.ts`) needs from this instance, gathered once so `call()`/`cachedCall()` don't rebuild it per invocation. */
2098
+ private get runOperationDependencies();
1740
2099
  /**
1741
2100
  * Makes a single logical LLM call, retrying on failure per the configured
1742
2101
  * 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.
2102
+ * aborted. Rejects with a normalized `LLMError` on exhausted retries.
1761
2103
  *
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).
2104
+ * Supports `tools`, `stream`, and JSON mode/schema, in any combination.
2105
+ * See the Tool Calling and Streaming docs for return-shape details and
2106
+ * the TypeScript overloads that select between them.
1782
2107
  *
1783
2108
  * @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`.
2109
+ * @returns The parsed response (or raw string if `jsonMode` is false), a
2110
+ * `CallWithToolsResult<T>` when `tools` is set, or a `{ chunks,
2111
+ * finalResult }` `StreamCallResult` when `stream: true`. See `StreamCallResult`.
1791
2112
  */
1792
2113
  call<T = unknown>(params: StreamEnabledCallParams<T> & ToolsDisabledCallParams<T>): Promise<StreamCallResult<ContentResult<T>>>;
1793
2114
  call<T = unknown, const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: StreamEnabledCallParams<T, Tools> & ToolEnabledCallParams<T, Tools>): Promise<StreamCallResult<CallWithToolsResult<T, Tools>>>;
@@ -1821,35 +2142,19 @@ declare class VernLLM {
1821
2142
  deleteCache(key: string): Promise<void>;
1822
2143
  /**
1823
2144
  * 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.
2145
+ * automatically get retry/timeout/circuit-breaker behavior. Concurrent
2146
+ * misses for the same `cacheKey` share a single in-flight call, avoiding
2147
+ * cache stampedes. Supports `stream: true` and `tools` in any combination.
1836
2148
  *
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.
2149
+ * When `call.tools` is set, this caches the whole result including any
2150
+ * `tool_calls` decision, not just final answers; use a short `ttl` or a
2151
+ * separate `cacheKey` if a tool's result shouldn't be reused across calls.
1841
2152
  *
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
2153
+ * @param params `cacheKey`, `ttl`, optional
1848
2154
  * `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`.
2155
+ * to pass through to `this.call(...)`. The top-level `signal` governs
2156
+ * the cached operation and its usage hooks only; to also abort the
2157
+ * underlying provider request, set `signal` inside `call`.
1853
2158
  * @returns The cached value on a hit, or the freshly-called result on a miss.
1854
2159
  */
1855
2160
  cachedCall<T, const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: CachedStreamToolCallParams<T, Tools>): Promise<StreamCallResult<CallWithToolsResult<T, Tools>>>;
@@ -1896,11 +2201,11 @@ declare class VernLLM {
1896
2201
  * @throws {RangeError} If `target.index` names no target.
1897
2202
  */
1898
2203
  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
2204
  }
2205
+
2206
+ //#endregion
2207
+ //#region src/paramsHelpers.d.ts
2208
+ //# sourceMappingURL=vernLLM.d.ts.map
1904
2209
  /**
1905
2210
  * Identity function preserving `params`'s own precise type, unlike a `:
1906
2211
  * CallParams<T>` annotation, which would widen `tools` away and break the
@@ -1939,7 +2244,7 @@ declare function defineCachedCallParams<P extends CachedCallParams<unknown>>(par
1939
2244
 
1940
2245
  //#endregion
1941
2246
  //#region src/adapters/internal/sse.d.ts
1942
- //# sourceMappingURL=vernLLM.d.ts.map
2247
+ //# sourceMappingURL=paramsHelpers.d.ts.map
1943
2248
  /**
1944
2249
  * Parses a Server-Sent-Events byte/text stream into the JSON payload of
1945
2250
  * each `data:` frame, in arrival order. Generic over transport: works with
@@ -2000,8 +2305,8 @@ type SupportedImageMimeType = (typeof SUPPORTED_IMAGE_MIME_TYPES)[number];
2000
2305
 
2001
2306
  /**
2002
2307
  * 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
2308
+ * schema-constrained output as its own request field. Anthropic's
2309
+ * `output_config.format` and Bedrock's `outputConfig.textFormat` separate
2005
2310
  * from `tools`/`tool_choice`, so it can be combined with real,
2006
2311
  * caller-supplied `tools` in the same request.
2007
2312
  *
@@ -2677,7 +2982,7 @@ type BedrockConverseStreamEvent = {
2677
2982
  interface BedrockAdapterOptions {
2678
2983
  /**
2679
2984
  * Optional preflight check for tool-use support, needed whenever a
2680
- * `jsonSchema` call ends up sending Converse `toolConfig` either the
2985
+ * `jsonSchema` call ends up sending Converse `toolConfig`, either the
2681
2986
  * legacy forced-single-tool-call emulation, or real `tools` sent
2682
2987
  * alongside native structured output (`outputConfig`). VernLLM never
2683
2988
  * guesses capability from a failed call's error message (AWS's error
@@ -3132,5 +3437,5 @@ declare const from01AI: typeof fromOpenAICompatible;
3132
3437
  //#endregion
3133
3438
  //# sourceMappingURL=openaiCompatible.d.ts.map
3134
3439
 
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 };
3440
+ export { AnthropicClient, AssistantContent, AttemptContext, BedrockConverseClient, CacheAdapter, CachedCallParams, CachedConditionalToolCallParams, CachedJsonModeDisabledCallParams, CachedJsonModeEnabledCallParams, CachedStreamCallParams, CachedStreamConditionalToolCallParams, CachedStreamJsonModeDisabledCallParams, CachedStreamJsonModeEnabledCallParams, CachedStreamToolCallParams, CachedToolCallParams, CallMeta, CallParams, CallResult, CallWithToolsResult, CircuitBreaker, CircuitBreakerOptions, CircuitState, CircuitTarget, ConditionalToolCallParams, ConsoleLogger, ContentBlock, ContentResult, ConversationTurn, CreateMiddlewareOptions, DuplicateToolNamesIssue, FallbackAttempt, FallbackExhaustedError, FallbackOn, FallbackTarget, FetchAdapterConfig, GeminiClient, HistoryToolResultIssue, ImageBlock, InMemoryCacheAdapter, JsonModeDisabledCallParams, JsonModeEnabledCallParams, JsonSchemaSpec, JsonValue, LLMClient, LLMError, LLMErrorCode, LLMErrorIssuesByCode, LLMErrorSnapshot, LLMErrorType, LLMRequestShape, LLMRequestSnapshot, Logger, MiddlewareCapabilities, MiddlewareContext, MiddlewareContextBase, MiddlewareStateBag, MiddlewareStateKey, NormalizedCacheAdapter, OnEvent, OnUsage, PreDispatchContext, 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, VernLLMMiddleware, VernLLMOptions, WireCallRequest, WireCallRequestPatch, WireMessage, WireRequest, WireResponseFormat, WireStreamChunk, WireTool, WireToolCall, 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
3441
  //# sourceMappingURL=index.d.ts.map