VernLLMVernLLM
API Reference

Configuration

Every VernLLM constructor option and per call option

Everything below is set once on the VernLLM instance and applies to every call() unless overridden per request. Nothing is required beyond client and model; every other option has a sensible default.

vernllm-instance-config.ts
import { InMemoryCacheAdapter } from 'vern-llm';

const llm = new VernLLM({
  client: openai,
  model: 'gpt-4o',
  name: 'primary',
  maxRetries: 1,
  timeoutMs: 25_000,
  chunkIdleTimeoutMs: 30_000,
  baseDelayMs: 500,
  defaultMaxTokens: 1000,
  nonRetryableStatus: [400, 401, 403, 404, 422],
  circuitBreaker: true,
  rateLimit: { requestsPerMinute: 500, tokensPerMinute: 200_000, maxConcurrent: 20 },
  fallback: { client: anthropicClient, model: 'claude-sonnet-5', circuitBreaker: true },
  debug: false,
  cache: new InMemoryCacheAdapter(),
  parseJson: (content) => {
    try {
      return JSON.parse(content);
    } catch {
      return undefined;
    }
  },
  onUsage: (usage) => billing.record(usage),
  onUsageFailure: (usage, error) => billing.record(usage),
  onEvent: (event) => observability.record(event),
  logger: myLogger,
});

Required

client

An LLMClient, an OpenAI SDK instance, or the result of wrapping another provider with an adapter (fromAnthropic, fromGemini, fromBedrock, fromFetch, etc). See Adapters.

model

The default model ID for calls that do not override it via call({ model: '...' }).

Provider identity

OptionDefaultNotes
name'primary'Optional application-level label for this VernLLM instance. It is included as provider in usage and observability events.

name does not affect request routing or the model sent to the provider. It is an application-level label that lets you distinguish multiple VernLLM instances in shared billing and observability pipelines.

Retry & timeout

OptionDefaultNotes
maxRetries1Retries after the first attempt, so 2 attempts total by default. maxRetries: 3 means up to 4 attempts.
timeoutMs25000Per-attempt timeout, not a total call timeout. Each retry gets its own fresh timeoutMs window. For stream: true, this bounds opening the stream and its first chunk; see chunkIdleTimeoutMs.
chunkIdleTimeoutMs30000stream: true only. Max gap between chunks once the stream has opened. Resets on every chunk, including provider keep-alive pings. See Per-chunk idle timeout.
baseDelayMs500Base for exponential backoff between retries. Actual delay grows per attempt with jitter. A Retry-After header overrides the computed delay when present.
nonRetryableStatus[400, 401, 403, 404, 422]HTTP status codes that fail immediately instead of retrying.
detectSoftFailurenone(result, meta) => LLMErrorCode | undefined. Reclassifies a response that parsed and validated fine as a failure. undefined leaves it a success. See Soft Failure Detection.

Request defaults

OptionDefaultNotes
defaultMaxTokens1000Used for any call that does not set maxTokens itself.
defaultTemperature0.2Used for any call that does not set temperature itself. Pass null to omit temperature by default. Claude Opus 4.7 and later, and every Claude 5 tier model (Sonnet 5, Fable 5, Mythos 5), reject a non-default temperature with a 400 error; set this to null for an instance pointed at one of those models. Separately, fromAnthropic/fromBedrock omit temperature automatically whenever budgetTokens/reasoningEffort is set, on any model, since Anthropic rejects temperature alongside any thinking mode.
defaultReasoningEffortNot sentUsed for any call that does not set reasoningEffort itself. See Call Params.
defaultBudgetTokensNot sentUsed for any call that does not set budgetTokens itself. See Call Params.
parseJsondefaultParseJsonCustom parser for non-standard JSON responses. Must return undefined or null on failure, either is treated the same way, and must not throw.

Caching

The cache option controls the adapter used by cachedCall.

By default, VernLLM creates an InMemoryCacheAdapter. This stores cached responses in the current process memory using a bounded in-memory map. It is useful for local development and single-process deployments, but entries are not shared between application instances.

For distributed deployments, provide your own adapter backed by shared storage such as Redis or Upstash.

InMemoryCacheAdapter accepts an optional maxSize argument to limit the number of entries kept in memory. When the limit is reached, it evicts by 'fifo' (default), removing the oldest inserted entry, or 'lru', removing the least recently used entry. See Eviction.

Two additional built-in adapters compose on top of any CacheAdapter: NormalizedCacheAdapter (normalizes keys before matching, including case, whitespace, and punctuation) and TieredCacheAdapter (checks a fast local adapter first, falling back to a shared one). See Caching for usage.

OptionDefaultNotes
cachenew InMemoryCacheAdapter()Used only by cachedCall, not plain call(). { maxSize, eviction } configures the built-in adapter (eviction: 'fifo' default or 'lru'), or pass a CacheAdapter directly for a shared backend. See Eviction.

Observability

OptionDefaultNotes
onUsagenoneCalled after every successful call with token counts when the provider returned usage data. The usage object includes provider, which is the name configured on the target.
onUsageFailurenoneCalled when a provider response arrived with usage data but VernLLM's post-processing then failed. Fires once per failed attempt with extractable usage. See Usage Tracking.
onEventnoneReceives events for retries, circuit state transitions, rate-limit waits, and fallback transitions. Fire and forget; cannot change the call result or retry behavior. See Observability.
loggerconsole-based ConsoleLoggerImplement the Logger interface to route output elsewhere. See Pluggable Logger.
debugfalseControls diagnostic logger.debug calls on the default console logger only. With a custom logger, its own debug() decides whether to emit. warn and error logs are not controlled by debug.
redactnoneApplied before every internal logger.debug() call, output on success and the provider error on failure, regardless of whether that call ends up emitting anything. See Redacting debug output.

See Observability for the full onEvent union and event shapes.

Circuit breaker

OptionDefaultNotes
circuitBreakerdisabledPass true for defaults (threshold: 5, cooldownMs: 30_000), or { threshold, cooldownMs, isolateByModel, halfOpenProbes, halfOpenSuccessRatio, cooldownBackoff, tripping, onStateChange }. See Circuit Breaker.

The circuit breaker tracks provider and transport-level health. Response parsing and validation errors do not count as provider failures. unknown_tool and duplicate_tool_call_id tool-contract errors also do not count.

With isolateByModel: false (the default), one circuit is shared across models used by the same target. With isolateByModel: true, each resolved model gets its own circuit and getCircuitState({ model }) can be used to inspect that model's state. Omitting model reads or acts on the target's own configured model rather than an unlabeled bucket.

halfOpenProbes (default 1) and halfOpenSuccessRatio (default 1) control how many trial calls run during the half-open window and what fraction must succeed to close the circuit again.

cooldownBackoff accepts ExponentialBackoffOptions | CooldownBackoff:

  • ExponentialBackoffOptions: { multiplier: number; maxMs?: number }. Scales cooldownMs by multiplier on each repeat open, capped at maxMs (default Infinity). Always applies full jitter.
  • CooldownBackoff: (reopenCount: number, baseCooldownMs: number) => number, an escape hatch for anything beyond exponential growth, or an exact deterministic value. Never jittered automatically.

Omitted (the default), cooldownMs stays fixed.

tripping accepts { kind: 'consecutive', threshold } | { kind: 'rolling', windowMs, minCalls, failureRatio } | TrippingPolicy:

  • { kind: 'consecutive', threshold }: the default, matching threshold on its own. Opens after that many failures in a row.
  • { kind: 'rolling', windowMs, minCalls, failureRatio }: opens once at least minCalls calls have landed in the trailing windowMs and the failure ratio among them reaches failureRatio.
  • TrippingPolicy: { onSuccess(), onFailure(): boolean, reset() }, an escape hatch for anything else. No class required, a plain object satisfying the interface works.

onStateChange, the circuit_state event, and the open-circuit error message always report a true consecutive-failure count, tracked independently of tripping.

Omitted (the default), tripping is { kind: 'consecutive', threshold }.

See Circuit Breaker for failure conditions, half-open behavior, cooldown backoff, tripping policy, failure attribution, per-model isolation, and state inspection.

Rate limiting

OptionDefaultNotes
rateLimitunlimited{ requestsPerMinute, tokensPerMinute, maxConcurrent, maxQueueMs, maxQueueSize, estimateTokens }, or a RateLimiterAdapter instance for cross process coordination. Queues calls locally to stay under configured limits. Every field is independent and optional. See Rate Limiting.

A local rate-limit rejection fails immediately with LLMError('rate_limited') and code: 'rate_limit_queue_timeout', 'rate_limit_queue_full', or 'rate_limit_capacity_exceeded'. It is never retried and does not count as a circuit-breaker failure.

A provider 429 carries code: 'provider_rate_limited' and is handled separately from local rate-limit rejections. See Error codes.

Retry budget

OptionDefaultNotes
retryBudgetdisabled{ windowMs, minCalls, retryRatio }. Caps how much of a target's recent traffic is allowed to be retries, independent of circuitBreaker. See Retry Budget.

Once at least minCalls calls have landed in the trailing windowMs and the retry ratio among them reaches retryRatio, further retries against that target fail immediately with LLMError('rate_limited') and code: 'retry_budget_exhausted'. It is never retried and does not count as a circuit-breaker failure. Like circuitBreaker and rateLimit, a target's own retryBudget is never inherited: each target needs its own if it wants one.

Fallback

OptionDefaultNotes
fallbacknoneA FallbackTarget or FallbackTarget[], tried in declaration order after the primary and after each earlier target's retries are exhausted or abandoned. See Provider Fallback.
fallbackOndefaultFallbackOn(error, { isLastTarget }) => 'next' | 'stop'. Decides whether a failed target is followed by the next one.

Each FallbackTarget needs its own client and model. name, maxRetries, timeoutMs, chunkIdleTimeoutMs, baseDelayMs, defaultMaxTokens, defaultTemperature, nonRetryableStatus, and detectSoftFailure inherit the parent instance's resolved value when omitted.

circuitBreaker, rateLimit, and retryBudget are never inherited. Each target has its own independent breaker, rate limiter, and retry budget when configured.

An open target breaker is treated as a target failure and can cause the chain to move to the next target according to fallbackOn.

When every target fails, call() throws FallbackExhaustedError containing the normalized error from each attempted target in declaration order. See FallbackExhaustedError.

Per call defaults and overrides

A handful of options are set per call() rather than on the instance. The following options have their own defaults worth knowing:

OptionDefaultNotes
temperature0.2, or the instance's defaultTemperatureLow by design. Override per call as needed. Pass null to omit temperature and use the provider's own default. Claude Opus 4.7 and later, and every Claude 5 tier model, reject a non-default value with a 400 error.
jsonModetrue, except false if tools is setResponses are parsed as JSON by default. Set jsonMode: false to get the raw string back.
systemPromptnoneOptional. Omitting it skips the system message entirely.
toolChoice'auto' when tools is setControls whether and which tool the model must call. See Tool Calling.

Usage metering

reserveUsage and refundUsage are provided per call, not on the VernLLM instance.

OptionDefaultNotes
reserveUsagenoneRuns before the provider request. If it throws, the call fails with LLMError('quota_exceeded') and no request is sent.
refundUsagenoneRuns after a successful reservation if the call ultimately fails. It is also called when an already-reserved request is aborted before the wrapped operation starts.

These hooks are application controlled. VernLLM only manages the reservation lifecycle; it does not decide quota rules or billing policy.

userContent, model, maxTokens, and reasoningEffort have no call-level default beyond falling back to the corresponding instance-level field, or being undefined when omitted.

schema, jsonSchema, tools, signal, and requestId have no instance-level equivalent; they are undefined unless set on the call itself.

requestId is an optional identifier you can provide per call:

request-id.ts
const result = await llm.call({
  userContent: 'Generate a summary',
  requestId: `document-summary:${documentId}`,
});

Multimodal userContent

ContentBlock is the shape used when userContent is passed as an array instead of a plain string:

content-block-types.ts
interface TextBlock {
  type: 'text';
  text: string;
}

interface ImageBlock {
  type: 'image';
  data: string; // base64-encoded bytes, no `data:` prefix
  mimeType: string; // e.g. 'image/png', 'image/jpeg', 'image/webp', 'image/gif'
}

type ContentBlock = TextBlock | ImageBlock;

Image blocks validate mimeType against VernLLM's supported image set before a provider request is created. Supported values are image/png, image/jpeg, image/gif, and image/webp.

Unsupported values fail with LLMError('validation').

On this page