vern-llm 2.2.0 → 2.3.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/README.md +5 -1
- package/dist/index.cjs +573 -127
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +344 -118
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +344 -118
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +572 -128
- package/dist/index.js.map +1 -1
- package/package.json +16 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["message: string","type: LLMErrorType","status?: number","issues?: unknown","cause?: unknown","retryAfterMs?: number","code?: LLMErrorCode","err: unknown","key: string","value: T","ttl: number","inner: CacheAdapter<T>","l1: CacheAdapter<T>","l2: CacheAdapter<T>","l1Ttl?: number","result: unknown","defaultFallbackOn: FallbackOn","attempts: FallbackAttempt[]","params: UsageHooks","coalesced: boolean","signal: AbortSignal | undefined","onRefundError: (logMessage: string, error: unknown) => void","logMessage: string","getResult: () => Promise<T>","result: T","openStream: () => Promise<{ chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> }>","opened: { chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> }","value: unknown","value: T | CallWithToolsResult<T>","hasTools: boolean","items: StreamChunk[]","promise: Promise<T | CallWithToolsResult<T>>","cache: CacheAdapter<unknown>","logger: Logger","key: string","logMessage: string","error: unknown","params: InternalCacheParams<T>","params: InternalCacheStreamParams<T>","hasTools: boolean","resolveInFlight!: (value: T) => void","rejectInFlight!: (error: unknown) => void","trackedResult: Promise<T>","options: CircuitBreakerOptions","model: string | undefined","bucket: CircuitBucket","to: CircuitState","model?: string","onEvent: ((event: VernLLMEvent) => void) | undefined","logger: Logger","circuitBreakerOption: boolean | CircuitBreakerOptions | undefined","providerName: string","defaultModel: string","ms: number | undefined","ms: number","fn: (signal: AbortSignal) => Promise<T>","timeoutMs: number","externalSignal?: AbortSignal","next: () => Promise<IteratorResult<T>>","timeoutMs: number | undefined","onIdle?: () => void","logger?: Pick<Logger, 'debug'>","error: unknown","err: unknown","baseDelayMs: number","attempt: number","delay: number","signal?: AbortSignal","err: unknown","error: unknown","value: unknown","signal?: AbortSignal","content: string","tools: ToolDefinition[]","toolCalls: ToolCall[]","wireToolCalls: WireToolCall[]","parsedArgs: unknown","options: RequestBuilderOptions","params: CallParams<T>","history: ConversationTurn[]","previousTurn: ConversationTurn | undefined","toolChoice: CallParams<unknown>['toolChoice']","turn: ConversationTurn","jsonSchema: CallParams<unknown>['jsonSchema']","useJson: boolean","iterator: AsyncIterator<WireStreamChunk>","first: IteratorResult<WireStreamChunk>","options: StreamAccumulatorOptions<T>","resolveFinal!: (value: T | CallWithToolsResult<T>) => void","rejectFinal!: (error: unknown) => void","buffered: StreamChunk[]","pending: Array<{\n resolve: (result: IteratorResult<StreamChunk>) => void;\n reject: (error: unknown) => void;\n }>","streamError: unknown","chunk: StreamChunk","error: unknown","chunks: AsyncIterable<StreamChunk>","usage: TokenUsage | undefined","result: IteratorResult<WireStreamChunk>","wireToolCalls: WireToolCall[] | undefined","providerName: string","client: LLMClient","model: string","options: CallExecutorOptions","model?: string","params: CallParams<T>","requestId: string","onAttempt?: () => void","attempt: number","release: ((actualTokens?: number) => void) | undefined","text: string","content: string | undefined","wireToolCalls: WireToolCall[] | undefined","rawContent: string | null | undefined","useJson: boolean","usage: TokenUsage | undefined","toolCalls: { id: string; name: string; arguments: unknown }[]","tools: NonNullable<CallParams<unknown>['tools']>","toolIssues: ToolIssue[]","fn: (attempt: number) => Promise<T>","signal?: AbortSignal","lastError: unknown","response: Awaited<ReturnType<LLMClient['chat']['completions']['create']>>","usage: TokenUsage","error: LLMError","content: string","schema?: CallParams<T>['schema']","parsed: unknown","error: unknown","debugEnabled: boolean","message: string","meta?: Record<string, unknown>","request: WireRequest","capacity: number","refillPerMs: number","amount: number","options: RateLimitOptions","estimatedTokens: number","signal?: AbortSignal","initialReason: RateLimitReason | undefined","waiter: Waiter","queueTimer: ReturnType<typeof setTimeout> | undefined","taken: Array<{ bucket: TokenBucket; amount: number }>","bucket: TokenBucket | undefined","reason: RateLimitReason","actualTokens?: number","options: VernLLMOptions","primaryTarget: FallbackTarget","declaredFallbacks: FallbackTarget[]","logMessage: string","error: unknown","params: Pick<CallParams<unknown>, 'model' | 'signal'>","requestId: string","attempt: (executor: CallExecutor, onAttempt: () => void) => Promise<R>","attempts: FallbackAttempt[]","params: CallParams<T>","params: InternalCacheParams<T>","key: string","params:\n | CachedCallParams<T>\n | CachedToolCallParams<T>\n | CachedStreamCallParams<T>\n | CachedStreamToolCallParams<T>","model?: string","source: AsyncIterable<Uint8Array | string>","text: string","boundary","frame: string","dataLines: string[]","mimeType: string","model: string","override?: ModelCapabilityOverride","blocks: ContentBlock[]","schema: Record<string, unknown>","toolName: string","toolChoice: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice']","tools: NonNullable<Parameters<LLMClient['chat']['completions']['create']>[0]['tools']>","toolChoiceParam: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice']","params: Parameters<LLMClient['chat']['completions']['create']>[0]","nativeStructuredOutputModels?: ModelCapabilityOverride","toolName: string | undefined","jsonInstruction: string | undefined","outputFormat: NonNullable<AnthropicRequestBody['output_config']>['format'] | undefined","tools: NonNullable<Parameters<AnthropicClient['messages']['create']>[0]['tools']> | undefined","toolChoice: Parameters<AnthropicClient['messages']['create']>[0]['tool_choice']","body: AnthropicRequestBody","anthropicClient: AnthropicClient","options?: AnthropicAdapterOptions","options","text: string","wireToolCalls: WireToolCall[] | undefined","mergeConsecutiveToolResults","messages: { role: 'user' | 'assistant'; content: string | AnthropicContentBlock[] }[]","m: (typeof messages)[number]","merged: (typeof messages)[number][]","m: Extract<\n Parameters<LLMClient['chat']['completions']['create']>[0]['messages'][number],\n { role: 'user' | 'assistant' | 'tool' }\n >","blocks: AnthropicContentBlock[]","input: unknown","blocks: ContentBlock[]","toolChoice: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice']","m: Extract<\n Parameters<LLMClient['chat']['completions']['create']>[0]['messages'][number],\n { role: 'user' | 'assistant' | 'tool' }\n >","parts: GeminiPart[]","text: string","toolName: string","parsed: unknown","contents: { role: 'user' | 'model'; parts: GeminiPart[] }[]","c: (typeof contents)[number]","merged: (typeof contents)[number][]","params: Parameters<LLMClient['chat']['completions']['create']>[0]","config: GeminiConfig","geminiClient: GeminiClient","wireToolCalls: WireToolCall[] | undefined","lastUsage:\n | NonNullable<Awaited<ReturnType<GeminiClient['generateContent']>>['usageMetadata']>\n | undefined","mimeType: string","data: string","blocks: ContentBlock[]","tools: NonNullable<Parameters<LLMClient['chat']['completions']['create']>[0]['tools']>","toolChoiceParam: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice']","params: Parameters<LLMClient['chat']['completions']['create']>[0]","toolUseSupportedModels: BedrockAdapterOptions['toolUseSupportedModels']","nativeStructuredOutputModels: BedrockAdapterOptions['nativeStructuredOutputModels']","toolName: string | undefined","jsonInstruction: string | undefined","toolConfig: NonNullable<BedrockRequest['toolConfig']> | undefined","outputConfig: NonNullable<BedrockRequest['outputConfig']> | undefined","request: BedrockRequest","bedrockClient: BedrockConverseClient","options?: BedrockAdapterOptions","text: string","wireToolCalls: WireToolCall[] | undefined","toolChoice: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice']","m: Extract<\n Parameters<LLMClient['chat']['completions']['create']>[0]['messages'][number],\n { role: 'user' | 'assistant' | 'tool' }\n >","blocks: BedrockContentBlock[]","input: unknown","messages: { role: 'user' | 'assistant'; content: BedrockContentBlock[] }[]","m: (typeof messages)[number]","merged: (typeof messages)[number][]","stream: ReadableStream<Uint8Array>","url: string","init: { method: string; headers: Record<string, string>; body?: string; signal?: AbortSignal }","config: FetchAdapterConfig","params: ChatRequest","requestBody: unknown","wireToolCalls: WireToolCall[] | undefined","blocks: ContentBlock[]","params: Parameters<LLMClient['chat']['completions']['create']>[0]","chunk: OpenAIStreamChunk","client: unknown","options: OpenAICompatibleAdapterOptions","options"],"sources":["../src/types/errors.ts","../src/types/cache.ts","../src/types/tools.ts","../src/types/fallback.ts","../src/internal/execution/usage.utils.ts","../src/internal/cache/replay.utils.ts","../src/internal/cache/cacheOrchestrator.ts","../src/circuitBreaker.ts","../src/internal/circuitBreaker.utils.ts","../src/internal/execution/retry.utils.ts","../src/internal/execution/errors.utils.ts","../src/internal/execution/parse.utils.ts","../src/internal/execution/wire.utils.ts","../src/internal/execution/requestBuilder.ts","../src/internal/execution/streamAccumulator.ts","../src/internal/execution/callExecutor.ts","../src/logger.ts","../src/rateLimit.ts","../src/vernLLM.ts","../src/adapters/internal/sse.ts","../src/adapters/internal/imageFormat.ts","../src/adapters/internal/nativeStructuredOutput.ts","../src/adapters/anthropic.ts","../src/adapters/gemini.ts","../src/adapters/bedrock.ts","../src/adapters/fetch.ts","../src/adapters/openaiCompatible.ts"],"sourcesContent":["export type LLMErrorType =\n | 'timeout'\n | 'api'\n | 'parse'\n | 'validation'\n | 'circuit_open'\n | 'quota_exceeded'\n | 'unknown'\n | 'aborted';\n\n/**\n * Machine readable discriminator within a `type`, for cases where `type`\n * alone is too coarse to act on. Optional and additive: errors thrown\n * before a given code existed simply omit it.\n */\nexport type LLMErrorCode =\n | 'unknown_tool'\n | 'duplicate_tool_call_id'\n | 'local_rate_limit'\n | 'provider_rate_limited'\n | 'fallback_exhausted'\n | 'invalid_credentials'\n | 'connection_failed'\n | 'tool_choice_none_violated';\n\n/** One tool call's contract failure, used to report every bad call in a response at once. */\nexport interface ToolIssue {\n name: string;\n toolCallId: string;\n code: LLMErrorCode;\n detail?: unknown;\n}\n\nexport class LLMError extends Error {\n constructor(\n message: string,\n public type: LLMErrorType,\n public status?: number,\n public issues?: unknown,\n public cause?: unknown,\n public retryAfterMs?: number,\n /** Stable discriminator within `type`. Absent on errors predating it. */\n public code?: LLMErrorCode,\n ) {\n super(message);\n this.name = 'LLMError';\n }\n\n /** Every tool contract failure in one response, when there is more than one. */\n toolIssues?: ToolIssue[];\n}\n\nexport function isLLMError(err: unknown): err is LLMError {\n return err instanceof LLMError;\n}\n","export interface CacheAdapter<T = unknown> {\n get(key: string): Promise<{ hit: boolean; value: T | null }>;\n set(key: string, value: T, ttl: number): Promise<void>;\n delete?(key: string): Promise<void>;\n resolveKey?(key: string): Promise<string>;\n}\n\n/**\n * Trivial default so the package works out of the box with no external deps\n * Not shared across processes, swap in Redis/Upstash/etc for production\n */\nexport class InMemoryCacheAdapter<T = unknown> implements CacheAdapter<T> {\n private store = new Map<string, { value: T; expiresAt: number }>();\n\n constructor(private readonly maxSize = 1000) {}\n\n async get(key: string): Promise<{ hit: boolean; value: T | null }> {\n const entry = this.store.get(key);\n\n if (!entry) return { hit: false, value: null };\n\n if (Date.now() >= entry.expiresAt) {\n this.store.delete(key);\n return { hit: false, value: null };\n }\n\n return { hit: true, value: entry.value };\n }\n\n async set(key: string, value: T, ttl: number): Promise<void> {\n this.cleanupExpiredEntries();\n\n this.store.set(key, {\n value,\n expiresAt: Date.now() + ttl * 1000,\n });\n\n this.enforceSizeLimit();\n }\n\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n private cleanupExpiredEntries(): void {\n const now = Date.now();\n\n for (const [key, entry] of this.store) {\n if (now >= entry.expiresAt) {\n this.store.delete(key);\n }\n }\n }\n\n private enforceSizeLimit(): void {\n while (this.store.size > this.maxSize) {\n const oldestKey = this.store.keys().next().value;\n\n if (oldestKey === undefined) break;\n\n this.store.delete(oldestKey);\n }\n }\n}\n\n/**\n * Normalizes keys before caching to avoid duplicate entries from formatting differences.\n */\nexport class NormalizedCacheAdapter<T = unknown> implements CacheAdapter<T> {\n constructor(private readonly inner: CacheAdapter<T> = new InMemoryCacheAdapter<T>()) {}\n\n private normalize(key: string): string {\n return key\n .toLowerCase()\n .trim()\n .replace(/[^\\p{L}\\p{N}\\s]/gu, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n }\n\n async resolveKey(key: string): Promise<string> {\n return this.normalize(key);\n }\n\n async get(key: string): Promise<{ hit: boolean; value: T | null }> {\n return this.inner.get(this.normalize(key));\n }\n\n async set(key: string, value: T, ttl: number): Promise<void> {\n await this.inner.set(this.normalize(key), value, ttl);\n }\n\n async delete(key: string): Promise<void> {\n await this.inner.delete?.(this.normalize(key));\n }\n}\n\n/**\n * Two-tier cache with fast local L1 and shared L2.\n * L2 hits are promoted back to L1.\n */\nexport class TieredCacheAdapter<T = unknown> implements CacheAdapter<T> {\n constructor(\n private readonly l1: CacheAdapter<T>,\n private readonly l2: CacheAdapter<T>,\n private readonly l1Ttl?: number,\n ) {}\n\n /**\n * Forwards to L1's `resolveKey` if it has one, otherwise L2's. L1 is\n * preferred since `get()` checks L1 first, so its notion of \"the same\n * key\" is the one that determines whether a lookup can skip L2 entirely.\n */\n async resolveKey(key: string): Promise<string> {\n if (this.l1.resolveKey) return this.l1.resolveKey(key);\n if (this.l2.resolveKey) return this.l2.resolveKey(key);\n return key;\n }\n\n async get(key: string): Promise<{ hit: boolean; value: T | null }> {\n const l1Result = await this.l1.get(key);\n if (l1Result.hit) return l1Result;\n\n const l2Result = await this.l2.get(key);\n\n if (l2Result.hit) {\n await this.l1.set(key, l2Result.value as T, this.l1Ttl ?? 60);\n }\n\n return l2Result;\n }\n\n async set(key: string, value: T, ttl: number): Promise<void> {\n await Promise.all([this.l1.set(key, value, this.l1Ttl ?? ttl), this.l2.set(key, value, ttl)]);\n }\n\n async delete(key: string): Promise<void> {\n await Promise.all([this.l1.delete?.(key), this.l2.delete?.(key)]);\n }\n}\n","import type { SchemaLike } from './schema.js';\n\n/**\n * Describes a capability the model may request, not the capability\n * itself. VernLLM transports this to the provider and parses what comes\n * back; it never executes anything.\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n /** JSON Schema for the tool's input. */\n parameters: Record<string, unknown>;\n /**\n * Optional client-side validator run on the parsed `arguments` before\n * they're handed back to the caller, mirroring the `schema: SchemaLike<T>`\n * pattern already used for response validation (see `types/schema.ts`).\n * Reuses that zero-dependency, `safeParse`-compatible shape instead of\n * requiring a JSON Schema validator (e.g. ajv) as a new dependency.\n * Failed validation throws `LLMError('validation')`. If omitted, VernLLM\n * parses arguments as JSON but does not validate them further.\n */\n argumentsSchema?: SchemaLike<unknown>;\n}\n\n/** A single tool invocation requested by the model. */\nexport interface ToolCall {\n id: string;\n name: string;\n /** Parsed JSON arguments (and validated, if `argumentsSchema` was set). */\n arguments: unknown;\n}\n\n/** The application's result of executing a `ToolCall`, sent back to the model. */\nexport interface ToolResult {\n toolCallId: string;\n content: unknown;\n /**\n * Signals a failed tool execution back to the model (matches Anthropic's\n * native `is_error` on tool_result blocks). Only `fromAnthropic` honors\n * this today, Gemini and Bedrock have no equivalent wire concept, so\n * other adapters ignore it silently.\n */\n isError?: boolean;\n}\n\n/** `call()` result when `tools` was set and the model produced a normal answer. */\nexport interface ContentResult<T> {\n type: 'content';\n content: T;\n}\n\n/** `call()` result when `tools` was set and the model requested one or more tools. */\nexport interface ToolCallResult {\n type: 'tool_calls';\n toolCalls: ToolCall[];\n /** Any text the model produced alongside the tool request, if present. */\n content?: string;\n}\n\nexport type CallWithToolsResult<T> = ContentResult<T> | ToolCallResult;\n\n/**\n * Runtime-safe check for whether a `call()` result is a `tool_calls`\n * result. Prefer this over relying on TypeScript's static narrowing\n * whenever `params` passed to `call()` wasn't a literal with `tools`\n * inlined (see the \"note on the overload\" in `VernLLM.call`'s docs), in\n * that case TS may have typed the result as plain `T` even though it's\n * actually a `CallWithToolsResult<T>` at runtime, and this check works\n * either way.\n */\nexport function isToolCallResult(result: unknown): result is ToolCallResult {\n return (\n typeof result === 'object' &&\n result !== null &&\n 'type' in result &&\n (result as { type: unknown }).type === 'tool_calls' &&\n Array.isArray((result as { toolCalls?: unknown }).toolCalls)\n );\n}\n\n/** What the model should do about tools on a given call. */\nexport type ToolChoice = 'auto' | 'none' | 'required' | { name: string };\n","import { LLMError } from './errors.js';\n\nimport type { CircuitBreakerOptions, CircuitState } from '../circuitBreaker.js';\nimport type { RateLimitOptions } from '../rateLimit.js';\nimport type { LLMClient } from './client.js';\n\n/**\n * One provider to try after the primary (or after an earlier fallback\n * target) fails. Order is the policy: VernLLM never reorders, scores, or\n * selects a target, it only walks the list as given.\n *\n * Most per-target overrides fall back to the parent `VernLLM` instance's\n * own option when omitted, so a target only needs to specify what's\n * actually different about it (a different client/model is the common\n * case). `circuitBreaker` and `rateLimit` are the exception: they are\n * never inherited from the parent, since a breaker or limiter tuned for\n * the primary provider's limits is rarely right for a fallback's. Leave\n * them unset on a target to run it without one, even if the parent has\n * one configured.\n */\nexport interface FallbackTarget {\n client: LLMClient;\n model: string;\n /** Label for events, errors, and `TokenUsage.provider`. Default `` `fallback[${index}]` ``. */\n name?: string;\n\n maxRetries?: number;\n timeoutMs?: number;\n chunkIdleTimeoutMs?: number;\n baseDelayMs?: number;\n defaultMaxTokens?: number;\n defaultTemperature?: number | null;\n nonRetryableStatus?: number[];\n /** This target's own circuit breaker, independent of every other target's. Not inherited from the parent's `circuitBreaker`. */\n circuitBreaker?: boolean | CircuitBreakerOptions;\n /** This target's own rate limiter, independent of every other target's. Not inherited from the parent's `rateLimit`. */\n rateLimit?: RateLimitOptions;\n}\n\n/**\n * Written into `CallParams['meta']` once `call()` resolves, so a caller\n * who wants provider identity on the same line as the result doesn't need\n * to read it back out of `onUsage`.\n */\nexport interface CallMeta {\n provider: string;\n model: string;\n /** `-1` if the primary target answered, otherwise the index into `fallback`. */\n fallbackIndex: number;\n usedFallback: boolean;\n /** Attempts made against the target that ultimately answered, including the successful one. */\n attempts: number;\n}\n\n/** One target's circuit state, as returned by `VernLLM.getCircuitStates()`. */\nexport interface TargetCircuitState {\n provider: string;\n /** Position in the chain: `0` for the primary, `1`+ for fallback targets. */\n index: number;\n isFallback: boolean;\n /** `undefined` if that target has no circuit breaker configured. */\n state: CircuitState | undefined;\n}\n\n/** One target's failure, recorded on the way to either the next target or `FallbackExhaustedError`. */\nexport interface FallbackAttempt {\n /** `-1` for the primary target. */\n index: number;\n provider: string;\n model: string;\n error: LLMError;\n}\n\n/**\n * Decides what happens after a target's own retries are exhausted or\n * abandoned early. Called once per failed target. `'retry'` is not a\n * valid return here: retrying already happened inside the target, this\n * only decides whether to move on to the next one or stop.\n */\nexport type FallbackOn = (error: LLMError, context: { isLastTarget: boolean }) => 'next' | 'stop';\n\n/** Tool contract failures are the model ignoring the request, not a sick provider: repeating it elsewhere can't help. */\nconst TOOL_CONTRACT_CODES = new Set(['unknown_tool', 'duplicate_tool_call_id']);\n\n/**\n * The default `fallbackOn` policy. Exported so a caller can wrap rather\n * than replace it, e.g. `fallbackOn: (e, ctx) => myCheck(e) ? 'stop' : defaultFallbackOn(e, ctx)`.\n */\nexport const defaultFallbackOn: FallbackOn = (error) => {\n if (error.type === 'parse' || error.type === 'validation' || error.type === 'aborted') {\n return 'stop';\n }\n\n if (error.type === 'quota_exceeded') return 'stop';\n\n if (error.code && TOOL_CONTRACT_CODES.has(error.code)) return 'stop';\n\n return 'next';\n};\n\n/**\n * Thrown when the chain gives up, whether because the last target failed\n * or `fallbackOn` chose to stop early. Carries each attempt in order so\n * an outage across providers stays debuggable without reproducing it.\n * Extends `LLMError` so `isLLMError` and any `instanceof LLMError` check\n * still passes, inheriting the last failure's `type`/`status`/`retryAfterMs`\n * so existing type-based handling, including reading `retryAfterMs` on an\n * `'api'`-typed error, keeps working on a fallback-exhausted error too.\n */\nexport class FallbackExhaustedError extends LLMError {\n constructor(public readonly attempts: FallbackAttempt[]) {\n const last = attempts[attempts.length - 1]?.error;\n\n super(\n `${attempts.length} provider${attempts.length === 1 ? '' : 's'} attempted and failed: ${attempts\n .map((a) => `${a.provider}(${a.error.type})`)\n .join(' then ')}`,\n last?.type ?? 'unknown',\n last?.status,\n undefined,\n last,\n last?.retryAfterMs,\n 'fallback_exhausted',\n );\n }\n}\n","import { LLMError, type StreamChunk, type UsageHooks } from '../../types/index.js';\n\n/**\n * Calls `params.reserveUsage`, if present, mapping any failure to a\n * `quota_exceeded` LLMError (or an aborted error, if the signal fired\n * during reservation). Returns whether a reservation was actually made,\n * so callers know whether a later refund is needed. Shared by\n * `withReservedUsage` and `withReservedUsageForStream`, which differ only\n * in whether `coalesced` is caller-supplied or always `false`.\n */\nasync function reserve(\n params: UsageHooks,\n coalesced: boolean,\n signal: AbortSignal | undefined,\n): Promise<boolean> {\n if (!params.reserveUsage) return false;\n\n try {\n await params.reserveUsage({ coalesced, signal });\n return true;\n } catch (error) {\n if (signal?.aborted) {\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n throw new LLMError(\n error instanceof Error ? error.message : 'Usage reservation failed',\n 'quota_exceeded',\n undefined,\n undefined,\n error,\n );\n }\n}\n\n/**\n * Builds a `(logMessage) => Promise<void>` refund function bound to the\n * given hooks/coalesced/signal, reporting (instead of throwing) any error\n * the refund hook itself raises, so a broken refund hook never masks the\n * original error it was called to clean up after.\n */\nfunction makeRefund(\n params: UsageHooks,\n coalesced: boolean,\n signal: AbortSignal | undefined,\n onRefundError: (logMessage: string, error: unknown) => void,\n): (logMessage: string) => Promise<void> {\n return async (logMessage: string) => {\n try {\n await params.refundUsage?.({ coalesced, signal });\n } catch (refundError) {\n onRefundError(logMessage, refundError);\n }\n };\n}\n\n/**\n * Runs `getResult` after reserving usage, if a `reserveUsage` hook was\n * provided. `refundUsage` fires only if a reservation was actually made.\n * `onRefundError` is called (instead of throwing) whenever a refund attempt\n * itself fails, so a broken refund hook never masks the original error.\n */\nexport async function withReservedUsage<T>(\n params: UsageHooks,\n coalesced: boolean,\n getResult: () => Promise<T>,\n signal: AbortSignal | undefined,\n onRefundError: (logMessage: string, error: unknown) => void,\n): Promise<T> {\n if (signal?.aborted) {\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n const reserved = await reserve(params, coalesced, signal);\n\n const refund = makeRefund(params, coalesced, signal, onRefundError);\n\n if (signal?.aborted) {\n if (reserved) await refund('[VernLLM] refundUsage failed after abort');\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n let result: T;\n\n try {\n result = await getResult();\n } catch (error) {\n if (reserved) await refund('[VernLLM] refundUsage failed');\n throw error;\n }\n\n if (signal?.aborted) {\n if (reserved) await refund('[VernLLM] refundUsage failed after abort');\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n return result;\n}\n\n/**\n * Streaming counterpart to `withReservedUsage`. `withReservedUsage` assumes\n * `getResult()` settling *is* the operation's final outcome, awaiting it\n * synchronously before reserve/refund resolve. Streaming can't satisfy that:\n * `call()` must return `{ chunks, finalResult }` as soon as the stream\n * opens, well before the real outcome (validation, schema/tool-call checks)\n * is known.\n *\n * Reserves usage before `openStream` runs, same failure mode as the\n * non-streaming path if `reserveUsage` itself throws (mapped to\n * `quota_exceeded`, nothing opened). If `openStream` itself throws (stream\n * never opened), refunds synchronously and rethrows, exactly like\n * `withReservedUsage` does today. If it succeeds, returns `{ chunks,\n * finalResult }` immediately, refund/report is deferred onto\n * `finalResult`'s continuation, since that's the only point the real\n * outcome is known. This means `onUsageFailure` (and any refund) can fire\n * well after this function itself has returned.\n */\nexport async function withReservedUsageForStream<T>(\n params: UsageHooks,\n openStream: () => Promise<{ chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> }>,\n signal: AbortSignal | undefined,\n onRefundError: (logMessage: string, error: unknown) => void,\n): Promise<{ chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> }> {\n if (signal?.aborted) {\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n const reserved = await reserve(params, false, signal);\n const refund = makeRefund(params, false, signal, onRefundError);\n\n if (signal?.aborted) {\n if (reserved) await refund('[VernLLM] refundUsage failed after abort');\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n let opened: { chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> };\n\n try {\n opened = await openStream();\n } catch (error) {\n if (reserved) await refund('[VernLLM] refundUsage failed after stream-open failure');\n throw error;\n }\n\n // Stream opened. The real outcome is only known once finalResult settles,\n // so refund is attached there instead of awaited inline, this is the\n // structural difference from withReservedUsage, not an optional variant.\n const finalResult = opened.finalResult.then(\n (value) => value,\n async (error) => {\n if (reserved) await refund('[VernLLM] refundUsage failed after stream error');\n throw error;\n },\n );\n\n // Same rationale as the no-op catch attached where finalResult is first\n // constructed: mark this derived promise observed too, so a caller that\n // only reads `chunks` doesn't get an unhandled-rejection warning from\n // this wrapper promise either.\n finalResult.catch(() => {});\n\n return { chunks: opened.chunks, finalResult };\n}\n","import type { StreamChunk } from '../../types/stream.js';\nimport type { CallWithToolsResult } from '../../types/tools.js';\n\n/**\n * Converts an already-known cache value back into a plausible \"text\" form\n * for a one-shot replay chunk: passed through unchanged if it's already a\n * string (the `jsonMode: false` case), otherwise `JSON.stringify`'d (the\n * `jsonMode: true` case, where the cached value is the *parsed* result, not\n * the original raw text). This is a reasonable reconstruction, not a\n * byte-identical replay of whatever text the model originally streamed,\n * good enough for `for await (const c of chunks)` call sites that don't\n * branch on hit vs. miss, which is the only thing a cache-hit replay needs\n * to support.\n */\nfunction toReplayText(value: unknown): string {\n return typeof value === 'string' ? value : (JSON.stringify(value) ?? '');\n}\n\n/**\n * Builds a trivially-exhausted one-shot `chunks` iterable from an\n * already-known value, used for a `cachedCall` cache hit, where there's no\n * live generation to relay (see `VernLLM.cachedCall`'s docs). No `usage`\n * chunk is emitted: a cache hit spent no real tokens, so there's nothing to\n * report, matching how non-streaming `cachedCall` never calls `onUsage` on\n * a hit either.\n *\n * `hasTools` must reflect whether the *original* call that produced this\n * cached value had `tools` set, that's what determines whether `value` is\n * `T` directly or a `CallWithToolsResult<T>` wrapper, and it isn't\n * something that can be reliably guessed from the value's shape alone\n * (a `schema`-validated `T` could coincidentally look like a\n * `CallWithToolsResult`).\n */\nexport function buildReplayChunks<T>(\n value: T | CallWithToolsResult<T>,\n hasTools: boolean,\n): AsyncIterable<StreamChunk> {\n const items: StreamChunk[] = [];\n\n if (hasTools) {\n const result = value as CallWithToolsResult<T>;\n\n if (result.type === 'tool_calls') {\n result.toolCalls.forEach((toolCall, index) => {\n items.push({\n type: 'tool_call_delta',\n index,\n id: toolCall.id,\n name: toolCall.name,\n argsDelta: JSON.stringify(toolCall.arguments ?? {}),\n // A replay is always the whole value in one shot, never a\n // fragment, same as Gemini's one-shot tool_call_delta chunks.\n complete: true,\n });\n });\n\n if (result.content) items.push({ type: 'text-delta', delta: result.content });\n } else {\n items.push({ type: 'text-delta', delta: toReplayText(result.content) });\n }\n } else {\n items.push({ type: 'text-delta', delta: toReplayText(value) });\n }\n\n return {\n async *[Symbol.asyncIterator]() {\n for (const item of items) yield item;\n },\n };\n}\n\n/**\n * Streaming counterpart to `buildReplayChunks` for a `cachedCall` that\n * *joined* an already-in-flight call for the same key rather than\n * triggering one itself (see `runCachedStream`'s in-flight-coalescing\n * path): there's no live stream to relay (it isn't this call's stream to\n * relay, see the joiner-path comment in `runCachedStream`), but there's\n * also no value yet, only a pending promise for one. Waits for `promise`,\n * then delegates to `buildReplayChunks`. If `promise` rejects, iterating\n * `chunks` throws that same error, consistent with how a live stream's\n * `chunks` throws on a mid-stream failure.\n */\nexport function buildReplayChunksFromPromise<T>(\n promise: Promise<T | CallWithToolsResult<T>>,\n hasTools: boolean,\n): AsyncIterable<StreamChunk> {\n return {\n async *[Symbol.asyncIterator]() {\n const value = await promise;\n\n yield* buildReplayChunks(value, hasTools);\n },\n };\n}\n","import { withReservedUsage, withReservedUsageForStream } from '../execution/usage.utils.js';\nimport { buildReplayChunks, buildReplayChunksFromPromise } from './replay.utils.js';\n\nimport type { Logger } from '../../logger.js';\nimport type { CacheAdapter, StreamChunk } from '../../types/index.js';\nimport type { InternalCacheParams, InternalCacheStreamParams } from './cache.utils.js';\n\n/**\n * Owns cache key resolution, cache reads/writes, and in-flight coalescing\n * for concurrent misses on the same key. Doesn't know about `CallExecutor`,\n * retries, or providers at all: `fn`/`openStream` are opaque callbacks\n * (`VernLLM.cachedCall` passes `() => this.call(...)`), so this class only\n * needs the cache adapter and a logger. Extracted from `VernLLM` since\n * caching and per-target call mechanics are independent concerns that\n * happened to live on the same class.\n */\nexport class CacheOrchestrator {\n private readonly inFlight = new Map<string, Promise<unknown>>();\n\n constructor(\n private readonly cache: CacheAdapter<unknown>,\n private readonly logger: Logger,\n ) {}\n\n /** Resolves a cache key through the adapter when it supports normalization. */\n async resolveCacheKey(key: string): Promise<string> {\n return this.cache.resolveKey ? await this.cache.resolveKey(key) : key;\n }\n\n /**\n * Removes a cached response by key when the configured cache adapter\n * supports deletion. Cache invalidation is the caller's responsibility;\n * only the application knows when cached data is stale.\n */\n async deleteCache(key: string): Promise<void> {\n if (!this.cache.delete) return;\n\n await this.cache.delete(await this.resolveCacheKey(key));\n }\n\n /** Logs a failed refundUsage attempt via the configured logger. */\n private logRefundError(logMessage: string, error: unknown): void {\n this.logger.error(logMessage, {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n\n /**\n * Internal cache primitive around caller-supplied logic. Concurrent misses\n * for the same `cacheKey` share a single in-flight call, avoiding cache\n * stampedes.\n *\n * Backs the public `VernLLM.cachedCall()`, which always composes this\n * with `call()` so cached results get the same retry/timeout/\n * circuit-breaker guarantees as any other LLM call.\n *\n * @param params `cacheKey`, `ttl`, `fn` (the work to run on a cache\n * miss, typically `() => this.call(...)`), and optional\n * `reserveUsage`/`refundUsage`/`signal`. See `InternalCacheParams`.\n * @returns The cached value on a hit, or the result of `fn()` on a miss.\n */\n async runCached<T>(params: InternalCacheParams<T>): Promise<T> {\n const resolvedKey = await this.resolveCacheKey(params.cacheKey);\n const resolvedParams =\n resolvedKey === params.cacheKey ? params : { ...params, cacheKey: resolvedKey };\n\n const cached = await this.cache.get(resolvedKey);\n\n if (cached.hit) return cached.value as T;\n\n const existing = this.inFlight.get(resolvedKey) as Promise<T> | undefined;\n\n if (existing) {\n return withReservedUsage(\n resolvedParams,\n true,\n () => existing,\n params.signal,\n (logMessage, error) => this.logRefundError(logMessage, error),\n );\n }\n\n return this.registerTrigger(resolvedParams);\n }\n\n /** Starts the shared fn() call for a cache miss and tracks it in the in-flight map until it settles. */\n private registerTrigger<T>(params: InternalCacheParams<T>): Promise<T> {\n const resultPromise = withReservedUsage(\n params,\n false,\n () => this.runAndCache(params),\n params.signal,\n (logMessage, error) => this.logRefundError(logMessage, error),\n );\n\n this.inFlight.set(params.cacheKey, resultPromise);\n\n void resultPromise\n .catch(() => {})\n .finally(() => {\n this.inFlight.delete(params.cacheKey);\n });\n\n return resultPromise;\n }\n\n /** Runs `fn` and writes its result to the cache. */\n private async runAndCache<T>(params: InternalCacheParams<T>): Promise<T> {\n const result = await params.fn();\n\n try {\n await this.cache.set(params.cacheKey, result, params.ttl);\n } catch (error) {\n this.logger.error('[VernLLM] cache write failed', {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n\n return result;\n }\n\n /**\n * Streaming counterpart to `runCached`. Three cases:\n *\n * - Hit: no live generation to relay. Returns immediately with\n * `finalResult` resolved to the cached value and a one-shot `chunks`\n * replay built from it, so `for await (const c of chunks)` call sites\n * work identically on a hit or a miss. No usage hooks fire, since\n * nothing was actually spent.\n * - Miss, nothing else in flight for this key: delegates to\n * `registerStreamTrigger`, which opens the stream and relays its\n * `chunks` live.\n * - Miss, but another call for the same key is already in flight: this\n * call has no live chunks of its own to relay, so it's treated like a\n * delayed hit. `finalResult` shares the trigger's in-flight promise\n * (the same in-flight map non-streaming `runCached` uses, so\n * streaming and non-streaming calls for the same key coalesce\n * against each other too), and `chunks` is a one-shot replay built\n * once that promise resolves.\n */\n async runCachedStream<T>(\n params: InternalCacheStreamParams<T>,\n hasTools: boolean,\n ): Promise<{ chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> }> {\n const resolvedKey = await this.resolveCacheKey(params.cacheKey);\n const resolvedParams =\n resolvedKey === params.cacheKey ? params : { ...params, cacheKey: resolvedKey };\n\n const cached = await this.cache.get(resolvedKey);\n\n if (cached.hit) {\n const value = cached.value as T;\n\n return { chunks: buildReplayChunks(value, hasTools), finalResult: Promise.resolve(value) };\n }\n\n const existing = this.inFlight.get(resolvedKey) as Promise<T> | undefined;\n\n if (existing) {\n const finalResult = withReservedUsage(\n resolvedParams,\n true,\n () => existing,\n params.signal,\n (logMessage, error) => this.logRefundError(logMessage, error),\n );\n\n // Mirror the no-op catch in withReservedUsageForStream: buildReplayChunksFromPromise\n // doesn't await this promise until `chunks` is iterated, so a caller that only reads\n // `finalResult` eagerly (or never reads `chunks`) could otherwise trigger an\n // unhandled-rejection warning.\n finalResult.catch(() => {});\n\n return { chunks: buildReplayChunksFromPromise(finalResult, hasTools), finalResult };\n }\n\n return this.registerStreamTrigger(resolvedParams);\n }\n\n /**\n * Opens the shared stream for a cache miss and tracks its settled value\n * in the in-flight map until it resolves or rejects. Writes to the cache\n * on success only, matching `runAndCache`.\n *\n * Registers the in-flight promise synchronously, before anything async\n * runs, so a concurrent `cachedCall` for the same key always sees it in\n * time to join instead of triggering its own stream. Settlement is\n * wired onto the whole `withReservedUsageForStream` call rather than a\n * line inside its callback, so any failure point (reserving usage,\n * opening the stream, or the stream itself) reliably settles the\n * in-flight entry instead of leaving it stuck.\n */\n private registerStreamTrigger<T>(\n params: InternalCacheStreamParams<T>,\n ): Promise<{ chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> }> {\n let resolveInFlight!: (value: T) => void;\n let rejectInFlight!: (error: unknown) => void;\n\n const inFlightResult = new Promise<T>((resolve, reject) => {\n resolveInFlight = resolve;\n rejectInFlight = reject;\n });\n\n this.inFlight.set(params.cacheKey, inFlightResult);\n\n void inFlightResult\n .catch(() => {})\n .finally(() => {\n this.inFlight.delete(params.cacheKey);\n });\n\n const streamPromise = withReservedUsageForStream(\n params,\n async () => {\n const opened = await params.openStream();\n\n const trackedResult: Promise<T> = opened.finalResult.then(\n async (value) => {\n try {\n await this.cache.set(params.cacheKey, value, params.ttl);\n } catch (error) {\n this.logger.error('[VernLLM] cache write failed', {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n\n return value;\n },\n (error: unknown) => {\n // Failed calls aren't cached, matching `runAndCache`, which\n // only calls `cache.set` after `fn()` succeeds. Rethrown\n // unchanged so both the refund logic attached downstream and\n // `inFlightResult` see the real failure.\n throw error;\n },\n );\n\n return { chunks: opened.chunks, finalResult: trackedResult };\n },\n params.signal,\n (logMessage, error) => this.logRefundError(logMessage, error),\n );\n\n // Settles `inFlightResult` (registered above) based on `streamPromise`'s\n // own outcome, not a line inside its callback. See this function's\n // docs for why.\n streamPromise.then(\n (opened) => {\n opened.finalResult.then(resolveInFlight, rejectInFlight);\n },\n (error: unknown) => {\n rejectInFlight(error);\n },\n );\n\n return streamPromise;\n }\n}\n","import { LLMError } from './types/errors.js';\n\nexport interface CircuitBreakerOptions {\n /** Consecutive failures before the circuit opens, default 5 */\n threshold?: number;\n /** How long the circuit stays open before allowing a trial request, in ms. Default 30000 */\n cooldownMs?: number;\n /**\n * Called after every real state change, never for a no-op transition\n * (e.g. open to open). `model` is the resolved model of whichever call\n * triggered this specific transition (the `model` passed to whichever\n * of `assertClosed`/`recordSuccess`/`recordFailure` caused it).\n *\n * With `isolateByModel` off (the default), this is a label only: the\n * breaker still counts failures across every model together, so a\n * threshold crossing can be the sum of several different models'\n * failures even though only the triggering call's `model` is reported\n * here. With `isolateByModel` on, it's exact: each model has its own\n * counter, so the transition really was caused solely by that model.\n */\n onStateChange?: (\n from: CircuitState,\n to: CircuitState,\n consecutiveFailures: number,\n model?: string,\n ) => void;\n /**\n * Track a separate circuit per resolved model instead of one shared\n * circuit for the whole instance. A failure on one model then never\n * opens another model's circuit, at the cost of slower detection for\n * an outage spread across many distinct models (each model's counter\n * must independently cross `threshold`). Default false: one shared\n * circuit, matching every version before this option existed.\n *\n * A call that omits `model` (only possible calling `CircuitBreaker`\n * directly, `VernLLM` always passes one) falls into one shared bucket\n * alongside every other call that also omits it.\n */\n isolateByModel?: boolean;\n}\n\nexport type CircuitState = 'closed' | 'open' | 'half-open';\n\n/** Mutable state for one circuit, either the single shared one or one model's bucket under `isolateByModel`. */\ninterface CircuitBucket {\n state: CircuitState;\n consecutiveFailures: number;\n openedAt: number;\n /**\n * True while a single half-open trial call is in flight. Guards against\n * multiple concurrent callers all treating themselves as \"the\" trial once\n * the cooldown elapses\n */\n trialInFlight: boolean;\n}\n\nfunction newBucket(): CircuitBucket {\n return { state: 'closed', consecutiveFailures: 0, openedAt: 0, trialInFlight: false };\n}\n\n/** Key a bucket lookup falls into when the call omitted `model` under `isolateByModel`. */\nconst UNLABELED_MODEL = '';\n\n/**\n * Per retry VernLLM-instance circuit breaker. Tracks consecutive failures across\n * calls. Once the threshold is hit, short-circuits new calls with an\n * LLMError('circuit_open') instead of hitting the provider, until the\n * cooldown elapses and a single trial call is allowed through\n */\nexport class CircuitBreaker {\n private readonly threshold: number;\n private readonly cooldownMs: number;\n private readonly onStateChange?: CircuitBreakerOptions['onStateChange'];\n private readonly isolateByModel: boolean;\n\n // Exactly one of these is used, chosen once at construction by\n // `isolateByModel`, so every method has a single, unambiguous place to\n // resolve a bucket from instead of branching on the flag repeatedly.\n private readonly sharedBucket: CircuitBucket = newBucket();\n private readonly bucketsByModel = new Map<string, CircuitBucket>();\n\n constructor(options: CircuitBreakerOptions = {}) {\n this.threshold = options.threshold ?? 5;\n this.cooldownMs = options.cooldownMs ?? 30_000;\n this.onStateChange = options.onStateChange;\n this.isolateByModel = options.isolateByModel ?? false;\n }\n\n /** Returns the bucket for a model if one already exists, without allocating. */\n private lookupBucket(model: string | undefined): CircuitBucket | undefined {\n if (!this.isolateByModel) return this.sharedBucket;\n\n const key = model ?? UNLABELED_MODEL;\n return this.bucketsByModel.get(key);\n }\n\n /** Creates and stores a bucket for a model when the first mutation needs one. */\n private ensureBucketFor(model: string | undefined): CircuitBucket {\n if (!this.isolateByModel) return this.sharedBucket;\n\n const key = model ?? UNLABELED_MODEL;\n let bucket = this.bucketsByModel.get(key);\n\n if (!bucket) {\n bucket = newBucket();\n this.bucketsByModel.set(key, bucket);\n }\n\n return bucket;\n }\n\n /** Every state mutation routes through here, so `onStateChange` fires exactly once per real change. */\n private transition(bucket: CircuitBucket, to: CircuitState, model: string | undefined): void {\n if (to === bucket.state) return;\n\n const from = bucket.state;\n bucket.state = to;\n this.onStateChange?.(from, to, bucket.consecutiveFailures, model);\n }\n\n /**\n * Throws if the circuit is open and the cooldown hasn't elapsed, or if\n * the circuit is half-open and a trial call is already in flight.\n * Otherwise, if the circuit just became eligible for a trial (cooldown\n * elapsed, or half-open with no trial currently running), this call\n * becomes that trial\n */\n assertClosed(model?: string): void {\n const bucket = this.ensureBucketFor(model);\n\n if (bucket.state === 'closed') return;\n\n if (bucket.state === 'open') {\n const elapsed = Date.now() - bucket.openedAt;\n if (elapsed < this.cooldownMs) {\n throw new LLMError(\n `Circuit open, provider has failed ${bucket.consecutiveFailures} times in a row. Retry in ${Math.ceil((this.cooldownMs - elapsed) / 1000)}s.`,\n 'circuit_open',\n );\n }\n\n // Set before transition(): a synchronous onStateChange observer\n // that re-enters (e.g. calls assertClosed again) must see this\n // call as already claiming the trial, not still eligible for one.\n bucket.trialInFlight = true;\n this.transition(bucket, 'half-open', model);\n return;\n }\n\n // state === 'half-open'\n if (bucket.trialInFlight) {\n throw new LLMError(\n 'Circuit half-open. A trial request is already in flight. Try again shortly.',\n 'circuit_open',\n );\n }\n\n bucket.trialInFlight = true;\n }\n\n recordSuccess(model?: string): void {\n const bucket = this.lookupBucket(model);\n\n if (!bucket) {\n return;\n }\n\n bucket.consecutiveFailures = 0;\n bucket.trialInFlight = false;\n this.transition(bucket, 'closed', model);\n\n if (this.isolateByModel && bucket.state === 'closed' && bucket.consecutiveFailures === 0) {\n this.bucketsByModel.delete(model ?? UNLABELED_MODEL);\n }\n }\n\n recordFailure(model?: string): void {\n const bucket = this.ensureBucketFor(model);\n\n bucket.consecutiveFailures += 1;\n bucket.trialInFlight = false;\n\n if (bucket.state === 'half-open') {\n // Trial call failed: reopen and reset the cooldown window. Set\n // before transition() for the same reason as assertClosed above:\n // a synchronous observer must see the fresh cooldown, not a stale\n // or zeroed one.\n bucket.openedAt = Date.now();\n this.transition(bucket, 'open', model);\n return;\n }\n\n if (bucket.consecutiveFailures >= this.threshold) {\n bucket.openedAt = Date.now();\n this.transition(bucket, 'open', model);\n }\n }\n\n /**\n * With `isolateByModel` off (the default), `model` is ignored and the\n * one shared circuit's state is returned, unchanged from every version\n * before this option existed. With `isolateByModel` on, returns that\n * model's own state, `'closed'` for a model never seen yet, same as a\n * fresh breaker.\n */\n getState(model?: string): CircuitState {\n return this.lookupBucket(model)?.state ?? 'closed';\n }\n}\n","import { CircuitBreaker, type CircuitBreakerOptions } from '../circuitBreaker.js';\n\nimport type { Logger } from '../logger.js';\nimport type { VernLLMEvent } from '../types/events.js';\n\n/**\n * Builds a `(event) => void` reporter that no-ops when `onEvent` is unset,\n * and otherwise calls it, swallowing and logging any error the handler\n * throws so a broken `onEvent` can't break the call that triggered it.\n * Shared by `buildCircuitBreaker` (which needs to report before any\n * executor exists) and `CallExecutor.reportEvent`, kept independent of the\n * executor for that reason.\n */\nexport function makeEventReporter(\n onEvent: ((event: VernLLMEvent) => void) | undefined,\n logger: Logger,\n): (event: VernLLMEvent) => void {\n return (event) => {\n if (!onEvent) return;\n\n try {\n onEvent(event);\n } catch (error) {\n logger.error('[VernLLM] onEvent failed', {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n };\n}\n\n/**\n * Builds the optional circuit breaker for one provider target, wiring its\n * `onStateChange` to emit a `circuit_state` event and chain any\n * caller-supplied `onStateChange`. Returns `undefined` when\n * `circuitBreakerOption` is falsy, matching the option's own semantics.\n *\n * Lives outside `CallExecutor` (and outside `VernLLM`, once this were\n * inlined) because the breaker has to exist *before* the executor it's\n * passed into, so its construction can't be an executor concern.\n * `onEvent` is called directly rather than through the executor for the\n * same reason: nothing executor-shaped exists yet at this point.\n *\n * Takes the specific fields it needs (rather than a full `VernLLMOptions`)\n * so it works identically for the primary target and for each fallback\n * target, which carry their own `circuitBreaker` override alongside the\n * shared `onEvent`.\n */\nexport function buildCircuitBreaker(\n circuitBreakerOption: boolean | CircuitBreakerOptions | undefined,\n providerName: string,\n defaultModel: string,\n onEvent: ((event: VernLLMEvent) => void) | undefined,\n logger: Logger,\n): CircuitBreaker | undefined {\n if (!circuitBreakerOption) return undefined;\n\n const breakerOptions =\n typeof circuitBreakerOption === 'object' ? circuitBreakerOption : undefined;\n const userOnStateChange = breakerOptions?.onStateChange;\n\n const reportEvent = makeEventReporter(onEvent, logger);\n\n return new CircuitBreaker({\n ...breakerOptions,\n onStateChange: (from, to, consecutiveFailures, model) => {\n reportEvent({\n kind: 'circuit_state',\n provider: providerName,\n model: model ?? defaultModel,\n from,\n to,\n consecutiveFailures,\n });\n\n // A caller-supplied onStateChange would otherwise be silently\n // discarded, since the spread above is overwritten by this\n // property. Chain it instead, same try/catch treatment as every\n // other user-supplied callback so it can't break breaker\n // bookkeeping or the call that triggered it.\n if (!userOnStateChange) return;\n\n try {\n userOnStateChange(from, to, consecutiveFailures, model);\n } catch (error) {\n logger.error('[VernLLM] circuitBreaker.onStateChange failed', {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n },\n });\n}\n","import { LLMError } from '../../types/errors.js';\n\nimport type { Logger } from '../../logger.js';\n\n/**\n * Default cap (ms) for both exponential backoff and honored Retry-After\n * values, so a misbehaving/adversarial Retry-After can't stall a caller\n * indefinitely\n */\nexport const DEFAULT_MAX_DELAY_MS = 10_000;\n\n/**\n * `setTimeout` silently clamps any delay above this (~24.8 days) or\n * `Infinity` down to ~1ms instead of erroring, so a caller passing\n * `Infinity` as \"no timeout\" gets the opposite of what they asked for.\n * Both timeout helpers below guard against this explicitly.\n */\nconst MAX_SETTIMEOUT_MS = 2_147_483_647;\n\n/**\n * Resolves a timeout value to the number `setTimeout` should actually use,\n * or `undefined` when the timeout should be treated as disabled (0,\n * negative, or `Infinity`). Returning the resolved value directly, rather\n * than a boolean, lets callers narrow `number | undefined` to `number`\n * without an `as number` cast.\n */\nfunction resolveActiveTimeoutMs(ms: number | undefined): number | undefined {\n return !ms || ms <= 0 || ms === Infinity ? undefined : ms;\n}\n\n/** Caps a timeout at the largest delay `setTimeout` actually honors. */\nfunction clampTimeoutMs(ms: number): number {\n return Math.min(ms, MAX_SETTIMEOUT_MS);\n}\n\n/**\n * Runs an async function and cancels it if it takes longer than the given\n * timeout. Creates an internal abort controller that fires after the\n * timeout elapses, and combines it with any external signal the caller\n * passed in so either one can cancel the underlying call. If the internal\n * timeout triggers and the underlying operation aborts, the error is\n * converted into an LLMError with type \"timeout\". External cancellations\n * continue to propagate as aborted errors. The internal timer is always\n * cleared afterward, whether the function succeeds, fails, or is aborted,\n * so nothing is left running in the background.\n *\n * `timeoutMs` of `Infinity` (or any value beyond what `setTimeout` can\n * represent) disables the timeout rather than firing almost immediately.\n */\nexport async function withTimeout<T>(\n fn: (signal: AbortSignal) => Promise<T>,\n timeoutMs: number,\n externalSignal?: AbortSignal,\n): Promise<T> {\n const controller = new AbortController();\n\n const activeTimeoutMs = resolveActiveTimeoutMs(timeoutMs);\n\n const timer =\n activeTimeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n controller.abort();\n }, clampTimeoutMs(activeTimeoutMs));\n\n const signal = externalSignal\n ? AbortSignal.any([externalSignal, controller.signal])\n : controller.signal;\n\n try {\n return await fn(signal);\n } catch (err) {\n if (\n controller.signal.aborted &&\n !externalSignal?.aborted &&\n err instanceof DOMException &&\n err.name === 'AbortError'\n ) {\n throw new LLMError('Request timed out', 'timeout');\n }\n\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Races one `iterator.next()` call against a per-call idle timer, to\n * bound the gap *between* chunks (unlike `withTimeout`, which only bounds\n * opening the stream and its first chunk). Without this, a connection\n * that streams one chunk then hangs would never fail.\n *\n * `timeoutMs` of 0/undefined/`Infinity` disables the check. Otherwise\n * rejects with `LLMError('timeout')` if `next()` doesn't settle in time.\n * The clock resets on every call, so the window is measured from the most\n * recent chunk, not from stream start.\n *\n * `onIdle`, if given, is called the moment the timer fires (before the\n * rejection), so callers can abort the underlying transport instead of\n * just walking away from an unread promise. `logger`, if given, records a\n * debug line if `next()` still settles *after* the idle timeout already\n * rejected. `resolve`/`reject` on an already-settled promise is otherwise\n * a silent no-op, so without this the late chunk (possibly the final\n * usage chunk) would vanish with no trace.\n */\nexport function withChunkIdleTimeout<T>(\n next: () => Promise<IteratorResult<T>>,\n timeoutMs: number | undefined,\n onIdle?: () => void,\n logger?: Pick<Logger, 'debug'>,\n): Promise<IteratorResult<T>> {\n const activeTimeoutMs = resolveActiveTimeoutMs(timeoutMs);\n\n if (activeTimeoutMs === undefined) {\n return next();\n }\n\n let settled = false;\n\n return new Promise<IteratorResult<T>>((resolve, reject) => {\n const timer = setTimeout(() => {\n settled = true;\n onIdle?.();\n reject(\n new LLMError(`No stream chunk received for ${activeTimeoutMs}ms (idle timeout)`, 'timeout'),\n );\n }, clampTimeoutMs(activeTimeoutMs));\n\n next().then(\n (result) => {\n clearTimeout(timer);\n if (settled) {\n logger?.debug('[VernLLM] chunk resolved after idle timeout already fired; discarding');\n return;\n }\n settled = true;\n resolve(result);\n },\n (error: unknown) => {\n clearTimeout(timer);\n if (settled) {\n logger?.debug(\n '[VernLLM] chunk rejection arrived after idle timeout already fired; discarding',\n );\n return;\n }\n settled = true;\n reject(error);\n },\n );\n });\n}\n\n/**\n * Looks inside an unknown error value for a Retry-After header and\n * converts it to milliseconds. Checks `.headers` first (fetch-style,\n * Headers-like with `.get()`), then `.response.headers` (axios-style,\n * plain object) since different client libraries surface headers\n * differently. Supports both the delta-seconds form (\"30\") and the\n * HTTP-date form (\"Wed, 21 Oct 2015 07:28:00 GMT\"). The result is capped\n * at maxDelayMs. Returns undefined when no usable Retry-After is present\n */\nexport function extractRetryAfterMs(\n err: unknown,\n maxDelayMs = DEFAULT_MAX_DELAY_MS,\n): number | undefined {\n if (!err || typeof err !== 'object') return undefined;\n\n const error = err as { headers?: unknown; response?: { headers?: unknown } };\n const headers = error.headers ?? error.response?.headers;\n\n if (!headers || typeof headers !== 'object') return undefined;\n\n const getter = headers as { get?: (name: string) => string | null };\n\n const raw =\n typeof getter.get === 'function'\n ? getter.get('Retry-After')\n : Object.entries(headers as Record<string, string>)\n .find(([name]) => name.toLowerCase() === 'retry-after')\n ?.at(1);\n\n if (typeof raw !== 'string' || raw.trim() === '') return undefined;\n\n const trimmed = raw.trim();\n\n if (/^\\d+$/.test(trimmed)) {\n return Math.max(0, Math.min(Number(trimmed) * 1000, maxDelayMs));\n }\n\n const dateMs = Date.parse(trimmed);\n if (!Number.isNaN(dateMs)) {\n return Math.max(0, Math.min(dateMs - Date.now(), maxDelayMs));\n }\n\n return undefined;\n}\n\n/**\n * Exponential backoff with jitter, capped at maxDelayMs.\n * Jitter avoids thundering-herd retries when many callers back off in lockstep,\n * the cap prevents unbounded delays when maxRetries is high\n */\nexport function getBackoffDelay(\n baseDelayMs: number,\n attempt: number,\n maxDelayMs = DEFAULT_MAX_DELAY_MS,\n): number {\n const exp = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);\n return exp / 2 + Math.random() * (exp / 2);\n}\n\n/**\n * Pauses execution for the given delay before a retry attempt. If an\n * abort signal is provided and it fires while waiting, the pending\n * timer is cancelled immediately and the wait rejects right away with\n * an aborted error instead of continuing to sit idle until the delay\n * would have finished on its own\n */\nexport async function waitForRetry(delay: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new LLMError('Operation aborted', 'aborted');\n }\n\n await new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(new LLMError('Operation aborted', 'aborted'));\n };\n\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, delay);\n\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","import { LLMError } from '../../types/errors.js';\nimport { extractRetryAfterMs } from './retry.utils.js';\n\n/**\n * Looks inside an unknown error value and pulls out an http status code\n * if one is present. Checks the status field first then the status code\n * field since different client libraries use different names for this,\n * falling back to AWS SDK v3's `$metadata.httpStatusCode` (e.g. Bedrock's\n * `ThrottlingException`), which doesn't set either of the other two.\n * Returns undefined when the error is not an object or carries no status\n */\nexport function extractStatus(err: unknown): number | undefined {\n if (!err || typeof err !== 'object') return undefined;\n\n const error = err as {\n status?: unknown;\n statusCode?: unknown;\n $metadata?: { httpStatusCode?: unknown };\n };\n\n if (typeof error.status === 'number') return error.status;\n if (typeof error.statusCode === 'number') return error.statusCode;\n if (typeof error.$metadata?.httpStatusCode === 'number') return error.$metadata.httpStatusCode;\n\n return undefined;\n}\n\n/**\n * POSIX/libuv error codes libuv (and so Node's `fetch`/undici) attaches to\n * genuine transport-level failures: connection refused, DNS lookup\n * failure, connection reset mid-request, a connect that never completed,\n * DNS server unreachable, broken pipe, or host/network unreachable.\n * Deliberately narrow: only codes that can only mean \"the connection\n * itself failed,\" not anything that could also indicate an application\n * error.\n */\nconst NETWORK_ERROR_CODES = new Set([\n 'ECONNREFUSED',\n 'ENOTFOUND',\n 'ECONNRESET',\n 'ETIMEDOUT',\n 'EAI_AGAIN',\n 'EPIPE',\n 'ECONNABORTED',\n 'EHOSTUNREACH',\n 'ENETUNREACH',\n]);\n\n/** `fetch`'s own wording for a transport-level failure, across runtimes/browsers. */\nconst NETWORK_ERROR_MESSAGES = new Set([\n 'fetch failed', // Node/undici\n 'failed to fetch', // Chromium\n 'load failed', // Safari\n 'networkerror when attempting to fetch resource.', // Firefox\n]);\n\n/**\n * Whether `error` is, with reasonable confidence, a transport-level\n * failure (never reached the provider, as opposed to the provider itself\n * responding with an error) rather than some other unexpected exception.\n * Checked via explicit, well-known signals only, so a genuinely unknown\n * error never gets misclassified as a connection failure just because it\n * also lacked an HTTP status.\n */\nfunction isNetworkError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false;\n\n const err = error as { code?: unknown; message?: unknown; cause?: unknown };\n\n if (typeof err.code === 'string' && NETWORK_ERROR_CODES.has(err.code)) return true;\n\n if (typeof err.message === 'string' && NETWORK_ERROR_MESSAGES.has(err.message.toLowerCase())) {\n return true;\n }\n\n // undici/Node's `fetch` wraps the real libuv error one level down, as\n // TypeError('fetch failed', { cause: <the real error, with .code> }).\n // The message check above already catches that wrapper by itself if\n // the cause is missing or unrecognized, this catches it by the cause's\n // code when the wrapper's own message wasn't matched (e.g. a runtime\n // that phrases the wrapper differently but still sets `cause.code`).\n if (err.cause && typeof err.cause === 'object') {\n const cause = err.cause as { code?: unknown };\n if (typeof cause.code === 'string' && NETWORK_ERROR_CODES.has(cause.code)) return true;\n }\n\n return false;\n}\n\nfunction formatSafely(value: unknown): string {\n try {\n return JSON.stringify(value, null, 2) ?? String(value);\n } catch {\n try {\n return String(value);\n } catch {\n return '[unprintable error]';\n }\n }\n}\n\n/**\n * Looks inside an unknown thrown value and pulls out a human-readable\n * description of it. Checks the `error` field first (the provider's raw\n * rejection body, JSON-stringified if possible) then falls back to the\n * message` field. Always returns a safe string, even when the thrown value\n * has hostile properties or cannot be serialized normally.\n */\nexport function describeError(err: unknown): string {\n if (err && typeof err === 'object') {\n try {\n const error = err as { message?: unknown; error?: unknown };\n\n if (error.error !== undefined) {\n return formatSafely(error.error);\n }\n\n if (typeof error.message === 'string') {\n return error.message;\n }\n } catch {\n // Fall through to safe string.\n }\n }\n\n return formatSafely(err);\n}\n\n/** Converts any thrown value into a well-typed LLMError. */\nexport function normalizeError(error: unknown, signal?: AbortSignal): LLMError {\n if (signal?.aborted) {\n return new LLMError('LLM request aborted', 'aborted');\n }\n\n if (error instanceof LLMError) {\n // A caller or adapter can throw an already-built LLMError directly\n // (bypassing the generic-SDK-error path below), so a 429/401/403\n // reaching us this way still needs the same `code` a generic error\n // with that status gets, without overwriting a `code` that error\n // already carries.\n if (error.code === undefined) {\n if (error.status === 429) {\n error.code = 'provider_rate_limited';\n } else if (error.status === 401 || error.status === 403) {\n error.code = 'invalid_credentials';\n }\n }\n\n return error;\n }\n\n const status = extractStatus(error);\n const retryAfterMs = extractRetryAfterMs(error);\n\n if (status !== undefined) {\n return new LLMError(\n 'LLM request failed',\n 'api',\n status,\n undefined,\n error,\n retryAfterMs,\n status === 429\n ? 'provider_rate_limited'\n : status === 401 || status === 403\n ? 'invalid_credentials'\n : undefined,\n );\n }\n\n // No extractable HTTP status: distinguish a genuine transport-level\n // failure (DNS, connection refused, connection reset) from any other\n // unexpected exception via explicit signals only, rather than assuming\n // every status-less error reaching here is a connection failure.\n return new LLMError(\n 'LLM request failed',\n 'unknown',\n undefined,\n undefined,\n error,\n retryAfterMs,\n isNetworkError(error) ? 'connection_failed' : undefined,\n );\n}\n","/** Default `parseJson`: `JSON.parse` wrapped in try/catch, returning `undefined` on failure. */\nexport function defaultParseJson(content: string): unknown {\n try {\n return JSON.parse(content);\n } catch {\n return undefined;\n }\n}\n","import { LLMError } from '../../types/errors.js';\n\nimport type { LLMClient, WireToolCall } from '../../types/client.js';\nimport type { ToolCall, ToolDefinition } from '../../types/tools.js';\n\n/** Translates app-facing `ToolDefinition[]` into the OpenAI-shaped wire tools array. */\nexport function toWireTools(\n tools: ToolDefinition[],\n): NonNullable<Parameters<LLMClient['chat']['completions']['create']>[0]['tools']> {\n return tools.map((tool) => ({\n type: 'function' as const,\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n }));\n}\n\n/** Translates app-facing `ToolCall[]` (e.g. from a replayed assistant turn) into wire tool_calls. */\nexport function toWireToolCalls(toolCalls: ToolCall[]): WireToolCall[] {\n return toolCalls.map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.arguments ?? {}),\n },\n }));\n}\n\n/**\n * Parses the provider's wire-shaped `tool_calls` back into VernLLM's\n * `ToolCall[]`. Malformed argument JSON is a `'parse'` error, same\n * convention as malformed JSON response bodies elsewhere in VernLLM.\n */\nexport function parseWireToolCalls(wireToolCalls: WireToolCall[]): ToolCall[] {\n return wireToolCalls.map((wc) => {\n let parsedArgs: unknown;\n\n try {\n parsedArgs = wc.function.arguments.trim() ? JSON.parse(wc.function.arguments) : {};\n } catch {\n throw new LLMError(`Invalid JSON arguments for tool call \"${wc.function.name}\"`, 'parse');\n }\n\n return { id: wc.id, name: wc.function.name, arguments: parsedArgs };\n });\n}\n","import { LLMError } from '../../types/errors.js';\nimport { toWireTools, toWireToolCalls } from './wire.utils.js';\n\nimport type {\n CallParams,\n ConversationTurn,\n WireMessage,\n WireToolChoice,\n} from '../../types/index.js';\n\n/** Everything `RequestBuilder` needs beyond a single call's own `CallParams`. */\nexport interface RequestBuilderOptions {\n model: string;\n defaultMaxTokens: number;\n defaultTemperature: number | null;\n}\n\n/**\n * Builds the wire request object for one call, applying per-instance\n * defaults (model, max tokens, temperature) and per-call overrides.\n * Owns every validation that depends only on shape, not on execution:\n * history alternation, duplicate/empty tool lists, `toolChoice` naming a\n * real tool. Has no knowledge of retry, timeouts, or the breaker, only\n * the three defaults a `FallbackTarget` can override per-target (see the\n * `defaultMaxTokens`/`defaultTemperature` overrides in the fallback\n * design), which is what keeps it separable from `CallExecutor`.\n */\nexport class RequestBuilder {\n private readonly model: string;\n private readonly defaultMaxTokens: number;\n private readonly defaultTemperature: number | null;\n\n constructor(options: RequestBuilderOptions) {\n this.model = options.model;\n this.defaultMaxTokens = options.defaultMaxTokens;\n this.defaultTemperature = options.defaultTemperature;\n }\n\n /** Applies per-call defaults and shapes params into the client's request object. */\n build<T>(params: CallParams<T>) {\n const {\n systemPrompt,\n userContent,\n history = [],\n maxTokens = this.defaultMaxTokens,\n model = this.model,\n reasoningEffort,\n jsonSchema,\n tools,\n toolChoice,\n } = params;\n\n const temperature =\n params.temperature === undefined ? this.defaultTemperature : params.temperature;\n\n if (tools && tools.length === 0) {\n throw new LLMError(\n '`tools` was an empty array. This is almost always a bug (e.g. a filtered tool list ' +\n 'that ended up empty). An empty `tools` array still switches on tool-call mode ' +\n '(response shape, jsonMode default, wire format) with nothing for the model to call. ' +\n 'Omit `tools` entirely for a normal call, or make sure the array is non-empty.',\n 'validation',\n );\n }\n\n if (tools) {\n const seen = new Set<string>();\n const duplicates = new Set<string>();\n\n for (const tool of tools) {\n if (seen.has(tool.name)) duplicates.add(tool.name);\n seen.add(tool.name);\n }\n\n if (duplicates.size) {\n throw new LLMError(\n `\\`tools\\` has duplicate name(s): [${[...duplicates].join(', ')}]. Tool names must be unique.`,\n 'validation',\n );\n }\n }\n\n if (toolChoice && !tools) {\n throw new LLMError(\n '`toolChoice` was set without `tools`. There is nothing for it to choose between. ' +\n 'Set `tools`, or remove `toolChoice`.',\n 'validation',\n );\n }\n\n if (tools && typeof toolChoice === 'object' && !tools.some((t) => t.name === toolChoice.name)) {\n throw new LLMError(\n `toolChoice names \"${toolChoice.name}\", which is not in \\`tools\\` ([${tools.map((t) => t.name).join(', ')}]).`,\n 'validation',\n );\n }\n\n // Defaults to false when tools are set and the caller didn't say otherwise,\n // since forcing a JSON response format alongside tool calling is unreliable\n // across providers.\n const jsonMode = params.jsonMode ?? (tools ? false : true);\n const useJson = jsonMode || Boolean(jsonSchema);\n\n if (params.schema && !useJson) {\n throw new LLMError(\n 'schema was provided but jsonMode: false disables JSON parsing, so nothing would validate it. Remove jsonMode: false, set jsonSchema, or remove schema.',\n 'validation',\n );\n }\n\n const responseFormat = this.buildResponseFormat(jsonSchema, useJson);\n\n this.validateHistory(history);\n\n const request = {\n model,\n ...(temperature !== null ? { temperature } : {}),\n max_tokens: maxTokens,\n ...(responseFormat ? { response_format: responseFormat } : {}),\n ...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),\n ...(tools ? { tools: toWireTools(tools) } : {}),\n ...(tools ? { tool_choice: this.buildWireToolChoice(toolChoice) } : {}),\n messages: [\n ...(systemPrompt ? [{ role: 'system' as const, content: systemPrompt }] : []),\n ...history.flatMap((turn): WireMessage[] => this.turnToWireMessages(turn)),\n { role: 'user' as const, content: userContent },\n ] satisfies WireMessage[],\n };\n\n return { useJson, model, request };\n }\n\n /**\n * Validates `history` alternates user/assistant turns, since providers\n * like Anthropic/Gemini reject or mishandle consecutive same-role turns.\n */\n private validateHistory(history: ConversationTurn[]): void {\n let previousTurn: ConversationTurn | undefined;\n\n for (const [index, turn] of history.entries()) {\n if (turn.role === 'tool') {\n if (previousTurn?.role !== 'assistant' || !previousTurn.toolCalls?.length) {\n throw new LLMError(\n `history[${index}] is a \"tool\" turn, but must immediately follow an \"assistant\" turn that requested tools`,\n 'validation',\n );\n }\n\n if (!turn.toolResults?.length) {\n throw new LLMError(\n `history[${index}] is a \"tool\" turn but has no toolResults`,\n 'validation',\n );\n }\n\n const requestedIds = new Set(previousTurn.toolCalls.map((tc) => tc.id));\n const resultIds = turn.toolResults.map((tr) => tr.toolCallId);\n\n const unknownIds = resultIds.filter((id) => !requestedIds.has(id));\n\n if (unknownIds.length) {\n throw new LLMError(\n `history[${index}].toolResults references unknown toolCallId(s) [${unknownIds.join(', ')}]`,\n 'validation',\n );\n }\n\n // Catches a duplicated toolCallId that would otherwise mask a different call's missing result.\n const seenIds = new Set<string>();\n const duplicateIds = new Set<string>();\n\n for (const id of resultIds) {\n if (seenIds.has(id)) duplicateIds.add(id);\n seenIds.add(id);\n }\n\n if (duplicateIds.size) {\n throw new LLMError(\n `history[${index}].toolResults has duplicate toolCallId(s) [${[...duplicateIds].join(', ')}]`,\n 'validation',\n );\n }\n\n const missingIds = [...requestedIds].filter((id) => !resultIds.includes(id));\n\n if (missingIds.length) {\n throw new LLMError(\n `history[${index}] is missing toolResults for toolCallId(s) [${missingIds.join(', ')}]`,\n 'validation',\n );\n }\n } else {\n if (turn.role === previousTurn?.role) {\n throw new LLMError(\n `history must alternate user/assistant turns: consecutive \"${turn.role}\" turns at history[${index - 1}] and history[${index}]`,\n 'validation',\n );\n }\n\n if (previousTurn?.role === 'assistant' && previousTurn.toolCalls?.length) {\n throw new LLMError(\n `history[${index}] follows an assistant tool request without tool results`,\n 'validation',\n );\n }\n }\n\n previousTurn = turn;\n }\n\n if (previousTurn?.role === 'assistant' && previousTurn.toolCalls?.length) {\n throw new LLMError(\n 'The last entry in history is an assistant tool request without tool results',\n 'validation',\n );\n }\n\n if (previousTurn?.role === 'user') {\n throw new LLMError(\n 'The last entry in history is a \"user\" turn, which would collide with the current userContent turn.',\n 'validation',\n );\n }\n }\n\n /** Maps VernLLM's app-facing `ToolChoice` onto the OpenAI-shaped wire `tool_choice`. */\n private buildWireToolChoice(toolChoice: CallParams<unknown>['toolChoice']): WireToolChoice {\n if (!toolChoice || toolChoice === 'auto') return 'auto';\n if (toolChoice === 'none' || toolChoice === 'required') return toolChoice;\n\n return { type: 'function', function: { name: toolChoice.name } };\n }\n\n /**\n * Expands one `ConversationTurn` into one or more wire messages. Plain\n * user/assistant turns map 1:1. An assistant turn with `toolCalls` maps\n * to an assistant message carrying wire-shaped `tool_calls`. A `'tool'`\n * turn expands into one wire `tool` message per `toolResult`, since\n * OpenAI-shaped wire format wants one message per tool_call_id.\n */\n private turnToWireMessages(turn: ConversationTurn): WireMessage[] {\n if (turn.role === 'tool') {\n return (turn.toolResults ?? []).map((tr) => ({\n role: 'tool' as const,\n tool_call_id: tr.toolCallId,\n content: typeof tr.content === 'string' ? tr.content : JSON.stringify(tr.content ?? null),\n ...(tr.isError ? { is_error: true } : {}),\n }));\n }\n\n if (turn.role === 'assistant' && turn.toolCalls?.length) {\n return [\n {\n role: 'assistant' as const,\n ...(turn.content ? { content: turn.content } : {}),\n tool_calls: toWireToolCalls(turn.toolCalls),\n },\n ];\n }\n\n return [{ role: turn.role as 'user' | 'assistant', content: turn.content ?? '' }];\n }\n\n /**\n * Chooses the response format: a provider-native `jsonSchema` takes\n * priority when supplied (constrains generation directly), otherwise\n * falls back to the looser `json_object` mode when JSON output is\n * requested, or no format at all for plain text responses.\n */\n private buildResponseFormat(jsonSchema: CallParams<unknown>['jsonSchema'], useJson: boolean) {\n if (jsonSchema) {\n return {\n type: 'json_schema' as const,\n json_schema: {\n name: jsonSchema.name,\n schema: jsonSchema.schema,\n strict: jsonSchema.strict ?? true,\n description: jsonSchema.description,\n },\n };\n }\n\n return useJson ? { type: 'json_object' as const } : undefined;\n }\n}\n","import { normalizeError } from './errors.utils.js';\nimport { withChunkIdleTimeout } from './retry.utils.js';\n\nimport type { Logger } from '../../logger.js';\nimport type { LLMError } from '../../types/errors.js';\nimport type {\n CallWithToolsResult,\n StreamChunk,\n TokenUsage,\n WireStreamChunk,\n WireToolCall,\n} from '../../types/index.js';\n\n/** Everything `buildStreamResult` needs beyond the raw iterator and first chunk. */\nexport interface StreamAccumulatorOptions<T> {\n requestId: string;\n model: string;\n providerName: string;\n /** Whether this attempt ran on a fallback target rather than the primary, mirroring `extractUsage`'s `usedFallback`. */\n isFallback: boolean;\n /** Per-call override, falling back to the instance default, mirroring every other per-call timeout. */\n chunkIdleTimeoutMs: number | undefined;\n streamController: AbortController;\n logger: Logger;\n /** External signal, forwarded to `normalizeError` so a transport error during an already-aborted call is reported as `'aborted'`, not whatever the transport itself threw. */\n signal?: AbortSignal;\n /**\n * Fires once, synchronously, right after the transport-level loop\n * finishes successfully, before `finalize` runs. Lets the caller record\n * circuit-breaker success and release rate-limiter capacity before the\n * (possibly failing) finalization step.\n */\n onStreamSuccess: (usage: TokenUsage | undefined) => void;\n /**\n * Fires once when the transport-level loop itself fails (idle timeout\n * or a transport error), before this stream's `finalResult` rejects.\n * `normalized.type === 'timeout'` is the one mid-stream failure that\n * should trip the breaker: otherwise a provider that hangs after one\n * chunk would always record a success and never open it.\n */\n onStreamFailure: (normalized: LLMError, usage: TokenUsage | undefined) => void;\n /**\n * Produces the final `T | CallWithToolsResult<T>` from the accumulated\n * text and tool-call deltas once the stream completes. Errors thrown\n * here are assumed already normalized and usage-failure-reported by the\n * caller (mirrors `finalizeResponse`'s own contract).\n */\n finalize: (\n textAcc: string,\n wireToolCalls: WireToolCall[] | undefined,\n usage: TokenUsage | undefined,\n ) => T | CallWithToolsResult<T>;\n}\n\n/**\n * The streaming accumulator: wraps the raw `WireStreamChunk` iterator in\n * an async generator that yields translated `StreamChunk`s to the caller\n * live, as they arrive, with no per-chunk timeout and no bound on total\n * duration, and accumulates text/tool-call deltas internally so that\n * `finalize` can produce `finalResult` once the stream completes.\n *\n * Two separate try/catches: the iteration loop's catch handles errors\n * the transport itself throws, which aren't normalized yet, so that\n * happens here, alongside the one `onStreamFailure` call for them. The\n * second catch, around `finalize`, does not re-normalize or re-report,\n * since `finalize`'s caller (`finalizeResponse`) already does both\n * internally.\n */\nexport function buildStreamResult<T>(\n iterator: AsyncIterator<WireStreamChunk>,\n first: IteratorResult<WireStreamChunk>,\n options: StreamAccumulatorOptions<T>,\n): { chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T | CallWithToolsResult<T>> } {\n const {\n requestId,\n model,\n providerName,\n isFallback,\n chunkIdleTimeoutMs,\n streamController,\n logger,\n signal,\n } = options;\n\n let resolveFinal!: (value: T | CallWithToolsResult<T>) => void;\n let rejectFinal!: (error: unknown) => void;\n\n const finalResult = new Promise<T | CallWithToolsResult<T>>((resolve, reject) => {\n resolveFinal = resolve;\n rejectFinal = reject;\n });\n\n // Avoid an unhandled-rejection warning for callers that only read `chunks`.\n finalResult.catch(() => {});\n\n // Push-based, not a pulled generator, so the pump always drives\n // `finalResult` to completion even if `chunks` is never read. Buffer\n // size (not \"has anyone started iterating yet\") is what caps memory,\n // since the pump can outrace the caller starting iteration.\n const MAX_BUFFERED_CHUNKS = 10_000;\n const buffered: StreamChunk[] = [];\n const pending: Array<{\n resolve: (result: IteratorResult<StreamChunk>) => void;\n reject: (error: unknown) => void;\n }> = [];\n let streamDone = false;\n let streamError: unknown;\n let hasLoggedEviction = false;\n\n const push = (chunk: StreamChunk) => {\n const waiter = pending.shift();\n\n if (waiter) {\n waiter.resolve({ done: false, value: chunk });\n return;\n }\n\n buffered.push(chunk);\n\n if (buffered.length > MAX_BUFFERED_CHUNKS * 2) {\n // Nothing else surfaces this: without a log, a caller that never\n // read (or fell behind on) `chunks` has no way to tell eviction,\n // not a provider or transport bug, is why chunks are missing.\n // Logged once per stream, not on every crossing, so an ignored\n // high-volume stream doesn't spam dozens of near-identical lines.\n if (!hasLoggedEviction) {\n hasLoggedEviction = true;\n logger.warn(\n `[VernLLM] stream chunk buffer exceeded cap (${MAX_BUFFERED_CHUNKS}), evicting ` +\n `${buffered.length - MAX_BUFFERED_CHUNKS} oldest chunk(s); buffered=${buffered.length}. ` +\n 'The chunks iterable was never read (or fell far behind) for this stream.',\n );\n }\n\n // Trim back down to the cap in one batch operation instead of\n // `shift()`ing a single element off on every push once the cap is\n // reached. A per-push `shift()` here is O(current length) in the\n // worst case, cheap for a handful of calls, but that cost is\n // paid on *every* push for the remainder of an ignored stream,\n // and its real-world cost isn't a stable, engine-independent\n // property: benchmarking this exact pattern at a similar backing\n // -array size showed multi-second stalls for what should be\n // sub-millisecond work. Letting the array grow to 2x the cap\n // before trimming amortizes the O(n) `splice` across\n // `MAX_BUFFERED_CHUNKS` pushes, so the average cost per push\n // stays O(1) regardless of how far past the cap the array is\n // allowed to grow before trimming.\n buffered.splice(0, buffered.length - MAX_BUFFERED_CHUNKS);\n }\n };\n\n const finish = () => {\n streamDone = true;\n\n for (const waiter of pending.splice(0)) {\n waiter.resolve({ done: true, value: undefined });\n }\n };\n\n const fail = (error: unknown) => {\n streamDone = true;\n streamError = error;\n\n for (const waiter of pending.splice(0)) {\n waiter.reject(error);\n }\n };\n\n const chunks: AsyncIterable<StreamChunk> = {\n [Symbol.asyncIterator]() {\n return {\n next(): Promise<IteratorResult<StreamChunk>> {\n if (buffered.length) {\n return Promise.resolve({ done: false, value: buffered.shift() as StreamChunk });\n }\n\n if (streamDone) {\n return streamError\n ? Promise.reject(streamError)\n : Promise.resolve({ done: true, value: undefined });\n }\n\n return new Promise((resolve, reject) => {\n pending.push({ resolve, reject });\n });\n },\n };\n },\n };\n\n const toolCallAcc = new Map<number, { id?: string; name?: string; args: string }>();\n\n let textAcc = '';\n let usage: TokenUsage | undefined;\n\n // Fires immediately, not lazily, so it always drives finalResult to\n // completion regardless of whether the caller reads chunks.\n void (async () => {\n try {\n let result: IteratorResult<WireStreamChunk> = first;\n\n while (!result.done) {\n const wireChunk = result.value;\n\n if (wireChunk.type === 'ping') {\n // No content to accumulate or push. Just resolving here\n // resets the idle-timeout clock on the next .next() call.\n } else if (wireChunk.type === 'text-delta') {\n textAcc += wireChunk.delta;\n push({ type: 'text-delta', delta: wireChunk.delta });\n } else if (wireChunk.type === 'tool_call_delta') {\n const entry = toolCallAcc.get(wireChunk.index) ?? { args: '' };\n\n entry.id ??= wireChunk.id;\n entry.name ??= wireChunk.name;\n entry.args += wireChunk.argumentsDelta ?? '';\n toolCallAcc.set(wireChunk.index, entry);\n\n push({\n type: 'tool_call_delta',\n index: wireChunk.index,\n id: wireChunk.id,\n name: wireChunk.name,\n argsDelta: wireChunk.argumentsDelta,\n complete: wireChunk.complete,\n });\n } else if (wireChunk.type === 'usage') {\n usage = {\n promptTokens: wireChunk.usage.prompt_tokens ?? 0,\n completionTokens: wireChunk.usage.completion_tokens ?? 0,\n totalTokens: wireChunk.usage.total_tokens ?? 0,\n requestId,\n model,\n provider: providerName,\n usedFallback: isFallback,\n };\n push({ type: 'usage', usage });\n }\n\n result = await withChunkIdleTimeout(\n () => iterator.next(),\n chunkIdleTimeoutMs,\n () => streamController.abort(),\n logger,\n );\n }\n } catch (error) {\n // Best-effort cleanup for a processing-time throw (as opposed to\n // `iterator.next()` rejecting, which usually means the adapter's\n // own generator already cleaned up). Two independent layers,\n // since neither is guaranteed to reach every SDK on its own:\n //\n // 1. `iterator.return()`: `iterator` is the async generator\n // returned by the adapter's `createStream`, and every\n // adapter's `createStream` body is a `for await...of` over\n // the SDK's raw stream. Calling `.return()` on a generator\n // suspended inside a `for await...of` forwards `.return()` to\n // the iterable being iterated, standard IteratorClose\n // behavior, so this one call closes the whole chain down to\n // the SDK's own stream, as long as the SDK's stream\n // implements `.return()` (true for every adapter here, see\n // the \"propagates .return()\" test in each adapter's stream\n // unit tests).\n // 2. `streamController.abort()`: aborts the same signal every\n // adapter received for this call. SDKs that honor an\n // AbortSignal for the life of the request, arguably the more\n // common pattern than implementing custom `.return()`\n // forwarding, get closed this way even if layer 1 has nothing\n // to forward to.\n try {\n await iterator.return?.();\n } catch {\n // Cleanup failing isn't the error being reported; swallow it.\n }\n streamController.abort();\n\n const normalized = normalizeError(error, signal);\n\n try {\n options.onStreamFailure(normalized, usage);\n } catch {\n // A throwing callback must not stop fail/rejectFinal from settling\n // the promises below; the stream failure itself is still reported.\n }\n\n fail(normalized);\n rejectFinal(normalized);\n\n return;\n }\n\n finish();\n\n try {\n options.onStreamSuccess(usage);\n } catch {\n // A throwing callback must not stop finalize/resolveFinal below from\n // running; the stream itself still completed successfully.\n }\n\n try {\n const wireToolCalls: WireToolCall[] | undefined = toolCallAcc.size\n ? [...toolCallAcc.entries()]\n .sort(([indexA], [indexB]) => indexA - indexB)\n .map(([, entry]) => ({\n id: entry.id ?? '',\n type: 'function' as const,\n function: { name: entry.name ?? '', arguments: entry.args },\n }))\n : undefined;\n\n const finalized = options.finalize(textAcc, wireToolCalls, usage);\n\n resolveFinal(finalized);\n } catch (error) {\n // finalize's caller has already normalized this error and\n // reported the usage failure internally. Just propagate it.\n rejectFinal(error);\n }\n })();\n\n return { chunks, finalResult };\n}\n","import { CircuitBreaker } from '../../circuitBreaker.js';\nimport { LLMError } from '../../types/errors.js';\nimport { makeEventReporter } from '../circuitBreaker.utils.js';\nimport { describeError, extractStatus, normalizeError } from './errors.utils.js';\nimport { defaultParseJson } from './parse.utils.js';\nimport { RequestBuilder } from './requestBuilder.js';\nimport { extractRetryAfterMs, getBackoffDelay, waitForRetry, withTimeout } from './retry.utils.js';\nimport { buildStreamResult } from './streamAccumulator.js';\nimport { parseWireToolCalls } from './wire.utils.js';\n\nimport type { Logger } from '../../logger.js';\nimport type { RateLimiter } from '../../rateLimit.js';\nimport type {\n CallParams,\n CallWithToolsResult,\n LLMClient,\n StreamChunk,\n TokenUsage,\n ToolIssue,\n VernLLMEvent,\n WireToolCall,\n} from '../../types/index.js';\n\n/** Everything one `CallExecutor` needs beyond the client and model. */\nexport interface CallExecutorOptions {\n maxRetries: number;\n timeoutMs: number;\n chunkIdleTimeoutMs: number;\n baseDelayMs: number;\n defaultMaxTokens: number;\n defaultTemperature: number | null;\n nonRetryableStatus: number[];\n parseJson?: (content: string) => unknown;\n logger: Logger;\n /** Applied to model output before it reaches the debug logger. See `VernLLMOptions.redact`. */\n redact?: (text: string) => string;\n onUsage?: (usage: TokenUsage) => void;\n onUsageFailure?: (usage: TokenUsage, error: LLMError) => void;\n onEvent?: (event: VernLLMEvent) => void;\n breaker?: CircuitBreaker;\n limiter?: RateLimiter;\n /** True for every target after the primary. Stamped onto reported `TokenUsage`. */\n isFallback?: boolean;\n}\n\n/**\n * Everything one provider target needs to attempt a call: request\n * building, retry with backoff, the per-target breaker, the per-target\n * limiter. Never exported publicly. `VernLLM` holds one per target and\n * owns the fallback loop and caching on top.\n */\nexport class CallExecutor {\n private readonly maxRetries: number;\n private readonly timeoutMs: number;\n private readonly chunkIdleTimeoutMs: number;\n private readonly baseDelayMs: number;\n private readonly nonRetryableStatus: number[];\n private readonly parseJson: (content: string) => unknown;\n private readonly logger: Logger;\n private readonly redact?: (text: string) => string;\n private readonly onUsage?: (usage: TokenUsage) => void;\n private readonly onUsageFailure?: (usage: TokenUsage, error: LLMError) => void;\n private readonly reportEvent: (event: VernLLMEvent) => void;\n private readonly breaker?: CircuitBreaker;\n private readonly limiter?: RateLimiter;\n private readonly isFallback: boolean;\n private readonly requestBuilder: RequestBuilder;\n\n constructor(\n readonly providerName: string,\n private readonly client: LLMClient,\n readonly model: string,\n options: CallExecutorOptions,\n ) {\n this.maxRetries = options.maxRetries;\n this.timeoutMs = options.timeoutMs;\n this.chunkIdleTimeoutMs = options.chunkIdleTimeoutMs;\n this.baseDelayMs = options.baseDelayMs;\n this.nonRetryableStatus = options.nonRetryableStatus;\n this.parseJson = options.parseJson ?? defaultParseJson;\n this.logger = options.logger;\n this.redact = options.redact;\n this.onUsage = options.onUsage;\n this.onUsageFailure = options.onUsageFailure;\n this.reportEvent = makeEventReporter(options.onEvent, this.logger);\n this.breaker = options.breaker;\n this.limiter = options.limiter;\n this.isFallback = options.isFallback ?? false;\n this.requestBuilder = new RequestBuilder({\n model,\n defaultMaxTokens: options.defaultMaxTokens,\n defaultTemperature: options.defaultTemperature,\n });\n }\n\n getCircuitState(model?: string) {\n return this.breaker?.getState(model);\n }\n\n /**\n * Throws if the breaker is open for this target/model, exactly like the\n * check `run`/`runStream` used to make internally. Exposed so `VernLLM`\n * can gate on it before reserving usage, avoiding a reserve-then-refund\n * round trip on a call that was never going to be attempted. `assertClosed`\n * has a stateful side effect (claiming a half-open trial slot), so it must\n * run exactly once per logical call: `run`/`runStream` no longer call it\n * themselves, this is now the only call site.\n */\n assertBreakerClosed(model?: string): void {\n this.breaker?.assertClosed(model ?? this.model);\n }\n\n /**\n * Runs a single logical call against this target: retry with backoff,\n * normalized error on exhaustion. Mirrors the old `VernLLM.call`'s\n * non-streaming branch, minus cache/usage-reservation and the breaker\n * check, which stay one layer up since they aren't per-target concerns\n * (see `assertBreakerClosed`).\n */\n async run<T>(\n params: CallParams<T>,\n requestId: string,\n onAttempt?: () => void,\n ): Promise<T | CallWithToolsResult<T>> {\n const model = params.model ?? this.model;\n\n try {\n return await this.retryWithBackoff(\n (attempt) => this.executeCall(params, requestId, attempt),\n requestId,\n model,\n params.signal,\n onAttempt,\n );\n } catch (error) {\n const normalized = normalizeError(error, params.signal);\n\n if (this.countsTowardBreaker(normalized)) {\n this.breaker?.recordFailure(model);\n }\n\n this.logger.debug(`[VernLLM:${requestId}] error:\\n${this.redactText(describeError(error))}`);\n\n throw normalized;\n }\n }\n\n /** Streaming counterpart to `run`. Mirrors the old streaming branch of `VernLLM.call`. */\n async runStream<T>(\n params: CallParams<T>,\n requestId: string,\n onAttempt?: () => void,\n ): Promise<{\n chunks: AsyncIterable<StreamChunk>;\n finalResult: Promise<T | CallWithToolsResult<T>>;\n }> {\n const model = params.model ?? this.model;\n\n try {\n return await this.retryWithBackoff(\n (attempt) => this.executeStreamCall(params, requestId, attempt),\n requestId,\n model,\n params.signal,\n onAttempt,\n );\n } catch (error) {\n const normalized = normalizeError(error, params.signal);\n\n if (this.countsTowardBreaker(normalized)) {\n this.breaker?.recordFailure(model);\n }\n\n this.logger.debug(\n `[VernLLM:${requestId}] stream-open error:\\n${this.redactText(describeError(error))}`,\n );\n\n throw normalized;\n }\n }\n\n /**\n * Performs a single attempt: builds the request (translating `tools` to\n * wire shape when present), dispatches it with a timeout, and shapes the\n * response into `T` or a `CallWithToolsResult<T>` when `params.tools` was\n * set. Throws on an empty response (no text and no tool_calls) so the\n * retry loop treats it like any other transient failure.\n */\n private async executeCall<T>(\n params: CallParams<T>,\n requestId: string,\n attempt: number,\n ): Promise<T | CallWithToolsResult<T>> {\n const { useJson, model, request } = this.requestBuilder.build(params);\n\n // A retry is a real request, so capacity is acquired per attempt\n // (inside the retry loop, via `executeCall` being re-invoked), not\n // once for the whole call.\n let release: ((actualTokens?: number) => void) | undefined;\n\n if (this.limiter) {\n const acquired = await this.limiter.acquire(this.limiter.estimate(request), params.signal);\n release = acquired.release;\n\n if (acquired.waitedMs > 0) {\n this.reportEvent({\n kind: 'rate_limited',\n requestId,\n provider: this.providerName,\n model,\n waitedMs: acquired.waitedMs,\n reason: acquired.reason ?? 'rpm',\n });\n }\n }\n\n try {\n const response = await withTimeout(\n (attemptSignal) => this.client.chat.completions.create(request, { signal: attemptSignal }),\n this.timeoutMs,\n params.signal,\n );\n\n // Extracted right after the response arrives, before anything else\n // touches it, so a post-response failure still gets its usage reported.\n const usage = this.extractUsage(response, requestId, model);\n\n // Reconcile against real usage, then hand off so `finally` below\n // can't release a second time.\n release?.(this.actualTokensFor(usage));\n release = undefined;\n\n // Raw and unvalidated on purpose. Extraction (including `.trim()`,\n // which throws on a non-string `content`) happens inside\n // `finalizeResponse`'s try/catch, so a malformed response still gets\n // normalized and its usage failure reported.\n const rawContent = response.choices?.[0]?.message?.content;\n const wireToolCalls = response.choices?.[0]?.message?.tool_calls;\n\n return this.finalizeResponse(\n rawContent,\n wireToolCalls,\n params,\n useJson,\n model,\n usage,\n requestId,\n attempt,\n );\n } finally {\n release?.();\n }\n }\n\n /** Applies `redact` (if configured); otherwise returns `text` unchanged. */\n private redactText(text: string): string {\n return this.redact ? this.redact(text) : text;\n }\n\n /**\n * Applies `redact` (if configured) to whatever the debug log is about\n * to show: real content when there is any, otherwise the tool-call\n * placeholder, which carries no user data and passes through\n * `redact` unchanged in practice but is included for a caller whose\n * `redact` does something structural (e.g. adding a marker) rather\n * than just scrubbing PII.\n */\n private redactedOutput(\n content: string | undefined,\n wireToolCalls: WireToolCall[] | undefined,\n ): string {\n return this.redactText(content ?? `[${wireToolCalls?.length ?? 0} tool call(s)]`);\n }\n\n /**\n * Shapes a fully-arrived response (content and/or tool_calls, already\n * extracted from the provider's payload) into `T` or a\n * `CallWithToolsResult<T>`. Reused by the streaming path once it has\n * buffered the full text/tool-call deltas, so there's no separate\n * parsing/validation logic for streaming.\n *\n * Normalizes and reports usage failure on error itself, so every caller\n * gets identical error handling without duplicating it.\n */\n private finalizeResponse<T>(\n rawContent: string | null | undefined,\n wireToolCalls: WireToolCall[] | undefined,\n params: CallParams<T>,\n useJson: boolean,\n model: string,\n usage: TokenUsage | undefined,\n requestId: string,\n attempt: number,\n ): T | CallWithToolsResult<T> {\n try {\n // `.trim()` runs inside this try: a malformed response shape\n // (e.g. a non-string `content`) throws here and is normalized and\n // reported like any other post-response failure.\n const content = rawContent?.trim();\n\n if (!content && !wireToolCalls?.length) {\n throw new LLMError('Empty LLM response', 'api');\n }\n\n this.logger.debug(\n `[VernLLM:${requestId}] output:\\n${this.redactedOutput(content, wireToolCalls).slice(0, 800)}`,\n );\n\n if (wireToolCalls?.length) {\n if (!params.tools) {\n throw new LLMError(\n 'Provider returned tool_calls but no `tools` were sent with this call.',\n 'api',\n );\n }\n\n if (params.toolChoice === 'none') {\n // `toolChoice: 'none'` is what lets `call()`'s type narrow to\n // `ContentResult<T>` (see `ToolsDisabledCallParams`). A\n // nonconforming provider/adapter returning tool_calls anyway\n // would silently break that guarantee for the caller, so this\n // is treated as a hard API-contract violation rather than\n // passed through as a normal tool_calls result. The request\n // itself is byte-for-byte identical on retry, so this repeats\n // deterministically like the other tool-contract failures\n // below: not retryable, and not the provider being unhealthy.\n throw new LLMError(\n \"Provider returned tool_calls despite toolChoice: 'none'.\",\n 'api',\n undefined,\n undefined,\n undefined,\n undefined,\n 'tool_choice_none_violated',\n );\n }\n\n const toolCalls = parseWireToolCalls(wireToolCalls);\n\n this.validateToolCallArguments(toolCalls, params.tools);\n this.breaker?.recordSuccess(model);\n this.reportUsage(usage);\n\n return { type: 'tool_calls', toolCalls, ...(content ? { content } : {}) };\n }\n\n // No tool_calls here, so content must be present.\n const textContent = content ?? '';\n\n if (!useJson) {\n this.breaker?.recordSuccess(model);\n this.reportUsage(usage);\n\n return params.tools ? { type: 'content', content: textContent as T } : (textContent as T);\n }\n\n const result = this.parseAndValidate<T>(textContent, params.schema);\n this.breaker?.recordSuccess(model);\n this.reportUsage(usage);\n\n return params.tools ? { type: 'content', content: result } : result;\n } catch (error) {\n // Normalized first so onUsageFailure always gets a real LLMError.\n // Also covers aborted signals: normalizeError returns type\n // 'aborted' in that case.\n const normalized = normalizeError(error, params.signal);\n\n if (usage && normalized.type !== 'aborted') {\n this.reportUsageFailure(usage, normalized, attempt);\n }\n\n throw normalized;\n }\n }\n\n /**\n * Opens a stream for a single attempt: builds the request exactly like\n * `executeCall`, then requires `createStream` on the client (a clear\n * `validation` error if the adapter doesn't support it). The timeout\n * wraps stream construction and the first `.next()` together, not just\n * construction: calling an `async function*` returns an iterator\n * synchronously without running its body until `.next()` is first\n * invoked, so timing only construction would time an operation that's\n * always instant, not the actual connection. Both are folded into a\n * single `withTimeout` so the same abort signal reaches whatever the\n * adapter's `createStream` uses internally for its first network\n * round-trip.\n *\n * Circuit-breaker success is recorded once the stream fully completes,\n * not on the first chunk arriving, so a connection that opens but then\n * dies mid-stream isn't masked as a success (see `buildStreamResult`).\n */\n private async executeStreamCall<T>(\n params: CallParams<T>,\n requestId: string,\n attempt: number,\n ): Promise<{\n chunks: AsyncIterable<StreamChunk>;\n finalResult: Promise<T | CallWithToolsResult<T>>;\n }> {\n const { useJson, model, request } = this.requestBuilder.build(params);\n\n const completions = this.client.chat.completions;\n\n if (!completions.createStream) {\n throw new LLMError('stream: true requires a client/adapter with createStream', 'validation');\n }\n\n const createStream = completions.createStream.bind(completions);\n\n // A stream holds a real connection for its whole life, so its\n // capacity is released on completion (in `buildStreamResult`), not\n // once opening succeeds.\n let release: ((actualTokens?: number) => void) | undefined;\n\n if (this.limiter) {\n const acquired = await this.limiter.acquire(this.limiter.estimate(request), params.signal);\n release = acquired.release;\n\n if (acquired.waitedMs > 0) {\n this.reportEvent({\n kind: 'rate_limited',\n requestId,\n provider: this.providerName,\n model,\n waitedMs: acquired.waitedMs,\n reason: acquired.reason ?? 'rpm',\n });\n }\n }\n\n // One controller for the entire stream, not just opening it. Adapters\n // already thread this signal into their transport for the life of the\n // request (that's how user-initiated cancellation works today), so\n // reusing it for the idle timeout means the same abort() call that\n // fires when the stream goes idle mid-way also tears down the\n // underlying connection, instead of only rejecting VernLLM's own\n // promise while the transport stays open.\n const streamController = new AbortController();\n const combinedExternal = params.signal\n ? AbortSignal.any([params.signal, streamController.signal])\n : streamController.signal;\n\n try {\n const { iterator, first } = await withTimeout(\n async (attemptSignal) => {\n const streamIterator = createStream(request, { signal: attemptSignal })[\n Symbol.asyncIterator\n ]();\n const firstResult = await streamIterator.next();\n\n return { iterator: streamIterator, first: firstResult };\n },\n this.timeoutMs,\n combinedExternal,\n );\n\n // An immediately-exhausted stream (no chunks at all) is the streaming\n // equivalent of `executeCall`'s empty-response check: surface the same\n // `LLMError('Empty LLM response', 'api')` so retry behaves identically\n // whether the empty result came from a non-streaming or streaming\n // attempt.\n if (first.done) {\n throw new LLMError('Empty LLM response', 'api');\n }\n\n // Snapshotted before the closures below are created, since `release`\n // (the outer variable) is reassigned to undefined right after this\n // call to hand ownership off. The callbacks only run later, once the\n // stream completes, so closing over the mutable variable itself\n // would see that later `undefined` instead of the value being\n // handed off.\n const releaseAtOpen = release;\n\n const result = buildStreamResult(iterator, first, {\n requestId,\n model,\n providerName: this.providerName,\n isFallback: this.isFallback,\n chunkIdleTimeoutMs: params.chunkIdleTimeoutMs ?? this.chunkIdleTimeoutMs,\n streamController,\n logger: this.logger,\n signal: params.signal,\n onStreamSuccess: (usage) => {\n this.breaker?.recordSuccess(model);\n releaseAtOpen?.(this.actualTokensFor(usage));\n },\n onStreamFailure: (normalized, usage) => {\n // Idle timeout is the one mid-stream failure that trips the\n // breaker: otherwise a provider that hangs after one chunk\n // would always record a success and never open it.\n if (normalized.type === 'timeout') {\n this.breaker?.recordFailure(model);\n }\n\n if (usage && normalized.type !== 'aborted') {\n this.reportUsageFailure(usage, normalized, attempt, true);\n }\n\n releaseAtOpen?.(this.actualTokensFor(usage));\n },\n finalize: (textAcc, wireToolCalls, usage) =>\n this.finalizeResponse(\n textAcc,\n wireToolCalls,\n params,\n useJson,\n model,\n usage,\n requestId,\n attempt,\n ),\n });\n\n // Ownership of `release` passes to `buildStreamResult` from here.\n release = undefined;\n\n return result;\n } finally {\n // Only reached if opening the stream itself threw; a successful\n // open hands `release` off above and leaves this a no-op.\n release?.();\n }\n }\n\n /**\n * Checks every `ToolCall` against the `tools` that were offered, catching\n * a hallucinated tool name and a duplicate call id before either reaches\n * the application's dispatch table, then runs each tool's\n * `argumentsSchema`, if present.\n *\n * Contract failures (unknown name, duplicate id) are collected across\n * every call and thrown together, since retrying a request that already\n * has these errors cannot help (`shouldRetry` excludes them by `code`)\n * and a caller fixing them wants to see every one, not just the first.\n * Schema failures keep the original single-error, `type: 'validation'`\n * shape rather than being folded into the aggregate, since they're a\n * distinct failure kind from the contract failures above (also excluded\n * from retry, by `type` rather than `code`; see `shouldRetry`).\n */\n private validateToolCallArguments(\n toolCalls: { id: string; name: string; arguments: unknown }[],\n tools: NonNullable<CallParams<unknown>['tools']>,\n ): void {\n const known = new Map(tools.map((t) => [t.name, t]));\n const seenIds = new Set<string>();\n const toolIssues: ToolIssue[] = [];\n\n for (const call of toolCalls) {\n if (seenIds.has(call.id)) {\n toolIssues.push({ name: call.name, toolCallId: call.id, code: 'duplicate_tool_call_id' });\n }\n seenIds.add(call.id);\n\n if (!known.has(call.name)) {\n toolIssues.push({ name: call.name, toolCallId: call.id, code: 'unknown_tool' });\n }\n }\n\n if (toolIssues.length > 0) {\n const unknownTool = toolIssues.find((i) => i.code === 'unknown_tool');\n const primary = unknownTool\n ? `Model requested tool \"${unknownTool.name}\", which was not in the tools offered ([${[...known.keys()].join(', ')}]).`\n : `Duplicate tool call id \"${toolIssues[0]!.toolCallId}\" in the model's response.`;\n\n // Most responses hit exactly one issue. When there's more than one,\n // say so, since toolCalls[0]'s problem alone would otherwise read as\n // the whole story.\n const message =\n toolIssues.length > 1\n ? `${primary} (${toolIssues.length} tool call issues total, see toolIssues.)`\n : primary;\n\n const error = new LLMError(\n message,\n 'api',\n undefined,\n undefined,\n undefined,\n undefined,\n unknownTool ? 'unknown_tool' : 'duplicate_tool_call_id',\n );\n error.toolIssues = toolIssues;\n throw error;\n }\n\n for (const call of toolCalls) {\n const definition = known.get(call.name);\n\n if (!definition?.argumentsSchema) continue;\n\n const result = definition.argumentsSchema.safeParse(call.arguments);\n\n if (!result.success) {\n throw new LLMError(\n `Arguments for tool call \"${call.name}\" failed validation`,\n 'validation',\n undefined,\n result.error,\n );\n }\n }\n }\n\n /** Runs `fn`, retrying with backoff according to `shouldRetry`. */\n private async retryWithBackoff<T>(\n fn: (attempt: number) => Promise<T>,\n requestId: string,\n model: string,\n signal?: AbortSignal,\n onAttempt?: () => void,\n ): Promise<T> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n if (attempt > 0) {\n await this.recoverDelay(requestId, model, attempt, lastError, signal);\n }\n\n onAttempt?.();\n return await fn(attempt);\n } catch (error) {\n lastError = error;\n\n if (!this.shouldRetry(error, signal)) break;\n }\n }\n\n throw lastError;\n }\n\n /**\n * Pulls `TokenUsage` out of a raw response, if the provider reported it.\n * Extraction doesn't depend on what happens to the response afterward, so\n * a malformed body can still yield usage if the provider's usage block\n * itself came through intact.\n */\n private extractUsage(\n response: Awaited<ReturnType<LLMClient['chat']['completions']['create']>>,\n requestId: string,\n model: string,\n ): TokenUsage | undefined {\n if (!response.usage) return undefined;\n\n return {\n promptTokens: response.usage.prompt_tokens ?? 0,\n completionTokens: response.usage.completion_tokens ?? 0,\n totalTokens: response.usage.total_tokens ?? 0,\n requestId,\n model,\n provider: this.providerName,\n usedFallback: this.isFallback,\n };\n }\n\n /**\n * The token count to reconcile the rate limiter against for a finished\n * attempt: `totalTokens` when reported, otherwise the sum of prompt and\n * completion tokens, matching `reportUsageFailure`'s own fallback below\n * for a hand-rolled client that reports the parts but omits the total.\n */\n private actualTokensFor(usage: TokenUsage | undefined): number | undefined {\n if (!usage) return undefined;\n return usage.totalTokens || usage.promptTokens + usage.completionTokens;\n }\n\n /** Reports token usage for a successful call, swallowing and logging any error `onUsage` throws. */\n private reportUsage(usage: TokenUsage | undefined): void {\n if (!usage || !this.onUsage) return;\n\n try {\n this.onUsage(usage);\n } catch (error) {\n this.logger.error('[VernLLM] onUsage failed', {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n }\n\n /**\n * Reports token usage spent on an attempt that then failed, so it isn't\n * dropped alongside the error. Covers any error thrown after usage\n * extraction, since all of them happen only after a response (real\n * spend) already arrived. Swallows and logs any error `onUsageFailure`\n * itself throws.\n */\n private reportUsageFailure(\n usage: TokenUsage,\n error: LLMError,\n attempt: number,\n terminal = false,\n ): void {\n // Falls back to promptTokens + completionTokens if totalTokens is 0\n // (e.g. a hand-rolled client that omits the total), so the log\n // doesn't understate real spend.\n const displayTokens = usage.totalTokens || usage.promptTokens + usage.completionTokens;\n\n // A mid-stream failure is terminal for that call (no further attempts\n // for this stream), unlike a stream-open failure where attempt N+1 may\n // still follow. Label them differently so the log doesn't imply a\n // retry that isn't coming.\n const attemptText = terminal\n ? 'mid-stream failure (terminal, no further attempts)'\n : `attempt ${attempt + 1}/${this.maxRetries + 1}`;\n\n this.logger.warn(\n `[VernLLM:${usage.requestId}] usage failure, ${attemptText}: ` +\n `type=${error.type} tokens=${displayTokens}`,\n );\n\n if (!this.onUsageFailure) return;\n\n try {\n this.onUsageFailure(usage, error);\n } catch (hookError) {\n this.logger.error('[VernLLM] onUsageFailure failed', {\n message: hookError instanceof Error ? hookError.message : 'unknown',\n });\n }\n }\n\n /** Parses response content as JSON and validates it against `schema` when supplied. */\n private parseAndValidate<T>(content: string, schema?: CallParams<T>['schema']): T {\n let parsed: unknown;\n\n try {\n parsed = this.parseJson(content);\n } catch {\n throw new LLMError('Invalid JSON response', 'parse');\n }\n\n if (parsed === null || parsed === undefined) {\n throw new LLMError('Invalid JSON response', 'parse');\n }\n\n if (!schema) return parsed as T;\n\n const result = schema.safeParse(parsed);\n\n if (!result.success) {\n throw new LLMError('Schema validation failed', 'validation', undefined, result.error);\n }\n\n return result.data;\n }\n\n /**\n * Waits out the backoff delay for a retry attempt, honoring a\n * Retry-After header on the failed attempt's error when present.\n * Both Retry-After and plain exponential backoff are capped at the same\n * max delay (see `DEFAULT_MAX_DELAY_MS` in `retry.utils.ts`).\n */\n private async recoverDelay(\n requestId: string,\n model: string,\n attempt: number,\n error: unknown,\n signal?: AbortSignal,\n ) {\n const retryAfterMs = extractRetryAfterMs(error);\n const delay = retryAfterMs ?? getBackoffDelay(this.baseDelayMs, attempt);\n const retryAfterHonored = retryAfterMs !== undefined;\n\n this.logger.warn(\n `[VernLLM:${requestId}] recovery attempt ${attempt}/${this.maxRetries}, waiting ${delay}ms` +\n (retryAfterHonored ? ' (honoring Retry-After)' : ''),\n );\n\n this.reportEvent({\n kind: 'retry',\n requestId,\n provider: this.providerName,\n model,\n attempt,\n maxRetries: this.maxRetries,\n delayMs: delay,\n retryAfterHonored,\n error: normalizeError(error, signal),\n });\n\n await waitForRetry(delay, signal);\n }\n\n private isNonRetryableToolContractError(error: unknown): error is LLMError {\n return (\n error instanceof LLMError &&\n (error.code === 'unknown_tool' ||\n error.code === 'duplicate_tool_call_id' ||\n error.code === 'tool_choice_none_violated')\n );\n }\n\n /** Decides whether a failed attempt is worth retrying. */\n private shouldRetry(error: unknown, signal?: AbortSignal): boolean {\n if (signal?.aborted) return false;\n\n if (error instanceof LLMError && (error.type === 'parse' || error.type === 'validation')) {\n return false;\n }\n\n // The queue wait already happened; retrying immediately would just\n // requeue behind the same limit with nothing changed.\n if (error instanceof LLMError && error.code === 'local_rate_limit') {\n return false;\n }\n\n // Tool contract failures repeat identically on retry: the wire request\n // is byte-for-byte the same, so nothing about a retry can change\n // whether the model names a real tool or reuses a call id.\n if (this.isNonRetryableToolContractError(error)) {\n return false;\n }\n\n const status = extractStatus(error);\n\n return !(status !== undefined && this.nonRetryableStatus.includes(status));\n }\n\n /**\n * Decides whether a failed attempt should count toward the circuit\n * breaker's failure threshold. A model hallucinating a tool name,\n * reusing a call id, or a provider ignoring `toolChoice: 'none'` isn't\n * the provider being unhealthy, it's a model/provider response defect\n * that will very likely recur regardless of provider health, so it\n * shouldn't push a healthy provider's circuit toward opening. Mirrors\n * the same reasoning `shouldRetry` already applies to\n * `parse`/`validation`/these same tool-contract codes.\n */\n private countsTowardBreaker(error: LLMError): boolean {\n if (\n error.type === 'validation' ||\n error.type === 'parse' ||\n error.type === 'aborted' ||\n // A local queue timeout/full-queue rejection never reached the\n // provider at all, so it says nothing about the provider's health\n // and shouldn't push a healthy provider's circuit toward opening.\n error.code === 'local_rate_limit' ||\n this.isNonRetryableToolContractError(error)\n ) {\n return false;\n }\n\n return true;\n }\n}\n","export interface Logger {\n debug(message: string): void;\n warn(message: string): void;\n error(message: string, meta?: Record<string, unknown>): void;\n}\n\n/**\n * Default logger. `debug` is gated by the `debug` option on VernLLM\n * warn/error always fire since they indicate real problems (retries, cache failures)\n */\nexport class ConsoleLogger implements Logger {\n constructor(private debugEnabled: boolean) {}\n\n debug(message: string): void {\n if (this.debugEnabled) console.debug(message);\n }\n\n warn(message: string): void {\n console.warn(message);\n }\n\n error(message: string, meta?: Record<string, unknown>): void {\n console.error(message, meta ?? '');\n }\n}\n","import { LLMError } from './types/errors.js';\n\nimport type { LLMClient } from './types/client.js';\n\n/** The request shape sent to `LLMClient['chat']['completions']['create']`, used for token estimation. */\nexport type WireRequest = Parameters<LLMClient['chat']['completions']['create']>[0];\n\n/** Which configured bucket is currently blocking a call. */\nexport type RateLimitReason = 'concurrency' | 'rpm' | 'tpm';\n\nexport interface RateLimitOptions {\n /** Max requests per minute. Omit for unlimited. */\n requestsPerMinute?: number;\n /**\n * Max tokens per minute. Enforced against a pre-flight estimate, then\n * reconciled against reported usage once the call completes. Omit for\n * unlimited.\n */\n tokensPerMinute?: number;\n /** Max requests in flight at once. Default 0, meaning unlimited. */\n maxConcurrent?: number;\n /**\n * Max time a call may sit queued waiting for capacity, in ms. Exceeding\n * it throws rather than hanging forever. Default 30000. Pass 0 to wait\n * indefinitely.\n */\n maxQueueMs?: number;\n /** Max queued calls before new ones reject immediately instead of queueing. Default 0, unbounded. */\n maxQueueSize?: number;\n /**\n * Pre-flight token estimate for `tokensPerMinute`. Defaults to a\n * chars/4 heuristic over message content plus `max_tokens`.\n */\n estimateTokens?: (request: WireRequest) => number;\n}\n\nexport interface RateLimitAcquireResult {\n /**\n * Releases the concurrency slot this attempt held and reconciles the\n * token bucket against real usage, when `actualTokens` is supplied.\n * Idempotent: only the first call does anything. Must run in a\n * `finally` block so a slot is never leaked on a failed attempt.\n */\n release: (actualTokens?: number) => void;\n /** How long this attempt waited in queue before capacity was available. */\n waitedMs: number;\n /** Which bucket was blocking this attempt just before it cleared, if any wait happened. */\n reason?: RateLimitReason;\n}\n\n/** Default `estimateTokens`: chars/4 over every message's content, plus the requested `max_tokens`. */\nexport function defaultEstimateTokens(request: WireRequest): number {\n const messagesChars = request.messages.reduce((sum, message) => {\n const content = (message as { content?: unknown }).content;\n\n if (typeof content === 'string') return sum + content.length;\n if (content === undefined || content === null) return sum;\n\n try {\n return sum + JSON.stringify(content).length;\n } catch {\n return sum;\n }\n }, 0);\n\n return Math.ceil(messagesChars / 4) + (request.max_tokens ?? 0);\n}\n\n/**\n * A capacity that refills continuously. Used for requests per minute and\n * tokens per minute, where `refillPerMs` is `capacity / 60000`, and for\n * concurrency, where `refillPerMs` is 0 and every release calls\n * `give(1)` instead of relying on the clock.\n */\nclass TokenBucket {\n private available: number;\n private lastRefill = Date.now();\n\n constructor(\n private readonly capacity: number,\n private readonly refillPerMs: number,\n ) {\n this.available = capacity;\n }\n\n private refill(): void {\n if (this.refillPerMs === 0) return;\n\n const now = Date.now();\n const elapsedMs = now - this.lastRefill;\n\n // A backward clock adjustment (NTP correction, VM migration, etc.)\n // makes `elapsedMs` negative, which would otherwise reduce `available`\n // on the next line, rate-limiting harder than configured for no\n // real-world reason. Treat a negative elapsed time as no time having\n // passed instead: `available` just doesn't grow this tick, rather\n // than shrinking, and `lastRefill` still advances so a subsequent\n // forward-moving `now` measures from here, not from the skewed past.\n this.available = Math.min(\n this.capacity,\n this.available + Math.max(0, elapsedMs) * this.refillPerMs,\n );\n this.lastRefill = now;\n }\n\n /** Refills, then takes `amount` if available. Leaves the bucket untouched if it can't. */\n tryTake(amount: number): boolean {\n this.refill();\n\n if (this.available < amount) return false;\n\n this.available -= amount;\n return true;\n }\n\n /**\n * Refills, then reports how many ms until this bucket could supply\n * `amount`, assuming nothing else takes from it meanwhile. Returns 0 if\n * it already can, `Infinity` if it never will on its own (a\n * concurrency bucket, `refillPerMs === 0`, only frees via `give`).\n */\n msUntilAvailable(amount: number): number {\n this.refill();\n\n if (this.available >= amount) return 0;\n if (this.refillPerMs === 0) return Infinity;\n\n return (amount - this.available) / this.refillPerMs;\n }\n\n /**\n * Gives capacity back. Not floored at 0: a bad token-usage estimate can\n * push `available` negative, and it self-corrects on the next refill\n * rather than being clamped away immediately. Only ceilinged at\n * `capacity`, so a give can never overfill the bucket.\n */\n give(amount: number): void {\n this.available = Math.min(this.capacity, this.available + amount);\n }\n\n /** The bucket's ceiling, e.g. so a request that could never fit can fail fast instead of queueing forever. */\n getCapacity(): number {\n return this.capacity;\n }\n}\n\n/**\n * `setTimeout` silently clamps any delay above this (~24.8 days) instead\n * of erroring, so an uncapped delay derived from a very small\n * `requestsPerMinute`/`tokensPerMinute` could wrap around to firing\n * almost immediately instead of waiting. Mirrors the same guard in\n * `withTimeout`/`withChunkIdleTimeout`.\n */\nconst MAX_WAKE_DELAY_MS = 2_147_483_647;\n\n/** One caller waiting for capacity, queued FIFO. */\ninterface Waiter {\n estimatedTokens: number;\n enqueuedAt: number;\n /** Reason recorded the last time this waiter was checked and found still blocked. */\n lastReason?: RateLimitReason;\n resolve: (result: RateLimitAcquireResult) => void;\n reject: (error: unknown) => void;\n}\n\n/**\n * Per-target rate limiter. Up to three buckets (requests/min, tokens/min,\n * concurrency) behind one FIFO queue, so a large call isn't starved by a\n * stream of small ones. Any bucket omitted from `options` has infinite\n * capacity and never blocks.\n */\nexport class RateLimiter {\n private readonly requests?: TokenBucket;\n private readonly tokens?: TokenBucket;\n private readonly concurrency?: TokenBucket;\n\n private readonly maxQueueMs: number;\n private readonly maxQueueSize: number;\n private readonly estimateTokensFn: (request: WireRequest) => number;\n\n private readonly queue: Waiter[] = [];\n\n /**\n * A single scheduled re-check for the head of the queue when it's\n * blocked on a bucket that refills on its own clock (rpm/tpm), so a\n * queue that nobody calls `acquire`/`release` on again isn't stuck\n * forever waiting for an external trigger to re-drain it. Not needed\n * for a concurrency block, which only clears via `release`.\n */\n private wakeTimer?: ReturnType<typeof setTimeout>;\n\n constructor(options: RateLimitOptions) {\n if (options.requestsPerMinute) {\n this.requests = new TokenBucket(\n options.requestsPerMinute,\n options.requestsPerMinute / 60_000,\n );\n }\n\n if (options.tokensPerMinute) {\n this.tokens = new TokenBucket(options.tokensPerMinute, options.tokensPerMinute / 60_000);\n }\n\n if (options.maxConcurrent) {\n this.concurrency = new TokenBucket(options.maxConcurrent, 0);\n }\n\n this.maxQueueMs = options.maxQueueMs ?? 30_000;\n this.maxQueueSize = options.maxQueueSize ?? 0;\n this.estimateTokensFn = options.estimateTokens ?? defaultEstimateTokens;\n }\n\n /** Pre-flight token estimate for a request, per the configured (or default) heuristic. */\n estimate(request: WireRequest): number {\n return this.estimateTokensFn(request);\n }\n\n /**\n * Waits for capacity in every configured bucket, then takes from each.\n * The returned `release` gives the concurrency slot back and reconciles\n * the token bucket against real usage; it must run in a `finally` block.\n */\n async acquire(estimatedTokens: number, signal?: AbortSignal): Promise<RateLimitAcquireResult> {\n if (signal?.aborted) {\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n // Guards `estimatedTokens` even on this directly-exported entry point\n // (not just the `VernLLM.executeCall`/`executeStreamCall` call sites):\n // an unchecked NaN or negative value would poison a bucket's\n // `available` permanently, since `NaN < amount` is always false and\n // would make `tryTake` wrongly report success forever after.\n if (!Number.isFinite(estimatedTokens) || estimatedTokens < 0) {\n throw new LLMError(\n `estimatedTokens must be a finite, non-negative number, got ${String(estimatedTokens)}`,\n 'validation',\n );\n }\n\n // A request over the bucket's own ceiling can never be satisfied by\n // any amount of waiting, refill included, so failing fast here also\n // avoids permanently stalling every waiter queued behind it in FIFO.\n if (this.tokens && estimatedTokens > this.tokens.getCapacity()) {\n throw new LLMError(\n `estimatedTokens (${estimatedTokens}) exceeds the configured tokensPerMinute capacity (${this.tokens.getCapacity()}); this call could never acquire capacity.`,\n 'quota_exceeded',\n undefined,\n undefined,\n undefined,\n undefined,\n 'local_rate_limit',\n );\n }\n\n // Fast path: nothing already queued, so try to go straight through\n // rather than paying queue bookkeeping for the common, uncontended case.\n if (this.queue.length === 0) {\n const attempt = this.tryAcquireBuckets(estimatedTokens);\n\n if (attempt.ok) {\n return { release: this.makeRelease(estimatedTokens), waitedMs: 0 };\n }\n\n // No maxQueueSize check needed here: the queue is empty (this\n // branch's own condition), so enqueueing this one waiter can never\n // exceed any maxQueueSize > 0. The check below only becomes\n // reachable once the queue is non-empty.\n return this.enqueue(estimatedTokens, attempt.reason, signal);\n }\n\n if (this.maxQueueSize > 0 && this.queue.length >= this.maxQueueSize) {\n throw this.queueFullError();\n }\n\n return this.enqueue(estimatedTokens, undefined, signal);\n }\n\n private queueFullError(): LLMError {\n return new LLMError(\n 'Rate limit queue is full',\n 'quota_exceeded',\n undefined,\n undefined,\n undefined,\n undefined,\n 'local_rate_limit',\n );\n }\n\n private enqueue(\n estimatedTokens: number,\n initialReason: RateLimitReason | undefined,\n signal?: AbortSignal,\n ): Promise<RateLimitAcquireResult> {\n return new Promise<RateLimitAcquireResult>((resolvePromise, rejectPromise) => {\n const waiter: Waiter = {\n estimatedTokens,\n enqueuedAt: Date.now(),\n lastReason: initialReason,\n resolve: (result) => {\n cleanup();\n resolvePromise(result);\n },\n reject: (error) => {\n cleanup();\n\n // The removed waiter may have been the head a wakeTimer was\n // scheduled around (or, for a concurrency block, the head with\n // no timer scheduled at all). Either way, re-drain immediately\n // so a successor with different requirements is evaluated now\n // instead of waiting on a stale timer or an unrelated\n // acquire/release call to trigger it.\n if (this.wakeTimer) {\n clearTimeout(this.wakeTimer);\n this.wakeTimer = undefined;\n }\n this.drain();\n\n rejectPromise(error);\n },\n };\n\n let queueTimer: ReturnType<typeof setTimeout> | undefined;\n\n const onAbort = () => {\n waiter.reject(new LLMError('LLM request aborted', 'aborted'));\n };\n\n const cleanup = () => {\n if (queueTimer) clearTimeout(queueTimer);\n signal?.removeEventListener('abort', onAbort);\n\n const index = this.queue.indexOf(waiter);\n if (index !== -1) this.queue.splice(index, 1);\n };\n\n if (this.maxQueueMs > 0) {\n queueTimer = setTimeout(() => {\n waiter.reject(\n new LLMError(\n 'Rate limit queue timed out before capacity was available',\n 'quota_exceeded',\n undefined,\n undefined,\n undefined,\n undefined,\n 'local_rate_limit',\n ),\n );\n }, this.maxQueueMs);\n }\n\n signal?.addEventListener('abort', onAbort, { once: true });\n\n this.queue.push(waiter);\n this.drain();\n });\n }\n\n /**\n * Checks and takes from every configured bucket as one atomic unit: if\n * any bucket lacks capacity, whatever was already taken from the\n * earlier ones in this attempt is rolled back before reporting which\n * bucket blocked.\n */\n private tryAcquireBuckets(\n estimatedTokens: number,\n ): { ok: true } | { ok: false; reason: RateLimitReason } {\n const taken: Array<{ bucket: TokenBucket; amount: number }> = [];\n\n const take = (bucket: TokenBucket | undefined, amount: number) => {\n if (!bucket) return true;\n if (!bucket.tryTake(amount)) return false;\n\n taken.push({ bucket, amount });\n return true;\n };\n\n if (!take(this.concurrency, 1)) {\n return { ok: false, reason: 'concurrency' };\n }\n\n if (!take(this.requests, 1)) {\n for (const entry of taken) entry.bucket.give(entry.amount);\n return { ok: false, reason: 'rpm' };\n }\n\n if (!take(this.tokens, estimatedTokens)) {\n for (const entry of taken) entry.bucket.give(entry.amount);\n return { ok: false, reason: 'tpm' };\n }\n\n return { ok: true };\n }\n\n /** Drains the queue head first. Stops at the first waiter that still can't proceed, so no one is starved out of turn. */\n private drain(): void {\n while (this.queue.length > 0) {\n const waiter = this.queue[0] as Waiter;\n const attempt = this.tryAcquireBuckets(waiter.estimatedTokens);\n\n if (!attempt.ok) {\n waiter.lastReason = attempt.reason;\n this.scheduleWake(attempt.reason, waiter.estimatedTokens);\n return;\n }\n\n const waitedMs = Date.now() - waiter.enqueuedAt;\n\n waiter.resolve({\n release: this.makeRelease(waiter.estimatedTokens),\n waitedMs,\n reason: waiter.lastReason,\n });\n }\n }\n\n /**\n * Schedules a one-shot re-check of the queue for whenever the bucket\n * that's currently blocking the head waiter should next have enough\n * capacity. A no-op for a concurrency block (only `release` can clear\n * that) or while a wake is already pending.\n */\n private scheduleWake(reason: RateLimitReason, estimatedTokens: number): void {\n if (this.wakeTimer) return;\n\n const ms =\n reason === 'rpm'\n ? this.requests?.msUntilAvailable(1)\n : reason === 'tpm'\n ? this.tokens?.msUntilAvailable(estimatedTokens)\n : undefined;\n\n if (ms === undefined || !Number.isFinite(ms)) return;\n\n // Capped, not just clamped-by-omission: `drain()` re-derives the\n // real remaining wait from live bucket state on every firing (it\n // doesn't trust the delay that got it there), so a wake that fires\n // early because the true wait exceeded the cap just re-schedules\n // correctly from where the bucket actually is, rather than looping\n // on a delay that never shrinks.\n const delay = Math.min(Math.max(1, Math.ceil(ms)), MAX_WAKE_DELAY_MS);\n\n this.wakeTimer = setTimeout(() => {\n this.wakeTimer = undefined;\n this.drain();\n }, delay);\n }\n\n /**\n * Builds the one-shot release closure for an acquired slot. Only the\n * concurrency bucket is given back on release; the requests-per-minute\n * bucket is a real spend that only recovers via its own refill, and the\n * tokens bucket is reconciled against `actualTokens` rather than fully\n * refunded, since real tokens really were spent.\n */\n private makeRelease(estimatedTokens: number): (actualTokens?: number) => void {\n let released = false;\n\n return (actualTokens?: number) => {\n if (released) return;\n released = true;\n\n this.concurrency?.give(1);\n\n // An invalid `actualTokens` (e.g. NaN from a malformed usage\n // report) must not reach `give`: `Math.min(capacity, available +\n // NaN)` is NaN, and a NaN `available` poisons every future\n // `tryTake` on that bucket (any comparison against NaN is false,\n // so it would look permanently under capacity and rate limiting\n // would silently stop happening). Falling back to no reconciliation\n // at least keeps the estimated debit, the safe direction to err.\n if (this.tokens && actualTokens !== undefined && Number.isFinite(actualTokens)) {\n this.tokens.give(estimatedTokens - actualTokens);\n }\n\n this.drain();\n };\n }\n}\n","import { randomUUID } from 'crypto';\n\nimport { CacheOrchestrator } from './internal/cache/cacheOrchestrator.js';\nimport { buildCircuitBreaker, makeEventReporter } from './internal/circuitBreaker.utils.js';\nimport { CallExecutor } from './internal/execution/callExecutor.js';\nimport { normalizeError } from './internal/execution/errors.utils.js';\nimport { withReservedUsage, withReservedUsageForStream } from './internal/execution/usage.utils.js';\nimport { ConsoleLogger, type Logger } from './logger.js';\nimport { RateLimiter } from './rateLimit.js';\nimport {\n InMemoryCacheAdapter,\n LLMError,\n FallbackExhaustedError,\n defaultFallbackOn,\n type CachedCallParams,\n type CachedStreamCallParams,\n type CachedStreamToolCallParams,\n type CachedToolCallParams,\n type CallParams,\n type CallWithToolsResult,\n type ContentResult,\n type FallbackAttempt,\n type FallbackOn,\n type FallbackTarget,\n type StreamCallResult,\n type TargetCircuitState,\n type StreamEnabledCallParams,\n type ToolEnabledCallParams,\n type ToolsDisabledCallParams,\n type VernLLMEvent,\n type VernLLMOptions,\n} from './types/index.js';\n\nimport type { InternalCacheParams } from './internal/cache/cache.utils.js';\n\n/**\n * A resilient layer around an LLM chat completions client. This is VernLLM!\n *\n * Adds retry with backoff and jitter, per-attempt timeouts, an optional\n * circuit breaker, JSON parsing with optional schema validation, usage\n * tracking, and an optional response cache. All configurable, all opt-in\n * beyond sensible defaults.\n */\nexport class VernLLM {\n private readonly logger: Logger;\n\n /**\n * One `CallExecutor` per provider target: index 0 is the primary,\n * everything after it is a `fallback` target, in the order declared.\n * Each owns its own request building, retry/timeout, circuit breaker,\n * and rate limiter. `call()` walks this array in `runFallbackChain`,\n * moving to the next entry only when `fallbackOn` says to.\n */\n private readonly executors: CallExecutor[];\n\n /** Decides whether a failed target is followed by the next one or the chain stops. See `VernLLMOptions['fallbackOn']`. */\n private readonly fallbackOn: FallbackOn;\n\n /** Reports a `'fallback'` event when the chain moves to the next target. Shared `onEvent` plumbing, same as every executor's. */\n private readonly reportEvent: (event: VernLLMEvent) => void;\n\n /**\n * Owns cache key resolution, cache reads/writes, and in-flight\n * coalescing for `cachedCall()`. Independent of `executor`: it only\n * ever calls back into `this.call()` as an opaque function.\n */\n private readonly cacheOrchestrator: CacheOrchestrator;\n\n /**\n * @param options Client, model, and tunables. Defaults: `maxRetries` 1,\n * `timeoutMs` 25000, `baseDelayMs` 500, `defaultMaxTokens` 1000,\n * `defaultTemperature` 0.2, `cache` an in-memory adapter,\n * `nonRetryableStatus` `[400, 401, 403, 404, 422]`, `debug` false.\n */\n constructor(options: VernLLMOptions) {\n this.logger = options.logger ?? new ConsoleLogger(options.debug ?? false);\n\n const providerName = options.name ?? 'primary';\n\n this.cacheOrchestrator = new CacheOrchestrator(\n options.cache ?? new InMemoryCacheAdapter(),\n this.logger,\n );\n\n this.fallbackOn = options.fallbackOn ?? defaultFallbackOn;\n this.reportEvent = makeEventReporter(options.onEvent, this.logger);\n\n // The primary target's shared knobs, resolved once here rather than\n // inline in the retry-tunable default below, since fallback targets\n // that omit a field inherit this resolved value, not the raw\n // (possibly-undefined) option.\n const primaryDefaultTemperature =\n options.defaultTemperature === undefined ? 0.2 : options.defaultTemperature;\n\n // The primary, shaped like a `FallbackTarget` so it walks the same\n // build loop as every declared fallback target below. Its own\n // `circuitBreaker`/`rateLimit` are read directly off `options`\n // instead of this list, since only the primary carries them at the\n // top level (a `FallbackTarget`'s copies are genuinely independent,\n // never inherited, see `FallbackTarget`'s docs).\n const primaryTarget: FallbackTarget = {\n client: options.client,\n model: options.model,\n name: providerName,\n maxRetries: options.maxRetries,\n timeoutMs: options.timeoutMs,\n chunkIdleTimeoutMs: options.chunkIdleTimeoutMs,\n baseDelayMs: options.baseDelayMs,\n defaultMaxTokens: options.defaultMaxTokens,\n defaultTemperature: primaryDefaultTemperature,\n nonRetryableStatus: options.nonRetryableStatus,\n circuitBreaker: options.circuitBreaker,\n rateLimit: options.rateLimit,\n };\n\n const declaredFallbacks: FallbackTarget[] = Array.isArray(options.fallback)\n ? options.fallback\n : options.fallback\n ? [options.fallback]\n : [];\n\n const targets = [primaryTarget, ...declaredFallbacks];\n\n this.executors = targets.map((target, i) => {\n const isFallback = i > 0;\n // `-1` for the primary, matching `FallbackAttempt.index`.\n const name = target.name ?? (isFallback ? `fallback[${i - 1}]` : providerName);\n\n // Built before the executor: onStateChange fires from inside the\n // breaker itself, which the executor is merely handed a reference to.\n const breaker = buildCircuitBreaker(\n target.circuitBreaker,\n name,\n target.model,\n options.onEvent,\n this.logger,\n );\n\n return new CallExecutor(name, target.client, target.model, {\n maxRetries: target.maxRetries ?? options.maxRetries ?? 1,\n timeoutMs: target.timeoutMs ?? options.timeoutMs ?? 25_000,\n chunkIdleTimeoutMs: target.chunkIdleTimeoutMs ?? options.chunkIdleTimeoutMs ?? 30_000,\n baseDelayMs: target.baseDelayMs ?? options.baseDelayMs ?? 500,\n defaultMaxTokens: target.defaultMaxTokens ?? options.defaultMaxTokens ?? 1000,\n defaultTemperature:\n target.defaultTemperature === undefined\n ? primaryDefaultTemperature\n : target.defaultTemperature,\n nonRetryableStatus: target.nonRetryableStatus ??\n options.nonRetryableStatus ?? [400, 401, 403, 404, 422],\n parseJson: options.parseJson,\n logger: this.logger,\n redact: options.redact,\n onUsage: options.onUsage,\n onUsageFailure: options.onUsageFailure,\n onEvent: options.onEvent,\n breaker,\n limiter: target.rateLimit ? new RateLimiter(target.rateLimit) : undefined,\n isFallback,\n });\n });\n }\n\n /** Logs a failed refundUsage attempt via the configured logger. */\n private logRefundError(logMessage: string, error: unknown): void {\n this.logger.error(logMessage, {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n\n /**\n * Walks `this.executors` in order, running `attempt` against each until\n * one succeeds or every target has failed. `run` on a lone target\n * (no `fallback` configured) throws exactly what it throws today: the\n * loop's single iteration path is unchanged from pre-fallback behavior.\n *\n * For streaming, `attempt` is `executor.runStream`, whose own retries\n * only cover *opening* the stream (see `CallExecutor.runStream`). A\n * mid-stream failure surfaces through `finalResult` after this function\n * has already returned, so it's never seen here and never falls over,\n * per the streaming limitation: splicing a second model's output into a\n * response the consumer has already partially rendered would corrupt\n * it.\n */\n private async runFallbackChain<R>(\n params: Pick<CallParams<unknown>, 'model' | 'signal'>,\n requestId: string,\n attempt: (executor: CallExecutor, onAttempt: () => void) => Promise<R>,\n skipBreakerCheckForFirst = false,\n ): Promise<{ result: R; executor: CallExecutor; index: number; attemptCount: number }> {\n const attempts: FallbackAttempt[] = [];\n\n for (let i = 0; i < this.executors.length; i++) {\n const executor = this.executors[i]!;\n const startedAt = Date.now();\n let attemptCount = 0;\n\n try {\n // Already checked once, before usage was reserved, when this is\n // the sole target (see `call()`). `assertClosed` claims a\n // half-open trial slot as a side effect on a non-throwing call,\n // so it must run exactly once per logical call: checking it\n // again here for the same executor could either falsely see\n // \"trial already in flight\" (from the check that just claimed\n // it) or double-claim a slot no concurrent caller actually has.\n if (!(i === 0 && skipBreakerCheckForFirst)) {\n executor.assertBreakerClosed(params.model);\n }\n\n const result = await attempt(executor, () => {\n attemptCount += 1;\n });\n return { result, executor, index: i, attemptCount };\n } catch (error) {\n const normalized = normalizeError(error, params.signal);\n\n attempts.push({\n index: i - 1,\n provider: executor.providerName,\n model: params.model ?? executor.model,\n error: normalized,\n });\n\n const isLast = i === this.executors.length - 1;\n // Always consult fallbackOn, including on the last target, so it\n // sees every failure and callers who log or count from inside it\n // get a complete picture. The chain still stops once the last\n // target fails regardless of what fallbackOn returns: there is no\n // next executor to fall over to.\n const policyDecision = this.fallbackOn(normalized, { isLastTarget: isLast });\n const decision = isLast ? 'stop' : policyDecision;\n\n if (decision === 'stop') {\n // A lone target (or a chain that stopped on its first failure)\n // throws its own error, unchanged from pre-fallback behavior.\n throw attempts.length > 1 ? new FallbackExhaustedError(attempts) : normalized;\n }\n\n const next = this.executors[i + 1]!;\n\n this.reportEvent({\n kind: 'fallback',\n requestId,\n from: executor.providerName,\n to: next.providerName,\n fromIndex: i - 1,\n toIndex: i,\n error: normalized,\n elapsedMs: Date.now() - startedAt,\n });\n }\n }\n\n // Unreachable: the loop above always either returns or throws before\n // running out of targets (the last iteration's `isLast` forces a\n // throw). Kept only to satisfy the return type.\n throw new LLMError('No provider targets configured', 'unknown');\n }\n\n /**\n * Makes a single logical LLM call, retrying on failure per the configured\n * policy. Fails fast if the breaker is open or the signal is already\n * aborted. Rejects with a normalized LLMError on exhausted retries.\n *\n * When `tools` is set, returns a `CallWithToolsResult<T>` instead of `T`:\n * `{ type: 'content', content }` or `{ type: 'tool_calls', toolCalls,\n * content? }`. VernLLM never executes tools; run them yourself and\n * continue via `history` (see `ConversationTurn`). Mutually exclusive\n * with `jsonSchema`/`schema`.\n *\n * TypeScript only picks the tools-aware overload when `tools` is\n * statically present on `params`. If set conditionally on a plain\n * `CallParams<T>`, use `isToolCallResult()` to check the shape at\n * runtime instead. See the Tool Calling docs for details.\n *\n * The same static-vs-dynamic caveat applies to `stream`: TypeScript only\n * selects the streaming overload (returning `StreamCallResult<...>`) when\n * `stream: true` is statically present on `params`. A `stream` value set\n * conditionally on a plain `CallParams<T>` still resolves to `Promise<T>`\n * (or `Promise<CallWithToolsResult<T>>`) at the type level even though\n * the actual runtime result is the `{ chunks, finalResult }` streaming\n * shape whenever `stream` evaluates to `true`, callers doing this should\n * narrow/cast accordingly rather than relying on the static return type.\n *\n * @param params System/user content plus per-call overrides. See `CallParams`.\n * @returns Without `tools` or `stream`: the parsed response, or raw\n * string if `jsonMode` is false. With `tools`: a `CallWithToolsResult<T>`,\n * narrowed to `ContentResult<T>` when `toolChoice: 'none'` is set, since\n * the model is then structurally barred from returning a `tool_calls`\n * result. With `stream: true` (statically): a `{ chunks, finalResult }`\n * `StreamCallResult`, `finalResult` resolving to whichever of the above\n * shapes applies once the stream completes. See `StreamCallResult`.\n */\n async call<T = unknown>(\n params: StreamEnabledCallParams<T> & ToolsDisabledCallParams<T>,\n ): Promise<StreamCallResult<ContentResult<T>>>;\n\n async call<T = unknown>(\n params: StreamEnabledCallParams<T> & ToolEnabledCallParams<T>,\n ): Promise<StreamCallResult<CallWithToolsResult<T>>>;\n\n async call<T = unknown>(params: StreamEnabledCallParams<T>): Promise<StreamCallResult<T>>;\n\n async call<T = unknown>(params: ToolsDisabledCallParams<T>): Promise<ContentResult<T>>;\n\n async call<T = unknown>(params: ToolEnabledCallParams<T>): Promise<CallWithToolsResult<T>>;\n\n async call<T = unknown>(params: CallParams<T>): Promise<T>;\n\n async call<T = unknown>(\n params: CallParams<T>,\n ): Promise<T | CallWithToolsResult<T> | StreamCallResult<T | CallWithToolsResult<T>>> {\n if (params.signal?.aborted) {\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n const requestId = params.requestId ?? randomUUID();\n\n // A lone target (no `fallback` configured) keeps the exact pre-fallback\n // contract: the breaker is checked once, up front, before usage is\n // reserved, so a call that's definitely blocked never pays a\n // reserve-then-refund round trip. This can only be hoisted out of the\n // chain for the sole-target case: `assertBreakerClosed` claims a\n // half-open trial slot as a side effect, which must happen exactly\n // once per logical call, so with more than one target the check has\n // to stay inside `runFallbackChain`, where an open primary is just\n // another target failure that `fallbackOn` can fall over from.\n const soleTarget = this.executors.length === 1;\n\n if (soleTarget) {\n this.executors[0]!.assertBreakerClosed(params.model);\n }\n\n if (params.stream) {\n // Same breaker/logging treatment as non-streaming, applied around\n // opening the stream; mid-stream failures are handled separately\n // inside the executor and never fall over (see `runFallbackChain`).\n // Usage refund/report is deferred onto finalResult, since call()\n // must return { chunks, finalResult } before the real outcome is\n // known, which is also why `params.meta` isn't populated for\n // streaming calls.\n return withReservedUsageForStream(\n params,\n async () => {\n const { result } = await this.runFallbackChain(\n params,\n requestId,\n (executor, onAttempt) => executor.runStream(params, requestId, onAttempt),\n soleTarget,\n );\n return result;\n },\n params.signal,\n (logMessage, error) => this.logRefundError(logMessage, error),\n );\n }\n\n return withReservedUsage(\n params,\n false,\n async () => {\n const { result, executor, index, attemptCount } = await this.runFallbackChain(\n params,\n requestId,\n (target, onAttempt) => target.run(params, requestId, onAttempt),\n soleTarget,\n );\n\n if (params.meta) {\n params.meta.current = {\n provider: executor.providerName,\n model: params.model ?? executor.model,\n fallbackIndex: index - 1,\n usedFallback: index > 0,\n attempts: attemptCount,\n };\n }\n\n return result;\n },\n params.signal,\n (logMessage, error) => this.logRefundError(logMessage, error),\n );\n }\n\n /**\n * Thin delegator kept private on `VernLLM` (rather than only existing on\n * `CacheOrchestrator`) since it's the one caching primitive exercised\n * directly by white-box tests, independent of the public `cachedCall()`\n * surface.\n */\n private runCached<T>(params: InternalCacheParams<T>) {\n return this.cacheOrchestrator.runCached(params);\n }\n\n /**\n * Removes a cached response by key when the configured cache adapter\n * supports deletion. Cache invalidation is the caller's responsibility;\n * only the application knows when cached data is stale.\n *\n * @param key The raw cache key (resolved through the adapter's\n * `resolveKey`, if any, before deletion).\n */\n async deleteCache(key: string): Promise<void> {\n await this.cacheOrchestrator.deleteCache(key);\n }\n\n /**\n * Cache wrapper composing `call` + caching, so cached LLM calls\n * automatically get retry/timeout/circuit-breaker behavior. `reserveUsage`/\n * `refundUsage` are read from the top-level params only. Concurrent misses\n * for the same `cacheKey` share a single in-flight call, avoiding cache\n * stampedes. Supports `stream: true` and `tools` in any combination.\n *\n * When `call.tools` is set, this caches the whole `CallWithToolsResult`,\n * including `tool_calls` results, not just final answers. Whether\n * that's appropriate depends on the tool: caching \"the model decided to\n * call get_weather\" is usually fine to reuse briefly, but caching a\n * decision made under permissions or account state that can change\n * between calls is not. Use a short `ttl` or a separate `cacheKey` for\n * such tools if this distinction matters.\n *\n * There is no public way to cache an arbitrary non-LLM function through\n * `VernLLM`. This method always composes with `call()`. For\n * general-purpose caching unrelated to an LLM call, use a dedicated\n * caching library at the application level instead.\n *\n * @param params `cacheKey`, `ttl`, and optional\n * `reserveUsage`/`refundUsage`/`signal`, plus `call`, the `CallParams`\n * (optionally with `tools` and/or `stream`) to pass through to\n * `this.call(...)`. The top-level `signal` governs the cached operation\n * and its usage hooks only; to also abort the underlying provider\n * request, set `signal` inside `call`.\n * @returns The cached value on a hit, or the freshly-called result on a miss.\n */\n async cachedCall<T>(\n params: CachedStreamToolCallParams<T>,\n ): Promise<StreamCallResult<CallWithToolsResult<T>>>;\n\n async cachedCall<T>(params: CachedStreamCallParams<T>): Promise<StreamCallResult<T>>;\n\n async cachedCall<T>(params: CachedToolCallParams<T>): Promise<CallWithToolsResult<T>>;\n\n async cachedCall<T>(params: CachedCallParams<T>): Promise<T>;\n\n async cachedCall<T>(\n params:\n | CachedCallParams<T>\n | CachedToolCallParams<T>\n | CachedStreamCallParams<T>\n | CachedStreamToolCallParams<T>,\n ): Promise<T | CallWithToolsResult<T> | StreamCallResult<T | CallWithToolsResult<T>>> {\n const { call: callParams, ...cacheParams } = params;\n\n // `callParams`'s type no longer includes reserveUsage/refundUsage (see\n // CachedCallParams et al.): a well-typed caller can't reach this branch\n // at all, TypeScript rejects it at the call site instead. This check is\n // a defense-in-depth backstop for callers that bypass the type system\n // (plain JS, or an `as any` cast), and now throws instead of silently\n // ignoring the hooks and continuing: reserveUsage/refundUsage exist as\n // a cost-control safety mechanism, so silently dropping them fails\n // open, not safe, which a warning callers may never see doesn't fix.\n const restCallParams = callParams as CallParams<T>;\n\n if (restCallParams.reserveUsage || restCallParams.refundUsage) {\n throw new LLMError(\n '`reserveUsage`/`refundUsage` were set inside `call`, where cachedCall ignores them. Move them ' +\n 'to the top level of the cachedCall() params, alongside cacheKey/ttl, instead.',\n 'validation',\n );\n }\n\n if (restCallParams.stream) {\n const streamParams = restCallParams as StreamEnabledCallParams<T>;\n\n return this.cacheOrchestrator.runCachedStream(\n {\n ...cacheParams,\n openStream: () => this.call(streamParams),\n },\n Boolean(restCallParams.tools),\n );\n }\n\n return this.runCached({\n ...cacheParams,\n fn: () => this.call(restCallParams),\n });\n }\n\n /**\n * @param model With `circuitBreaker.isolateByModel` on, returns that\n * model's own circuit state instead of the shared one. Ignored\n * otherwise. Omit for the shared circuit (the default) or, under\n * isolation, the state of calls that didn't resolve a model.\n * @returns The current circuit breaker state (`'closed' | 'open' |\n * 'half-open'`), or undefined if no circuit breaker was configured.\n */\n getCircuitState(model?: string) {\n return this.executors[0]!.getCircuitState(model);\n }\n\n /**\n * @param model With `circuitBreaker.isolateByModel` on, returns each\n * target's circuit state for that model instead of its shared state.\n * Ignored otherwise. Omit for the shared circuit (the default) or, under\n * isolation, the state of calls that didn't resolve a model.\n * @returns The current circuit state for every target in declaration\n * order, including the primary and all fallback targets. Each entry\n * includes the target's provider name, chain index, whether it is a\n * fallback, and its circuit state, or undefined if that target has no\n * circuit breaker configured.\n */\n getCircuitStates(model?: string): TargetCircuitState[] {\n return this.executors.map((executor, index) => ({\n provider: executor.providerName,\n index,\n isFallback: index > 0,\n state: executor.getCircuitState(model),\n }));\n }\n}\n","import { LLMError } from '../../types/errors.js';\n\n/**\n * Parses a Server-Sent-Events byte/text stream into the JSON payload of\n * each `data:` frame, in arrival order. Generic over transport: works with\n * anything that hands back progressively-arriving `Uint8Array` or `string`\n * chunks via async iteration: native `fetch`'s `response.body` (wrapped\n * to be iterable, see `webStreamToAsyncIterable` in `fetch.ts`), axios's\n * Node `Readable` (already async-iterable, no wrapping needed), etc, so\n * this framing layer doesn't care which transport produced the bytes.\n *\n * Follows the SSE spec's frame-delimiting rules closely enough for LLM\n * streaming responses: frames are separated by a blank line, each frame\n * may carry one or more `data:` lines (joined with `\\n` per spec when\n * there's more than one), `:`-prefixed lines are comments and ignored, and\n * other SSE fields (`event:`, `id:`, `retry:`) are ignored since VernLLM\n * only needs the payload. A frame whose data is exactly `[DONE]` (the\n * sentinel several providers, notably OpenAI, send to mark stream end)\n * ends iteration without yielding it.\n *\n * Line endings: `\\r\\n` and bare `\\r` (both legal per the SSE spec, alongside `\\n`) are normalized\n * to `\\n` before frame splitting. A `\\r` at the very end of the currently-buffered text is left\n * alone until either more text arrives (in case it's the first half of a split `\\r\\n` pair) or the\n * stream ends, so a `\\r\\n` pair split across two transport chunks is never misread as two blank\n * lines.\n *\n * Malformed JSON in a frame throws `LLMError('parse')`, consistent with\n * how malformed JSON is handled elsewhere in VernLLM.\n */\nexport async function* parseSseStream(\n source: AsyncIterable<Uint8Array | string>,\n): AsyncGenerator<unknown> {\n // `fatal: true` makes invalid UTF-8 throw instead of silently decoding\n // to U+FFFD replacement characters, which could otherwise land inside a\n // JSON string and either corrupt it unnoticeably or, worse, still parse\n // as syntactically valid JSON with silently-wrong content.\n const decoder = new TextDecoder('utf-8', { fatal: true });\n let buffer = '';\n\n for await (const chunk of source) {\n let text: string;\n\n try {\n text = typeof chunk === 'string' ? chunk : decoder.decode(chunk, { stream: true });\n } catch (cause) {\n throw new LLMError('Invalid UTF-8 in SSE stream', 'parse', undefined, undefined, cause);\n }\n\n // Normalized against the whole buffer, not just the newly-arrived\n // chunk: a `\\r\\n` delimiter can straddle a chunk boundary (one chunk\n // ending in `\\r`, the next starting with `\\n`), and normalizing only\n // the new text would miss that split pair. A bare trailing `\\r` (not\n // followed by anything yet) is left as-is for the same reason; it's\n // converted once either more text or end-of-stream resolves whether\n // it was standalone or the start of a split `\\r\\n`.\n buffer = (buffer + text).replace(/\\r\\n/g, '\\n').replace(/\\r(?!$)/g, '\\n');\n\n let boundary = buffer.indexOf('\\n\\n');\n\n while (boundary !== -1) {\n const frame = buffer.slice(0, boundary);\n\n buffer = buffer.slice(boundary + 2);\n\n const event = parseSseFrame(frame);\n\n if (event === DONE) return;\n if (event !== NO_DATA) yield event;\n\n boundary = buffer.indexOf('\\n\\n');\n }\n }\n\n // Flush any bytes TextDecoder held back mid-decode, so a truncated\n // multi-byte char surfaces as a parse error instead of silently\n // vanishing (and possibly leaving behind valid-looking, wrong JSON).\n try {\n buffer += decoder.decode();\n } catch (cause) {\n throw new LLMError('Invalid UTF-8 in SSE stream', 'parse', undefined, undefined, cause);\n }\n\n // The stream has ended, so a trailing `\\r` still held back above (it\n // could have been the start of a split `\\r\\n` pair) can only be a bare\n // CR line ending now. Normalize it and re-check for any frame boundary\n // it just completed.\n buffer = buffer.replace(/\\r$/, '\\n');\n\n let boundary = buffer.indexOf('\\n\\n');\n\n while (boundary !== -1) {\n const frame = buffer.slice(0, boundary);\n\n buffer = buffer.slice(boundary + 2);\n\n const event = parseSseFrame(frame);\n\n if (event === DONE) return;\n if (event !== NO_DATA) yield event;\n\n boundary = buffer.indexOf('\\n\\n');\n }\n\n // Flush a final frame that arrived without a trailing blank line: some\n // servers close the connection right after the last `data:` line instead\n // of sending one more `\\n\\n` first.\n const trailing = buffer.trim();\n\n if (trailing) {\n const event = parseSseFrame(trailing);\n\n if (event !== DONE && event !== NO_DATA) yield event;\n }\n}\n\nconst DONE = Symbol('sse-stream-done');\nconst NO_DATA = Symbol('sse-frame-no-data');\n\n/**\n * Sentinel yielded by `parseSseStream` for a comment-only frame (no\n * `data:` payload), the mechanism providers use for SSE keep-alive\n * pings. Exported so a consumer (e.g. `fromFetch`) can react to \"still\n * alive\" separately from a genuinely empty frame (`NO_DATA`, kept internal).\n */\nexport const SSE_PING = Symbol('sse-frame-ping');\n\n/** Extracts and JSON-parses the `data:` payload of one SSE frame (the text between two blank lines). */\nfunction parseSseFrame(frame: string): unknown {\n const dataLines: string[] = [];\n let sawComment = false;\n\n for (const line of frame.split('\\n')) {\n if (line.startsWith(':')) {\n sawComment = true; // comment line, also used as a keep-alive ping\n continue;\n }\n if (!line.startsWith('data:')) continue; // ignore event:/id:/retry:/blank lines\n\n // A single space after the colon is stripped per the SSE spec; further\n // leading whitespace is preserved as part of the payload.\n dataLines.push(line.startsWith('data: ') ? line.slice(6) : line.slice(5));\n }\n\n if (!dataLines.length) return sawComment ? SSE_PING : NO_DATA;\n\n const data = dataLines.join('\\n');\n\n if (data === '[DONE]') return DONE;\n\n try {\n return JSON.parse(data);\n } catch (cause) {\n throw new LLMError(\n `Invalid JSON in SSE frame: ${data.slice(0, 200)}`,\n 'parse',\n undefined,\n undefined,\n cause,\n );\n }\n}\n","import { LLMError } from '../../types/index.js';\n\n/**\n * MIME types accepted for `ImageBlock.mimeType` across all adapters. This is\n * the intersection of what Anthropic, Gemini, OpenAI-compatible, and Bedrock\n * Converse all natively support, so a `ContentBlock[]` that validates for\n * one provider validates for all of them.\n */\nexport const SUPPORTED_IMAGE_MIME_TYPES = [\n 'image/png',\n 'image/jpeg',\n 'image/gif',\n 'image/webp',\n] as const;\n\nexport type SupportedImageMimeType = (typeof SUPPORTED_IMAGE_MIME_TYPES)[number];\n\n/**\n * Validates an `ImageBlock.mimeType` against the shared supported set.\n * Throws a non-retryable `LLMError('validation')`, since an unsupported\n * mimeType is a permanent failure, retrying the same input can't fix it,\n * the same way a schema-validation or JSON-parse failure isn't retried.\n */\nexport function assertSupportedImageMimeType(mimeType: string): SupportedImageMimeType {\n if ((SUPPORTED_IMAGE_MIME_TYPES as readonly string[]).includes(mimeType)) {\n return mimeType as SupportedImageMimeType;\n }\n\n throw new LLMError(\n `Unsupported image mimeType \"${mimeType}\": expected one of ${SUPPORTED_IMAGE_MIME_TYPES.join(', ')}`,\n 'validation',\n );\n}\n","/**\n * A static allow-list or predicate naming which models support native,\n * schema-constrained output as its own request field — Anthropic's\n * `output_config.format`, Bedrock's `outputConfig.textFormat` — separate\n * from `tools`/`tool_choice`, so it can be combined with real,\n * caller-supplied `tools` in the same request.\n *\n * There is no built-in default list here. Which models support this is\n * Anthropic's and Bedrock's call to make, not this package's, and it\n * changes over time; hardcoding a guessed list would risk silently\n * routing a request onto a field a given model doesn't actually support,\n * trading a clear `LLMError('validation')` for a confusing error from the\n * provider instead. So this is opt-in: pass the model IDs you've verified\n * against the provider's own docs (or a predicate). Left unset, no model\n * is treated as native-capable, `jsonSchema` keeps using the older\n * forced-single-tool-call emulation, and `tools` + `jsonSchema` together\n * is rejected, exactly this package's behavior before native support was\n * added.\n */\nexport type ModelCapabilityOverride = string[] | ((model: string) => boolean);\n\n/** Resolves whether `model` is covered by a caller-supplied allow-list/predicate. */\nexport function supportsNativeStructuredOutput(\n model: string,\n override?: ModelCapabilityOverride,\n): boolean {\n if (!override) return false;\n\n return Array.isArray(override) ? override.includes(model) : override(model);\n}\n","import {\n LLMError,\n type ContentBlock,\n type LLMClient,\n type WireStreamChunk,\n type WireToolCall,\n} from '../types/index.js';\nimport {\n assertSupportedImageMimeType,\n type SupportedImageMimeType,\n} from './internal/imageFormat.js';\nimport {\n supportsNativeStructuredOutput,\n type ModelCapabilityOverride,\n} from './internal/nativeStructuredOutput.js';\n\n/** Anthropic's native per-block content shape for a message. */\ntype AnthropicContentBlock =\n | { type: 'text'; text: string }\n | {\n type: 'image';\n source: { type: 'base64'; media_type: SupportedImageMimeType; data: string };\n }\n | { type: 'tool_use'; id: string; name: string; input: unknown }\n | { type: 'tool_result'; tool_use_id: string; content: string; is_error?: boolean };\n\n/** Minimal structural type for the Anthropic SDK's `messages.create` */\nexport interface AnthropicClient {\n messages: {\n create(\n params: {\n model: string;\n max_tokens: number;\n temperature?: number;\n system?: string;\n messages: Array<{ role: 'user' | 'assistant'; content: string | AnthropicContentBlock[] }>;\n tools?: Array<{\n name: string;\n description?: string;\n // The real Anthropic SDK's `Tool.input_schema` requires the\n // literal `type: 'object'` (VernLLM's own public `tools` API\n // accepts freeform JSON Schema, so this is narrower than that);\n // see the two call sites below for how a caller's schema is\n // asserted into this shape.\n input_schema: { type: 'object'; [key: string]: unknown };\n strict?: boolean;\n }>;\n tool_choice?:\n | { type: 'auto' }\n | { type: 'any' }\n | { type: 'none' }\n | { type: 'tool'; name: string };\n /**\n * Native, schema-constrained output: a separate request field from\n * `tools`/`tool_choice`, so it can be sent alongside real tool\n * calls. Only built by this adapter for models covered by\n * `nativeStructuredOutputModels` (opt-in, see\n * `AnthropicAdapterOptions`); other models keep getting\n * `jsonSchema` emulated as a forced single tool call, the\n * pre-existing behavior.\n *\n * Matches the real Anthropic API's `output_config.format` shape\n * exactly: just `type` and `schema`, no `name`/`description`/\n * `strict`. Those three exist on VernLLM's own `jsonSchema` API\n * (and are still forwarded on the legacy forced-tool-call path,\n * where they're real `Tool` fields), but the native structured-\n * output endpoint has no equivalent for any of them.\n */\n output_config?: {\n format: {\n type: 'json_schema';\n schema: Record<string, unknown>;\n };\n };\n },\n options: { signal: AbortSignal },\n ): Promise<{\n content: Array<{ type: string; text?: string; id?: string; name?: string; input?: unknown }>;\n usage?: { input_tokens?: number; output_tokens?: number };\n }>;\n };\n}\n\n/**\n * Translates a VernLLM `ContentBlock[]` (our provider-agnostic multimodal\n * shape) into Anthropic's native content-block array: text blocks pass\n * through as-is, image blocks become `{ type: 'image', source: { type:\n * 'base64', media_type, data } }`.\n */\nfunction toAnthropicContent(blocks: ContentBlock[]): AnthropicContentBlock[] {\n return blocks.map((block) =>\n block.type === 'image'\n ? {\n type: 'image',\n source: {\n type: 'base64',\n media_type: assertSupportedImageMimeType(block.mimeType),\n data: block.data,\n },\n }\n : { type: 'text', text: block.text },\n );\n}\n\n/**\n * Asserts a caller-supplied JSON Schema is an object schema before it's\n * used as Anthropic's `Tool.input_schema`, which (like every other\n * provider's function-calling API) requires `type: 'object'`. VernLLM's own\n * public `tools`/`jsonSchema` APIs accept freeform `Record<string,\n * unknown>` JSON Schema, so nothing upstream guarantees this at compile\n * time; this is the runtime check that stands in for that, so a schema\n * missing (or mistyping) `type: 'object'` fails loudly and immediately\n * instead of being silently forwarded to Anthropic malformed.\n */\nfunction assertObjectSchema(\n schema: Record<string, unknown>,\n toolName: string,\n): { type: 'object'; [key: string]: unknown } {\n if (schema.type !== 'object') {\n throw new LLMError(\n `Tool \"${toolName}\"'s schema must have \"type\": \"object\" (Anthropic requires object-shaped tool parameters).`,\n 'validation',\n );\n }\n\n return schema as { type: 'object'; [key: string]: unknown };\n}\n\n/**\n * Translates VernLLM's OpenAI-shaped wire `tool_choice` into Anthropic's\n * `{ type: 'auto' | 'any' | 'none' | 'tool', name? }` shape. `'required'`\n * maps to `'any'` (Anthropic's \"must call some tool\" equivalent).\n */\nfunction toAnthropicToolChoice(\n toolChoice: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice'],\n):\n | { type: 'auto' }\n | { type: 'any' }\n | { type: 'none' }\n | { type: 'tool'; name: string }\n | undefined {\n if (!toolChoice || toolChoice === 'auto') return { type: 'auto' };\n if (toolChoice === 'none') return { type: 'none' };\n if (toolChoice === 'required') return { type: 'any' };\n\n return { type: 'tool', name: toolChoice.function.name };\n}\n\n/** One SSE event of an Anthropic `messages.create({ stream: true })` stream. */\ntype AnthropicStreamEvent =\n | { type: 'message_start'; message: { usage?: { input_tokens?: number } } }\n | {\n type: 'content_block_start';\n index: number;\n content_block: { type: string; id?: string; name?: string };\n }\n | {\n type: 'content_block_delta';\n index: number;\n delta:\n | { type: 'text_delta'; text: string }\n | { type: 'input_json_delta'; partial_json: string };\n }\n | { type: 'content_block_stop'; index: number }\n | { type: 'message_delta'; usage?: { output_tokens?: number } }\n | { type: 'message_stop' }\n // Keep-alive event during long streams (e.g. extended thinking). Modeled\n // so the event switch has somewhere to route it. See createStream.\n | { type: 'ping' };\n\ntype AnthropicRequestBody = Parameters<AnthropicClient['messages']['create']>[0];\n\n/**\n * Maps VernLLM's OpenAI-shaped wire `tools`/`tool_choice` into Anthropic's\n * `tools`/`tool_choice` shape. Shared by the two call sites that build real\n * (non-schema-forced) tool definitions: the plain tools-only branch, and\n * the native-structured-output branch, which sends real tools alongside\n * `output_config` rather than instead of it.\n */\nfunction buildAnthropicTools(\n tools: NonNullable<Parameters<LLMClient['chat']['completions']['create']>[0]['tools']>,\n toolChoiceParam: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice'],\n): {\n tools: NonNullable<Parameters<AnthropicClient['messages']['create']>[0]['tools']>;\n toolChoice: Parameters<AnthropicClient['messages']['create']>[0]['tool_choice'];\n} {\n return {\n tools: tools.map((t) => ({\n name: t.function.name,\n description: t.function.description,\n // Tool parameters are always object schemas in practice (every\n // provider's function-calling API requires it).\n input_schema: assertObjectSchema(t.function.parameters, t.function.name),\n })),\n toolChoice: toAnthropicToolChoice(toolChoiceParam),\n };\n}\n\n/**\n * Builds the Anthropic-shaped request body from VernLLM's wire params,\n * shared between `create` and `createStream` so both go through identical\n * translation (system prompt, message shaping, and the jsonSchema →\n * forced-single-tool mapping all happen exactly once, not once per entry\n * point).\n *\n * Returns `toolName` alongside the body: when set, the model was forced to\n * call a single synthetic tool standing in for `jsonSchema` output (the\n * legacy path, for models without native structured-output support), and\n * both `create` and `createStream` need to know this so they can unwrap\n * that tool call back into plain text content instead of treating it like\n * a real tool call. On the native path (model supports `output_config`),\n * `toolName` is `undefined`: the schema-conforming JSON already arrives as\n * ordinary text content, nothing to unwrap, and any real tool calls in\n * `params.tools` are left for the normal, non-forced tool-call handling\n * both `create` and `createStream` already do when `toolName` is unset.\n */\nfunction buildAnthropicRequestBody(\n params: Parameters<LLMClient['chat']['completions']['create']>[0],\n nativeStructuredOutputModels?: ModelCapabilityOverride,\n): { body: AnthropicRequestBody; toolName: string | undefined } {\n const systemMessage = params.messages.find((m) => m.role === 'system');\n\n // Keep user, assistant, and tool turns, in order. Anthropic has no\n // separate 'tool' role: tool results travel as a user-role message\n // containing tool_result content blocks, and an assistant's tool\n // requests travel as tool_use content blocks on its own turn.\n const conversationMessages = params.messages.filter(\n (m) => m.role === 'user' || m.role === 'assistant' || m.role === 'tool',\n );\n\n const jsonSchema =\n params.response_format?.type === 'json_schema' ? params.response_format.json_schema : undefined;\n\n const schemaName = jsonSchema?.name.trim();\n\n if (jsonSchema && !schemaName) {\n throw new LLMError('json_schema.name must not be empty.', 'validation');\n }\n\n const isNative =\n Boolean(jsonSchema) &&\n supportsNativeStructuredOutput(params.model, nativeStructuredOutputModels);\n\n if (jsonSchema && params.tools?.length && !isNative) {\n throw new LLMError(\n `Anthropic model \"${params.model}\" is not covered by nativeStructuredOutputModels, so ` +\n '`jsonSchema` is emulated as a forced single tool call there, which collides with the ' +\n '`tools` you also provided. Either drop `tools` or `jsonSchema` for this call, or pass ' +\n \"this model in fromAnthropic's `nativeStructuredOutputModels` option once you've \" +\n \"confirmed it supports Anthropic's `output_config.format`.\",\n 'validation',\n );\n }\n\n let toolName: string | undefined;\n let jsonInstruction: string | undefined;\n let outputFormat: NonNullable<AnthropicRequestBody['output_config']>['format'] | undefined;\n let tools: NonNullable<Parameters<AnthropicClient['messages']['create']>[0]['tools']> | undefined;\n let toolChoice: Parameters<AnthropicClient['messages']['create']>[0]['tool_choice'];\n\n if (jsonSchema && isNative) {\n // Native path: the schema goes in its own request field, independent\n // of tools/tool_choice, so real tools (if any) are built exactly like\n // the tools-only branch below and sent alongside it.\n //\n // Only `type` and `schema` are sent: the real Anthropic API's\n // `output_config.format` has no `name`/`description`/`strict` fields,\n // unlike the legacy forced-tool-call path below, where those are real\n // `Tool` fields. `schemaName` is still required and validated above\n // (a caller-facing identifier, useful for logging/debugging on their\n // end), it just never reaches this particular wire request.\n outputFormat = { type: 'json_schema', schema: jsonSchema.schema };\n\n if (params.tools?.length) {\n ({ tools, toolChoice } = buildAnthropicTools(params.tools, params.tool_choice));\n }\n } else if (jsonSchema && schemaName) {\n // Legacy path: jsonSchema alone (or with tools, on a native model — see\n // above), on a model without native support, becomes a forced single\n // tool call, unchanged from before this adapter had a native path.\n const { schema, description, strict } = jsonSchema;\n\n toolName = schemaName;\n // VernLLM's public `jsonSchema` API accepts a freeform JSON Schema\n // object (`Record<string, unknown>`), not necessarily typed with a\n // literal `type: 'object'`, but tool/function parameters are always\n // object schemas in practice (every provider's function-calling API\n // requires it), so this assertion reflects that existing convention\n // rather than changing behavior.\n tools = [\n { name: toolName, description, input_schema: assertObjectSchema(schema, toolName), strict },\n ];\n toolChoice = { type: 'tool', name: toolName };\n } else if (params.response_format?.type === 'json_object') {\n // No schema to build a tool from, fall back to a prompt instruction.\n // This does not exclude real `tools`: `json_object` mode is just a\n // system-prompt nudge, not a request field that could collide with\n // `tools`/`tool_choice`, so both are set independently below.\n jsonInstruction = 'Respond with valid JSON only, no prose or markdown fences.';\n }\n\n if (!jsonSchema && params.tools?.length) {\n ({ tools, toolChoice } = buildAnthropicTools(params.tools, params.tool_choice));\n }\n\n // `reasoning_effort` (OpenAI o-series/gpt-5 style) has no direct Anthropic\n // equivalent. Claude's extended thinking uses a token budget, not a tier\n // string, so it's intentionally dropped here rather than guessed at.\n\n const system = [systemMessage?.content, jsonInstruction].filter(Boolean).join('\\n\\n');\n\n const body: AnthropicRequestBody = {\n model: params.model,\n max_tokens: params.max_tokens,\n ...(params.temperature !== undefined ? { temperature: params.temperature } : {}),\n system: system || undefined,\n messages: mergeConsecutiveToolResults(conversationMessages.map((m) => toAnthropicMessage(m))),\n ...(tools ? { tools, tool_choice: toolChoice } : {}),\n ...(outputFormat ? { output_config: { format: outputFormat } } : {}),\n };\n\n return { body, toolName };\n}\n\n/** Optional configuration for `fromAnthropic`. */\nexport interface AnthropicAdapterOptions {\n /**\n * Which models support native, schema-constrained output\n * (`output_config.format`), independent of `tools`/`tool_choice`, so it\n * can be combined with real `tools` in one request. Pass a static list\n * of model IDs (verified against Anthropic's own docs) or a predicate.\n *\n * There is no built-in default here (see `supportsNativeStructuredOutput`\n * for why). Left unset, every model uses the older forced-single-tool-\n * call emulation, and `tools` + `jsonSchema` together is rejected,\n * exactly this adapter's behavior before native support was added.\n */\n nativeStructuredOutputModels?: ModelCapabilityOverride;\n}\n\n/**\n * Wraps an Anthropic SDK client so it satisfies the same `LLMClient`\n * interface VernLLM uses for OpenAI/Groq.\n *\n * `response_format: json_schema`, on a model covered by\n * `options.nativeStructuredOutputModels`, is sent as `output_config.format`,\n * its own request field, independent of `tools`/`tool_choice`, so it can be\n * combined with real, caller-supplied `tools` in the same request. Only\n * `type` and `schema` are sent on this path, the real Anthropic API's\n * `output_config.format` has no `name`/`description`/`strict` fields.\n *\n * On any other model (the default, since `nativeStructuredOutputModels` is\n * opt-in), `response_format: json_schema` is mapped to Anthropic's forced\n * tool-use instead: a single tool is defined with `input_schema` set to\n * the caller's schema, `description` forwarded when provided, and `strict`\n * forwarded when set, and `tool_choice` forces the model to call it. This\n * legacy path cannot be combined with real `tools` (both would need the\n * same `tools`/`tool_choice` field), and a call that tries throws\n * `LLMError('validation')` before reaching the API. Provider-constrained\n * schema matching applies only when `strict: true` is forwarded and\n * supported.\n *\n * `response_format: json_object` (no schema to build a tool from) falls\n * back to a system-prompt instruction, since there's nothing to constrain\n * generation against. Unlike `jsonSchema`, this combines with real `tools`\n * freely on every model: it's a prompt nudge, not a request field, so\n * there's nothing for it to collide with.\n */\nexport function fromAnthropic(\n anthropicClient: AnthropicClient,\n options?: AnthropicAdapterOptions,\n): LLMClient {\n const nativeStructuredOutputModels = options?.nativeStructuredOutputModels;\n // The Anthropic SDK's `messages.create`, called with `stream: true`,\n // returns an AsyncIterable of `AnthropicStreamEvent` rather than\n // `AnthropicClient['messages']['create']`'s normal single-message return\n // type, hence `unknown` here and a cast at the call site, same\n // rationale as `fromOpenAICompatible`'s `rawCreate`: the wire contract,\n // not the SDK's own TS types, is what's actually relied on.\n const rawMessagesCreate = anthropicClient.messages.create.bind(\n anthropicClient.messages,\n ) as unknown as (\n params: unknown,\n options: { signal: AbortSignal },\n ) => Promise<unknown> | AsyncIterable<AnthropicStreamEvent>;\n\n return {\n chat: {\n completions: {\n async create(params, options) {\n const { body, toolName } = buildAnthropicRequestBody(\n params,\n nativeStructuredOutputModels,\n );\n\n const response = await anthropicClient.messages.create(body, options);\n\n let text: string;\n let wireToolCalls: WireToolCall[] | undefined;\n if (toolName) {\n const toolUse = response.content.find(\n (block) => block.type === 'tool_use' && block.name === toolName,\n );\n\n if (!toolUse) {\n throw new LLMError(\n `Anthropic did not return the required structured output tool \"${toolName}\".`,\n 'validation',\n );\n }\n\n if (\n !toolUse.input ||\n typeof toolUse.input !== 'object' ||\n Array.isArray(toolUse.input)\n ) {\n throw new LLMError(\n `Anthropic returned invalid structured output for tool \"${toolName}\". Expected an object.`,\n 'validation',\n );\n }\n\n text = JSON.stringify(toolUse.input);\n } else {\n text = response.content\n .filter((block) => block.type === 'text')\n .map((block) => block.text ?? '')\n .join('');\n\n const toolUses = response.content.filter((block) => block.type === 'tool_use');\n\n if (toolUses.length) {\n wireToolCalls = toolUses.map((block) => ({\n id: block.id!,\n type: 'function' as const,\n function: { name: block.name!, arguments: JSON.stringify(block.input ?? {}) },\n }));\n }\n }\n\n return {\n choices: [\n {\n message: { content: text, ...(wireToolCalls ? { tool_calls: wireToolCalls } : {}) },\n },\n ],\n usage: {\n prompt_tokens: response.usage?.input_tokens,\n completion_tokens: response.usage?.output_tokens,\n total_tokens:\n (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0),\n },\n };\n },\n\n async *createStream(params, options) {\n const { body, toolName } = buildAnthropicRequestBody(\n params,\n nativeStructuredOutputModels,\n );\n\n const stream = (await rawMessagesCreate(\n { ...body, stream: true },\n options,\n )) as AsyncIterable<AnthropicStreamEvent>;\n\n // Tracks which content-block index is which kind, since Anthropic\n // interleaves text and tool_use blocks under a shared `index`\n // sequence and later delta events only carry that index, not the\n // kind. `json-tool` is the forced single-tool-call standing in\n // for `jsonSchema` output (see `toolName` above): its\n // input_json_delta fragments are re-emitted as `text-delta`, not\n // `tool_call_delta`, so the accumulated result lands in\n // finalizeResponse's `content` path exactly like the\n // non-streaming `create` branch above unwraps it.\n const blockKinds = new Map<number, 'text' | 'tool_use' | 'json-tool'>();\n let inputTokens = 0;\n let sawJsonTool = false;\n\n for await (const event of stream) {\n if (event.type === 'message_start') {\n inputTokens = event.message.usage?.input_tokens ?? 0;\n } else if (event.type === 'content_block_start') {\n if (event.content_block.type === 'tool_use') {\n const kind = event.content_block.name === toolName ? 'json-tool' : 'tool_use';\n\n blockKinds.set(event.index, kind);\n\n if (kind === 'json-tool') {\n sawJsonTool = true;\n } else if (!toolName) {\n // Only surface tool_use blocks as tool_call_delta chunks\n // when there's no forced structured-output tool in play.\n // When `toolName` is set, any non-matching tool_use block\n // is unexpected (Anthropic forces exactly one tool), and\n // surfacing it would corrupt the caller's expectation of\n // receiving only the forced tool's JSON payload as text.\n yield {\n type: 'tool_call_delta',\n index: event.index,\n id: event.content_block.id,\n name: event.content_block.name,\n };\n }\n } else {\n blockKinds.set(event.index, 'text');\n }\n } else if (event.type === 'content_block_delta') {\n if (event.delta.type === 'text_delta') {\n // Only surfaced as real content when there's no forced\n // json-schema tool in play. When `toolName` is set, the\n // *only* content that should end up in the accumulated\n // text is the forced tool's own JSON payload (its\n // input_json_delta fragments, handled below), exactly\n // what the non-streaming `create` branch above does by\n // discarding every content block except the matching\n // tool_use one. Anthropic can still emit genuine text\n // blocks alongside a forced tool call (a model narrating\n // before calling it, say), and without this guard those\n // would get concatenated into the same buffer as the\n // tool's JSON, corrupting it.\n if (!toolName) {\n yield { type: 'text-delta', delta: event.delta.text };\n }\n } else if (event.delta.type === 'input_json_delta') {\n const kind = blockKinds.get(event.index);\n\n if (kind === 'json-tool') {\n yield { type: 'text-delta', delta: event.delta.partial_json };\n } else if (!toolName) {\n yield {\n type: 'tool_call_delta',\n index: event.index,\n argumentsDelta: event.delta.partial_json,\n };\n }\n }\n } else if (event.type === 'message_delta') {\n const outputTokens = event.usage?.output_tokens ?? 0;\n\n yield {\n type: 'usage',\n usage: {\n prompt_tokens: inputTokens,\n completion_tokens: outputTokens,\n total_tokens: inputTokens + outputTokens,\n },\n } satisfies WireStreamChunk;\n } else if (event.type === 'ping') {\n // Keep-alive with no content. Yielding it resets the\n // idle-timeout clock in VernLLM's stream loop.\n yield { type: 'ping' };\n }\n }\n\n if (toolName && !sawJsonTool) {\n throw new LLMError(\n `Anthropic did not return the required structured output tool \"${toolName}\".`,\n 'validation',\n );\n }\n },\n },\n },\n };\n}\n\n/**\n * Anthropic requires strict role alternation, so the per-wire-message\n * mapping above (one `{role:'user', content:[tool_result]}` per VernLLM\n * wire tool message) needs merging back together when an assistant turn\n * requested more than one tool: multiple consecutive user turns would\n * violate that alternation, and Anthropic's API rejects it outright. This\n * merges any run of tool-result-only user messages into one, with all\n * their tool_result blocks combined, the shape Anthropic expects for \"here\n * are the results of everything you just asked for.\"\n */\nfunction mergeConsecutiveToolResults(\n messages: { role: 'user' | 'assistant'; content: string | AnthropicContentBlock[] }[],\n): { role: 'user' | 'assistant'; content: string | AnthropicContentBlock[] }[] {\n const isToolResultOnly = (\n m: (typeof messages)[number],\n ): m is { role: 'user'; content: AnthropicContentBlock[] } =>\n m.role === 'user' &&\n Array.isArray(m.content) &&\n m.content.length > 0 &&\n m.content.every((b) => b.type === 'tool_result');\n\n const merged: (typeof messages)[number][] = [];\n\n for (const m of messages) {\n const prev = merged.at(-1);\n\n if (isToolResultOnly(m) && prev && isToolResultOnly(prev)) {\n prev.content.push(...m.content);\n } else {\n merged.push(m);\n }\n }\n\n return merged;\n}\n\n/**\n * Translates one VernLLM wire message (OpenAI-shaped: plain user/assistant\n * turns, an assistant turn with `tool_calls`, or a `tool` turn) into\n * Anthropic's `{ role: 'user' | 'assistant', content }` shape.\n */\nfunction toAnthropicMessage(\n m: Extract<\n Parameters<LLMClient['chat']['completions']['create']>[0]['messages'][number],\n { role: 'user' | 'assistant' | 'tool' }\n >,\n): { role: 'user' | 'assistant'; content: string | AnthropicContentBlock[] } {\n if (m.role === 'tool') {\n // Anthropic has no 'tool' role: results travel as a user turn with\n // tool_result blocks.\n return {\n role: 'user',\n content: [\n {\n type: 'tool_result',\n tool_use_id: m.tool_call_id,\n content: m.content,\n ...(m.is_error ? { is_error: true } : {}),\n },\n ],\n };\n }\n\n if (m.role === 'assistant' && m.tool_calls?.length) {\n const blocks: AnthropicContentBlock[] = [];\n\n if (m.content) blocks.push({ type: 'text', text: m.content });\n\n for (const tc of m.tool_calls) {\n let input: unknown;\n\n try {\n input = tc.function.arguments.trim() ? JSON.parse(tc.function.arguments) : {};\n } catch (cause) {\n throw new LLMError(\n `Assistant tool call \"${tc.function.name}\" (${tc.id}) has arguments that are not valid JSON.`,\n 'validation',\n undefined,\n undefined,\n cause,\n );\n }\n\n if (input === null || Array.isArray(input) || typeof input !== 'object') {\n throw new LLMError(\n `Assistant tool call \"${tc.function.name}\" (${tc.id}) arguments must be a JSON object.`,\n 'validation',\n );\n }\n\n blocks.push({ type: 'tool_use', id: tc.id, name: tc.function.name, input });\n }\n\n return { role: 'assistant', content: blocks };\n }\n\n return {\n role: m.role,\n content: Array.isArray(m.content) ? toAnthropicContent(m.content) : (m.content ?? ''),\n };\n}\n","import {\n LLMError,\n type ContentBlock,\n type LLMClient,\n type WireStreamChunk,\n type WireToolCall,\n} from '../types/index.js';\nimport { assertSupportedImageMimeType } from './internal/imageFormat.js';\n\n/** Gemini's native per-part content shape for a `contents` entry. */\ntype GeminiPart =\n | { text: string }\n | { inlineData: { mimeType: string; data: string } }\n | { functionCall: { name: string; args: unknown } }\n | { functionResponse: { name: string; response: unknown } };\n\n/**\n * Structural type matching the real `@google/genai` SDK's `ai.models`\n * object: `generateContent`/`generateContentStream` both take a single\n * `{ model, contents, config }` argument (config carries\n * `systemInstruction`, `tools`, `toolConfig`, generation settings, and\n * `abortSignal` all together), matching the real SDK closely enough that\n * `fromGemini(ai.models)` works directly, e.g:\n *\n * ```ts\n * import { GoogleGenAI } from '@google/genai';\n * const ai = new GoogleGenAI({ apiKey: '...' });\n * const llm = new VernLLM({ client: fromGemini(ai.models), model: 'gemini-2.5-flash' });\n * ```\n */\nexport interface GeminiClient {\n generateContent(params: {\n model?: string;\n contents: Array<{ role: 'user' | 'model'; parts: GeminiPart[] }>;\n config?: {\n systemInstruction?: { parts: Array<{ text: string }> };\n temperature?: number;\n maxOutputTokens?: number;\n responseMimeType?: string;\n responseSchema?: Record<string, unknown>;\n tools?: Array<{\n functionDeclarations: Array<{\n name: string;\n description?: string;\n parameters: Record<string, unknown>;\n }>;\n }>;\n toolConfig?: {\n functionCallingConfig: {\n mode: 'AUTO' | 'ANY' | 'NONE';\n allowedFunctionNames?: string[];\n };\n };\n abortSignal?: AbortSignal;\n };\n }): Promise<{\n candidates?: Array<{\n content?: {\n parts?: Array<{ text?: string; functionCall?: { name: string; args: unknown } }>;\n };\n }>;\n usageMetadata?: {\n promptTokenCount?: number;\n candidatesTokenCount?: number;\n totalTokenCount?: number;\n };\n }>;\n\n /**\n * Optional. Required only for `stream: true` calls. Takes the same\n * request shape as `generateContent`. Matching the real SDK's own\n * `generateContentStream`, this resolves to an `AsyncIterable` (rather\n * than returning one synchronously) of partial responses, each chunk\n * holding the same `candidates[].content.parts[]` structure as\n * `generateContent`'s response, just incremental.\n */\n generateContentStream?(params: Parameters<GeminiClient['generateContent']>[0]): Promise<\n AsyncIterable<{\n candidates?: Array<{\n content?: {\n parts?: Array<{ text?: string; functionCall?: { name: string; args: unknown } }>;\n };\n }>;\n usageMetadata?: {\n promptTokenCount?: number;\n candidatesTokenCount?: number;\n totalTokenCount?: number;\n };\n }>\n >;\n}\n\n/**\n * Translates a VernLLM `ContentBlock[]` into Gemini's native `parts` array:\n * text blocks become `{ text }`, image blocks become inline data parts\n * (`{ inlineData: { mimeType, data } }`), Gemini's shape for embedding raw\n * base64 image bytes directly in the request.\n */\nfunction toGeminiParts(blocks: ContentBlock[]): GeminiPart[] {\n return blocks.map((block) =>\n block.type === 'image'\n ? { inlineData: { mimeType: assertSupportedImageMimeType(block.mimeType), data: block.data } }\n : { text: block.text },\n );\n}\n\n/** Maps VernLLM's OpenAI-shaped wire `tool_choice` onto Gemini's `functionCallingConfig`. */\nfunction toGeminiToolConfig(\n toolChoice: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice'],\n): NonNullable<\n NonNullable<Parameters<GeminiClient['generateContent']>[0]['config']>['toolConfig']\n> {\n if (!toolChoice || toolChoice === 'auto') {\n return { functionCallingConfig: { mode: 'AUTO' } };\n }\n if (toolChoice === 'none') {\n return { functionCallingConfig: { mode: 'NONE' } };\n }\n if (toolChoice === 'required') {\n return { functionCallingConfig: { mode: 'ANY' } };\n }\n\n return {\n functionCallingConfig: { mode: 'ANY', allowedFunctionNames: [toolChoice.function.name] },\n };\n}\n\n/**\n * Translates one VernLLM wire message into a Gemini `contents` entry.\n * Gemini has no separate 'tool' role: a prior assistant tool request\n * becomes a `'model'` turn with `functionCall` parts, and its result\n * becomes a `'user'` turn with `functionResponse` parts.\n */\nfunction toGeminiContent(\n m: Extract<\n Parameters<LLMClient['chat']['completions']['create']>[0]['messages'][number],\n { role: 'user' | 'assistant' | 'tool' }\n >,\n): { role: 'user' | 'model'; parts: GeminiPart[] } {\n if (m.role === 'tool') {\n // Gemini's functionResponse identifies the call by function *name*, and\n // the Gemini branch of this adapter sets wire tool_call ids equal to\n // the function name for exactly this reason, so tool_call_id here is\n // already the name Gemini expects.\n return {\n role: 'user',\n parts: [\n {\n functionResponse: {\n name: m.tool_call_id,\n response: parseToolResult(m.content),\n },\n },\n ],\n };\n }\n\n if (m.role === 'assistant' && m.tool_calls?.length) {\n const parts: GeminiPart[] = [];\n\n if (typeof m.content === 'string' && m.content) {\n parts.push({ text: m.content });\n }\n\n parts.push(\n ...m.tool_calls.map((tc) => ({\n functionCall: {\n name: tc.function.name,\n args: parseToolArguments(tc.function.arguments, tc.function.name),\n },\n })),\n );\n\n return {\n role: 'model',\n parts,\n };\n }\n\n return {\n role: m.role === 'assistant' ? 'model' : 'user',\n parts: Array.isArray(m.content) ? toGeminiParts(m.content) : [{ text: m.content ?? '' }],\n };\n}\n\nfunction parseToolArguments(text: string, toolName: string): Record<string, unknown> {\n let parsed: unknown;\n\n try {\n parsed = text.trim() ? JSON.parse(text) : {};\n } catch (cause) {\n throw new LLMError(\n `Tool call \"${toolName}\" arguments are not valid JSON.`,\n 'validation',\n undefined,\n undefined,\n cause,\n );\n }\n\n if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') {\n throw new LLMError(`Tool call \"${toolName}\" arguments must be a JSON object.`, 'validation');\n }\n\n return parsed as Record<string, unknown>;\n}\n\nfunction parseToolResult(text: string): unknown {\n try {\n return text.trim() ? JSON.parse(text) : '';\n } catch {\n return text;\n }\n}\n\n/**\n * Gemini expects the results of everything the model asked for in one turn\n * to arrive together as multiple `functionResponse` parts on a single\n * `'user'` entry, not as separate consecutive `'user'` entries. The\n * per-wire-message mapping above produces one `'user'` entry per VernLLM\n * wire tool message, so when an assistant turn requested more than one\n * tool, this merges the resulting run of functionResponse-only `'user'`\n * entries back into one.\n */\nfunction mergeConsecutiveFunctionResponses(\n contents: { role: 'user' | 'model'; parts: GeminiPart[] }[],\n): { role: 'user' | 'model'; parts: GeminiPart[] }[] {\n const isFunctionResponseOnly = (\n c: (typeof contents)[number],\n ): c is { role: 'user'; parts: GeminiPart[] } =>\n c.role === 'user' && c.parts.length > 0 && c.parts.every((p) => 'functionResponse' in p);\n\n const merged: (typeof contents)[number][] = [];\n\n for (const c of contents) {\n const prev = merged.at(-1);\n\n if (isFunctionResponseOnly(c) && prev && isFunctionResponseOnly(prev)) {\n prev.parts.push(...c.parts);\n } else {\n merged.push(c);\n }\n }\n\n return merged;\n}\n\ntype GeminiRequest = Parameters<GeminiClient['generateContent']>[0];\ntype GeminiConfig = NonNullable<GeminiRequest['config']>;\n\n/**\n * Builds the Gemini-shaped request from VernLLM's wire params, shared\n * between `create` and `createStream` so both go through identical\n * translation (contents shaping, `responseSchema`/`responseMimeType`\n * mapping, and tool/toolConfig translation all happen exactly once).\n * `abortSignal` is folded into `config` by the caller (`create`/\n * `createStream`), once the request options are available.\n */\nfunction buildGeminiRequest(\n params: Parameters<LLMClient['chat']['completions']['create']>[0],\n): GeminiRequest {\n const systemMessage = params.messages.find((m) => m.role === 'system');\n // Keep user, assistant, and tool turns, in order.\n const conversationMessages = params.messages.filter(\n (m) => m.role === 'user' || m.role === 'assistant' || m.role === 'tool',\n );\n\n const wantsJson = Boolean(params.response_format);\n const config: GeminiConfig = {\n ...(params.temperature !== undefined ? { temperature: params.temperature } : {}),\n maxOutputTokens: params.max_tokens,\n ...(systemMessage\n ? // System turns are always plain strings; only user turns can carry ContentBlock[]\n { systemInstruction: { parts: [{ text: systemMessage.content as string }] } }\n : {}),\n };\n\n if (wantsJson) {\n config.responseMimeType = 'application/json';\n }\n\n if (params.response_format?.type === 'json_schema') {\n const { schema, description } = params.response_format.json_schema;\n\n config.responseSchema = {\n ...schema,\n ...(description ? { description } : {}),\n };\n }\n\n if (params.tools?.length) {\n config.tools = [\n {\n functionDeclarations: params.tools.map((t) => ({\n name: t.function.name,\n description: t.function.description,\n parameters: t.function.parameters,\n })),\n },\n ];\n config.toolConfig = toGeminiToolConfig(params.tool_choice);\n }\n\n return {\n model: params.model,\n contents: mergeConsecutiveFunctionResponses(\n conversationMessages.map((m) => toGeminiContent(m)),\n ),\n config,\n };\n}\n\n/**\n * Wraps a Gemini client so it satisfies the `LLMClient` interface VernLLM\n * uses for OpenAI-compatible APIs. Gemini's shape differs on nearly every\n * axis: a `contents` array instead of `messages`, a separate\n * `systemInstruction` field instead of a `system` role message,\n * `generationConfig` instead of top-level `temperature`/`max_tokens`, and\n * native JSON Schema support via `responseMimeType: 'application/json'` +\n * `responseSchema`. `reasoning_effort` has no equivalent. Gemini's thinking\n * models use a token budget, not an effort tier, so it's dropped, same as\n * Anthropic.\n *\n * `tools` maps to Gemini's native `functionDeclarations`/`functionCall`;\n * `tool_choice` maps to `toolConfig.functionCallingConfig`. Gemini accepts\n * `responseSchema` and `tools` in the same request natively, so both are\n * set independently here and no special-casing is needed for the\n * combination, unlike `fromAnthropic`/`fromBedrock`.\n *\n * `createStream` calls `generateContentStream` (optional on `GeminiClient`\n *, required only if the caller sets `stream: true`) and translates each\n * partial response into `WireStreamChunk`s. Unlike OpenAI/Anthropic,\n * Gemini's own function-calling API doesn't stream tool-call arguments\n * incrementally: a `functionCall` part always arrives whole in one chunk,\n * so each one is emitted as a single, complete `tool_call_delta` (a\n * one-shot \"delta\" containing the full arguments) rather than accumulated\n * fragments, that's a real difference in the underlying API, not\n * something this adapter can smooth over. `usageMetadata` is (per Gemini's\n * own behavior) only reliably present on the last chunk, so the `usage`\n * `WireStreamChunk` is emitted once, after the stream completes, from\n * whichever chunk's `usageMetadata` was seen last.\n */\nexport function fromGemini(geminiClient: GeminiClient): LLMClient {\n return {\n chat: {\n completions: {\n async create(params, options) {\n const request = buildGeminiRequest(params);\n request.config = { ...request.config, abortSignal: options.signal };\n\n const response = await geminiClient.generateContent(request);\n\n const parts = response.candidates?.[0]?.content?.parts ?? [];\n const text = parts.map((p) => p.text ?? '').join('');\n const functionCalls = parts.filter((p) => p.functionCall);\n\n let wireToolCalls: WireToolCall[] | undefined;\n\n if (functionCalls.length) {\n wireToolCalls = functionCalls.map((p) => ({\n // Gemini's functionResponse correlates by function *name*, not\n // a call id (Gemini has no call-id concept at all), so the id\n // here is just the name. This means two calls to the *same*\n // tool within one turn can't be told apart when results come\n // back, a real limitation of Gemini's own function-calling\n // API, not something VernLLM can paper over.\n id: p.functionCall!.name,\n type: 'function' as const,\n function: {\n name: p.functionCall!.name,\n arguments: JSON.stringify(p.functionCall!.args ?? {}),\n },\n }));\n }\n\n return {\n choices: [\n {\n message: { content: text, ...(wireToolCalls ? { tool_calls: wireToolCalls } : {}) },\n },\n ],\n usage: {\n prompt_tokens: response.usageMetadata?.promptTokenCount,\n completion_tokens: response.usageMetadata?.candidatesTokenCount,\n total_tokens: response.usageMetadata?.totalTokenCount,\n },\n };\n },\n\n async *createStream(params, options) {\n if (!geminiClient.generateContentStream) {\n throw new LLMError(\n 'stream: true requires a Gemini client with generateContentStream',\n 'validation',\n );\n }\n\n const request = buildGeminiRequest(params);\n request.config = { ...request.config, abortSignal: options.signal };\n\n const stream = await geminiClient.generateContentStream(request);\n\n let toolCallIndex = 0;\n let lastUsage:\n | NonNullable<Awaited<ReturnType<GeminiClient['generateContent']>>['usageMetadata']>\n | undefined;\n\n for await (const chunk of stream) {\n const parts = chunk.candidates?.[0]?.content?.parts ?? [];\n\n for (const part of parts) {\n if (part.text) {\n yield { type: 'text-delta', delta: part.text };\n }\n\n if (part.functionCall) {\n yield {\n type: 'tool_call_delta',\n index: toolCallIndex,\n id: part.functionCall.name,\n name: part.functionCall.name,\n argumentsDelta: JSON.stringify(part.functionCall.args ?? {}),\n // INVARIANT: assumes Gemini always sends a complete\n // function-call args blob per part, no incremental\n // streaming, per current API behavior. There is no field\n // on this minimal structural type to derive this from.\n // If Gemini ever starts streaming args incrementally,\n // this becomes silently wrong. See the test titled\n // \"INVARIANT: hardcodes complete: true on\n // tool_call_delta\" in gemini.stream.unit.test.ts, which\n // exists specifically to catch that drift.\n complete: true,\n } satisfies WireStreamChunk;\n toolCallIndex++;\n }\n }\n\n if (chunk.usageMetadata) {\n lastUsage = chunk.usageMetadata;\n }\n }\n\n if (lastUsage) {\n yield {\n type: 'usage',\n usage: {\n prompt_tokens: lastUsage.promptTokenCount,\n completion_tokens: lastUsage.candidatesTokenCount,\n total_tokens: lastUsage.totalTokenCount,\n },\n };\n }\n },\n },\n },\n };\n}\n","import {\n LLMError,\n type ContentBlock,\n type LLMClient,\n type WireStreamChunk,\n type WireToolCall,\n} from '../types/index.js';\nimport { assertSupportedImageMimeType } from './internal/imageFormat.js';\nimport {\n supportsNativeStructuredOutput,\n type ModelCapabilityOverride,\n} from './internal/nativeStructuredOutput.js';\n\n/** Bedrock Converse's supported inline image formats. */\ntype BedrockImageFormat = 'png' | 'jpeg' | 'gif' | 'webp';\n\n/** Bedrock Converse's native per-block content shape for a message. */\ntype BedrockContentBlock =\n | { text: string }\n | { image: { format: BedrockImageFormat; source: { bytes: Uint8Array } } }\n | { toolUse: { toolUseId: string; name: string; input: unknown } }\n | {\n toolResult: {\n toolUseId: string;\n content: Array<{ text: string }>;\n status?: 'success' | 'error';\n };\n };\n\n/**\n * Minimal structural type matching AWS Bedrock's Converse API. This is\n * intentionally NOT `BedrockRuntimeClient` itself, the AWS SDK v3 client\n * exposes `.send(command)`, not a direct `.converse()` method, and pulling\n * in `@aws-sdk/client-bedrock-runtime` as a dependency just for its types\n * isn't worth it for a structural adapter. Wrap your client, e.g:\n *\n * ```ts\n * import { BedrockRuntimeClient, ConverseCommand } from '@aws-sdk/client-bedrock-runtime';\n * const client = new BedrockRuntimeClient({ region: 'us-east-1' });\n * const converseClient = {\n * converse: (params, options) =>\n * client.send(new ConverseCommand(params), { abortSignal: options.signal }),\n * };\n * ```\n */\nexport interface BedrockConverseClient {\n converse(\n params: {\n modelId: string;\n messages: Array<{ role: 'user' | 'assistant'; content: BedrockContentBlock[] }>;\n system?: Array<{ text: string }>;\n inferenceConfig?: { temperature?: number; maxTokens?: number };\n toolConfig?: {\n tools: Array<{\n toolSpec: {\n name: string;\n description?: string;\n inputSchema: { json: Record<string, unknown> };\n strict?: boolean;\n };\n }>;\n toolChoice?:\n | { tool: { name: string } }\n | { auto: Record<string, never> }\n | { any: Record<string, never> };\n };\n /**\n * Native, schema-constrained output: a separate request field from\n * `toolConfig`, so it can be sent alongside real tool calls. Only\n * built by this adapter for models covered by\n * `nativeStructuredOutputModels` (opt-in, see\n * `BedrockAdapterOptions`); other models keep getting `jsonSchema`\n * emulated as a forced single tool call via `toolConfig`, the\n * pre-existing behavior.\n *\n * Matches the real Bedrock Converse API's `outputConfig.textFormat`\n * shape exactly: the schema itself is nested one level deeper, under\n * `structure.jsonSchema`, not flat on `textFormat`, and `schema` is\n * a JSON-encoded *string*, not a parsed object, unlike every other\n * schema field this adapter builds (`toolSpec.inputSchema.json`\n * included). There is no `strict` field here, unlike `toolSpec`.\n */\n outputConfig?: {\n textFormat: {\n type: 'json_schema';\n structure: {\n jsonSchema: {\n schema: string;\n name?: string;\n description?: string;\n };\n };\n };\n };\n },\n options: { signal: AbortSignal },\n ): Promise<{\n output?: {\n message?: {\n content?: Array<{\n text?: string;\n toolUse?: { toolUseId?: string; name?: string; input?: unknown };\n }>;\n };\n };\n usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number };\n }>;\n\n /**\n * Optional. Required only for `stream: true` calls. Takes the same\n * request shape `converse` does, returning `{ stream }`, matching\n * `ConverseStreamCommand`'s real AWS SDK v3 output shape, an\n * `AsyncIterable` of incremental events under a `stream` property,\n * rather than the whole response being the iterable directly.\n */\n converseStream?(\n params: Parameters<BedrockConverseClient['converse']>[0],\n options: { signal: AbortSignal },\n ): Promise<{ stream: AsyncIterable<BedrockConverseStreamEvent> }>;\n}\n\n/**\n * One event of a Bedrock `ConverseStreamCommand` response's `stream`.\n * Content blocks (text or toolUse) are identified by `contentBlockIndex`,\n * Converse's own convention for correlating start/delta/stop events across\n * possibly-interleaved blocks, mirrored directly by VernLLM's\n * `tool_call_delta.index`.\n */\ntype BedrockConverseStreamEvent =\n | { messageStart: { role: 'assistant' } }\n | {\n contentBlockStart: {\n contentBlockIndex: number;\n start?: { toolUse?: { toolUseId?: string; name?: string } };\n };\n }\n | {\n contentBlockDelta: {\n contentBlockIndex: number;\n delta?: { text?: string } | { toolUse?: { input?: string } };\n };\n }\n | { contentBlockStop: { contentBlockIndex: number } }\n | { messageStop: { stopReason?: string } }\n | {\n metadata: {\n usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number };\n };\n }\n // The above are the happy-path events. Bedrock Converse streams can also\n // emit these as in-band exception events (not promise rejections), so\n // they need to be modeled and handled explicitly below, an unmatched\n // event previously fell through the if/else chain silently, either\n // truncating output or leaving the stream hanging until an unrelated\n // idle timeout fired.\n | { internalServerException: { message?: string } }\n | { modelStreamErrorException: { message?: string; originalStatusCode?: number } }\n | { validationException: { message?: string } }\n | { throttlingException: { message?: string } }\n | { serviceUnavailableException: { message?: string } };\n\n/** Maps a `ContentBlock` image MIME type, already validated, to Converse's `format` enum. */\nfunction toBedrockImageFormat(mimeType: string): BedrockImageFormat {\n switch (assertSupportedImageMimeType(mimeType)) {\n case 'image/png':\n return 'png';\n case 'image/jpeg':\n return 'jpeg';\n case 'image/gif':\n return 'gif';\n case 'image/webp':\n return 'webp';\n }\n}\n\n/**\n * Decodes base64 image data into the raw `Uint8Array` bytes Converse's\n * `image.source.bytes` expects (unlike Anthropic/Gemini/OpenAI, which all\n * take base64 strings directly). Uses `Buffer`, since this adapter, like\n * the rest of the package, targets Node.\n */\nfunction decodeBase64(data: string): Uint8Array {\n return new Uint8Array(Buffer.from(data, 'base64'));\n}\n\n/**\n * Translates a VernLLM `ContentBlock[]` into Converse's native content-block\n * array: text blocks pass through as `{ text }`, image blocks become\n * `{ image: { format, source: { bytes } } }` with the base64 payload decoded\n * to raw bytes, since Converse doesn't accept base64 strings directly.\n */\nfunction toBedrockContent(blocks: ContentBlock[]): BedrockContentBlock[] {\n return blocks.map((block) =>\n block.type === 'image'\n ? {\n image: {\n format: toBedrockImageFormat(block.mimeType),\n source: { bytes: decodeBase64(block.data) },\n },\n }\n : { text: block.text },\n );\n}\n\n/**\n * Optional configuration for `fromBedrock`.\n */\nexport interface BedrockAdapterOptions {\n /**\n * Optional preflight check for tool-use support, needed whenever a\n * `jsonSchema` call ends up sending Converse `toolConfig` — either the\n * legacy forced-single-tool-call emulation, or real `tools` sent\n * alongside native structured output (`outputConfig`). VernLLM never\n * guesses capability from a failed call's error message (AWS's error\n * text isn't a documented, stable contract), so this is opt-in: pass\n * either a static list of tool-use-capable model IDs, or a predicate\n * function, and VernLLM will reject unsupported models with a clear\n * `LLMError('validation')` *before* dispatching the request, instead of\n * on the wire.\n *\n * Left unset (default), no preflight check runs, and a `jsonSchema` call\n * to an unsupported model surfaces Bedrock's raw `converse` error as-is.\n */\n toolUseSupportedModels?: string[] | ((modelId: string) => boolean);\n\n /**\n * Which models support native, schema-constrained output\n * (`outputConfig.textFormat`), independent of `toolConfig`, so it can be\n * combined with real `tools` in one request. Pass a static list of\n * model IDs (verified against Bedrock's own docs) or a predicate.\n *\n * There is no built-in default here (see `supportsNativeStructuredOutput`\n * for why). Left unset, every model uses the older forced-single-tool-\n * call emulation via `toolConfig`, and `tools` + `jsonSchema` together is\n * rejected, exactly this adapter's behavior before native support was\n * added.\n */\n nativeStructuredOutputModels?: ModelCapabilityOverride;\n}\n\ntype BedrockRequest = Parameters<BedrockConverseClient['converse']>[0];\n\n/**\n * Maps VernLLM's OpenAI-shaped wire `tools`/`tool_choice` into Converse's\n * `toolConfig` shape. Shared by the two call sites that build real\n * (non-schema-forced) tool definitions: the plain tools-only branch, and\n * the native-structured-output branch, which sends real tools alongside\n * `outputConfig` rather than instead of it.\n */\nfunction buildBedrockToolConfig(\n tools: NonNullable<Parameters<LLMClient['chat']['completions']['create']>[0]['tools']>,\n toolChoiceParam: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice'],\n): NonNullable<BedrockRequest['toolConfig']> {\n return {\n tools: tools.map((t) => ({\n toolSpec: {\n name: t.function.name,\n description: t.function.description,\n inputSchema: { json: t.function.parameters },\n },\n })),\n toolChoice: toBedrockToolChoice(toolChoiceParam),\n };\n}\n\n/**\n * Builds the Converse-shaped request from VernLLM's wire params, shared\n * between `create` and `createStream` so both go through identical\n * translation (system prompt, message shaping, the jsonSchema →\n * forced-single-tool mapping, and the `toolUseSupportedModels` preflight\n * check all happen exactly once).\n *\n * Returns `toolName` alongside the request: when set, the model was forced\n * to call a single synthetic tool standing in for `jsonSchema` output (the\n * legacy path, for models not covered by `nativeStructuredOutputModels`),\n * and both `create` and `createStream` need to know this so they can\n * unwrap that tool call back into plain text content instead of treating\n * it like a real tool call. On the native path (model covered by\n * `nativeStructuredOutputModels`), `toolName` is `undefined`: the\n * schema-conforming JSON already arrives as ordinary text content, nothing\n * to unwrap, and any real tool calls in `params.tools` are left for the\n * normal, non-forced tool-call handling both `create` and `createStream`\n * already do when `toolName` is unset.\n */\nfunction buildBedrockRequest(\n params: Parameters<LLMClient['chat']['completions']['create']>[0],\n toolUseSupportedModels: BedrockAdapterOptions['toolUseSupportedModels'],\n nativeStructuredOutputModels: BedrockAdapterOptions['nativeStructuredOutputModels'],\n): { request: BedrockRequest; toolName: string | undefined } {\n const systemMessage = params.messages.find((m) => m.role === 'system');\n\n // Keep user, assistant, and tool turns, in order. Converse has no\n // separate 'tool' role: tool results travel as a user-role message\n // with toolResult content blocks, and an assistant's tool\n // requests travel as toolUse content blocks on its own turn.\n const conversationMessages = params.messages.filter(\n (m) => m.role === 'user' || m.role === 'assistant' || m.role === 'tool',\n );\n\n const jsonSchema =\n params.response_format?.type === 'json_schema' ? params.response_format.json_schema : undefined;\n\n const schemaName = jsonSchema?.name.trim();\n\n if (jsonSchema && !schemaName) {\n throw new LLMError('json_schema.name must not be empty.', 'validation');\n }\n\n const isNative =\n Boolean(jsonSchema) &&\n supportsNativeStructuredOutput(params.model, nativeStructuredOutputModels);\n\n if (jsonSchema && params.tools?.length && !isNative) {\n throw new LLMError(\n `Bedrock model \"${params.model}\" is not covered by nativeStructuredOutputModels, so ` +\n '`jsonSchema` is emulated as a forced single tool call there (via `toolConfig`), which ' +\n 'collides with the `tools` you also provided. Either drop `tools` or `jsonSchema` for this ' +\n \"call, or pass this model in fromBedrock's `nativeStructuredOutputModels` option once \" +\n \"you've confirmed it supports Converse's `outputConfig.textFormat`.\",\n 'validation',\n );\n }\n\n let toolName: string | undefined;\n let jsonInstruction: string | undefined;\n let toolConfig: NonNullable<BedrockRequest['toolConfig']> | undefined;\n let outputConfig: NonNullable<BedrockRequest['outputConfig']> | undefined;\n\n if (jsonSchema && isNative) {\n // Native path: the schema goes in its own request field, independent\n // of toolConfig, so real tools (if any) are built exactly like the\n // tools-only branch below and sent alongside it.\n //\n // Unlike every other schema this adapter builds, the real Bedrock\n // Converse API requires `schema` here as a JSON-encoded *string*, not\n // a parsed object, nested under `structure.jsonSchema` rather than\n // flat on `textFormat`. There is no `strict` field on this path,\n // unlike `toolSpec`.\n const { schema, description } = jsonSchema;\n\n outputConfig = {\n textFormat: {\n type: 'json_schema',\n structure: {\n jsonSchema: { schema: JSON.stringify(schema), name: schemaName, description },\n },\n },\n };\n } else if (jsonSchema && schemaName) {\n // Legacy path: jsonSchema alone (or with tools, on a native model;\n // see above), on a model without native support, becomes a forced\n // single tool call via toolConfig, unchanged from before this adapter\n // had a native path.\n const { schema, description, strict } = jsonSchema;\n\n toolName = schemaName;\n toolConfig = {\n tools: [{ toolSpec: { name: toolName, description, inputSchema: { json: schema }, strict } }],\n toolChoice: { tool: { name: toolName } },\n };\n } else if (params.response_format?.type === 'json_object') {\n // No schema to build a tool from, fall back to a prompt instruction.\n // This does not exclude real `tools`: `json_object` mode is just a\n // system-prompt nudge, not a request field that could collide with\n // `toolConfig`, so both are set independently below.\n jsonInstruction = 'Respond with valid JSON only, no prose or markdown fences.';\n }\n\n // Runs after the branch chain, unified across every case that still\n // wants real tools built: the native-with-tools case above (toolConfig\n // still unset there, only outputConfig is), and the no-jsonSchema-at-all\n // case. `toolName` is the one signal that distinguishes them from the\n // legacy forced-single-tool-call branch, which builds its own toolConfig\n // inline and must not be overwritten here.\n if (params.tools?.length && !toolName) {\n toolConfig = buildBedrockToolConfig(params.tools, params.tool_choice);\n }\n\n // Runs whenever a jsonSchema call actually ends up sending toolConfig,\n // whether that's the legacy forced-single-tool-call path, or the native\n // path with real `tools` also present (native structured output doesn't\n // need Converse tool-use support, but real tools alongside it still do).\n if (jsonSchema && toolConfig && toolUseSupportedModels) {\n const isSupported = Array.isArray(toolUseSupportedModels)\n ? toolUseSupportedModels.includes(params.model)\n : toolUseSupportedModels(params.model);\n\n if (!isSupported) {\n throw new LLMError(\n `Bedrock model \"${params.model}\" is not listed in toolUseSupportedModels, but this call ` +\n 'requires Converse tool use (either jsonSchema emulated as a forced tool call, or real ' +\n '`tools` sent alongside native structured output).',\n 'validation',\n );\n }\n }\n\n const systemParts = [systemMessage?.content, jsonInstruction].filter((s): s is string =>\n Boolean(s),\n );\n\n const request: BedrockRequest = {\n modelId: params.model,\n messages: mergeConsecutiveToolResults(conversationMessages.map((m) => toBedrockMessage(m))),\n system: systemParts.length ? systemParts.map((text) => ({ text })) : undefined,\n inferenceConfig: {\n ...(params.temperature !== undefined ? { temperature: params.temperature } : {}),\n maxTokens: params.max_tokens,\n },\n ...(toolConfig ? { toolConfig } : {}),\n ...(outputConfig ? { outputConfig } : {}),\n };\n\n return { request, toolName };\n}\n\n/**\n * Wraps a Bedrock Converse-API client so it satisfies the `LLMClient`\n * interface VernLLM uses for OpenAI/Groq. The Converse API is unified\n * across Bedrock's model families (Anthropic, Titan, Llama, Mistral, etc.),\n * so unlike raw per-model Bedrock invocation, this one adapter works\n * regardless of which underlying model `modelId` points at, as long as\n * that model supports Converse (most current-generation ones do)\n *\n * `response_format: json_schema`, on a model covered by\n * `options.nativeStructuredOutputModels` (opt-in, unset by default), is\n * sent as `outputConfig.textFormat`, its own request field, independent of\n * `toolConfig`, so it can be combined with real, caller-supplied `tools`\n * in the same request. Matches the real Converse API's shape exactly: the\n * schema is nested under `structure.jsonSchema` and JSON-encoded as a\n * string, not the parsed object `toolConfig`'s tool schemas use, and there\n * is no `strict` field on this path.\n *\n * On any other model (the default), `response_format: json_schema` is\n * mapped to Converse's `toolConfig` instead: a single tool is defined from\n * the schema, description, and strictness settings, and `toolChoice`\n * forces the model to call it. This legacy path cannot be combined with\n * real `tools` (both would need the same `toolConfig`), and a call that\n * tries throws `LLMError('validation')` before reaching the API.\n * Provider-constrained schema matching applies only when `strict: true` is\n * forwarded and supported. Native tool support varies by model family;\n * pass `toolUseSupportedModels` to preflight-check it (see\n * `BedrockAdapterOptions`), otherwise a `jsonSchema` call to an\n * unsupported model surfaces Bedrock's raw error unchanged.\n *\n * `response_format: json_object` (no schema to build a tool from) and\n * `reasoning_effort` (no Converse equivalent) fall back to a system-prompt\n * instruction and are dropped respectively. Unlike `jsonSchema`,\n * `json_object` combines with real `tools` freely on every model: it's a\n * prompt nudge, not a request field, so there's nothing for it to collide\n * with.\n *\n * `tools` alone maps to Converse's native `toolConfig`/`toolUse`/\n * `toolResult`; `tool_choice` maps to `toolConfig.toolChoice`.\n *\n * `createStream` calls `converseStream` (optional on `BedrockConverseClient`\n *, required only if the caller sets `stream: true`) and translates its\n * `contentBlockStart`/`contentBlockDelta`/`metadata` events into\n * `WireStreamChunk`s. Content blocks are tracked by `contentBlockIndex`,\n * same as `fromAnthropic`'s block-index tracking (Converse's streaming\n * shape is structurally close to Anthropic's own, both being tool-use-aware\n * content-block streams), including the same `json-tool` unwrapping: a\n * `jsonSchema`-forced tool's `toolUse.input` deltas are re-emitted as\n * `text-delta`, not `tool_call_delta`, so the accumulated result lands in\n * `finalizeResponse`'s `content` path exactly like the non-streaming\n * `create` branch above unwraps it.\n */\nexport function fromBedrock(\n bedrockClient: BedrockConverseClient,\n options?: BedrockAdapterOptions,\n): LLMClient {\n const toolUseSupportedModels = options?.toolUseSupportedModels;\n const nativeStructuredOutputModels = options?.nativeStructuredOutputModels;\n\n return {\n chat: {\n completions: {\n async create(params, requestOptions) {\n const { request, toolName } = buildBedrockRequest(\n params,\n toolUseSupportedModels,\n nativeStructuredOutputModels,\n );\n\n const response = await bedrockClient.converse(request, requestOptions);\n\n let text: string;\n let wireToolCalls: WireToolCall[] | undefined;\n\n if (toolName) {\n // Forced tool-use: the schema-conforming payload arrives as the\n // toolUse content block's already-parsed `input`, not as text.\n // Re-serialize it to JSON so it flows through the same\n // string-content contract every other adapter uses.\n const toolUseBlock = response.output?.message?.content?.find(\n (block) => block.toolUse?.name === toolName,\n );\n\n text = toolUseBlock?.toolUse ? JSON.stringify(toolUseBlock.toolUse.input) : '';\n } else {\n const blocks = response.output?.message?.content ?? [];\n\n text = blocks.map((c) => c.text ?? '').join('');\n\n const toolUses = blocks.filter(\n (\n block,\n ): block is { toolUse: { toolUseId?: string; name?: string; input?: unknown } } =>\n Boolean(block.toolUse),\n );\n\n if (toolUses.length) {\n wireToolCalls = toolUses.map((block, i) => {\n const toolUse = block.toolUse;\n\n if (!toolUse.name) {\n throw new LLMError(\n `Bedrock returned a toolUse block without a name at index ${i}.`,\n 'validation',\n );\n }\n\n return {\n id: toolUse.toolUseId ?? `${toolUse.name}_${i}`,\n type: 'function' as const,\n function: {\n name: toolUse.name,\n arguments: JSON.stringify(toolUse.input ?? {}),\n },\n };\n });\n }\n }\n\n return {\n choices: [\n {\n message: { content: text, ...(wireToolCalls ? { tool_calls: wireToolCalls } : {}) },\n },\n ],\n usage: {\n prompt_tokens: response.usage?.inputTokens,\n completion_tokens: response.usage?.outputTokens,\n total_tokens: response.usage?.totalTokens,\n },\n };\n },\n\n async *createStream(params, requestOptions) {\n if (!bedrockClient.converseStream) {\n throw new LLMError(\n 'stream: true requires a Bedrock client with converseStream',\n 'validation',\n );\n }\n\n const { request, toolName } = buildBedrockRequest(\n params,\n toolUseSupportedModels,\n nativeStructuredOutputModels,\n );\n\n const { stream } = await bedrockClient.converseStream(request, requestOptions);\n\n const blockKinds = new Map<number, 'text' | 'tool_use' | 'json-tool'>();\n\n for await (const event of stream) {\n if ('contentBlockStart' in event) {\n const { contentBlockIndex, start } = event.contentBlockStart;\n\n if (start?.toolUse) {\n const kind = start.toolUse.name === toolName ? 'json-tool' : 'tool_use';\n\n blockKinds.set(contentBlockIndex, kind);\n\n if (kind === 'tool_use' && !toolName) {\n yield {\n type: 'tool_call_delta',\n index: contentBlockIndex,\n id: start.toolUse.toolUseId,\n name: start.toolUse.name,\n };\n }\n } else {\n blockKinds.set(contentBlockIndex, 'text');\n }\n } else if ('contentBlockDelta' in event) {\n const { contentBlockIndex, delta } = event.contentBlockDelta;\n\n // Only surfaced as real content when there's no forced\n // json-schema tool in play, see the identical guard (and\n // its full rationale) in `fromAnthropic`'s `createStream`.\n // Converse's streaming shape is structurally close enough to\n // Anthropic's own that the same corruption risk applies: a\n // genuine text block alongside a forced tool call would\n // otherwise get concatenated into the same buffer as the\n // tool's JSON payload.\n if (delta && 'text' in delta && delta.text !== undefined && !toolName) {\n yield { type: 'text-delta', delta: delta.text };\n } else if (delta && 'toolUse' in delta && delta.toolUse?.input !== undefined) {\n const kind = blockKinds.get(contentBlockIndex);\n\n if (kind === 'json-tool') {\n yield { type: 'text-delta', delta: delta.toolUse.input };\n } else if (!toolName) {\n yield {\n type: 'tool_call_delta',\n index: contentBlockIndex,\n argumentsDelta: delta.toolUse.input,\n } satisfies WireStreamChunk;\n }\n }\n } else if ('metadata' in event && event.metadata.usage) {\n yield {\n type: 'usage',\n usage: {\n prompt_tokens: event.metadata.usage.inputTokens,\n completion_tokens: event.metadata.usage.outputTokens,\n total_tokens: event.metadata.usage.totalTokens,\n },\n };\n } else if ('throttlingException' in event) {\n throw new LLMError(\n event.throttlingException.message ?? 'Bedrock throttled the request mid-stream',\n 'api',\n 429,\n );\n } else if ('validationException' in event) {\n throw new LLMError(\n event.validationException.message ?? 'Bedrock rejected the request mid-stream',\n 'validation',\n );\n } else if (\n 'internalServerException' in event ||\n 'serviceUnavailableException' in event ||\n 'modelStreamErrorException' in event\n ) {\n const detail =\n ('internalServerException' in event && event.internalServerException.message) ||\n ('serviceUnavailableException' in event &&\n event.serviceUnavailableException.message) ||\n ('modelStreamErrorException' in event && event.modelStreamErrorException.message) ||\n 'Bedrock reported a mid-stream error';\n\n const status =\n ('modelStreamErrorException' in event &&\n event.modelStreamErrorException.originalStatusCode) ||\n ('serviceUnavailableException' in event && 503) ||\n 500;\n\n throw new LLMError(detail, 'api', status);\n }\n }\n },\n },\n },\n };\n}\n\n/** Maps VernLLM's OpenAI-shaped wire `tool_choice` onto Converse's `toolChoice`. */\nfunction toBedrockToolChoice(\n toolChoice: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice'],\n): NonNullable<Parameters<BedrockConverseClient['converse']>[0]['toolConfig']>['toolChoice'] {\n if (!toolChoice || toolChoice === 'auto') return { auto: {} };\n if (toolChoice === 'required') return { any: {} };\n\n if (toolChoice === 'none') {\n // Converse's toolConfig.toolChoice has no 'none' option. The only way\n // to guarantee no tool use is to omit toolConfig.tools entirely, which\n // isn't an option here since tools were explicitly requested. Silently\n // falling back to 'auto' would let the model call tools despite the\n // caller explicitly asking it not to, so this fails loudly instead.\n throw new LLMError(\n \"'none' is not supported by fromBedrock: Bedrock Converse has no \" +\n '`tool_choice` equivalent to forbidding tool use while tools are still offered. Omit ' +\n '`tools` entirely for this call instead.',\n 'validation',\n );\n }\n\n return { tool: { name: toolChoice.function.name } };\n}\n/**\n * Translates one VernLLM wire message into Converse's\n * `{ role: 'user' | 'assistant', content }` shape.\n */\nfunction toBedrockMessage(\n m: Extract<\n Parameters<LLMClient['chat']['completions']['create']>[0]['messages'][number],\n { role: 'user' | 'assistant' | 'tool' }\n >,\n): { role: 'user' | 'assistant'; content: BedrockContentBlock[] } {\n if (m.role === 'tool') {\n return {\n role: 'user',\n content: [\n {\n toolResult: {\n toolUseId: m.tool_call_id,\n content: [{ text: m.content }],\n status: m.is_error ? 'error' : 'success',\n },\n },\n ],\n };\n }\n\n if (m.role === 'assistant' && m.tool_calls?.length) {\n const blocks: BedrockContentBlock[] = [];\n\n if (m.content) blocks.push({ text: m.content });\n\n for (const tc of m.tool_calls) {\n let input: unknown;\n\n if (!tc.function.arguments.trim()) {\n input = {};\n } else {\n try {\n input = JSON.parse(tc.function.arguments);\n } catch (cause) {\n throw new LLMError(\n `Assistant tool call \"${tc.function.name}\" (${tc.id}) has arguments that are not valid JSON.`,\n 'validation',\n undefined,\n undefined,\n cause,\n );\n }\n }\n\n blocks.push({ toolUse: { toolUseId: tc.id, name: tc.function.name, input } });\n }\n\n return { role: 'assistant', content: blocks };\n }\n\n return {\n role: m.role,\n content: Array.isArray(m.content) ? toBedrockContent(m.content) : [{ text: m.content ?? '' }],\n };\n}\n\n/**\n * Converse expects the results of everything the model asked for in one\n * turn to arrive together as multiple `toolResult` content blocks on a\n * single `'user'` message, not as separate consecutive `'user'` messages.\n * The per-wire-message mapping above produces one `'user'` message per\n * VernLLM wire tool message, so when an assistant turn requested more than\n * one tool, this merges the resulting run of toolResult-only `'user'`\n * messages back into one.\n */\nfunction mergeConsecutiveToolResults(\n messages: { role: 'user' | 'assistant'; content: BedrockContentBlock[] }[],\n): { role: 'user' | 'assistant'; content: BedrockContentBlock[] }[] {\n const isToolResultOnly = (\n m: (typeof messages)[number],\n ): m is { role: 'user'; content: BedrockContentBlock[] } =>\n m.role === 'user' && m.content.length > 0 && m.content.every((b) => 'toolResult' in b);\n\n const merged: (typeof messages)[number][] = [];\n\n for (const m of messages) {\n const prev = merged.at(-1);\n\n if (isToolResultOnly(m) && prev && isToolResultOnly(prev)) {\n prev.content.push(...m.content);\n } else {\n merged.push(m);\n }\n }\n\n return merged;\n}\n","import {\n LLMError,\n type LLMClient,\n type WireStreamChunk,\n type WireToolCall,\n} from '../types/index.js';\nimport { parseSseStream, SSE_PING } from './internal/sse.js';\n\n/** The chat-completion-shaped request VernLLM builds internally */\ntype ChatRequest = Parameters<LLMClient['chat']['completions']['create']>[0];\n\n/**\n * The minimal shape the fetch adapter needs from a response object.\n * Native `fetch`'s `Response` satisfies this, but so do wrappers around\n * `axios`, `node-fetch`, `undici`, etc, which makes `request` swappable\n * without forcing consumers to polyfill the full `Response` interface\n */\nexport interface ResponseLike {\n ok: boolean;\n status: number;\n headers: {\n get(name: string): string | null;\n };\n text(): Promise<string>;\n json(): Promise<unknown>;\n}\n\n/** A fetch-compatible request function; defaults to native `fetch` */\nexport type RequestLike = (\n url: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body?: string;\n signal?: AbortSignal;\n },\n) => Promise<ResponseLike>;\n\n/**\n * A streaming-capable request function. Unlike `RequestLike`, which returns\n * a fully-buffered `ResponseLike`, this resolves to an `AsyncIterable` of\n * progressively-arriving chunks, the common ground across transports:\n * native `fetch`'s `response.body` (wrapped to be iterable; see\n * `webStreamToAsyncIterable` below), axios's Node `Readable` in\n * `responseType: 'stream'` mode (already async-iterable, no wrapping\n * needed), `node-fetch`, `undici`, etc, all satisfy this with little or no\n * glue code. Defaults to native `fetch`.\n */\nexport type StreamRequestLike = (\n url: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body?: string;\n signal?: AbortSignal;\n },\n) => Promise<AsyncIterable<Uint8Array | string>>;\n\nexport interface FetchAdapterConfig {\n /** Endpoint URL, or a function of the request in case it depends on model/params */\n url: string | ((params: ChatRequest) => string);\n /** Static headers, or a function (sync or async) for things like refreshed auth tokens */\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n /** HTTP method. Default 'POST' */\n method?: string;\n /**\n * The function used to make the HTTP request. Defaults to native `fetch`.\n * Swap in `axios`, `node-fetch`, or any other transport, as long as it\n * resolves to a `ResponseLike` object\n */\n request?: RequestLike;\n /** Maps VernLLMs internal chat-completion request into the providers raw request body */\n mapRequest: (params: ChatRequest) => unknown;\n /**\n * Maps the providers raw JSON response into `{ content, usage?, toolCalls? }`\n * `content` is the assistants text (JSON string when JSON mode was requested).\n * `content` may be empty/omitted when the model responded with only tool\n * calls and no text.\n *\n * `toolCalls`, when the model requested one or more tools, is the list of\n * calls as flat `{ id, name, arguments }` entries (matching this config's\n * own `toolCalls?: Array<{ id: string; name: string; arguments: string }>`\n * return type below), each entry's `arguments` already JSON-*encoded* as a\n * string (not the parsed object), mirroring the wire format every\n * OpenAI-compatible provider uses. `fromFetch` itself converts these into\n * `WireToolCall`'s `type`/`function`-wrapped shape before returning them\n * from `create`. VernLLM parses (and validates, if `argumentsSchema` was\n * set) the arguments string internally, mapResponse doesn't need to do\n * that itself.\n */\n mapResponse: (json: unknown) => {\n content?: string;\n usage?: { promptTokens?: number; completionTokens?: number; totalTokens?: number };\n toolCalls?: Array<{ id: string; name: string; arguments: string }>;\n };\n\n /**\n * Optional. Required only for `stream: true` calls. The function used to\n * open a streaming HTTP request. Takes the same request shape as\n * `request`, but resolves to an `AsyncIterable` of progressively-arriving\n * `Uint8Array` or `string` chunks instead of a buffered `ResponseLike`.\n * Defaults to native `fetch`.\n */\n requestStream?: StreamRequestLike;\n\n /**\n * Optional. How the raw stream bytes are split into individual event\n * payloads. Defaults to Server-Sent Events framing (`data: ...` blocks\n * separated by a blank line, `[DONE]` sentinel honored, see\n * `parseSseStream`), which covers the large majority of LLM providers'\n * streaming HTTP endpoints. Override this for a provider that frames its\n * stream differently, e.g. newline-delimited JSON (NDJSON) with no SSE\n * envelope.\n */\n parseStreamFrames?: (chunks: AsyncIterable<Uint8Array | string>) => AsyncIterable<unknown>;\n\n /**\n * Optional. Required only for `stream: true` calls. Maps one parsed\n * stream event (already extracted from its frame by `parseStreamFrames`)\n * into zero, one, or more `WireStreamChunk`s, mirrors `mapResponse`'s\n * role for the non-streaming path, just per-event instead of once for\n * the whole body. Return `undefined` to skip an event that carries\n * nothing VernLLM needs (e.g. a provider's keep-alive ping). Configs\n * that don't implement this make `stream: true` throw a clear\n * `LLMError('validation')` rather than a confusing runtime failure or a\n * silently empty stream.\n */\n mapStreamEvent?: (event: unknown) => WireStreamChunk | WireStreamChunk[] | undefined;\n}\n\n/**\n * Wraps a WHATWG `ReadableStream` (what `response.body` is) so it can be\n * consumed with `for await`. Implemented via `getReader()` rather than\n * relying on `ReadableStream` having a native `Symbol.asyncIterator`,\n * that support varies across runtimes/versions, and this works everywhere\n * a `ReadableStream` does.\n */\nasync function* webStreamToAsyncIterable(\n stream: ReadableStream<Uint8Array>,\n): AsyncGenerator<Uint8Array> {\n const reader = stream.getReader();\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n\n if (done) return;\n if (value) yield value;\n }\n } finally {\n // Cancel before releasing the lock so an early-terminated or\n // downstream-failed consumer still tells the underlying source to\n // stop, instead of leaving it running with nothing left to read it.\n // An already-errored stream rejects cancel(); that rejection isn't\n // useful to the caller here, so it's swallowed.\n try {\n await reader.cancel();\n } catch {\n // Ignore: the stream may already be errored/closed.\n }\n\n reader.releaseLock();\n }\n}\n\n/** Default `requestStream`: native `fetch`, with the same error/`.status` contract non-streaming errors get. */\nasync function defaultRequestStream(\n url: string,\n init: { method: string; headers: Record<string, string>; body?: string; signal?: AbortSignal },\n): Promise<AsyncIterable<Uint8Array | string>> {\n const res = await fetch(url, init);\n\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n const err = new Error(\n `Fetch adapter stream request failed (${res.status}): ${body.slice(0, 500)}`,\n ) as Error & { status?: number; headers?: ResponseLike['headers'] };\n\n err.status = res.status;\n err.headers = res.headers;\n\n throw err;\n }\n\n if (!res.body) {\n throw new Error('Fetch adapter stream request received a response with no body.');\n }\n\n return webStreamToAsyncIterable(res.body);\n}\n\n/** Builds the shared `{ method, headers, body? }` request-init for both `create` and `createStream`. */\nasync function buildRequestInit(\n config: FetchAdapterConfig,\n params: ChatRequest,\n requestBody: unknown,\n): Promise<{ url: string; method: string; headers: Record<string, string>; body?: string }> {\n const url = typeof config.url === 'function' ? config.url(params) : config.url;\n const headers = typeof config.headers === 'function' ? await config.headers() : config.headers;\n const method = config.method ?? 'POST';\n\n // GET/HEAD requests can't carry a body, so skip both the body and\n // the Content-Type header for them rather than sending a body a\n // server may reject\n const supportsBody = !['GET', 'HEAD'].includes(method.toUpperCase());\n\n return {\n url,\n method,\n headers: supportsBody ? { 'Content-Type': 'application/json', ...headers } : { ...headers },\n ...(supportsBody ? { body: JSON.stringify(requestBody) } : {}),\n };\n}\n\n/**\n * A fetch-based escape hatch for providers with no SDK, or where pulling one\n * in isnt worth it. You supply the URL, headers, and two small mapping\n * functions; this handles the HTTP call and slots the result into the same\n * `LLMClient` shape every other adapter produces, so retries, timeouts,\n * the circuit breaker, and JSON/schema handling all still work unmodified\n *\n * Non-2xx responses throw an error with `.status` set to the HTTP status\n * code, so VernLLMs `nonRetryableStatus` handling (e.g. failing fast on\n * 401/403) applies here too\n *\n * Tool calling works the same way as every other adapter: `mapRequest`\n * receives the full `ChatRequest`, including `tools`/`toolChoice`, so it can\n * translate them into whatever shape the provider's wire format expects\n * (typically an OpenAI-`function`-wrapped `tools` array plus a `tool_choice`\n * field). On the way back, `mapResponse` may return a `toolCalls` array\n * (id/name/JSON-encoded-arguments-string per call) alongside or instead of\n * `content`; VernLLM parses and (if `argumentsSchema` was set) validates\n * those arguments the same way it does for every other adapter. For\n * `stream: true`, tool-call deltas go through the existing\n * `mapStreamEvent` seam via `WireStreamChunk`'s `tool_call_delta` variant,\n * no separate config is needed for streaming vs non-streaming tool calls.\n *\n\n * `createStream` requires `mapStreamEvent` (there's no non-streaming\n * response to fall back on, unlike the other three optional streaming\n * seams). It opens the request via `requestStream` (defaults to native\n * `fetch`), splits the raw bytes into individual events via\n * `parseStreamFrames` (defaults to SSE framing, see `parseSseStream`),\n * and translates each event into `WireStreamChunk`(s) via\n * `mapStreamEvent`. Both seams are overridable per-config for providers\n * that don't fit the SSE-over-fetch default. If a custom `request`\n * transport is configured, `requestStream` must be configured too,\n * `requestStream` never silently falls back to `request` (see\n * `createStream`'s own comment for why), so a `stream: true` call with\n * `request` set but no `requestStream` throws a clear\n * `LLMError('validation')` instead of quietly using unrelated native\n * `fetch`.\n */\nexport function fromFetch(config: FetchAdapterConfig): LLMClient {\n return {\n chat: {\n completions: {\n async create(params, options) {\n const { url, method, headers, body } = await buildRequestInit(\n config,\n params,\n config.mapRequest(params),\n );\n const request = config.request ?? fetch;\n\n const res = await request(url, { method, headers, body, signal: options.signal });\n\n if (!res.ok) {\n const responseBody = await res.text().catch(() => '');\n const err = new Error(\n `Fetch adapter request failed (${res.status}): ${responseBody.slice(0, 500)}`,\n ) as Error & { status?: number; headers?: ResponseLike['headers'] };\n err.status = res.status;\n // Attach headers so downstream retry logic (e.g. rate-limit\n // handling) can read things like `Retry-After`\n err.headers = res.headers;\n throw err;\n }\n\n const json = await res.json();\n const { content, usage, toolCalls } = config.mapResponse(json);\n\n // `.length` guard, not just truthiness: an empty array is\n // semantically \"no tool calls\", same as `undefined`. Kept\n // explicit here rather than relying on downstream consumers\n // (e.g. `finalizeResponse`'s `wireToolCalls?.length` check) to\n // treat `[]` as absent, so this stays correct even if that\n // convention ever changes.\n const wireToolCalls: WireToolCall[] | undefined = toolCalls?.length\n ? toolCalls.map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: { name: tc.name, arguments: tc.arguments },\n }))\n : undefined;\n\n return {\n choices: [\n {\n message: {\n content,\n ...(wireToolCalls ? { tool_calls: wireToolCalls } : {}),\n },\n },\n ],\n usage: usage\n ? {\n prompt_tokens: usage.promptTokens,\n completion_tokens: usage.completionTokens,\n total_tokens: usage.totalTokens,\n }\n : undefined,\n };\n },\n\n async *createStream(params, options) {\n if (!config.mapStreamEvent) {\n throw new LLMError(\n 'stream: true requires mapStreamEvent to be configured on fromFetch',\n 'validation',\n );\n }\n\n // A custom `request` transport (proxying, special auth, test\n // mocking, etc.) is silently irrelevant to streaming unless the\n // caller *also* configures `requestStream`, `requestStream`\n // defaults to plain native `fetch`, not to `config.request`,\n // since `RequestLike`'s buffered `ResponseLike` has no way to\n // expose a byte stream generically. Falling back to native\n // `fetch` anyway would be a surprising, easy-to-miss divergence\n // (bypassing whatever `request` was there for, a proxy, custom\n // auth, or a test's mocked transport, and potentially hitting\n // the real network). Failing loudly here instead of guessing.\n if (config.request && !config.requestStream) {\n throw new LLMError(\n '`stream: true` requires `requestStream` to be configured on fromFetch when a ' +\n 'custom `request` transport is set. `requestStream` does not fall back to ' +\n \"`request` (it needs an async-iterable byte stream, which `RequestLike`'s \" +\n 'buffered `ResponseLike` has no way to provide), without it, `stream: true` ' +\n 'would silently use plain native `fetch` instead of your configured transport. ' +\n 'Add a `requestStream` that opens the same connection your `request` does, or ' +\n 'omit `request` if native `fetch` is fine for both.',\n 'validation',\n );\n }\n\n const { url, method, headers, body } = await buildRequestInit(\n config,\n params,\n config.mapRequest(params),\n );\n const requestStream = config.requestStream ?? defaultRequestStream;\n const parseFrames = config.parseStreamFrames ?? parseSseStream;\n\n const byteStream = await requestStream(url, {\n method,\n headers,\n body,\n signal: options.signal,\n });\n\n for await (const event of parseFrames(byteStream)) {\n // Only the default parseSseStream produces this sentinel (an\n // SSE comment line used as a keep-alive ping). Handled here,\n // before mapStreamEvent, since provider-specific mapping\n // shouldn't need to know about SSE framing internals.\n if (event === SSE_PING) {\n yield { type: 'ping' };\n continue;\n }\n\n const wireChunks = config.mapStreamEvent(event);\n\n if (!wireChunks) continue;\n\n if (Array.isArray(wireChunks)) {\n yield* wireChunks;\n } else {\n yield wireChunks;\n }\n }\n },\n },\n },\n };\n}\n","import { assertSupportedImageMimeType } from './internal/imageFormat.js';\n\nimport type { ContentBlock, LLMClient, WireStreamChunk } from '../types/index.js';\n\n/** OpenAI's native per-part content shape for a user message. */\ntype OpenAIContentPart =\n | { type: 'text'; text: string }\n | { type: 'image_url'; image_url: { url: string } };\n\n/**\n * Translates a VernLLM `ContentBlock[]` into OpenAI's wire-level content\n * array. Text blocks become `{ type: 'text', text }`; image blocks become\n * `{ type: 'image_url', image_url: { url } }` with the base64 payload\n * inlined as a `data:` URL, since our `ContentBlock` shape (`{ type:\n * 'image', data, mimeType }`) is provider-agnostic and doesn't itself match\n * OpenAI's wire format.\n */\nfunction toOpenAIContent(blocks: ContentBlock[]): OpenAIContentPart[] {\n return blocks.map((block) =>\n block.type === 'image'\n ? {\n type: 'image_url',\n image_url: {\n url: `data:${assertSupportedImageMimeType(block.mimeType)};base64,${block.data}`,\n },\n }\n : { type: 'text', text: block.text },\n );\n}\n\n/** One chunk of an OpenAI-shaped `chat.completions.create({ stream: true })` SSE stream. */\ninterface OpenAIStreamChunk {\n choices?: Array<{\n delta?: {\n content?: string | null;\n tool_calls?: Array<{\n index: number;\n id?: string;\n function?: { name?: string; arguments?: string };\n }>;\n };\n }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number };\n}\n\n/**\n * Translates VernLLM's provider-agnostic `messages` (the one part of a\n * request that isn't a pure passthrough for OpenAI-compatible clients) into\n * OpenAI's native wire shape. Shared between `create` and `createStream` so\n * both go through identical message translation.\n */\nfunction toOpenAIMessages(\n params: Parameters<LLMClient['chat']['completions']['create']>[0],\n): unknown[] {\n return params.messages.map((m) => {\n if (m.role === 'user' && Array.isArray(m.content)) {\n return { ...m, content: toOpenAIContent(m.content) };\n }\n\n if (m.role === 'tool') {\n const { is_error: _isError, ...openAIToolMessage } = m;\n return openAIToolMessage;\n }\n\n return m;\n });\n}\n\n/**\n * Translates one OpenAI-shaped SSE chunk into zero or more `WireStreamChunk`s.\n * A single chunk can carry a text delta, one or more tool-call argument\n * deltas (each keyed by `index`, OpenAI's own convention for streaming\n * parallel tool calls, mirrored directly by VernLLM's `tool_call_delta`\n * shape so accumulation composes without translation), and/or a final\n * usage block (present only when `stream_options.include_usage` is set,\n * which this adapter always sets).\n */\nfunction* toWireStreamChunks(chunk: OpenAIStreamChunk): Generator<WireStreamChunk> {\n const delta = chunk.choices?.[0]?.delta;\n\n if (delta?.content) {\n yield { type: 'text-delta', delta: delta.content };\n }\n\n if (delta?.tool_calls?.length) {\n for (const toolCall of delta.tool_calls) {\n yield {\n type: 'tool_call_delta',\n index: toolCall.index,\n id: toolCall.id,\n name: toolCall.function?.name,\n argumentsDelta: toolCall.function?.arguments,\n };\n }\n }\n\n if (chunk.usage) {\n yield { type: 'usage', usage: chunk.usage };\n }\n}\n\n/**\n * Adapter for any SDK/client whose `chat.completions.create` already\n * matches the OpenAI wire format: this covers most hosted inference\n * providers, since \"OpenAI-compatible\" is a de facto standard for chat\n * completion APIs. Almost everything passes straight through untouched,\n * this exists purely so call sites read clearly (`fromMistral(client)` vs\n * handing a Mistral client to something typed for OpenAI) and so a real\n * transformation could be added later, per-provider, without a breaking\n * change.\n *\n * The one thing that isn't a pure passthrough: a `ContentBlock[]`\n * `userContent` is translated into OpenAI's native `image_url` content-part\n * shape, since VernLLM's `ContentBlock` is intentionally provider-agnostic\n * rather than a copy of any one provider's wire format.\n *\n * Not every SDKs own TypeScript types line up exactly with `LLMClient`\n * (extra fields, stricter unions, etc.), so this takes `unknown` and casts:\n * the actual compatibility contract is the JSON each provider sends and\n * receives over the wire, not the SDKs TS types.\n *\n * `createStream` is implemented by calling the same underlying\n * `chat.completions.create` with `stream: true` (and, for providers that\n * support it, `stream_options: { include_usage: true }`, so a final usage\n * block arrives), the OpenAI SDK, and every OpenAI-compatible client\n * modeled on it, returns an `AsyncIterable` of SSE chunks instead of a\n * single completion object when `stream: true` is set. Each chunk is\n * translated into `WireStreamChunk`(s) via `toWireStreamChunks`.\n *\n * Note on long-running reasoning models: this adapter consumes the\n * underlying SDK's already-parsed stream rather than raw SSE bytes, so\n * unlike `fromFetch`/`fromAnthropic` it cannot see comment-only keep-alive\n * ping frames. Combined with `chunkIdleTimeoutMs`'s 30 second default and\n * `reasoningEffort` (documented to have long silent gaps for o-series and\n * similar models), a long-running reasoning call on this adapter can trip\n * the idle timeout even though the provider is still working. Raise or\n * disable `chunkIdleTimeoutMs` per call for those routes, see `CallParams`.\n */\nexport interface OpenAICompatibleAdapterOptions {\n /**\n * Whether the provider supports `stream_options.include_usage`. Not\n * every \"OpenAI-compatible\" provider is guaranteed to, so this defaults\n * to `true` (matching OpenAI, Groq, Mistral, and most others observed)\n * and should be set to `false` for a provider verified not to support\n * it. When `false`, `stream_options` is omitted entirely and no usage\n * block will arrive on the stream; callers relying on streamed `usage`\n * with such a provider won't get one.\n */\n supportsStreamUsage?: boolean;\n}\n\nexport function fromOpenAICompatible(\n client: unknown,\n options: OpenAICompatibleAdapterOptions = {},\n): LLMClient {\n const raw = client as LLMClient;\n const { supportsStreamUsage = true } = options;\n\n // The underlying client's `create`, called with `stream: true`, returns\n // an AsyncIterable of `OpenAIStreamChunk` rather than\n // `LLMClient['create']`'s normal single-completion return type, hence\n // `unknown` here and a cast at the call site, same rationale as casting\n // the whole client above: the wire contract, not the SDK's own TS types,\n // is what's actually being relied on.\n const rawCreate = raw.chat.completions.create.bind(raw.chat.completions) as unknown as (\n params: unknown,\n options: { signal: AbortSignal },\n ) => Promise<unknown> | AsyncIterable<OpenAIStreamChunk>;\n\n return {\n chat: {\n completions: {\n async create(params, options) {\n const messages = toOpenAIMessages(params);\n\n return raw.chat.completions.create(\n { ...params, messages } as Parameters<LLMClient['chat']['completions']['create']>[0],\n options,\n );\n },\n\n async *createStream(params, options) {\n const messages = toOpenAIMessages(params);\n\n const stream = (await rawCreate(\n {\n ...params,\n messages,\n stream: true,\n ...(supportsStreamUsage ? { stream_options: { include_usage: true } } : {}),\n },\n options,\n )) as AsyncIterable<OpenAIStreamChunk>;\n\n for await (const chunk of stream) {\n yield* toWireStreamChunks(chunk);\n }\n },\n },\n },\n };\n}\n\n// LLM aliases\n\n/**\n * Named alias for the OpenAI SDK itself. A raw `new OpenAI(...)` instance\n * structurally matches most of `LLMClient`, but newer `openai` SDK major\n * versions have widened `ChatCompletionContentPart` (e.g. adding a `file`\n * variant) in ways that no longer structurally satisfy VernLLM's\n * provider-agnostic `ContentBlock[]` on `userContent`, so passing the SDK\n * instance directly can fail to typecheck depending on the installed\n * `openai` version. Wrapping with `fromOpenAI()` (a plain alias of\n * `fromOpenAICompatible()`) sidesteps that by translating through\n * `unknown` at the boundary, and also picks up multimodal image\n * translation and `createStream` wiring that a raw client doesn't have.\n * See Migration Notes for details.\n */\nexport const fromOpenAI = fromOpenAICompatible;\n\n/** Groqs SDK matches the OpenAI wire format */\nexport const fromGroq = fromOpenAICompatible;\n\n/**\n * Mistrals `chat.completions`-shaped client (or their OpenAI-compat\n * endpoint). Mistral supports `stream_options.include_usage` (added after\n * an earlier period where it returned a 422 for unrecognized fields, per\n * Mistral's changelog and streaming docs), so this is a plain alias like\n * the others, `supportsStreamUsage` defaults to `true`.\n */\nexport const fromMistral = fromOpenAICompatible;\n\n/** DeepSeeks API is OpenAI-compatible */\nexport const fromDeepSeek = fromOpenAICompatible;\n\n/** Cerebras inference API is OpenAI-compatible */\nexport const fromCerebras = fromOpenAICompatible;\n\n/** Together AIs API is OpenAI-compatible */\nexport const fromTogether = fromOpenAICompatible;\n\n/** Fireworks AIs API is OpenAI-compatible */\nexport const fromFireworks = fromOpenAICompatible;\n\n/**\n * Ollama exposes an OpenAI-compatible endpoint at `/v1/chat/completions`\n * (as opposed to its native `/api/chat` format, which differs). Point an\n * OpenAI SDK instances `baseURL` at your Ollama server and pass it here:\n * this does not talk to Ollamas native API directly.\n */\nexport const fromOllama = fromOpenAICompatible;\n\n/** OpenRouter's API is OpenAI-compatible */\nexport const fromOpenRouter = fromOpenAICompatible;\n\n/** Perplexity's API is OpenAI-compatible */\nexport const fromPerplexity = fromOpenAICompatible;\n\n/** DeepInfra's API is OpenAI-compatible */\nexport const fromDeepInfra = fromOpenAICompatible;\n\n/** Novita's API is OpenAI-compatible */\nexport const fromNovita = fromOpenAICompatible;\n\n/** Hyperbolic's API is OpenAI-compatible */\nexport const fromHyperbolic = fromOpenAICompatible;\n\n/** Moonshot's (Kimi) API is OpenAI-compatible */\nexport const fromMoonshot = fromOpenAICompatible;\n\n/** Zhipu's (GLM) API is OpenAI-compatible */\nexport const fromZhipu = fromOpenAICompatible;\n\n/**\n * LM Studio exposes an OpenAI-compatible endpoint at `/v1/chat/completions`.\n * Point an OpenAI SDK instance's `baseURL` at your local LM Studio server.\n */\nexport const fromLMStudio = fromOpenAICompatible;\n\n/**\n * vLLM's OpenAI-compatible server mode exposes `/v1/chat/completions`.\n * Point an OpenAI SDK instance's `baseURL` at your vLLM server.\n */\nexport const fromVLLM = fromOpenAICompatible;\n\n/** xAI's Grok API is OpenAI-compatible */\nexport const fromXAI = fromOpenAICompatible;\n\n/** NVIDIA NIM's hosted and self-hosted endpoints are OpenAI-compatible */\nexport const fromNvidiaNIM = fromOpenAICompatible;\n\n/** Vercel AI Gateway is OpenAI-compatible */\nexport const fromVercelAIGateway = fromOpenAICompatible;\n\n/** Cloudflare Workers AI exposes an OpenAI-compatible endpoint */\nexport const fromCloudflareWorkersAI = fromOpenAICompatible;\n\n/** Nebius AI Studio is OpenAI-compatible */\nexport const fromNebius = fromOpenAICompatible;\n\n/** SambaNova Cloud's API is OpenAI-compatible */\nexport const fromSambaNova = fromOpenAICompatible;\n\n/** Baseten's model hosting exposes an OpenAI-compatible endpoint */\nexport const fromBaseten = fromOpenAICompatible;\n\n/** Featherless AI's API is OpenAI-compatible */\nexport const fromFeatherless = fromOpenAICompatible;\n\n/** Friendli AI's serving endpoint is OpenAI-compatible */\nexport const fromFriendli = fromOpenAICompatible;\n\n/** SiliconFlow's API is OpenAI-compatible */\nexport const fromSiliconFlow = fromOpenAICompatible;\n\n/** Parasail's inference API is OpenAI-compatible */\nexport const fromParasail = fromOpenAICompatible;\n\n/** StepFun's API is OpenAI-compatible */\nexport const fromStepFun = fromOpenAICompatible;\n\n/** MiniMax's API is OpenAI-compatible */\nexport const fromMiniMax = fromOpenAICompatible;\n\n/** Lambda Labs' Inference API is OpenAI-compatible */\nexport const fromLambdaLabs = fromOpenAICompatible;\n\n/** Snowflake Cortex's LLM endpoint is OpenAI-compatible */\nexport const fromSnowflakeCortex = fromOpenAICompatible;\n\n/** Anyscale Endpoints' API is OpenAI-compatible */\nexport const fromAnyscale = fromOpenAICompatible;\n\n/** Lepton AI's inference API is OpenAI-compatible */\nexport const fromLepton = fromOpenAICompatible;\n\n/** Inference.net's API is OpenAI-compatible */\nexport const fromInferenceNet = fromOpenAICompatible;\n\n/** Infermatic's API is OpenAI-compatible */\nexport const fromInfermatic = fromOpenAICompatible;\n\n/** AtlasCloud's inference API is OpenAI-compatible */\nexport const fromAtlasCloud = fromOpenAICompatible;\n\n/** 01.AI's (Yi models) API is OpenAI-compatible */\nexport const from01AI = fromOpenAICompatible;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["NON_RETRYABLE_TOOL_CONTRACT_CODES: ReadonlySet<LLMErrorCode>","LOCAL_RATE_LIMIT_CODES: ReadonlySet<LLMErrorCode>","NON_RETRYABLE_TYPES: ReadonlySet<LLMErrorType>","type: LLMErrorType","code: LLMErrorCode | undefined","issues: unknown","attempts: RetryAttempt[] | undefined","message: string","options: LLMErrorOptions","err: unknown","err: LLMError","code: C","key: string","value: T","ttl: number","inner: CacheAdapter<T>","l1: CacheAdapter<T>","l2: CacheAdapter<T>","l1Ttl?: number","result: unknown","defaultFallbackOn: FallbackOn","attempts: FallbackAttempt[]","err: unknown","params: UsageHooks","coalesced: boolean","signal: AbortSignal | undefined","onRefundError: (logMessage: string, error: unknown) => void","logMessage: string","getResult: () => Promise<T>","result: T","openStream: () => Promise<{ chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> }>","opened: { chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> }","value: unknown","value: T | CallWithToolsResult<T>","hasTools: boolean","items: StreamChunk[]","promise: Promise<T | CallWithToolsResult<T>>","cache: CacheAdapter<unknown>","logger: Logger","key: string","logMessage: string","error: unknown","params: InternalCacheParams<T>","params: InternalCacheStreamParams<T>","hasTools: boolean","resolveInFlight!: (value: T) => void","rejectInFlight!: (error: unknown) => void","trackedResult: Promise<T>","options: CircuitBreakerOptions","model: string | undefined","bucket: CircuitBucket","to: CircuitState","model?: string","onEvent: ((event: VernLLMEvent) => void) | undefined","logger: Logger","circuitBreakerOption: boolean | CircuitBreakerOptions | undefined","providerName: string","defaultModel: string","ms: number | undefined","ms: number","fn: (signal: AbortSignal) => Promise<T>","timeoutMs: number","externalSignal?: AbortSignal","next: () => Promise<IteratorResult<T>>","timeoutMs: number | undefined","onIdle?: () => void","logger?: Pick<Logger, 'debug'>","error: unknown","err: unknown","baseDelayMs: number","attempt: number","delay: number","signal?: AbortSignal","err: unknown","error: unknown","value: unknown","status: number","signal?: AbortSignal","attempts?: RetryAttempt[]","content: string","tools: ToolDefinition[]","toolCalls: ToolCall[]","wireToolCalls: WireToolCall[]","parsedArgs: unknown","options: RequestBuilderOptions","params: CallParams<T>","history: ConversationTurn[]","previousTurn: ConversationTurn | undefined","toolChoice: CallParams<unknown>['toolChoice']","turn: ConversationTurn","jsonSchema: CallParams<unknown>['jsonSchema']","useJson: boolean","iterator: AsyncIterator<WireStreamChunk>","first: IteratorResult<WireStreamChunk>","options: StreamAccumulatorOptions<T>","resolveFinal!: (value: T | CallWithToolsResult<T>) => void","rejectFinal!: (error: unknown) => void","buffered: StreamChunk[]","pending: Array<{\n resolve: (result: IteratorResult<StreamChunk>) => void;\n reject: (error: unknown) => void;\n }>","streamError: unknown","chunk: StreamChunk","error: unknown","chunks: AsyncIterable<StreamChunk>","usage: TokenUsage | undefined","result: IteratorResult<WireStreamChunk>","wireToolCalls: WireToolCall[] | undefined","providerName: string","client: LLMClient","model: string","options: CallExecutorOptions","model?: string","params: CallParams<T>","requestId: string","onAttempt?: () => void","attempts: RetryAttempt[]","attempt: number","release: ((actualTokens?: number) => void) | undefined","text: string","content: string | undefined","wireToolCalls: WireToolCall[] | undefined","rawContent: string | null | undefined","useJson: boolean","usage: TokenUsage | undefined","toolCalls: { id: string; name: string; arguments: unknown }[]","tools: NonNullable<CallParams<unknown>['tools']>","toolIssues: ToolIssue[]","fn: (attempt: number) => Promise<T>","signal?: AbortSignal","attempts?: RetryAttempt[]","lastError: unknown","response: Awaited<ReturnType<LLMClient['chat']['completions']['create']>>","usage: TokenUsage","error: LLMError","content: string","schema?: CallParams<T>['schema']","parsed: unknown","error: unknown","logger: Logger","method: M","value: unknown","result: unknown","debugEnabled: boolean","message: string","meta?: Record<string, unknown>","request: WireRequest","capacity: number","refillPerMs: number","amount: number","options: RateLimitOptions","estimatedTokens: number","signal?: AbortSignal","initialReason: RateLimitReason | undefined","waiter: Waiter","queueTimer: ReturnType<typeof setTimeout> | undefined","taken: Array<{ bucket: TokenBucket; amount: number }>","bucket: TokenBucket | undefined","reason: RateLimitReason","actualTokens?: number","options: VernLLMOptions","primaryTarget: FallbackTarget","declaredFallbacks: FallbackTarget[]","logMessage: string","error: unknown","params: Pick<CallParams<unknown>, 'model' | 'signal'>","requestId: string","attempt: (executor: CallExecutor, onAttempt: () => void) => Promise<R>","attempts: FallbackAttempt[]","params: CallParams<T>","params: InternalCacheParams<T>","key: string","params:\n | CachedCallParams<T>\n | CachedToolCallParams<T>\n | CachedStreamCallParams<T>\n | CachedStreamToolCallParams<T>","target?: CircuitTarget","model?: string","index: number","caller: string","isolateByModel: boolean","model: string | undefined","source: AsyncIterable<Uint8Array | string>","text: string","boundary","frame: string","dataLines: string[]","mimeType: string","model: string","override?: ModelCapabilityOverride","blocks: ContentBlock[]","schema: Record<string, unknown>","toolName: string","toolChoice: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice']","tools: NonNullable<Parameters<LLMClient['chat']['completions']['create']>[0]['tools']>","toolChoiceParam: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice']","params: Parameters<LLMClient['chat']['completions']['create']>[0]","nativeStructuredOutputModels?: ModelCapabilityOverride","toolName: string | undefined","jsonInstruction: string | undefined","outputFormat: NonNullable<AnthropicRequestBody['output_config']>['format'] | undefined","tools: NonNullable<Parameters<AnthropicClient['messages']['create']>[0]['tools']> | undefined","toolChoice: Parameters<AnthropicClient['messages']['create']>[0]['tool_choice']","body: AnthropicRequestBody","anthropicClient: AnthropicClient","options?: AnthropicAdapterOptions","options","text: string","wireToolCalls: WireToolCall[] | undefined","mergeConsecutiveToolResults","messages: { role: 'user' | 'assistant'; content: string | AnthropicContentBlock[] }[]","m: (typeof messages)[number]","merged: (typeof messages)[number][]","m: Extract<\n Parameters<LLMClient['chat']['completions']['create']>[0]['messages'][number],\n { role: 'user' | 'assistant' | 'tool' }\n >","blocks: AnthropicContentBlock[]","input: unknown","blocks: ContentBlock[]","toolChoice: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice']","m: Extract<\n Parameters<LLMClient['chat']['completions']['create']>[0]['messages'][number],\n { role: 'user' | 'assistant' | 'tool' }\n >","parts: GeminiPart[]","text: string","toolName: string","parsed: unknown","contents: { role: 'user' | 'model'; parts: GeminiPart[] }[]","c: (typeof contents)[number]","merged: (typeof contents)[number][]","params: Parameters<LLMClient['chat']['completions']['create']>[0]","config: GeminiConfig","geminiClient: GeminiClient","wireToolCalls: WireToolCall[] | undefined","lastUsage:\n | NonNullable<Awaited<ReturnType<GeminiClient['generateContent']>>['usageMetadata']>\n | undefined","mimeType: string","data: string","blocks: ContentBlock[]","tools: NonNullable<Parameters<LLMClient['chat']['completions']['create']>[0]['tools']>","toolChoiceParam: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice']","params: Parameters<LLMClient['chat']['completions']['create']>[0]","toolUseSupportedModels: BedrockAdapterOptions['toolUseSupportedModels']","nativeStructuredOutputModels: BedrockAdapterOptions['nativeStructuredOutputModels']","toolName: string | undefined","jsonInstruction: string | undefined","toolConfig: NonNullable<BedrockRequest['toolConfig']> | undefined","outputConfig: NonNullable<BedrockRequest['outputConfig']> | undefined","request: BedrockRequest","bedrockClient: BedrockConverseClient","options?: BedrockAdapterOptions","text: string","wireToolCalls: WireToolCall[] | undefined","toolChoice: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice']","m: Extract<\n Parameters<LLMClient['chat']['completions']['create']>[0]['messages'][number],\n { role: 'user' | 'assistant' | 'tool' }\n >","blocks: BedrockContentBlock[]","input: unknown","messages: { role: 'user' | 'assistant'; content: BedrockContentBlock[] }[]","m: (typeof messages)[number]","merged: (typeof messages)[number][]","stream: ReadableStream<Uint8Array>","url: string","init: { method: string; headers: Record<string, string>; body?: string; signal?: AbortSignal }","config: FetchAdapterConfig","params: ChatRequest","requestBody: unknown","wireToolCalls: WireToolCall[] | undefined","blocks: ContentBlock[]","params: Parameters<LLMClient['chat']['completions']['create']>[0]","chunk: OpenAIStreamChunk","client: unknown","options: OpenAICompatibleAdapterOptions","options"],"sources":["../src/types/errors.ts","../src/types/cache.ts","../src/types/tools.ts","../src/types/fallback.ts","../src/internal/execution/usage.utils.ts","../src/internal/cache/replay.utils.ts","../src/internal/cache/cacheOrchestrator.ts","../src/circuitBreaker.ts","../src/internal/circuitBreaker.utils.ts","../src/internal/execution/retry.utils.ts","../src/internal/execution/errors.utils.ts","../src/internal/execution/parse.utils.ts","../src/internal/execution/wire.utils.ts","../src/internal/execution/requestBuilder.ts","../src/internal/execution/streamAccumulator.ts","../src/internal/execution/callExecutor.ts","../src/internal/logger.utils.ts","../src/logger.ts","../src/rateLimit.ts","../src/vernLLM.ts","../src/adapters/internal/sse.ts","../src/adapters/internal/imageFormat.ts","../src/adapters/internal/nativeStructuredOutput.ts","../src/adapters/anthropic.ts","../src/adapters/gemini.ts","../src/adapters/bedrock.ts","../src/adapters/fetch.ts","../src/adapters/openaiCompatible.ts"],"sourcesContent":["export type LLMErrorType =\n | 'timeout'\n | 'api'\n | 'network'\n | 'parse'\n | 'validation'\n | 'invalid_params'\n | 'rate_limited'\n | 'quota_exceeded'\n | 'circuit_open'\n | 'fallback_exhausted'\n | 'aborted'\n | 'unknown';\n\n/**\n * Machine readable discriminator within a `type`, for cases where `type`\n * alone is too coarse to act on. Optional and additive: errors thrown\n * before a given code existed simply omit it. Not owned by a single type;\n * e.g. `authentication`/`authorization` apply the same way regardless of\n * which type wraps them.\n */\nexport type LLMErrorCode =\n // Tool contract (validation)\n | 'unknown_tool'\n | 'duplicate_tool_call_id'\n | 'tool_choice_none_violated'\n | 'unexpected_tool_calls'\n // Caller input (invalid_params)\n | 'unsupported_capability'\n | 'duplicate_tool_names'\n | 'unknown_tool_choice'\n | 'duplicate_tool_result_ids'\n | 'unknown_tool_result_ids'\n | 'missing_tool_results'\n // Rate limiting (rate_limited)\n | 'rate_limit_queue_full'\n | 'rate_limit_queue_timeout'\n | 'rate_limit_capacity_exceeded'\n | 'provider_rate_limited'\n // Timeouts (timeout)\n | 'request_timeout'\n | 'idle_timeout'\n // HTTP status (api)\n | 'authentication'\n | 'authorization'\n | 'not_found'\n | 'payload_too_large'\n | 'server_error'\n | 'empty_response'\n // Connectivity (network)\n | 'connection_failed'\n // Circuit breaker (circuit_open)\n | 'circuit_cooling_down'\n | 'circuit_trial_in_flight'\n // Fallback (fallback_exhausted)\n | 'fallback_exhausted'\n // Parsing (parse)\n | 'tool_arguments_parse_failed'\n | 'stream_frame_invalid';\n\n/**\n * Tool contract codes: a model or provider response defect, not a\n * transient provider fault. Deterministic on the wire request, so\n * retrying can't change the outcome and it shouldn't count toward the\n * circuit breaker either. Shared by `LLMError.retryable` below and by\n * `CallExecutor`'s own retry/breaker accounting, so the two can't drift\n * apart.\n */\nexport const NON_RETRYABLE_TOOL_CONTRACT_CODES: ReadonlySet<LLMErrorCode> = new Set([\n 'unknown_tool',\n 'duplicate_tool_call_id',\n 'tool_choice_none_violated',\n 'unexpected_tool_calls',\n]);\n\n/**\n * Local rate-limit codes: the call never reached the provider, so it says\n * nothing about the provider's health, and retrying either just requeues\n * behind the same limit (the two queue codes) or can never succeed at all\n * (`rate_limit_capacity_exceeded`). Shared for the same reason as\n * {@link NON_RETRYABLE_TOOL_CONTRACT_CODES}.\n */\nexport const LOCAL_RATE_LIMIT_CODES: ReadonlySet<LLMErrorCode> = new Set([\n 'rate_limit_queue_full',\n 'rate_limit_queue_timeout',\n 'rate_limit_capacity_exceeded',\n]);\n\n/**\n * Types that are never worth retrying on their own: deterministic\n * caller-input, model-response, or cancellation failures rather than a\n * transient provider fault.\n */\nconst NON_RETRYABLE_TYPES: ReadonlySet<LLMErrorType> = new Set([\n 'parse',\n 'validation',\n 'invalid_params',\n 'aborted',\n]);\n\n/**\n * Shared retryability rule behind both `LLMError.retryable` and\n * `LLMErrorSnapshot.retryable`. Pulled out so the two can't drift apart:\n * a snapshot is a point-in-time copy of an error's fields, and this is\n * one of them, so it has to be computed the same way in both places.\n */\nfunction computeRetryable(type: LLMErrorType, code: LLMErrorCode | undefined): boolean {\n if (NON_RETRYABLE_TYPES.has(type)) return false;\n if (code && NON_RETRYABLE_TOOL_CONTRACT_CODES.has(code)) return false;\n if (code && LOCAL_RATE_LIMIT_CODES.has(code)) return false;\n return true;\n}\n\n/**\n * Returns `issues` unchanged when it can survive `JSON.stringify`.\n * Most `issues` values are VernLLM's own structured shapes (see\n * `LLMErrorIssuesByCode`) and always safe. The one exception is a\n * schema validation failure, where `issues` is a caller supplied\n * `SchemaLike` validator's own `error: unknown`, not controlled by\n * VernLLM and not guaranteed to be circular free. Rather than silently\n * dropping it in that case, this returns a marker string so a reader\n * of serialized output can tell \"no issues data\" apart from \"issues\n * existed but could not be shown\".\n */\nfunction safeIssues(issues: unknown): unknown {\n if (issues === undefined) return undefined;\n try {\n JSON.stringify(issues);\n return issues;\n } catch {\n return '[Unserializable: issues contained a circular reference]';\n }\n}\n\n/**\n * Depth cap for `safeAttempts`, guarding against a pathological,\n * self referential `attempts` array. `attempts` is a public\n * `LLMErrorOptions` field, so a caller can construct one by hand; this\n * keeps that path bounded the same way a circular `issues` value is\n * bounded, rather than assuming well formed input.\n */\nconst MAX_ATTEMPTS_DEPTH = 20;\n\n/**\n * Returns a copy of `attempts` with every nested snapshot's `issues`\n * re-checked through `safeIssues`, recursively through each snapshot's\n * own `attempts`. Needed for two reasons: `safeIssues` returns a safe\n * `issues` value by reference, so a shared object can be mutated into a\n * circular one after the snapshot was created, and `attempts` is a\n * public constructor option, so a caller can hand build a `RetryAttempt`\n * (or a whole `LLMErrorSnapshot`) with a circular `issues` and pass it\n * in directly, never touching `toSnapshot()` at all. Extra fields on an\n * attempt (e.g. `FallbackAttempt`'s `provider`/`model`) are preserved.\n */\nfunction safeAttempts(attempts: RetryAttempt[] | undefined, depth = 0): RetryAttempt[] | undefined {\n if (attempts === undefined) return undefined;\n if (depth >= MAX_ATTEMPTS_DEPTH) return [];\n\n return attempts.map((attempt) => ({\n ...attempt,\n error: {\n ...attempt.error,\n issues: safeIssues(attempt.error.issues),\n attempts: safeAttempts(attempt.error.attempts, depth + 1),\n },\n }));\n}\n\n/** One tool call's contract failure, used to report every bad call in a response at once. */\nexport interface ToolIssue {\n name: string;\n toolCallId: string;\n code: LLMErrorCode;\n detail?: unknown;\n}\n\n/**\n * The specific values behind a `duplicate_tool_names` failure: the\n * offending call's `tools` array had more than one entry sharing a name.\n */\nexport interface DuplicateToolNamesIssue {\n names: string[];\n}\n\n/**\n * The specific values behind an `unknown_tool_choice` failure: `toolChoice`\n * named a tool that wasn't in the call's own `tools` array.\n */\nexport interface UnknownToolChoiceIssue {\n requested: string;\n available: string[];\n}\n\n/**\n * The specific values behind a `duplicate_tool_result_ids` /\n * `unknown_tool_result_ids` / `missing_tool_results` failure: which\n * `history` turn was affected, and which `toolCallId`s were the problem.\n */\nexport interface HistoryToolResultIssue {\n historyIndex: number;\n ids: string[];\n}\n\n/**\n * The specific values behind an `unsupported_capability` failure: which\n * capability the current adapter/client/model doesn't support.\n */\nexport interface UnsupportedCapabilityIssue {\n capability: string;\n}\n\n/**\n * Maps each `LLMErrorCode` that carries structured `issues` to that\n * payload's exact shape. Not every code appears here: most `invalid_params`\n * failures are a single deterministic fact the `message` already states in\n * full, so adding a typed `issues` entry for them would only duplicate the\n * message into a field, the same near-duplicate-code problem `code` itself\n * avoids. Codes that repeat here are exactly the ones whose `message`\n * already string-joins a list a caller might want to consume directly\n * rather than re-parse out of prose, or that otherwise want a place to\n * report the exact captured values of a failure.\n *\n * Deliberately not a mapped type over the whole `LLMErrorCode` union: a\n * schema-validation failure's `issues` (the caller's own Zod-compatible\n * validator's error object) has no code and no shape VernLLM could know in\n * advance, so it stays untyped on `LLMError.issues` itself rather than\n * forcing every code into this table.\n */\nexport interface LLMErrorIssuesByCode {\n unknown_tool: ToolIssue[];\n duplicate_tool_call_id: ToolIssue[];\n duplicate_tool_names: DuplicateToolNamesIssue;\n unknown_tool_choice: UnknownToolChoiceIssue;\n duplicate_tool_result_ids: HistoryToolResultIssue;\n unknown_tool_result_ids: HistoryToolResultIssue;\n missing_tool_results: HistoryToolResultIssue;\n unsupported_capability: UnsupportedCapabilityIssue;\n}\n\n/**\n * Point-in-time copy of an `LLMError`'s fields, produced by\n * `LLMError.toSnapshot()`. This is what `RetryAttempt.error` holds\n * instead of a live `LLMError`.\n *\n * A past attempt only needs to be describable (message, type, code,\n * whether it was retryable), never thrown again. So it skips `Error`'s\n * behavior, `instanceof` identity, and any live getter. Using the full\n * `LLMError` class here would also make the type self referential\n * through its own `attempts` field.\n *\n * Has no `cause`. `cause` is `unknown` and never validated by VernLLM,\n * and it is meant to be read directly on the live error you just\n * caught, not carried indefinitely inside history. `type`, `code`,\n * `status`, and `issues` are the structured fields a snapshot carries\n * instead.\n *\n * `attempts` is still present, since a recorded attempt can itself be\n * the terminal failure of an inner retry loop with its own history (see\n * `FallbackAttempt`). That's a tree of past data, not a cycle.\n */\nexport interface LLMErrorSnapshot {\n message: string;\n type: LLMErrorType;\n status?: number;\n issues?: unknown;\n retryAfterMs?: number;\n code?: LLMErrorCode;\n /** Computed once, at snapshot time, since a snapshot has no live getter. */\n retryable: boolean;\n /** This attempt's own prior attempts, if it was itself the terminal failure of a retry loop. */\n attempts?: RetryAttempt[];\n}\n\n/**\n * One failed attempt on the way to a terminal error: which attempt index\n * it was, and a snapshot of the error it failed with. The base shape\n * every richer attempt record (e.g. `FallbackAttempt`) extends, rather\n * than duplicates.\n */\nexport interface RetryAttempt {\n index: number;\n error: LLMErrorSnapshot;\n}\n\n/** Optional fields for constructing an {@link LLMError}. `message` and `type` stay positional since every throw site sets both. */\nexport interface LLMErrorOptions {\n status?: number;\n issues?: unknown;\n cause?: unknown;\n retryAfterMs?: number;\n /** Stable discriminator within `type`. Absent on errors predating it. */\n code?: LLMErrorCode;\n /** Every attempt made before this error was thrown, in order. Absent when nothing was retried. */\n attempts?: RetryAttempt[];\n}\n\nexport class LLMError extends Error {\n public status?: number;\n public issues?: unknown;\n public cause?: unknown;\n public retryAfterMs?: number;\n /** Stable discriminator within `type`. Absent on errors predating it. */\n public code?: LLMErrorCode;\n /** Every attempt made before this error was thrown, in order. Absent when nothing was retried. */\n public attempts?: RetryAttempt[];\n\n constructor(\n message: string,\n public type: LLMErrorType,\n options: LLMErrorOptions = {},\n ) {\n super(message);\n this.name = 'LLMError';\n this.status = options.status;\n this.issues = options.issues;\n this.cause = options.cause;\n this.retryAfterMs = options.retryAfterMs;\n this.code = options.code;\n this.attempts = options.attempts;\n }\n\n /**\n * Computed purely from `type`/`code`, independent of any specific call's\n * `nonRetryableStatus` list. False for `parse`/`validation`/\n * `invalid_params`/`aborted` types (the caller's own input, the model's\n * own response, or intentional cancellation, none of which are the\n * provider being unhealthy), the tool contract codes, and the local\n * rate limit codes. Subclasses (see `FallbackExhaustedError`) may\n * override this when `type` alone carries no retry signal.\n */\n get retryable(): boolean {\n return computeRetryable(this.type, this.code);\n }\n\n /**\n * Copies this error's fields into an {@link LLMErrorSnapshot}, for\n * recording as a `RetryAttempt`/`FallbackAttempt`. `retryable` is\n * captured here since a snapshot has no getter of its own. `cause` is\n * not copied, see `LLMErrorSnapshot`'s own doc. `issues` and every\n * nested `attempts` entry's own `issues` go through `safeAttempts`,\n * since a schema validation failure's `issues` is a caller supplied\n * value, not controlled by VernLLM, and `attempts` is itself a public\n * constructor option a caller can hand build.\n */\n toSnapshot(): LLMErrorSnapshot {\n return {\n message: this.message,\n type: this.type,\n status: this.status,\n issues: safeIssues(this.issues),\n retryAfterMs: this.retryAfterMs,\n code: this.code,\n retryable: this.retryable,\n attempts: safeAttempts(this.attempts),\n };\n }\n\n /**\n * Controls what `JSON.stringify(err)` produces. Omits `cause` for the\n * same reason `toSnapshot()` does: `cause` is `unknown` and never\n * validated by VernLLM, and some SDK errors carry circular structures\n * `JSON.stringify` cannot serialize at all. Read `err.cause` directly\n * instead. `issues`, including every nested `attempts` entry's own\n * `issues`, goes through `safeAttempts` for the same reason: a schema\n * validation failure's `issues` is caller supplied and not guaranteed\n * circular free. Also includes `message` and `retryable`, which a\n * plain property walk would otherwise miss: `message` is\n * non-enumerable on `Error`, and `retryable` is a getter, not an own\n * property.\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n type: this.type,\n status: this.status,\n issues: safeIssues(this.issues),\n retryAfterMs: this.retryAfterMs,\n code: this.code,\n retryable: this.retryable,\n attempts: safeAttempts(this.attempts),\n };\n }\n}\n\nexport function isLLMError(err: unknown): err is LLMError {\n return err instanceof LLMError;\n}\n\n/**\n * Narrows `err.issues` to the exact shape {@link LLMErrorIssuesByCode} maps\n * `code` to, for any code listed there. `code` stays the only discriminator\n * VernLLM uses; this just gives that existing check a typed return instead\n * of requiring a manual cast of `issues`:\n *\n * ```ts\n * if (isLLMError(err) && hasIssues(err, 'duplicate_tool_names')) {\n * console.log(err.issues.names); // string[], no cast needed\n * }\n * ```\n */\nexport function hasIssues<C extends keyof LLMErrorIssuesByCode>(\n err: LLMError,\n code: C,\n): err is LLMError & { code: C; issues: LLMErrorIssuesByCode[C] } {\n return err.code === code && err.issues !== undefined;\n}\n","export interface CacheAdapter<T = unknown> {\n get(key: string): Promise<{ hit: boolean; value: T | null }>;\n set(key: string, value: T, ttl: number): Promise<void>;\n delete?(key: string): Promise<void>;\n resolveKey?(key: string): Promise<string>;\n}\n\n/**\n * Trivial default so the package works out of the box with no external deps\n * Not shared across processes, swap in Redis/Upstash/etc for production\n */\nexport class InMemoryCacheAdapter<T = unknown> implements CacheAdapter<T> {\n private store = new Map<string, { value: T; expiresAt: number }>();\n\n constructor(private readonly maxSize = 1000) {}\n\n async get(key: string): Promise<{ hit: boolean; value: T | null }> {\n const entry = this.store.get(key);\n\n if (!entry) return { hit: false, value: null };\n\n if (Date.now() >= entry.expiresAt) {\n this.store.delete(key);\n return { hit: false, value: null };\n }\n\n return { hit: true, value: entry.value };\n }\n\n async set(key: string, value: T, ttl: number): Promise<void> {\n this.cleanupExpiredEntries();\n\n this.store.set(key, {\n value,\n expiresAt: Date.now() + ttl * 1000,\n });\n\n this.enforceSizeLimit();\n }\n\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n private cleanupExpiredEntries(): void {\n const now = Date.now();\n\n for (const [key, entry] of this.store) {\n if (now >= entry.expiresAt) {\n this.store.delete(key);\n }\n }\n }\n\n private enforceSizeLimit(): void {\n while (this.store.size > this.maxSize) {\n const oldestKey = this.store.keys().next().value;\n\n if (oldestKey === undefined) break;\n\n this.store.delete(oldestKey);\n }\n }\n}\n\n/**\n * Normalizes keys before caching to avoid duplicate entries from formatting differences.\n */\nexport class NormalizedCacheAdapter<T = unknown> implements CacheAdapter<T> {\n constructor(private readonly inner: CacheAdapter<T> = new InMemoryCacheAdapter<T>()) {}\n\n private normalize(key: string): string {\n return key\n .toLowerCase()\n .trim()\n .replace(/[^\\p{L}\\p{N}\\s]/gu, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n }\n\n async resolveKey(key: string): Promise<string> {\n return this.normalize(key);\n }\n\n async get(key: string): Promise<{ hit: boolean; value: T | null }> {\n return this.inner.get(this.normalize(key));\n }\n\n async set(key: string, value: T, ttl: number): Promise<void> {\n await this.inner.set(this.normalize(key), value, ttl);\n }\n\n async delete(key: string): Promise<void> {\n await this.inner.delete?.(this.normalize(key));\n }\n}\n\n/**\n * Two-tier cache with fast local L1 and shared L2.\n * L2 hits are promoted back to L1.\n */\nexport class TieredCacheAdapter<T = unknown> implements CacheAdapter<T> {\n constructor(\n private readonly l1: CacheAdapter<T>,\n private readonly l2: CacheAdapter<T>,\n private readonly l1Ttl?: number,\n ) {}\n\n /**\n * Forwards to L1's `resolveKey` if it has one, otherwise L2's. L1 is\n * preferred since `get()` checks L1 first, so its notion of \"the same\n * key\" is the one that determines whether a lookup can skip L2 entirely.\n */\n async resolveKey(key: string): Promise<string> {\n if (this.l1.resolveKey) return this.l1.resolveKey(key);\n if (this.l2.resolveKey) return this.l2.resolveKey(key);\n return key;\n }\n\n async get(key: string): Promise<{ hit: boolean; value: T | null }> {\n const l1Result = await this.l1.get(key);\n if (l1Result.hit) return l1Result;\n\n const l2Result = await this.l2.get(key);\n\n if (l2Result.hit) {\n await this.l1.set(key, l2Result.value as T, this.l1Ttl ?? 60);\n }\n\n return l2Result;\n }\n\n async set(key: string, value: T, ttl: number): Promise<void> {\n await Promise.all([this.l1.set(key, value, this.l1Ttl ?? ttl), this.l2.set(key, value, ttl)]);\n }\n\n async delete(key: string): Promise<void> {\n await Promise.all([this.l1.delete?.(key), this.l2.delete?.(key)]);\n }\n}\n","import type { SchemaLike } from './schema.js';\n\n/**\n * Describes a capability the model may request, not the capability\n * itself. VernLLM transports this to the provider and parses what comes\n * back; it never executes anything.\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n /** JSON Schema for the tool's input. */\n parameters: Record<string, unknown>;\n /**\n * Optional client-side validator run on the parsed `arguments` before\n * they're handed back to the caller, mirroring the `schema: SchemaLike<T>`\n * pattern already used for response validation (see `types/schema.ts`).\n * Reuses that zero-dependency, `safeParse`-compatible shape instead of\n * requiring a JSON Schema validator (e.g. ajv) as a new dependency.\n * Failed validation throws `LLMError('validation')`. If omitted, VernLLM\n * parses arguments as JSON but does not validate them further.\n */\n argumentsSchema?: SchemaLike<unknown>;\n}\n\n/** A single tool invocation requested by the model. */\nexport interface ToolCall {\n id: string;\n name: string;\n /** Parsed JSON arguments (and validated, if `argumentsSchema` was set). */\n arguments: unknown;\n}\n\n/** The application's result of executing a `ToolCall`, sent back to the model. */\nexport interface ToolResult {\n toolCallId: string;\n content: unknown;\n /**\n * Signals a failed tool execution back to the model (matches Anthropic's\n * native `is_error` on tool_result blocks). Only `fromAnthropic` honors\n * this today, Gemini and Bedrock have no equivalent wire concept, so\n * other adapters ignore it silently.\n */\n isError?: boolean;\n}\n\n/** `call()` result when `tools` was set and the model produced a normal answer. */\nexport interface ContentResult<T> {\n type: 'content';\n content: T;\n}\n\n/** `call()` result when `tools` was set and the model requested one or more tools. */\nexport interface ToolCallResult {\n type: 'tool_calls';\n toolCalls: ToolCall[];\n /** Any text the model produced alongside the tool request, if present. */\n content?: string;\n}\n\nexport type CallWithToolsResult<T> = ContentResult<T> | ToolCallResult;\n\n/**\n * Runtime-safe check for whether a `call()` result is a `tool_calls`\n * result. Prefer this over relying on TypeScript's static narrowing\n * whenever `params` passed to `call()` wasn't a literal with `tools`\n * inlined (see the \"note on the overload\" in `VernLLM.call`'s docs), in\n * that case TS may have typed the result as plain `T` even though it's\n * actually a `CallWithToolsResult<T>` at runtime, and this check works\n * either way.\n */\nexport function isToolCallResult(result: unknown): result is ToolCallResult {\n return (\n typeof result === 'object' &&\n result !== null &&\n 'type' in result &&\n (result as { type: unknown }).type === 'tool_calls' &&\n Array.isArray((result as { toolCalls?: unknown }).toolCalls)\n );\n}\n\n/** What the model should do about tools on a given call. */\nexport type ToolChoice = 'auto' | 'none' | 'required' | { name: string };\n","import { LLMError, type RetryAttempt } from './errors.js';\n\nimport type { CircuitBreakerOptions, CircuitState } from '../circuitBreaker.js';\nimport type { RateLimitOptions } from '../rateLimit.js';\nimport type { LLMClient } from './client.js';\n\n/**\n * One provider to try after the primary (or after an earlier fallback\n * target) fails. Order is the policy: VernLLM never reorders, scores, or\n * selects a target, it only walks the list as given.\n *\n * Most per-target overrides fall back to the parent `VernLLM` instance's\n * own option when omitted, so a target only needs to specify what's\n * actually different about it (a different client/model is the common\n * case). `circuitBreaker` and `rateLimit` are the exception: they are\n * never inherited from the parent, since a breaker or limiter tuned for\n * the primary provider's limits is rarely right for a fallback's. Leave\n * them unset on a target to run it without one, even if the parent has\n * one configured.\n */\nexport interface FallbackTarget {\n client: LLMClient;\n model: string;\n /** Label for events, errors, and `TokenUsage.provider`. Default `` `fallback[${index}]` ``. */\n name?: string;\n\n maxRetries?: number;\n timeoutMs?: number;\n chunkIdleTimeoutMs?: number;\n baseDelayMs?: number;\n defaultMaxTokens?: number;\n defaultTemperature?: number | null;\n nonRetryableStatus?: number[];\n /** This target's own circuit breaker, independent of every other target's. Not inherited from the parent's `circuitBreaker`. */\n circuitBreaker?: boolean | CircuitBreakerOptions;\n /** This target's own rate limiter, independent of every other target's. Not inherited from the parent's `rateLimit`. */\n rateLimit?: RateLimitOptions;\n}\n\n/**\n * Written into `CallParams['meta']` once `call()` resolves, so a caller\n * who wants provider identity on the same line as the result doesn't need\n * to read it back out of `onUsage`.\n */\nexport interface CallMeta {\n provider: string;\n model: string;\n /** `-1` if the primary target answered, otherwise the index into `fallback`. */\n fallbackIndex: number;\n usedFallback: boolean;\n /** Attempts made against the target that ultimately answered, including the successful one. */\n attempts: number;\n}\n\n/** One target's circuit state, as returned by `VernLLM.getCircuitStates()`. */\nexport interface TargetCircuitState {\n provider: string;\n /** Position in the chain: `0` for the primary, `1`+ for fallback targets. */\n index: number;\n isFallback: boolean;\n /** Whether this target tracks failures per model. `false` means `model` on `getCircuitStates` had no effect on this entry. */\n isolateByModel: boolean;\n /** `undefined` if that target has no circuit breaker configured. */\n state: CircuitState | undefined;\n}\n\n/** Which target/model `VernLLM.getCircuitState`, `openCircuit`, and `closeCircuit` act on. */\nexport interface CircuitTarget {\n /** Which target to act on. `0` is the primary, `1`+ are fallbacks. Defaults to `0`. */\n index?: number;\n /** Which model bucket to act on, if the resolved target isolates by model. */\n model?: string;\n}\n\n/**\n * One target's failure, recorded on the way to either the next target or\n * `FallbackExhaustedError`. Extends `RetryAttempt`: `index` is `-1` for\n * the primary target here (rather than a plain retry count), and\n * `provider`/`model` identify which target failed.\n */\nexport interface FallbackAttempt extends RetryAttempt {\n provider: string;\n model: string;\n}\n\n/**\n * Decides what happens after a target's own retries are exhausted or\n * abandoned early. Called once per failed target. `'retry'` is not a\n * valid return here: retrying already happened inside the target, this\n * only decides whether to move on to the next one or stop.\n */\nexport type FallbackOn = (error: LLMError, context: { isLastTarget: boolean }) => 'next' | 'stop';\n\n/** Tool contract failures are the model ignoring the request, not a sick provider: repeating it elsewhere can't help. */\nconst TOOL_CONTRACT_CODES = new Set([\n 'unknown_tool',\n 'duplicate_tool_call_id',\n 'tool_choice_none_violated',\n 'unexpected_tool_calls',\n]);\n\n/**\n * The default `fallbackOn` policy. Exported so a caller can wrap rather\n * than replace it, e.g. `fallbackOn: (e, ctx) => myCheck(e) ? 'stop' : defaultFallbackOn(e, ctx)`.\n */\nexport const defaultFallbackOn: FallbackOn = (error) => {\n if (error.type === 'parse' || error.type === 'validation' || error.type === 'aborted') {\n return 'stop';\n }\n\n if (error.type === 'quota_exceeded') return 'stop';\n\n if (error.code && TOOL_CONTRACT_CODES.has(error.code)) return 'stop';\n\n return 'next';\n};\n\n/**\n * Thrown when the chain gives up, whether because the last target failed\n * or `fallbackOn` chose to stop early. Carries each attempt in order so\n * an outage across providers stays debuggable without reproducing it.\n * Extends `LLMError` so `isLLMError` and any `instanceof LLMError` check\n * still passes, inheriting the last failure's `type`/`status`/`retryAfterMs`\n * so existing type-based handling, including reading `retryAfterMs` on an\n * `'api'`-typed error, keeps working on a fallback-exhausted error too.\n */\nexport class FallbackExhaustedError extends LLMError {\n constructor(public override readonly attempts: FallbackAttempt[]) {\n const last = attempts[attempts.length - 1]?.error;\n\n // `type` is always `'fallback_exhausted'`, never inherited from the last\n // target's own type: every target failing is a meaningfully different\n // event from any single target's own failure. `status` and\n // `retryAfterMs` still inherit from the last attempt.\n super(\n `${attempts.length} provider${attempts.length === 1 ? '' : 's'} attempted and failed: ${attempts\n .map((a) => `${a.provider}(${a.error.type})`)\n .join(' then ')}`,\n 'fallback_exhausted',\n {\n status: last?.status,\n // `last` is an `LLMErrorSnapshot`, not the live `LLMError` that\n // target actually threw, so `cause` here is the same descriptive\n // data a caller would get from `err.attempts.at(-1)!.error`\n // rather than a distinct object.\n cause: last,\n retryAfterMs: last?.retryAfterMs,\n code: 'fallback_exhausted',\n attempts,\n },\n );\n }\n\n /**\n * `type: 'fallback_exhausted'` by itself says nothing about whether\n * retrying could help; the reason the last target failed does. Defers to\n * that attempt's own `retryable` instead of anything about this class's\n * own type.\n */\n override get retryable(): boolean {\n const last = this.attempts[this.attempts.length - 1]?.error;\n return last ? last.retryable : super.retryable;\n }\n}\n\n/** Narrows `err` to {@link FallbackExhaustedError}, for direct access to its `attempts` (`provider`/`model` per failed target) without a manual `instanceof` check. */\nexport function isFallbackExhaustedError(err: unknown): err is FallbackExhaustedError {\n return err instanceof FallbackExhaustedError;\n}\n","import { LLMError, type StreamChunk, type UsageHooks } from '../../types/index.js';\n\n/**\n * Calls `params.reserveUsage`, if present, mapping any failure to a\n * `quota_exceeded` LLMError (or an aborted error, if the signal fired\n * during reservation). Returns whether a reservation was actually made,\n * so callers know whether a later refund is needed. Shared by\n * `withReservedUsage` and `withReservedUsageForStream`, which differ only\n * in whether `coalesced` is caller-supplied or always `false`.\n */\nasync function reserve(\n params: UsageHooks,\n coalesced: boolean,\n signal: AbortSignal | undefined,\n): Promise<boolean> {\n if (!params.reserveUsage) return false;\n\n try {\n await params.reserveUsage({ coalesced, signal });\n return true;\n } catch (error) {\n if (signal?.aborted) {\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n throw new LLMError(\n error instanceof Error ? error.message : 'Usage reservation failed',\n 'quota_exceeded',\n { cause: error },\n );\n }\n}\n\n/**\n * Builds a `(logMessage) => Promise<void>` refund function bound to the\n * given hooks/coalesced/signal, reporting (instead of throwing) any error\n * the refund hook itself raises, so a broken refund hook never masks the\n * original error it was called to clean up after.\n */\nfunction makeRefund(\n params: UsageHooks,\n coalesced: boolean,\n signal: AbortSignal | undefined,\n onRefundError: (logMessage: string, error: unknown) => void,\n): (logMessage: string) => Promise<void> {\n return async (logMessage: string) => {\n try {\n await params.refundUsage?.({ coalesced, signal });\n } catch (refundError) {\n onRefundError(logMessage, refundError);\n }\n };\n}\n\n/**\n * Runs `getResult` after reserving usage, if a `reserveUsage` hook was\n * provided. `refundUsage` fires only if a reservation was actually made.\n * `onRefundError` is called (instead of throwing) whenever a refund attempt\n * itself fails, so a broken refund hook never masks the original error.\n */\nexport async function withReservedUsage<T>(\n params: UsageHooks,\n coalesced: boolean,\n getResult: () => Promise<T>,\n signal: AbortSignal | undefined,\n onRefundError: (logMessage: string, error: unknown) => void,\n): Promise<T> {\n if (signal?.aborted) {\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n const reserved = await reserve(params, coalesced, signal);\n\n const refund = makeRefund(params, coalesced, signal, onRefundError);\n\n if (signal?.aborted) {\n if (reserved) await refund('[VernLLM] refundUsage failed after abort');\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n let result: T;\n\n try {\n result = await getResult();\n } catch (error) {\n if (reserved) await refund('[VernLLM] refundUsage failed');\n throw error;\n }\n\n if (signal?.aborted) {\n if (reserved) await refund('[VernLLM] refundUsage failed after abort');\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n return result;\n}\n\n/**\n * Streaming counterpart to `withReservedUsage`. `withReservedUsage` assumes\n * `getResult()` settling *is* the operation's final outcome, awaiting it\n * synchronously before reserve/refund resolve. Streaming can't satisfy that:\n * `call()` must return `{ chunks, finalResult }` as soon as the stream\n * opens, well before the real outcome (validation, schema/tool-call checks)\n * is known.\n *\n * Reserves usage before `openStream` runs, same failure mode as the\n * non-streaming path if `reserveUsage` itself throws (mapped to\n * `quota_exceeded`, nothing opened). If `openStream` itself throws (stream\n * never opened), refunds synchronously and rethrows, exactly like\n * `withReservedUsage` does today. If it succeeds, returns `{ chunks,\n * finalResult }` immediately, refund/report is deferred onto\n * `finalResult`'s continuation, since that's the only point the real\n * outcome is known. This means `onUsageFailure` (and any refund) can fire\n * well after this function itself has returned.\n */\nexport async function withReservedUsageForStream<T>(\n params: UsageHooks,\n openStream: () => Promise<{ chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> }>,\n signal: AbortSignal | undefined,\n onRefundError: (logMessage: string, error: unknown) => void,\n): Promise<{ chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> }> {\n if (signal?.aborted) {\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n const reserved = await reserve(params, false, signal);\n const refund = makeRefund(params, false, signal, onRefundError);\n\n if (signal?.aborted) {\n if (reserved) await refund('[VernLLM] refundUsage failed after abort');\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n let opened: { chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> };\n\n try {\n opened = await openStream();\n } catch (error) {\n if (reserved) await refund('[VernLLM] refundUsage failed after stream-open failure');\n throw error;\n }\n\n // Stream opened. The real outcome is only known once finalResult settles,\n // so refund is attached there instead of awaited inline, this is the\n // structural difference from withReservedUsage, not an optional variant.\n const finalResult = opened.finalResult.then(\n (value) => value,\n async (error) => {\n if (reserved) await refund('[VernLLM] refundUsage failed after stream error');\n throw error;\n },\n );\n\n // Same rationale as the no-op catch attached where finalResult is first\n // constructed: mark this derived promise observed too, so a caller that\n // only reads `chunks` doesn't get an unhandled-rejection warning from\n // this wrapper promise either.\n finalResult.catch(() => {});\n\n return { chunks: opened.chunks, finalResult };\n}\n","import type { StreamChunk } from '../../types/stream.js';\nimport type { CallWithToolsResult } from '../../types/tools.js';\n\n/**\n * Converts an already-known cache value back into a plausible \"text\" form\n * for a one-shot replay chunk: passed through unchanged if it's already a\n * string (the `jsonMode: false` case), otherwise `JSON.stringify`'d (the\n * `jsonMode: true` case, where the cached value is the *parsed* result, not\n * the original raw text). This is a reasonable reconstruction, not a\n * byte-identical replay of whatever text the model originally streamed,\n * good enough for `for await (const c of chunks)` call sites that don't\n * branch on hit vs. miss, which is the only thing a cache-hit replay needs\n * to support.\n */\nfunction toReplayText(value: unknown): string {\n return typeof value === 'string' ? value : (JSON.stringify(value) ?? '');\n}\n\n/**\n * Builds a trivially-exhausted one-shot `chunks` iterable from an\n * already-known value, used for a `cachedCall` cache hit, where there's no\n * live generation to relay (see `VernLLM.cachedCall`'s docs). No `usage`\n * chunk is emitted: a cache hit spent no real tokens, so there's nothing to\n * report, matching how non-streaming `cachedCall` never calls `onUsage` on\n * a hit either.\n *\n * `hasTools` must reflect whether the *original* call that produced this\n * cached value had `tools` set, that's what determines whether `value` is\n * `T` directly or a `CallWithToolsResult<T>` wrapper, and it isn't\n * something that can be reliably guessed from the value's shape alone\n * (a `schema`-validated `T` could coincidentally look like a\n * `CallWithToolsResult`).\n */\nexport function buildReplayChunks<T>(\n value: T | CallWithToolsResult<T>,\n hasTools: boolean,\n): AsyncIterable<StreamChunk> {\n const items: StreamChunk[] = [];\n\n if (hasTools) {\n const result = value as CallWithToolsResult<T>;\n\n if (result.type === 'tool_calls') {\n result.toolCalls.forEach((toolCall, index) => {\n items.push({\n type: 'tool_call_delta',\n index,\n id: toolCall.id,\n name: toolCall.name,\n argsDelta: JSON.stringify(toolCall.arguments ?? {}),\n // A replay is always the whole value in one shot, never a\n // fragment, same as Gemini's one-shot tool_call_delta chunks.\n complete: true,\n });\n });\n\n if (result.content) items.push({ type: 'text-delta', delta: result.content });\n } else {\n items.push({ type: 'text-delta', delta: toReplayText(result.content) });\n }\n } else {\n items.push({ type: 'text-delta', delta: toReplayText(value) });\n }\n\n return {\n async *[Symbol.asyncIterator]() {\n for (const item of items) yield item;\n },\n };\n}\n\n/**\n * Streaming counterpart to `buildReplayChunks` for a `cachedCall` that\n * *joined* an already-in-flight call for the same key rather than\n * triggering one itself (see `runCachedStream`'s in-flight-coalescing\n * path): there's no live stream to relay (it isn't this call's stream to\n * relay, see the joiner-path comment in `runCachedStream`), but there's\n * also no value yet, only a pending promise for one. Waits for `promise`,\n * then delegates to `buildReplayChunks`. If `promise` rejects, iterating\n * `chunks` throws that same error, consistent with how a live stream's\n * `chunks` throws on a mid-stream failure.\n */\nexport function buildReplayChunksFromPromise<T>(\n promise: Promise<T | CallWithToolsResult<T>>,\n hasTools: boolean,\n): AsyncIterable<StreamChunk> {\n return {\n async *[Symbol.asyncIterator]() {\n const value = await promise;\n\n yield* buildReplayChunks(value, hasTools);\n },\n };\n}\n","import { withReservedUsage, withReservedUsageForStream } from '../execution/usage.utils.js';\nimport { buildReplayChunks, buildReplayChunksFromPromise } from './replay.utils.js';\n\nimport type { Logger } from '../../logger.js';\nimport type { CacheAdapter, StreamChunk } from '../../types/index.js';\nimport type { InternalCacheParams, InternalCacheStreamParams } from './cache.utils.js';\n\n/**\n * Owns cache key resolution, cache reads/writes, and in-flight coalescing\n * for concurrent misses on the same key. Doesn't know about `CallExecutor`,\n * retries, or providers at all: `fn`/`openStream` are opaque callbacks\n * (`VernLLM.cachedCall` passes `() => this.call(...)`), so this class only\n * needs the cache adapter and a logger. Extracted from `VernLLM` since\n * caching and per-target call mechanics are independent concerns that\n * happened to live on the same class.\n */\nexport class CacheOrchestrator {\n private readonly inFlight = new Map<string, Promise<unknown>>();\n\n constructor(\n private readonly cache: CacheAdapter<unknown>,\n private readonly logger: Logger,\n ) {}\n\n /** Resolves a cache key through the adapter when it supports normalization. */\n async resolveCacheKey(key: string): Promise<string> {\n return this.cache.resolveKey ? await this.cache.resolveKey(key) : key;\n }\n\n /**\n * Removes a cached response by key when the configured cache adapter\n * supports deletion. Cache invalidation is the caller's responsibility;\n * only the application knows when cached data is stale.\n */\n async deleteCache(key: string): Promise<void> {\n if (!this.cache.delete) return;\n\n try {\n await this.cache.delete(await this.resolveCacheKey(key));\n } catch (error) {\n this.logger.warn(\n `[VernLLM] cache delete failed: ${error instanceof Error ? error.message : 'unknown'}`,\n );\n }\n }\n\n /** Logs a failed refundUsage attempt via the configured logger. */\n private logRefundError(logMessage: string, error: unknown): void {\n this.logger.error(logMessage, {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n\n /**\n * Reads from the cache, treating a failed adapter read as a miss rather\n * than letting it fail the call. The request still falls through to a\n * real provider call, but that fallback is now logged instead of silent.\n */\n private async getCached(key: string): Promise<{ hit: boolean; value?: unknown }> {\n try {\n return await this.cache.get(key);\n } catch (error) {\n this.logger.warn(\n `[VernLLM] cache read failed: ${error instanceof Error ? error.message : 'unknown'}`,\n );\n\n return { hit: false };\n }\n }\n\n /**\n * Internal cache primitive around caller-supplied logic. Concurrent misses\n * for the same `cacheKey` share a single in-flight call, avoiding cache\n * stampedes.\n *\n * Backs the public `VernLLM.cachedCall()`, which always composes this\n * with `call()` so cached results get the same retry/timeout/\n * circuit-breaker guarantees as any other LLM call.\n *\n * @param params `cacheKey`, `ttl`, `fn` (the work to run on a cache\n * miss, typically `() => this.call(...)`), and optional\n * `reserveUsage`/`refundUsage`/`signal`. See `InternalCacheParams`.\n * @returns The cached value on a hit, or the result of `fn()` on a miss.\n */\n async runCached<T>(params: InternalCacheParams<T>): Promise<T> {\n const resolvedKey = await this.resolveCacheKey(params.cacheKey);\n const resolvedParams =\n resolvedKey === params.cacheKey ? params : { ...params, cacheKey: resolvedKey };\n\n const cached = await this.getCached(resolvedKey);\n\n if (cached.hit) return cached.value as T;\n\n const existing = this.inFlight.get(resolvedKey) as Promise<T> | undefined;\n\n if (existing) {\n return withReservedUsage(\n resolvedParams,\n true,\n () => existing,\n params.signal,\n (logMessage, error) => this.logRefundError(logMessage, error),\n );\n }\n\n return this.registerTrigger(resolvedParams);\n }\n\n /** Starts the shared fn() call for a cache miss and tracks it in the in-flight map until it settles. */\n private registerTrigger<T>(params: InternalCacheParams<T>): Promise<T> {\n const resultPromise = withReservedUsage(\n params,\n false,\n () => this.runAndCache(params),\n params.signal,\n (logMessage, error) => this.logRefundError(logMessage, error),\n );\n\n this.inFlight.set(params.cacheKey, resultPromise);\n\n void resultPromise\n .catch(() => {})\n .finally(() => {\n this.inFlight.delete(params.cacheKey);\n });\n\n return resultPromise;\n }\n\n /** Runs `fn` and writes its result to the cache. */\n private async runAndCache<T>(params: InternalCacheParams<T>): Promise<T> {\n const result = await params.fn();\n\n try {\n await this.cache.set(params.cacheKey, result, params.ttl);\n } catch (error) {\n this.logger.warn(\n `[VernLLM] cache write failed: ${error instanceof Error ? error.message : 'unknown'}`,\n );\n }\n\n return result;\n }\n\n /**\n * Streaming counterpart to `runCached`. Three cases:\n *\n * - Hit: no live generation to relay. Returns immediately with\n * `finalResult` resolved to the cached value and a one-shot `chunks`\n * replay built from it, so `for await (const c of chunks)` call sites\n * work identically on a hit or a miss. No usage hooks fire, since\n * nothing was actually spent.\n * - Miss, nothing else in flight for this key: delegates to\n * `registerStreamTrigger`, which opens the stream and relays its\n * `chunks` live.\n * - Miss, but another call for the same key is already in flight: this\n * call has no live chunks of its own to relay, so it's treated like a\n * delayed hit. `finalResult` shares the trigger's in-flight promise\n * (the same in-flight map non-streaming `runCached` uses, so\n * streaming and non-streaming calls for the same key coalesce\n * against each other too), and `chunks` is a one-shot replay built\n * once that promise resolves.\n */\n async runCachedStream<T>(\n params: InternalCacheStreamParams<T>,\n hasTools: boolean,\n ): Promise<{ chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> }> {\n const resolvedKey = await this.resolveCacheKey(params.cacheKey);\n const resolvedParams =\n resolvedKey === params.cacheKey ? params : { ...params, cacheKey: resolvedKey };\n\n const cached = await this.getCached(resolvedKey);\n\n if (cached.hit) {\n const value = cached.value as T;\n\n return { chunks: buildReplayChunks(value, hasTools), finalResult: Promise.resolve(value) };\n }\n\n const existing = this.inFlight.get(resolvedKey) as Promise<T> | undefined;\n\n if (existing) {\n const finalResult = withReservedUsage(\n resolvedParams,\n true,\n () => existing,\n params.signal,\n (logMessage, error) => this.logRefundError(logMessage, error),\n );\n\n // Mirror the no-op catch in withReservedUsageForStream: buildReplayChunksFromPromise\n // doesn't await this promise until `chunks` is iterated, so a caller that only reads\n // `finalResult` eagerly (or never reads `chunks`) could otherwise trigger an\n // unhandled-rejection warning.\n finalResult.catch(() => {});\n\n return { chunks: buildReplayChunksFromPromise(finalResult, hasTools), finalResult };\n }\n\n return this.registerStreamTrigger(resolvedParams);\n }\n\n /**\n * Opens the shared stream for a cache miss and tracks its settled value\n * in the in-flight map until it resolves or rejects. Writes to the cache\n * on success only, matching `runAndCache`.\n *\n * Registers the in-flight promise synchronously, before anything async\n * runs, so a concurrent `cachedCall` for the same key always sees it in\n * time to join instead of triggering its own stream. Settlement is\n * wired onto the whole `withReservedUsageForStream` call rather than a\n * line inside its callback, so any failure point (reserving usage,\n * opening the stream, or the stream itself) reliably settles the\n * in-flight entry instead of leaving it stuck.\n */\n private registerStreamTrigger<T>(\n params: InternalCacheStreamParams<T>,\n ): Promise<{ chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T> }> {\n let resolveInFlight!: (value: T) => void;\n let rejectInFlight!: (error: unknown) => void;\n\n const inFlightResult = new Promise<T>((resolve, reject) => {\n resolveInFlight = resolve;\n rejectInFlight = reject;\n });\n\n this.inFlight.set(params.cacheKey, inFlightResult);\n\n void inFlightResult\n .catch(() => {})\n .finally(() => {\n this.inFlight.delete(params.cacheKey);\n });\n\n const streamPromise = withReservedUsageForStream(\n params,\n async () => {\n const opened = await params.openStream();\n\n const trackedResult: Promise<T> = opened.finalResult.then(\n async (value) => {\n try {\n await this.cache.set(params.cacheKey, value, params.ttl);\n } catch (error) {\n this.logger.warn(\n `[VernLLM] cache write failed: ${error instanceof Error ? error.message : 'unknown'}`,\n );\n }\n\n return value;\n },\n (error: unknown) => {\n // Failed calls aren't cached, matching `runAndCache`, which\n // only calls `cache.set` after `fn()` succeeds. Rethrown\n // unchanged so both the refund logic attached downstream and\n // `inFlightResult` see the real failure.\n throw error;\n },\n );\n\n return { chunks: opened.chunks, finalResult: trackedResult };\n },\n params.signal,\n (logMessage, error) => this.logRefundError(logMessage, error),\n );\n\n // Settles `inFlightResult` (registered above) based on `streamPromise`'s\n // own outcome, not a line inside its callback. See this function's\n // docs for why.\n streamPromise.then(\n (opened) => {\n opened.finalResult.then(resolveInFlight, rejectInFlight);\n },\n (error: unknown) => {\n rejectInFlight(error);\n },\n );\n\n return streamPromise;\n }\n}\n","import { LLMError } from './types/errors.js';\n\nexport interface CircuitBreakerOptions {\n /** Consecutive failures before the circuit opens, default 5 */\n threshold?: number;\n /** How long the circuit stays open before allowing a trial request, in ms. Default 30000 */\n cooldownMs?: number;\n /**\n * Called after every real state change, never for a no-op transition\n * (e.g. open to open). `model` is the resolved model of whichever call\n * triggered this specific transition (the `model` passed to whichever\n * of `assertClosed`/`recordSuccess`/`recordFailure` caused it).\n *\n * With `isolateByModel` off (the default), this is a label only: the\n * breaker still counts failures across every model together, so a\n * threshold crossing can be the sum of several different models'\n * failures even though only the triggering call's `model` is reported\n * here. With `isolateByModel` on, it's exact: each model has its own\n * counter, so the transition really was caused solely by that model.\n */\n onStateChange?: (\n from: CircuitState,\n to: CircuitState,\n consecutiveFailures: number,\n model?: string,\n ) => void;\n /**\n * Track a separate circuit per resolved model instead of one shared\n * circuit for the whole instance. A failure on one model then never\n * opens another model's circuit, at the cost of slower detection for\n * an outage spread across many distinct models (each model's counter\n * must independently cross `threshold`). Default false: one shared\n * circuit, matching every version before this option existed.\n *\n * A call that omits `model` (only possible calling `CircuitBreaker`\n * directly, `VernLLM` always passes one) falls into one shared bucket\n * alongside every other call that also omits it.\n */\n isolateByModel?: boolean;\n}\n\nexport type CircuitState = 'closed' | 'open' | 'half-open';\n\n/** Mutable state for one circuit, either the single shared one or one model's bucket under `isolateByModel`. */\ninterface CircuitBucket {\n state: CircuitState;\n consecutiveFailures: number;\n openedAt: number;\n /**\n * True while a single half-open trial call is in flight. Guards against\n * multiple concurrent callers all treating themselves as \"the\" trial once\n * the cooldown elapses\n */\n trialInFlight: boolean;\n}\n\nfunction newBucket(): CircuitBucket {\n return { state: 'closed', consecutiveFailures: 0, openedAt: 0, trialInFlight: false };\n}\n\n/** Key a bucket lookup falls into when the call omitted `model` under `isolateByModel`. */\nconst UNLABELED_MODEL = '';\n\n/**\n * Per retry VernLLM-instance circuit breaker. Tracks consecutive failures across\n * calls. Once the threshold is hit, short-circuits new calls with an\n * LLMError('circuit_open') instead of hitting the provider, until the\n * cooldown elapses and a single trial call is allowed through\n */\nexport class CircuitBreaker {\n private readonly threshold: number;\n private readonly cooldownMs: number;\n private readonly onStateChange?: CircuitBreakerOptions['onStateChange'];\n /** Whether this breaker tracks failures per model instead of one shared circuit. Read by `CallExecutor`/`VernLLM` to report per-target in `getCircuitStates`. */\n readonly isolateByModel: boolean;\n\n // Exactly one of these is used, chosen once at construction by\n // `isolateByModel`, so every method has a single, unambiguous place to\n // resolve a bucket from instead of branching on the flag repeatedly.\n private readonly sharedBucket: CircuitBucket = newBucket();\n private readonly bucketsByModel = new Map<string, CircuitBucket>();\n\n constructor(options: CircuitBreakerOptions = {}) {\n this.threshold = options.threshold ?? 5;\n this.cooldownMs = options.cooldownMs ?? 30_000;\n this.onStateChange = options.onStateChange;\n this.isolateByModel = options.isolateByModel ?? false;\n }\n\n /** Returns the bucket for a model if one already exists, without allocating. */\n private lookupBucket(model: string | undefined): CircuitBucket | undefined {\n if (!this.isolateByModel) return this.sharedBucket;\n\n const key = model ?? UNLABELED_MODEL;\n return this.bucketsByModel.get(key);\n }\n\n /** Creates and stores a bucket for a model when the first mutation needs one. */\n private ensureBucketFor(model: string | undefined): CircuitBucket {\n if (!this.isolateByModel) return this.sharedBucket;\n\n const key = model ?? UNLABELED_MODEL;\n let bucket = this.bucketsByModel.get(key);\n\n if (!bucket) {\n bucket = newBucket();\n this.bucketsByModel.set(key, bucket);\n }\n\n return bucket;\n }\n\n /** Every state mutation routes through here, so `onStateChange` fires exactly once per real change. */\n private transition(bucket: CircuitBucket, to: CircuitState, model: string | undefined): void {\n if (to === bucket.state) return;\n\n const from = bucket.state;\n bucket.state = to;\n this.onStateChange?.(from, to, bucket.consecutiveFailures, model);\n }\n\n /**\n * Throws if the circuit is open and the cooldown hasn't elapsed, or if\n * the circuit is half-open and a trial call is already in flight.\n * Otherwise, if the circuit just became eligible for a trial (cooldown\n * elapsed, or half-open with no trial currently running), this call\n * becomes that trial\n */\n assertClosed(model?: string): void {\n const bucket = this.ensureBucketFor(model);\n\n if (bucket.state === 'closed') return;\n\n if (bucket.state === 'open') {\n const elapsed = Date.now() - bucket.openedAt;\n if (elapsed < this.cooldownMs) {\n throw new LLMError(\n `Circuit open, provider has failed ${bucket.consecutiveFailures} times in a row. Retry in ${Math.ceil((this.cooldownMs - elapsed) / 1000)}s.`,\n 'circuit_open',\n { code: 'circuit_cooling_down' },\n );\n }\n\n // Set before transition(): a synchronous onStateChange observer\n // that re-enters (e.g. calls assertClosed again) must see this\n // call as already claiming the trial, not still eligible for one.\n bucket.trialInFlight = true;\n this.transition(bucket, 'half-open', model);\n return;\n }\n\n // state === 'half-open'\n if (bucket.trialInFlight) {\n throw new LLMError(\n 'Circuit half-open. A trial request is already in flight. Try again shortly.',\n 'circuit_open',\n { code: 'circuit_trial_in_flight' },\n );\n }\n\n bucket.trialInFlight = true;\n }\n\n recordSuccess(model?: string): void {\n const bucket = this.lookupBucket(model);\n\n if (!bucket) {\n return;\n }\n\n bucket.consecutiveFailures = 0;\n bucket.trialInFlight = false;\n this.transition(bucket, 'closed', model);\n\n if (this.isolateByModel && bucket.state === 'closed' && bucket.consecutiveFailures === 0) {\n this.bucketsByModel.delete(model ?? UNLABELED_MODEL);\n }\n }\n\n recordFailure(model?: string): void {\n const bucket = this.ensureBucketFor(model);\n\n bucket.consecutiveFailures += 1;\n bucket.trialInFlight = false;\n\n if (bucket.state === 'half-open') {\n // Trial call failed: reopen and reset the cooldown window. Set\n // before transition() for the same reason as assertClosed above:\n // a synchronous observer must see the fresh cooldown, not a stale\n // or zeroed one.\n bucket.openedAt = Date.now();\n this.transition(bucket, 'open', model);\n return;\n }\n\n if (bucket.consecutiveFailures >= this.threshold) {\n bucket.openedAt = Date.now();\n this.transition(bucket, 'open', model);\n }\n }\n\n /**\n * With `isolateByModel` off (the default), `model` is ignored and the\n * one shared circuit's state is returned, unchanged from every version\n * before this option existed. With `isolateByModel` on, returns that\n * model's own state, `'closed'` for a model never seen yet, same as a\n * fresh breaker.\n */\n getState(model?: string): CircuitState {\n return this.lookupBucket(model)?.state ?? 'closed';\n }\n\n /**\n * Manually opens the circuit, as if `threshold` consecutive failures had\n * just happened, e.g. to pull a provider out of rotation ahead of known\n * maintenance. Resets the cooldown window from now, same as a real\n * threshold-crossing failure would, and clears any in-flight half-open\n * trial since it no longer applies once the circuit is (re)opened.\n */\n open(model?: string): void {\n const bucket = this.ensureBucketFor(model);\n\n bucket.openedAt = Date.now();\n bucket.trialInFlight = false;\n this.transition(bucket, 'open', model);\n }\n\n /**\n * Manually closes the circuit and resets its failure count, e.g. once a\n * provider is confirmed healthy again without waiting out the cooldown.\n * Mirrors `recordSuccess`'s bookkeeping (including dropping the\n * per-model bucket under `isolateByModel`, once idle) but without\n * requiring an actual successful call first.\n */\n close(model?: string): void {\n const bucket = this.ensureBucketFor(model);\n\n bucket.consecutiveFailures = 0;\n bucket.trialInFlight = false;\n this.transition(bucket, 'closed', model);\n\n // transition() may have synchronously re-entered (e.g. an\n // onStateChange callback that calls open(model) on this same\n // bucket), so re-check state/consecutiveFailures rather than\n // assuming they still hold the values set above, same as\n // recordSuccess does.\n if (this.isolateByModel && bucket.state === 'closed' && bucket.consecutiveFailures === 0) {\n this.bucketsByModel.delete(model ?? UNLABELED_MODEL);\n }\n }\n}\n","import { CircuitBreaker, type CircuitBreakerOptions } from '../circuitBreaker.js';\n\nimport type { Logger } from '../logger.js';\nimport type { VernLLMEvent } from '../types/events.js';\n\n/**\n * Builds a `(event) => void` reporter that no-ops when `onEvent` is unset,\n * and otherwise calls it, swallowing and logging any error the handler\n * throws so a broken `onEvent` can't break the call that triggered it.\n * Shared by `buildCircuitBreaker` (which needs to report before any\n * executor exists) and `CallExecutor.reportEvent`, kept independent of the\n * executor for that reason.\n */\nexport function makeEventReporter(\n onEvent: ((event: VernLLMEvent) => void) | undefined,\n logger: Logger,\n): (event: VernLLMEvent) => void {\n return (event) => {\n if (!onEvent) return;\n\n try {\n onEvent(event);\n } catch (error) {\n logger.error('[VernLLM] onEvent failed', {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n };\n}\n\n/**\n * Builds the optional circuit breaker for one provider target, wiring its\n * `onStateChange` to emit a `circuit_state` event and chain any\n * caller-supplied `onStateChange`. Returns `undefined` when\n * `circuitBreakerOption` is falsy, matching the option's own semantics.\n *\n * Lives outside `CallExecutor` (and outside `VernLLM`, once this were\n * inlined) because the breaker has to exist *before* the executor it's\n * passed into, so its construction can't be an executor concern.\n * `onEvent` is called directly rather than through the executor for the\n * same reason: nothing executor-shaped exists yet at this point.\n *\n * Takes the specific fields it needs (rather than a full `VernLLMOptions`)\n * so it works identically for the primary target and for each fallback\n * target, which carry their own `circuitBreaker` override alongside the\n * shared `onEvent`.\n */\nexport function buildCircuitBreaker(\n circuitBreakerOption: boolean | CircuitBreakerOptions | undefined,\n providerName: string,\n defaultModel: string,\n onEvent: ((event: VernLLMEvent) => void) | undefined,\n logger: Logger,\n): CircuitBreaker | undefined {\n if (!circuitBreakerOption) return undefined;\n\n const breakerOptions =\n typeof circuitBreakerOption === 'object' ? circuitBreakerOption : undefined;\n const userOnStateChange = breakerOptions?.onStateChange;\n\n const reportEvent = makeEventReporter(onEvent, logger);\n\n return new CircuitBreaker({\n ...breakerOptions,\n onStateChange: (from, to, consecutiveFailures, model) => {\n reportEvent({\n kind: 'circuit_state',\n provider: providerName,\n model: model ?? defaultModel,\n from,\n to,\n consecutiveFailures,\n });\n\n // A caller-supplied onStateChange would otherwise be silently\n // discarded, since the spread above is overwritten by this\n // property. Chain it instead, same try/catch treatment as every\n // other user-supplied callback so it can't break breaker\n // bookkeeping or the call that triggered it.\n if (!userOnStateChange) return;\n\n try {\n userOnStateChange(from, to, consecutiveFailures, model);\n } catch (error) {\n logger.error('[VernLLM] circuitBreaker.onStateChange failed', {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n },\n });\n}\n","import { LLMError } from '../../types/errors.js';\n\nimport type { Logger } from '../../logger.js';\n\n/**\n * Default cap (ms) for both exponential backoff and honored Retry-After\n * values, so a misbehaving/adversarial Retry-After can't stall a caller\n * indefinitely\n */\nexport const DEFAULT_MAX_DELAY_MS = 10_000;\n\n/**\n * `setTimeout` silently clamps any delay above this (~24.8 days) or\n * `Infinity` down to ~1ms instead of erroring, so a caller passing\n * `Infinity` as \"no timeout\" gets the opposite of what they asked for.\n * Both timeout helpers below guard against this explicitly.\n */\nconst MAX_SETTIMEOUT_MS = 2_147_483_647;\n\n/**\n * Resolves a timeout value to the number `setTimeout` should actually use,\n * or `undefined` when the timeout should be treated as disabled (0,\n * negative, or `Infinity`). Returning the resolved value directly, rather\n * than a boolean, lets callers narrow `number | undefined` to `number`\n * without an `as number` cast.\n */\nfunction resolveActiveTimeoutMs(ms: number | undefined): number | undefined {\n return !ms || ms <= 0 || ms === Infinity ? undefined : ms;\n}\n\n/** Caps a timeout at the largest delay `setTimeout` actually honors. */\nfunction clampTimeoutMs(ms: number): number {\n return Math.min(ms, MAX_SETTIMEOUT_MS);\n}\n\n/**\n * Runs an async function and cancels it if it takes longer than the given\n * timeout. Creates an internal abort controller that fires after the\n * timeout elapses, and combines it with any external signal the caller\n * passed in so either one can cancel the underlying call. If the internal\n * timeout triggers and the underlying operation aborts, the error is\n * converted into an LLMError with type \"timeout\". External cancellations\n * continue to propagate as aborted errors. The internal timer is always\n * cleared afterward, whether the function succeeds, fails, or is aborted,\n * so nothing is left running in the background.\n *\n * `timeoutMs` of `Infinity` (or any value beyond what `setTimeout` can\n * represent) disables the timeout rather than firing almost immediately.\n */\nexport async function withTimeout<T>(\n fn: (signal: AbortSignal) => Promise<T>,\n timeoutMs: number,\n externalSignal?: AbortSignal,\n): Promise<T> {\n const controller = new AbortController();\n\n const activeTimeoutMs = resolveActiveTimeoutMs(timeoutMs);\n\n const timer =\n activeTimeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n controller.abort();\n }, clampTimeoutMs(activeTimeoutMs));\n\n const signal = externalSignal\n ? AbortSignal.any([externalSignal, controller.signal])\n : controller.signal;\n\n try {\n return await fn(signal);\n } catch (err) {\n if (\n controller.signal.aborted &&\n !externalSignal?.aborted &&\n err instanceof DOMException &&\n err.name === 'AbortError'\n ) {\n throw new LLMError('Request timed out', 'timeout', { code: 'request_timeout' });\n }\n\n throw err;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Races one `iterator.next()` call against a per-call idle timer, to\n * bound the gap *between* chunks (unlike `withTimeout`, which only bounds\n * opening the stream and its first chunk). Without this, a connection\n * that streams one chunk then hangs would never fail.\n *\n * `timeoutMs` of 0/undefined/`Infinity` disables the check. Otherwise\n * rejects with `LLMError('timeout')` if `next()` doesn't settle in time.\n * The clock resets on every call, so the window is measured from the most\n * recent chunk, not from stream start.\n *\n * `onIdle`, if given, is called the moment the timer fires (before the\n * rejection), so callers can abort the underlying transport instead of\n * just walking away from an unread promise. `logger`, if given, records a\n * debug line if `next()` still settles *after* the idle timeout already\n * rejected. `resolve`/`reject` on an already-settled promise is otherwise\n * a silent no-op, so without this the late chunk (possibly the final\n * usage chunk) would vanish with no trace.\n */\nexport function withChunkIdleTimeout<T>(\n next: () => Promise<IteratorResult<T>>,\n timeoutMs: number | undefined,\n onIdle?: () => void,\n logger?: Pick<Logger, 'debug'>,\n): Promise<IteratorResult<T>> {\n const activeTimeoutMs = resolveActiveTimeoutMs(timeoutMs);\n\n if (activeTimeoutMs === undefined) {\n return next();\n }\n\n let settled = false;\n\n return new Promise<IteratorResult<T>>((resolve, reject) => {\n const timer = setTimeout(() => {\n settled = true;\n onIdle?.();\n reject(\n new LLMError(\n `No stream chunk received for ${activeTimeoutMs}ms (idle timeout)`,\n 'timeout',\n {\n code: 'idle_timeout',\n },\n ),\n );\n }, clampTimeoutMs(activeTimeoutMs));\n\n next().then(\n (result) => {\n clearTimeout(timer);\n if (settled) {\n logger?.debug('[VernLLM] chunk resolved after idle timeout already fired; discarding');\n return;\n }\n settled = true;\n resolve(result);\n },\n (error: unknown) => {\n clearTimeout(timer);\n if (settled) {\n logger?.debug(\n '[VernLLM] chunk rejection arrived after idle timeout already fired; discarding',\n );\n return;\n }\n settled = true;\n reject(error);\n },\n );\n });\n}\n\n/**\n * Looks inside an unknown error value for a Retry-After header and\n * converts it to milliseconds. Checks `.headers` first (fetch-style,\n * Headers-like with `.get()`), then `.response.headers` (axios-style,\n * plain object) since different client libraries surface headers\n * differently. Supports both the delta-seconds form (\"30\") and the\n * HTTP-date form (\"Wed, 21 Oct 2015 07:28:00 GMT\"). The result is capped\n * at maxDelayMs. Returns undefined when no usable Retry-After is present\n */\nexport function extractRetryAfterMs(\n err: unknown,\n maxDelayMs = DEFAULT_MAX_DELAY_MS,\n): number | undefined {\n if (!err || typeof err !== 'object') return undefined;\n\n const error = err as { headers?: unknown; response?: { headers?: unknown } };\n const headers = error.headers ?? error.response?.headers;\n\n if (!headers || typeof headers !== 'object') return undefined;\n\n const getter = headers as { get?: (name: string) => string | null };\n\n const raw =\n typeof getter.get === 'function'\n ? getter.get('Retry-After')\n : Object.entries(headers as Record<string, string>)\n .find(([name]) => name.toLowerCase() === 'retry-after')\n ?.at(1);\n\n if (typeof raw !== 'string' || raw.trim() === '') return undefined;\n\n const trimmed = raw.trim();\n\n if (/^\\d+$/.test(trimmed)) {\n return Math.max(0, Math.min(Number(trimmed) * 1000, maxDelayMs));\n }\n\n const dateMs = Date.parse(trimmed);\n if (!Number.isNaN(dateMs)) {\n return Math.max(0, Math.min(dateMs - Date.now(), maxDelayMs));\n }\n\n return undefined;\n}\n\n/**\n * Exponential backoff with jitter, capped at maxDelayMs.\n * Jitter avoids thundering-herd retries when many callers back off in lockstep,\n * the cap prevents unbounded delays when maxRetries is high\n */\nexport function getBackoffDelay(\n baseDelayMs: number,\n attempt: number,\n maxDelayMs = DEFAULT_MAX_DELAY_MS,\n): number {\n const exp = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);\n return exp / 2 + Math.random() * (exp / 2);\n}\n\n/**\n * Pauses execution for the given delay before a retry attempt. If an\n * abort signal is provided and it fires while waiting, the pending\n * timer is cancelled immediately and the wait rejects right away with\n * an aborted error instead of continuing to sit idle until the delay\n * would have finished on its own\n */\nexport async function waitForRetry(delay: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new LLMError('Operation aborted', 'aborted');\n }\n\n await new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(new LLMError('Operation aborted', 'aborted'));\n };\n\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, delay);\n\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","import { LLMError, type LLMErrorCode, type RetryAttempt } from '../../types/errors.js';\nimport { extractRetryAfterMs } from './retry.utils.js';\n\n/**\n * Looks inside an unknown error value and pulls out an http status code\n * if one is present. Checks the status field first then the status code\n * field since different client libraries use different names for this,\n * falling back to AWS SDK v3's `$metadata.httpStatusCode` (e.g. Bedrock's\n * `ThrottlingException`), which doesn't set either of the other two.\n * Returns undefined when the error is not an object or carries no status\n */\nexport function extractStatus(err: unknown): number | undefined {\n if (!err || typeof err !== 'object') return undefined;\n\n const error = err as {\n status?: unknown;\n statusCode?: unknown;\n $metadata?: { httpStatusCode?: unknown };\n };\n\n if (typeof error.status === 'number') return error.status;\n if (typeof error.statusCode === 'number') return error.statusCode;\n if (typeof error.$metadata?.httpStatusCode === 'number') return error.$metadata.httpStatusCode;\n\n return undefined;\n}\n\n/**\n * POSIX/libuv error codes libuv (and so Node's `fetch`/undici) attaches to\n * genuine transport-level failures: connection refused, DNS lookup\n * failure, connection reset mid-request, a connect that never completed,\n * DNS server unreachable, broken pipe, or host/network unreachable.\n * Deliberately narrow: only codes that can only mean \"the connection\n * itself failed,\" not anything that could also indicate an application\n * error.\n */\nconst NETWORK_ERROR_CODES = new Set([\n 'ECONNREFUSED',\n 'ENOTFOUND',\n 'ECONNRESET',\n 'ETIMEDOUT',\n 'EAI_AGAIN',\n 'EPIPE',\n 'ECONNABORTED',\n 'EHOSTUNREACH',\n 'ENETUNREACH',\n]);\n\n/** `fetch`'s own wording for a transport-level failure, across runtimes/browsers. */\nconst NETWORK_ERROR_MESSAGES = new Set([\n 'fetch failed', // Node/undici\n 'failed to fetch', // Chromium\n 'load failed', // Safari\n 'networkerror when attempting to fetch resource.', // Firefox\n]);\n\n/**\n * Whether `error` is, with reasonable confidence, a transport-level\n * failure (never reached the provider, as opposed to the provider itself\n * responding with an error) rather than some other unexpected exception.\n * Checked via explicit, well-known signals only, so a genuinely unknown\n * error never gets misclassified as a connection failure just because it\n * also lacked an HTTP status.\n */\nfunction isNetworkError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false;\n\n const err = error as { code?: unknown; message?: unknown; cause?: unknown };\n\n if (typeof err.code === 'string' && NETWORK_ERROR_CODES.has(err.code)) return true;\n\n if (typeof err.message === 'string' && NETWORK_ERROR_MESSAGES.has(err.message.toLowerCase())) {\n return true;\n }\n\n // undici/Node's `fetch` wraps the real libuv error one level down, as\n // TypeError('fetch failed', { cause: <the real error, with .code> }).\n // The message check above already catches that wrapper by itself if\n // the cause is missing or unrecognized, this catches it by the cause's\n // code when the wrapper's own message wasn't matched (e.g. a runtime\n // that phrases the wrapper differently but still sets `cause.code`).\n if (err.cause && typeof err.cause === 'object') {\n const cause = err.cause as { code?: unknown };\n if (typeof cause.code === 'string' && NETWORK_ERROR_CODES.has(cause.code)) return true;\n }\n\n return false;\n}\n\nfunction formatSafely(value: unknown): string {\n try {\n return JSON.stringify(value, null, 2) ?? String(value);\n } catch {\n try {\n return String(value);\n } catch {\n return '[unprintable error]';\n }\n }\n}\n\n/**\n * Looks inside an unknown thrown value and pulls out a human-readable\n * description of it. Checks the `error` field first (the provider's raw\n * rejection body, JSON-stringified if possible) then falls back to the\n * message` field. Always returns a safe string, even when the thrown value\n * has hostile properties or cannot be serialized normally.\n */\nexport function describeError(err: unknown): string {\n if (err && typeof err === 'object') {\n try {\n const error = err as { message?: unknown; error?: unknown };\n\n if (error.error !== undefined) {\n return formatSafely(error.error);\n }\n\n if (typeof error.message === 'string') {\n return error.message;\n }\n } catch {\n // Fall through to safe string.\n }\n }\n\n return formatSafely(err);\n}\n\n/**\n * Maps an HTTP status to its corresponding `LLMErrorCode`, derived purely\n * from the status itself so it applies the same way regardless of which\n * adapter or client raised the error. Used both when building a fresh\n * `LLMError` and when filling in a `code` on an already-normalized one\n * that doesn't have one yet, so the two paths can't drift apart.\n */\nfunction codeForStatus(status: number): LLMErrorCode | undefined {\n switch (status) {\n case 429:\n return 'provider_rate_limited';\n case 401:\n return 'authentication';\n case 403:\n return 'authorization';\n case 404:\n return 'not_found';\n case 413:\n return 'payload_too_large';\n default:\n return status >= 500 ? 'server_error' : undefined;\n }\n}\n\n/**\n * Converts any thrown value into a well-typed LLMError. `attempts`, when\n * given, is the accumulated record of every attempt made before `error`\n * was thrown; it's passed straight into the constructed error's options\n * rather than assigned onto the error afterward, so `attempts` is always\n * settled once, through the constructor, like every other field on\n * `LLMError`.\n */\nexport function normalizeError(\n error: unknown,\n signal?: AbortSignal,\n attempts?: RetryAttempt[],\n): LLMError {\n if (signal?.aborted) {\n return new LLMError('LLM request aborted', 'aborted', { attempts });\n }\n\n if (error instanceof LLMError) {\n // A caller or adapter can throw an already-built LLMError directly\n // (bypassing the generic-SDK-error path below), so a status reaching\n // us this way still needs the same `code` a generic error with that\n // status gets, without overwriting a `code` that error already carries.\n if (error.code === undefined && error.status !== undefined) {\n error.code = codeForStatus(error.status);\n }\n\n // Same rule for `attempts`: fill it in if this already-built error\n // doesn't carry one of its own, without overwriting one it does.\n if (error.attempts === undefined && attempts !== undefined) {\n error.attempts = attempts;\n }\n\n return error;\n }\n\n const status = extractStatus(error);\n const retryAfterMs = extractRetryAfterMs(error);\n\n if (status !== undefined) {\n return new LLMError('LLM request failed', 'api', {\n status,\n cause: error,\n retryAfterMs,\n code: codeForStatus(status),\n attempts,\n });\n }\n\n // No extractable HTTP status: distinguish a genuine transport-level\n // failure (DNS, connection refused, connection reset) from any other\n // unexpected exception via explicit signals only, rather than assuming\n // every status-less error reaching here is a connection failure.\n if (isNetworkError(error)) {\n return new LLMError('LLM request failed', 'network', {\n cause: error,\n retryAfterMs,\n code: 'connection_failed',\n attempts,\n });\n }\n\n return new LLMError('LLM request failed', 'unknown', { cause: error, retryAfterMs, attempts });\n}\n","/** Default `parseJson`: `JSON.parse` wrapped in try/catch, returning `undefined` on failure. */\nexport function defaultParseJson(content: string): unknown {\n try {\n return JSON.parse(content);\n } catch {\n return undefined;\n }\n}\n","import { LLMError } from '../../types/errors.js';\n\nimport type { LLMClient, WireToolCall } from '../../types/client.js';\nimport type { ToolCall, ToolDefinition } from '../../types/tools.js';\n\n/** Translates app-facing `ToolDefinition[]` into the OpenAI-shaped wire tools array. */\nexport function toWireTools(\n tools: ToolDefinition[],\n): NonNullable<Parameters<LLMClient['chat']['completions']['create']>[0]['tools']> {\n return tools.map((tool) => ({\n type: 'function' as const,\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n }));\n}\n\n/** Translates app-facing `ToolCall[]` (e.g. from a replayed assistant turn) into wire tool_calls. */\nexport function toWireToolCalls(toolCalls: ToolCall[]): WireToolCall[] {\n return toolCalls.map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.arguments ?? {}),\n },\n }));\n}\n\n/**\n * Parses the provider's wire-shaped `tool_calls` back into VernLLM's\n * `ToolCall[]`. Malformed argument JSON is a `'parse'` error, same\n * convention as malformed JSON response bodies elsewhere in VernLLM.\n */\nexport function parseWireToolCalls(wireToolCalls: WireToolCall[]): ToolCall[] {\n return wireToolCalls.map((wc) => {\n let parsedArgs: unknown;\n\n try {\n parsedArgs = wc.function.arguments.trim() ? JSON.parse(wc.function.arguments) : {};\n } catch {\n throw new LLMError(`Invalid JSON arguments for tool call \"${wc.function.name}\"`, 'parse', {\n code: 'tool_arguments_parse_failed',\n });\n }\n\n return { id: wc.id, name: wc.function.name, arguments: parsedArgs };\n });\n}\n","import { LLMError } from '../../types/errors.js';\nimport { toWireTools, toWireToolCalls } from './wire.utils.js';\n\nimport type {\n CallParams,\n ConversationTurn,\n WireMessage,\n WireToolChoice,\n} from '../../types/index.js';\n\n/** Everything `RequestBuilder` needs beyond a single call's own `CallParams`. */\nexport interface RequestBuilderOptions {\n model: string;\n defaultMaxTokens: number;\n defaultTemperature: number | null;\n}\n\n/**\n * Builds the wire request object for one call, applying per-instance\n * defaults (model, max tokens, temperature) and per-call overrides.\n * Owns every check that depends only on the caller's own input shape, not\n * on execution: history alternation, duplicate/empty tool lists,\n * `toolChoice` naming a real tool. All deterministic on the call site's\n * own input and never touch the network, so every throw here is\n * `type: 'invalid_params'`, not `'validation'` (which is reserved for the\n * model/provider's own response failing a contract check). Has no\n * knowledge of retry, timeouts, or the breaker, only\n * the three defaults a `FallbackTarget` can override per-target (see the\n * `defaultMaxTokens`/`defaultTemperature` overrides in the fallback\n * design), which is what keeps it separable from `CallExecutor`.\n */\nexport class RequestBuilder {\n private readonly model: string;\n private readonly defaultMaxTokens: number;\n private readonly defaultTemperature: number | null;\n\n constructor(options: RequestBuilderOptions) {\n this.model = options.model;\n this.defaultMaxTokens = options.defaultMaxTokens;\n this.defaultTemperature = options.defaultTemperature;\n }\n\n /** Applies per-call defaults and shapes params into the client's request object. */\n build<T>(params: CallParams<T>) {\n const {\n systemPrompt,\n userContent,\n history = [],\n maxTokens = this.defaultMaxTokens,\n model = this.model,\n reasoningEffort,\n jsonSchema,\n tools,\n toolChoice,\n } = params;\n\n const temperature =\n params.temperature === undefined ? this.defaultTemperature : params.temperature;\n\n if (tools && tools.length === 0) {\n throw new LLMError(\n '`tools` was an empty array. This is almost always a bug (e.g. a filtered tool list ' +\n 'that ended up empty). An empty `tools` array still switches on tool-call mode ' +\n '(response shape, jsonMode default, wire format) with nothing for the model to call. ' +\n 'Omit `tools` entirely for a normal call, or make sure the array is non-empty.',\n 'invalid_params',\n );\n }\n\n if (tools) {\n const seen = new Set<string>();\n const duplicates = new Set<string>();\n\n for (const tool of tools) {\n if (seen.has(tool.name)) duplicates.add(tool.name);\n seen.add(tool.name);\n }\n\n if (duplicates.size) {\n throw new LLMError(\n `\\`tools\\` has duplicate name(s): [${[...duplicates].join(', ')}]. Tool names must be unique.`,\n 'invalid_params',\n { code: 'duplicate_tool_names', issues: { names: [...duplicates] } },\n );\n }\n }\n\n if (toolChoice && !tools) {\n throw new LLMError(\n '`toolChoice` was set without `tools`. There is nothing for it to choose between. ' +\n 'Set `tools`, or remove `toolChoice`.',\n 'invalid_params',\n );\n }\n\n if (tools && typeof toolChoice === 'object' && !tools.some((t) => t.name === toolChoice.name)) {\n throw new LLMError(\n `toolChoice names \"${toolChoice.name}\", which is not in \\`tools\\` ([${tools.map((t) => t.name).join(', ')}]).`,\n 'invalid_params',\n {\n code: 'unknown_tool_choice',\n issues: { requested: toolChoice.name, available: tools.map((t) => t.name) },\n },\n );\n }\n\n // Defaults to false when tools are set and the caller didn't say otherwise,\n // since forcing a JSON response format alongside tool calling is unreliable\n // across providers.\n const jsonMode = params.jsonMode ?? (tools ? false : true);\n const useJson = jsonMode || Boolean(jsonSchema);\n\n if (params.schema && !useJson) {\n throw new LLMError(\n 'schema was provided but jsonMode: false disables JSON parsing, so nothing would validate it. Remove jsonMode: false, set jsonSchema, or remove schema.',\n 'invalid_params',\n );\n }\n\n const responseFormat = this.buildResponseFormat(jsonSchema, useJson);\n\n this.validateHistory(history);\n\n const request = {\n model,\n ...(temperature !== null ? { temperature } : {}),\n max_tokens: maxTokens,\n ...(responseFormat ? { response_format: responseFormat } : {}),\n ...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),\n ...(tools ? { tools: toWireTools(tools) } : {}),\n ...(tools ? { tool_choice: this.buildWireToolChoice(toolChoice) } : {}),\n messages: [\n ...(systemPrompt ? [{ role: 'system' as const, content: systemPrompt }] : []),\n ...history.flatMap((turn): WireMessage[] => this.turnToWireMessages(turn)),\n { role: 'user' as const, content: userContent },\n ] satisfies WireMessage[],\n };\n\n return { useJson, model, request };\n }\n\n /**\n * Validates `history` alternates user/assistant turns, since providers\n * like Anthropic/Gemini reject or mishandle consecutive same-role turns.\n */\n private validateHistory(history: ConversationTurn[]): void {\n let previousTurn: ConversationTurn | undefined;\n\n for (const [index, turn] of history.entries()) {\n if (turn.role === 'tool') {\n if (previousTurn?.role !== 'assistant' || !previousTurn.toolCalls?.length) {\n throw new LLMError(\n `history[${index}] is a \"tool\" turn, but must immediately follow an \"assistant\" turn that requested tools`,\n 'invalid_params',\n );\n }\n\n if (!turn.toolResults?.length) {\n throw new LLMError(\n `history[${index}] is a \"tool\" turn but has no toolResults`,\n 'invalid_params',\n );\n }\n\n const requestedIds = new Set(previousTurn.toolCalls.map((tc) => tc.id));\n const resultIds = turn.toolResults.map((tr) => tr.toolCallId);\n\n const unknownIds = resultIds.filter((id) => !requestedIds.has(id));\n\n if (unknownIds.length) {\n throw new LLMError(\n `history[${index}].toolResults references unknown toolCallId(s) [${unknownIds.join(', ')}]`,\n 'invalid_params',\n { code: 'unknown_tool_result_ids', issues: { historyIndex: index, ids: unknownIds } },\n );\n }\n\n // Catches a duplicated toolCallId that would otherwise mask a different call's missing result.\n const seenIds = new Set<string>();\n const duplicateIds = new Set<string>();\n\n for (const id of resultIds) {\n if (seenIds.has(id)) duplicateIds.add(id);\n seenIds.add(id);\n }\n\n if (duplicateIds.size) {\n throw new LLMError(\n `history[${index}].toolResults has duplicate toolCallId(s) [${[...duplicateIds].join(', ')}]`,\n 'invalid_params',\n {\n code: 'duplicate_tool_result_ids',\n issues: { historyIndex: index, ids: [...duplicateIds] },\n },\n );\n }\n\n const missingIds = [...requestedIds].filter((id) => !resultIds.includes(id));\n\n if (missingIds.length) {\n throw new LLMError(\n `history[${index}] is missing toolResults for toolCallId(s) [${missingIds.join(', ')}]`,\n 'invalid_params',\n { code: 'missing_tool_results', issues: { historyIndex: index, ids: missingIds } },\n );\n }\n } else {\n if (turn.role === previousTurn?.role) {\n throw new LLMError(\n `history must alternate user/assistant turns: consecutive \"${turn.role}\" turns at history[${index - 1}] and history[${index}]`,\n 'invalid_params',\n );\n }\n\n if (previousTurn?.role === 'assistant' && previousTurn.toolCalls?.length) {\n throw new LLMError(\n `history[${index}] follows an assistant tool request without tool results`,\n 'invalid_params',\n );\n }\n }\n\n previousTurn = turn;\n }\n\n if (previousTurn?.role === 'assistant' && previousTurn.toolCalls?.length) {\n throw new LLMError(\n 'The last entry in history is an assistant tool request without tool results',\n 'invalid_params',\n );\n }\n\n if (previousTurn?.role === 'user') {\n throw new LLMError(\n 'The last entry in history is a \"user\" turn, which would collide with the current userContent turn.',\n 'invalid_params',\n );\n }\n }\n\n /** Maps VernLLM's app-facing `ToolChoice` onto the OpenAI-shaped wire `tool_choice`. */\n private buildWireToolChoice(toolChoice: CallParams<unknown>['toolChoice']): WireToolChoice {\n if (!toolChoice || toolChoice === 'auto') return 'auto';\n if (toolChoice === 'none' || toolChoice === 'required') return toolChoice;\n\n return { type: 'function', function: { name: toolChoice.name } };\n }\n\n /**\n * Expands one `ConversationTurn` into one or more wire messages. Plain\n * user/assistant turns map 1:1. An assistant turn with `toolCalls` maps\n * to an assistant message carrying wire-shaped `tool_calls`. A `'tool'`\n * turn expands into one wire `tool` message per `toolResult`, since\n * OpenAI-shaped wire format wants one message per tool_call_id.\n */\n private turnToWireMessages(turn: ConversationTurn): WireMessage[] {\n if (turn.role === 'tool') {\n return (turn.toolResults ?? []).map((tr) => ({\n role: 'tool' as const,\n tool_call_id: tr.toolCallId,\n content: typeof tr.content === 'string' ? tr.content : JSON.stringify(tr.content ?? null),\n ...(tr.isError ? { is_error: true } : {}),\n }));\n }\n\n if (turn.role === 'assistant' && turn.toolCalls?.length) {\n return [\n {\n role: 'assistant' as const,\n ...(turn.content ? { content: turn.content } : {}),\n tool_calls: toWireToolCalls(turn.toolCalls),\n },\n ];\n }\n\n return [{ role: turn.role as 'user' | 'assistant', content: turn.content ?? '' }];\n }\n\n /**\n * Chooses the response format: a provider-native `jsonSchema` takes\n * priority when supplied (constrains generation directly), otherwise\n * falls back to the looser `json_object` mode when JSON output is\n * requested, or no format at all for plain text responses.\n */\n private buildResponseFormat(jsonSchema: CallParams<unknown>['jsonSchema'], useJson: boolean) {\n if (jsonSchema) {\n return {\n type: 'json_schema' as const,\n json_schema: {\n name: jsonSchema.name,\n schema: jsonSchema.schema,\n strict: jsonSchema.strict ?? true,\n description: jsonSchema.description,\n },\n };\n }\n\n return useJson ? { type: 'json_object' as const } : undefined;\n }\n}\n","import { normalizeError } from './errors.utils.js';\nimport { withChunkIdleTimeout } from './retry.utils.js';\n\nimport type { Logger } from '../../logger.js';\nimport type { LLMError } from '../../types/errors.js';\nimport type {\n CallWithToolsResult,\n StreamChunk,\n TokenUsage,\n WireStreamChunk,\n WireToolCall,\n} from '../../types/index.js';\n\n/** Everything `buildStreamResult` needs beyond the raw iterator and first chunk. */\nexport interface StreamAccumulatorOptions<T> {\n requestId: string;\n model: string;\n providerName: string;\n /** Whether this attempt ran on a fallback target rather than the primary, mirroring `extractUsage`'s `usedFallback`. */\n isFallback: boolean;\n /** Per-call override, falling back to the instance default, mirroring every other per-call timeout. */\n chunkIdleTimeoutMs: number | undefined;\n streamController: AbortController;\n logger: Logger;\n /** External signal, forwarded to `normalizeError` so a transport error during an already-aborted call is reported as `'aborted'`, not whatever the transport itself threw. */\n signal?: AbortSignal;\n /**\n * Fires once, synchronously, right after the transport-level loop\n * finishes successfully, before `finalize` runs. Lets the caller record\n * circuit-breaker success and release rate-limiter capacity before the\n * (possibly failing) finalization step.\n */\n onStreamSuccess: (usage: TokenUsage | undefined) => void;\n /**\n * Fires once when the transport-level loop itself fails (idle timeout\n * or a transport error), before this stream's `finalResult` rejects.\n * `normalized.type === 'timeout'` is the one mid-stream failure that\n * should trip the breaker: otherwise a provider that hangs after one\n * chunk would always record a success and never open it.\n */\n onStreamFailure: (normalized: LLMError, usage: TokenUsage | undefined) => void;\n /**\n * Produces the final `T | CallWithToolsResult<T>` from the accumulated\n * text and tool-call deltas once the stream completes. Errors thrown\n * here are assumed already normalized and usage-failure-reported by the\n * caller (mirrors `finalizeResponse`'s own contract).\n */\n finalize: (\n textAcc: string,\n wireToolCalls: WireToolCall[] | undefined,\n usage: TokenUsage | undefined,\n ) => T | CallWithToolsResult<T>;\n}\n\n/**\n * The streaming accumulator: wraps the raw `WireStreamChunk` iterator in\n * an async generator that yields translated `StreamChunk`s to the caller\n * live, as they arrive, with no per-chunk timeout and no bound on total\n * duration, and accumulates text/tool-call deltas internally so that\n * `finalize` can produce `finalResult` once the stream completes.\n *\n * Two separate try/catches: the iteration loop's catch handles errors\n * the transport itself throws, which aren't normalized yet, so that\n * happens here, alongside the one `onStreamFailure` call for them. The\n * second catch, around `finalize`, does not re-normalize or re-report,\n * since `finalize`'s caller (`finalizeResponse`) already does both\n * internally.\n */\nexport function buildStreamResult<T>(\n iterator: AsyncIterator<WireStreamChunk>,\n first: IteratorResult<WireStreamChunk>,\n options: StreamAccumulatorOptions<T>,\n): { chunks: AsyncIterable<StreamChunk>; finalResult: Promise<T | CallWithToolsResult<T>> } {\n const {\n requestId,\n model,\n providerName,\n isFallback,\n chunkIdleTimeoutMs,\n streamController,\n logger,\n signal,\n } = options;\n\n let resolveFinal!: (value: T | CallWithToolsResult<T>) => void;\n let rejectFinal!: (error: unknown) => void;\n\n const finalResult = new Promise<T | CallWithToolsResult<T>>((resolve, reject) => {\n resolveFinal = resolve;\n rejectFinal = reject;\n });\n\n // Avoid an unhandled-rejection warning for callers that only read `chunks`.\n finalResult.catch(() => {});\n\n // Push-based, not a pulled generator, so the pump always drives\n // `finalResult` to completion even if `chunks` is never read. Buffer\n // size (not \"has anyone started iterating yet\") is what caps memory,\n // since the pump can outrace the caller starting iteration.\n const MAX_BUFFERED_CHUNKS = 10_000;\n const buffered: StreamChunk[] = [];\n const pending: Array<{\n resolve: (result: IteratorResult<StreamChunk>) => void;\n reject: (error: unknown) => void;\n }> = [];\n let streamDone = false;\n let streamError: unknown;\n let hasLoggedEviction = false;\n\n const push = (chunk: StreamChunk) => {\n const waiter = pending.shift();\n\n if (waiter) {\n waiter.resolve({ done: false, value: chunk });\n return;\n }\n\n buffered.push(chunk);\n\n if (buffered.length > MAX_BUFFERED_CHUNKS * 2) {\n // Nothing else surfaces this: without a log, a caller that never\n // read (or fell behind on) `chunks` has no way to tell eviction,\n // not a provider or transport bug, is why chunks are missing.\n // Logged once per stream, not on every crossing, so an ignored\n // high-volume stream doesn't spam dozens of near-identical lines.\n if (!hasLoggedEviction) {\n hasLoggedEviction = true;\n logger.warn(\n `[VernLLM] stream chunk buffer exceeded cap (${MAX_BUFFERED_CHUNKS}), evicting ` +\n `${buffered.length - MAX_BUFFERED_CHUNKS} oldest chunk(s); buffered=${buffered.length}. ` +\n 'The chunks iterable was never read (or fell far behind) for this stream.',\n );\n }\n\n // Trim back down to the cap in one batch operation instead of\n // `shift()`ing a single element off on every push once the cap is\n // reached. A per-push `shift()` here is O(current length) in the\n // worst case, cheap for a handful of calls, but that cost is\n // paid on *every* push for the remainder of an ignored stream,\n // and its real-world cost isn't a stable, engine-independent\n // property: benchmarking this exact pattern at a similar backing\n // -array size showed multi-second stalls for what should be\n // sub-millisecond work. Letting the array grow to 2x the cap\n // before trimming amortizes the O(n) `splice` across\n // `MAX_BUFFERED_CHUNKS` pushes, so the average cost per push\n // stays O(1) regardless of how far past the cap the array is\n // allowed to grow before trimming.\n buffered.splice(0, buffered.length - MAX_BUFFERED_CHUNKS);\n }\n };\n\n const finish = () => {\n streamDone = true;\n\n for (const waiter of pending.splice(0)) {\n waiter.resolve({ done: true, value: undefined });\n }\n };\n\n const fail = (error: unknown) => {\n streamDone = true;\n streamError = error;\n\n for (const waiter of pending.splice(0)) {\n waiter.reject(error);\n }\n };\n\n const chunks: AsyncIterable<StreamChunk> = {\n [Symbol.asyncIterator]() {\n return {\n next(): Promise<IteratorResult<StreamChunk>> {\n if (buffered.length) {\n return Promise.resolve({ done: false, value: buffered.shift() as StreamChunk });\n }\n\n if (streamDone) {\n return streamError\n ? Promise.reject(streamError)\n : Promise.resolve({ done: true, value: undefined });\n }\n\n return new Promise((resolve, reject) => {\n pending.push({ resolve, reject });\n });\n },\n };\n },\n };\n\n const toolCallAcc = new Map<number, { id?: string; name?: string; args: string }>();\n\n let textAcc = '';\n let usage: TokenUsage | undefined;\n\n // Fires immediately, not lazily, so it always drives finalResult to\n // completion regardless of whether the caller reads chunks.\n void (async () => {\n try {\n let result: IteratorResult<WireStreamChunk> = first;\n\n while (!result.done) {\n const wireChunk = result.value;\n\n if (wireChunk.type === 'ping') {\n // No content to accumulate or push. Just resolving here\n // resets the idle-timeout clock on the next .next() call.\n } else if (wireChunk.type === 'text-delta') {\n textAcc += wireChunk.delta;\n push({ type: 'text-delta', delta: wireChunk.delta });\n } else if (wireChunk.type === 'tool_call_delta') {\n const entry = toolCallAcc.get(wireChunk.index) ?? { args: '' };\n\n entry.id ??= wireChunk.id;\n entry.name ??= wireChunk.name;\n entry.args += wireChunk.argumentsDelta ?? '';\n toolCallAcc.set(wireChunk.index, entry);\n\n push({\n type: 'tool_call_delta',\n index: wireChunk.index,\n id: wireChunk.id,\n name: wireChunk.name,\n argsDelta: wireChunk.argumentsDelta,\n complete: wireChunk.complete,\n });\n } else if (wireChunk.type === 'usage') {\n usage = {\n promptTokens: wireChunk.usage.prompt_tokens ?? 0,\n completionTokens: wireChunk.usage.completion_tokens ?? 0,\n totalTokens: wireChunk.usage.total_tokens ?? 0,\n requestId,\n model,\n provider: providerName,\n usedFallback: isFallback,\n };\n push({ type: 'usage', usage });\n }\n\n result = await withChunkIdleTimeout(\n () => iterator.next(),\n chunkIdleTimeoutMs,\n () => streamController.abort(),\n logger,\n );\n }\n } catch (error) {\n // Best-effort cleanup for a processing-time throw (as opposed to\n // `iterator.next()` rejecting, which usually means the adapter's\n // own generator already cleaned up). Two independent layers,\n // since neither is guaranteed to reach every SDK on its own:\n //\n // 1. `iterator.return()`: `iterator` is the async generator\n // returned by the adapter's `createStream`, and every\n // adapter's `createStream` body is a `for await...of` over\n // the SDK's raw stream. Calling `.return()` on a generator\n // suspended inside a `for await...of` forwards `.return()` to\n // the iterable being iterated, standard IteratorClose\n // behavior, so this one call closes the whole chain down to\n // the SDK's own stream, as long as the SDK's stream\n // implements `.return()` (true for every adapter here, see\n // the \"propagates .return()\" test in each adapter's stream\n // unit tests).\n // 2. `streamController.abort()`: aborts the same signal every\n // adapter received for this call. SDKs that honor an\n // AbortSignal for the life of the request, arguably the more\n // common pattern than implementing custom `.return()`\n // forwarding, get closed this way even if layer 1 has nothing\n // to forward to.\n try {\n await iterator.return?.();\n } catch {\n // Cleanup failing isn't the error being reported; swallow it.\n }\n streamController.abort();\n\n const normalized = normalizeError(error, signal);\n\n try {\n options.onStreamFailure(normalized, usage);\n } catch {\n // A throwing callback must not stop fail/rejectFinal from settling\n // the promises below; the stream failure itself is still reported.\n }\n\n fail(normalized);\n rejectFinal(normalized);\n\n return;\n }\n\n finish();\n\n try {\n options.onStreamSuccess(usage);\n } catch {\n // A throwing callback must not stop finalize/resolveFinal below from\n // running; the stream itself still completed successfully.\n }\n\n try {\n const wireToolCalls: WireToolCall[] | undefined = toolCallAcc.size\n ? [...toolCallAcc.entries()]\n .sort(([indexA], [indexB]) => indexA - indexB)\n .map(([, entry]) => ({\n id: entry.id ?? '',\n type: 'function' as const,\n function: { name: entry.name ?? '', arguments: entry.args },\n }))\n : undefined;\n\n const finalized = options.finalize(textAcc, wireToolCalls, usage);\n\n resolveFinal(finalized);\n } catch (error) {\n // finalize's caller has already normalized this error and\n // reported the usage failure internally. Just propagate it.\n rejectFinal(error);\n }\n })();\n\n return { chunks, finalResult };\n}\n","import { CircuitBreaker } from '../../circuitBreaker.js';\nimport { LLMError } from '../../types/errors.js';\nimport { makeEventReporter } from '../circuitBreaker.utils.js';\nimport { describeError, extractStatus, normalizeError } from './errors.utils.js';\nimport { defaultParseJson } from './parse.utils.js';\nimport { RequestBuilder } from './requestBuilder.js';\nimport { extractRetryAfterMs, getBackoffDelay, waitForRetry, withTimeout } from './retry.utils.js';\nimport { buildStreamResult } from './streamAccumulator.js';\nimport { parseWireToolCalls } from './wire.utils.js';\n\nimport type { Logger } from '../../logger.js';\nimport type { RateLimiter } from '../../rateLimit.js';\nimport type {\n CallParams,\n CallWithToolsResult,\n LLMClient,\n RetryAttempt,\n StreamChunk,\n TokenUsage,\n ToolIssue,\n VernLLMEvent,\n WireToolCall,\n} from '../../types/index.js';\n\n/** Everything one `CallExecutor` needs beyond the client and model. */\nexport interface CallExecutorOptions {\n maxRetries: number;\n timeoutMs: number;\n chunkIdleTimeoutMs: number;\n baseDelayMs: number;\n defaultMaxTokens: number;\n defaultTemperature: number | null;\n nonRetryableStatus: number[];\n parseJson?: (content: string) => unknown;\n logger: Logger;\n /** Applied to model output before it reaches the debug logger. See `VernLLMOptions.redact`. */\n redact?: (text: string) => string;\n onUsage?: (usage: TokenUsage) => void;\n onUsageFailure?: (usage: TokenUsage, error: LLMError) => void;\n onEvent?: (event: VernLLMEvent) => void;\n breaker?: CircuitBreaker;\n limiter?: RateLimiter;\n /** True for every target after the primary. Stamped onto reported `TokenUsage`. */\n isFallback?: boolean;\n}\n\n/**\n * Everything one provider target needs to attempt a call: request\n * building, retry with backoff, the per-target breaker, the per-target\n * limiter. Never exported publicly. `VernLLM` holds one per target and\n * owns the fallback loop and caching on top.\n */\nexport class CallExecutor {\n private readonly maxRetries: number;\n private readonly timeoutMs: number;\n private readonly chunkIdleTimeoutMs: number;\n private readonly baseDelayMs: number;\n private readonly nonRetryableStatus: number[];\n private readonly parseJson: (content: string) => unknown;\n private readonly logger: Logger;\n private readonly redact?: (text: string) => string;\n private readonly onUsage?: (usage: TokenUsage) => void;\n private readonly onUsageFailure?: (usage: TokenUsage, error: LLMError) => void;\n private readonly reportEvent: (event: VernLLMEvent) => void;\n private readonly breaker?: CircuitBreaker;\n private readonly limiter?: RateLimiter;\n private readonly isFallback: boolean;\n private readonly requestBuilder: RequestBuilder;\n\n constructor(\n readonly providerName: string,\n private readonly client: LLMClient,\n readonly model: string,\n options: CallExecutorOptions,\n ) {\n this.maxRetries = options.maxRetries;\n this.timeoutMs = options.timeoutMs;\n this.chunkIdleTimeoutMs = options.chunkIdleTimeoutMs;\n this.baseDelayMs = options.baseDelayMs;\n this.nonRetryableStatus = options.nonRetryableStatus;\n this.parseJson = options.parseJson ?? defaultParseJson;\n this.logger = options.logger;\n this.redact = options.redact;\n this.onUsage = options.onUsage;\n this.onUsageFailure = options.onUsageFailure;\n this.reportEvent = makeEventReporter(options.onEvent, this.logger);\n this.breaker = options.breaker;\n this.limiter = options.limiter;\n this.isFallback = options.isFallback ?? false;\n this.requestBuilder = new RequestBuilder({\n model,\n defaultMaxTokens: options.defaultMaxTokens,\n defaultTemperature: options.defaultTemperature,\n });\n }\n\n getCircuitState(model?: string) {\n return this.breaker?.getState(model);\n }\n\n /** Whether this target's breaker tracks failures per model. `false` if no breaker is configured. */\n get isolateByModel(): boolean {\n return this.breaker?.isolateByModel ?? false;\n }\n\n /** Manually opens this target's circuit breaker, if one is configured. No-op otherwise. */\n openCircuit(model?: string): void {\n this.breaker?.open(model);\n }\n\n /** Manually closes this target's circuit breaker, if one is configured. No-op otherwise. */\n closeCircuit(model?: string): void {\n this.breaker?.close(model);\n }\n\n /**\n * Throws if the breaker is open for this target/model, exactly like the\n * check `run`/`runStream` used to make internally. Exposed so `VernLLM`\n * can gate on it before reserving usage, avoiding a reserve-then-refund\n * round trip on a call that was never going to be attempted. `assertClosed`\n * has a stateful side effect (claiming a half-open trial slot), so it must\n * run exactly once per logical call: `run`/`runStream` no longer call it\n * themselves, this is now the only call site.\n */\n assertBreakerClosed(model?: string): void {\n this.breaker?.assertClosed(model ?? this.model);\n }\n\n /**\n * Runs a single logical call against this target: retry with backoff,\n * normalized error on exhaustion. Mirrors the old `VernLLM.call`'s\n * non-streaming branch, minus cache/usage-reservation and the breaker\n * check, which stay one layer up since they aren't per-target concerns\n * (see `assertBreakerClosed`).\n */\n async run<T>(\n params: CallParams<T>,\n requestId: string,\n onAttempt?: () => void,\n ): Promise<T | CallWithToolsResult<T>> {\n const model = params.model ?? this.model;\n const attempts: RetryAttempt[] = [];\n\n try {\n return await this.retryWithBackoff(\n (attempt) => this.executeCall(params, requestId, attempt),\n requestId,\n model,\n params.signal,\n onAttempt,\n attempts,\n );\n } catch (error) {\n // `attempts` only holds prior attempts that were actually retried\n // past. It's `[]` when nothing was retried, so normalize that to\n // `undefined` per `LLMError.attempts`'s contract.\n const normalized = normalizeError(\n error,\n params.signal,\n attempts.length > 0 ? attempts : undefined,\n );\n\n if (this.countsTowardBreaker(normalized)) {\n this.breaker?.recordFailure(model);\n }\n\n this.logger.debug(`[VernLLM:${requestId}] error:\\n${this.redactText(describeError(error))}`);\n\n throw normalized;\n }\n }\n\n /** Streaming counterpart to `run`. Mirrors the old streaming branch of `VernLLM.call`. */\n async runStream<T>(\n params: CallParams<T>,\n requestId: string,\n onAttempt?: () => void,\n ): Promise<{\n chunks: AsyncIterable<StreamChunk>;\n finalResult: Promise<T | CallWithToolsResult<T>>;\n }> {\n const model = params.model ?? this.model;\n const attempts: RetryAttempt[] = [];\n\n try {\n return await this.retryWithBackoff(\n (attempt) => this.executeStreamCall(params, requestId, attempt),\n requestId,\n model,\n params.signal,\n onAttempt,\n attempts,\n );\n } catch (error) {\n // See the matching comment in `run`.\n const normalized = normalizeError(\n error,\n params.signal,\n attempts.length > 0 ? attempts : undefined,\n );\n\n if (this.countsTowardBreaker(normalized)) {\n this.breaker?.recordFailure(model);\n }\n\n this.logger.debug(\n `[VernLLM:${requestId}] stream-open error:\\n${this.redactText(describeError(error))}`,\n );\n\n throw normalized;\n }\n }\n\n /**\n * Performs a single attempt: builds the request (translating `tools` to\n * wire shape when present), dispatches it with a timeout, and shapes the\n * response into `T` or a `CallWithToolsResult<T>` when `params.tools` was\n * set. Throws on an empty response (no text and no tool_calls) so the\n * retry loop treats it like any other transient failure.\n */\n private async executeCall<T>(\n params: CallParams<T>,\n requestId: string,\n attempt: number,\n ): Promise<T | CallWithToolsResult<T>> {\n const { useJson, model, request } = this.requestBuilder.build(params);\n\n // A retry is a real request, so capacity is acquired per attempt\n // (inside the retry loop, via `executeCall` being re-invoked), not\n // once for the whole call.\n let release: ((actualTokens?: number) => void) | undefined;\n\n if (this.limiter) {\n const acquired = await this.limiter.acquire(this.limiter.estimate(request), params.signal);\n release = acquired.release;\n\n if (acquired.waitedMs > 0) {\n this.reportEvent({\n kind: 'rate_limited',\n requestId,\n provider: this.providerName,\n model,\n waitedMs: acquired.waitedMs,\n reason: acquired.reason ?? 'rpm',\n });\n }\n }\n\n try {\n const response = await withTimeout(\n (attemptSignal) => this.client.chat.completions.create(request, { signal: attemptSignal }),\n this.timeoutMs,\n params.signal,\n );\n\n // Extracted right after the response arrives, before anything else\n // touches it, so a post-response failure still gets its usage reported.\n const usage = this.extractUsage(response, requestId, model);\n\n // Reconcile against real usage, then hand off so `finally` below\n // can't release a second time.\n release?.(this.actualTokensFor(usage));\n release = undefined;\n\n // Raw and unvalidated on purpose. Extraction (including `.trim()`,\n // which throws on a non-string `content`) happens inside\n // `finalizeResponse`'s try/catch, so a malformed response still gets\n // normalized and its usage failure reported.\n const rawContent = response.choices?.[0]?.message?.content;\n const wireToolCalls = response.choices?.[0]?.message?.tool_calls;\n\n return this.finalizeResponse(\n rawContent,\n wireToolCalls,\n params,\n useJson,\n model,\n usage,\n requestId,\n attempt,\n );\n } finally {\n release?.();\n }\n }\n\n /** Applies `redact` (if configured); otherwise returns `text` unchanged. */\n private redactText(text: string): string {\n return this.redact ? this.redact(text) : text;\n }\n\n /**\n * Applies `redact` (if configured) to whatever the debug log is about\n * to show: real content when there is any, otherwise the tool-call\n * placeholder, which carries no user data and passes through\n * `redact` unchanged in practice but is included for a caller whose\n * `redact` does something structural (e.g. adding a marker) rather\n * than just scrubbing PII.\n */\n private redactedOutput(\n content: string | undefined,\n wireToolCalls: WireToolCall[] | undefined,\n ): string {\n return this.redactText(content ?? `[${wireToolCalls?.length ?? 0} tool call(s)]`);\n }\n\n /**\n * Shapes a fully-arrived response (content and/or tool_calls, already\n * extracted from the provider's payload) into `T` or a\n * `CallWithToolsResult<T>`. Reused by the streaming path once it has\n * buffered the full text/tool-call deltas, so there's no separate\n * parsing/validation logic for streaming.\n *\n * Normalizes and reports usage failure on error itself, so every caller\n * gets identical error handling without duplicating it.\n */\n private finalizeResponse<T>(\n rawContent: string | null | undefined,\n wireToolCalls: WireToolCall[] | undefined,\n params: CallParams<T>,\n useJson: boolean,\n model: string,\n usage: TokenUsage | undefined,\n requestId: string,\n attempt: number,\n ): T | CallWithToolsResult<T> {\n try {\n // `.trim()` runs inside this try: a malformed response shape\n // (e.g. a non-string `content`) throws here and is normalized and\n // reported like any other post-response failure.\n const content = rawContent?.trim();\n\n if (!content && !wireToolCalls?.length) {\n throw new LLMError('Empty LLM response', 'api', { code: 'empty_response' });\n }\n\n this.logger.debug(\n `[VernLLM:${requestId}] output:\\n${this.redactedOutput(content, wireToolCalls).slice(0, 800)}`,\n );\n\n if (wireToolCalls?.length) {\n if (!params.tools) {\n // Same class of problem as the other tool-contract codes below:\n // a provider contract violation, not an HTTP failure, so this is\n // `type: 'validation'` rather than `'api'`. Byte-for-byte\n // identical on retry, so not retryable.\n throw new LLMError(\n 'Provider returned tool_calls but no `tools` were sent with this call.',\n 'validation',\n { code: 'unexpected_tool_calls' },\n );\n }\n\n if (params.toolChoice === 'none') {\n // `toolChoice: 'none'` is what lets `call()`'s type narrow to\n // `ContentResult<T>` (see `ToolsDisabledCallParams`). A\n // nonconforming provider/adapter returning tool_calls anyway\n // would silently break that guarantee for the caller, so this\n // is treated as a hard API-contract violation rather than\n // passed through as a normal tool_calls result. The request\n // itself is byte-for-byte identical on retry, so this repeats\n // deterministically like the other tool-contract failures\n // below: not retryable, and not the provider being unhealthy.\n throw new LLMError(\n \"Provider returned tool_calls despite toolChoice: 'none'.\",\n 'validation',\n {\n code: 'tool_choice_none_violated',\n },\n );\n }\n\n const toolCalls = parseWireToolCalls(wireToolCalls);\n\n this.validateToolCallArguments(toolCalls, params.tools);\n this.breaker?.recordSuccess(model);\n this.reportUsage(usage);\n\n return { type: 'tool_calls', toolCalls, ...(content ? { content } : {}) };\n }\n\n // No tool_calls here, so content must be present.\n const textContent = content ?? '';\n\n if (!useJson) {\n this.breaker?.recordSuccess(model);\n this.reportUsage(usage);\n\n return params.tools ? { type: 'content', content: textContent as T } : (textContent as T);\n }\n\n const result = this.parseAndValidate<T>(textContent, params.schema);\n this.breaker?.recordSuccess(model);\n this.reportUsage(usage);\n\n return params.tools ? { type: 'content', content: result } : result;\n } catch (error) {\n // Normalized first so onUsageFailure always gets a real LLMError.\n // Also covers aborted signals: normalizeError returns type\n // 'aborted' in that case.\n const normalized = normalizeError(error, params.signal);\n\n if (usage && normalized.type !== 'aborted') {\n this.reportUsageFailure(usage, normalized, attempt);\n }\n\n throw normalized;\n }\n }\n\n /**\n * Opens a stream for a single attempt: builds the request exactly like\n * `executeCall`, then requires `createStream` on the client (a clear\n * `validation` error if the adapter doesn't support it). The timeout\n * wraps stream construction and the first `.next()` together, not just\n * construction: calling an `async function*` returns an iterator\n * synchronously without running its body until `.next()` is first\n * invoked, so timing only construction would time an operation that's\n * always instant, not the actual connection. Both are folded into a\n * single `withTimeout` so the same abort signal reaches whatever the\n * adapter's `createStream` uses internally for its first network\n * round-trip.\n *\n * Circuit-breaker success is recorded once the stream fully completes,\n * not on the first chunk arriving, so a connection that opens but then\n * dies mid-stream isn't masked as a success (see `buildStreamResult`).\n */\n private async executeStreamCall<T>(\n params: CallParams<T>,\n requestId: string,\n attempt: number,\n ): Promise<{\n chunks: AsyncIterable<StreamChunk>;\n finalResult: Promise<T | CallWithToolsResult<T>>;\n }> {\n const { useJson, model, request } = this.requestBuilder.build(params);\n\n const completions = this.client.chat.completions;\n\n if (!completions.createStream) {\n throw new LLMError(\n 'stream: true requires a client/adapter with createStream',\n 'invalid_params',\n {\n code: 'unsupported_capability',\n issues: { capability: 'createStream' },\n },\n );\n }\n\n const createStream = completions.createStream.bind(completions);\n\n // A stream holds a real connection for its whole life, so its\n // capacity is released on completion (in `buildStreamResult`), not\n // once opening succeeds.\n let release: ((actualTokens?: number) => void) | undefined;\n\n if (this.limiter) {\n const acquired = await this.limiter.acquire(this.limiter.estimate(request), params.signal);\n release = acquired.release;\n\n if (acquired.waitedMs > 0) {\n this.reportEvent({\n kind: 'rate_limited',\n requestId,\n provider: this.providerName,\n model,\n waitedMs: acquired.waitedMs,\n reason: acquired.reason ?? 'rpm',\n });\n }\n }\n\n // One controller for the entire stream, not just opening it. Adapters\n // already thread this signal into their transport for the life of the\n // request (that's how user-initiated cancellation works today), so\n // reusing it for the idle timeout means the same abort() call that\n // fires when the stream goes idle mid-way also tears down the\n // underlying connection, instead of only rejecting VernLLM's own\n // promise while the transport stays open.\n const streamController = new AbortController();\n const combinedExternal = params.signal\n ? AbortSignal.any([params.signal, streamController.signal])\n : streamController.signal;\n\n try {\n const { iterator, first } = await withTimeout(\n async (attemptSignal) => {\n const streamIterator = createStream(request, { signal: attemptSignal })[\n Symbol.asyncIterator\n ]();\n const firstResult = await streamIterator.next();\n\n return { iterator: streamIterator, first: firstResult };\n },\n this.timeoutMs,\n combinedExternal,\n );\n\n // An immediately-exhausted stream (no chunks at all) is the streaming\n // equivalent of `executeCall`'s empty-response check: surface the same\n // `LLMError('Empty LLM response', 'api')` so retry behaves identically\n // whether the empty result came from a non-streaming or streaming\n // attempt.\n if (first.done) {\n throw new LLMError('Empty LLM response', 'api');\n }\n\n // Snapshotted before the closures below are created, since `release`\n // (the outer variable) is reassigned to undefined right after this\n // call to hand ownership off. The callbacks only run later, once the\n // stream completes, so closing over the mutable variable itself\n // would see that later `undefined` instead of the value being\n // handed off.\n const releaseAtOpen = release;\n\n const result = buildStreamResult(iterator, first, {\n requestId,\n model,\n providerName: this.providerName,\n isFallback: this.isFallback,\n chunkIdleTimeoutMs: params.chunkIdleTimeoutMs ?? this.chunkIdleTimeoutMs,\n streamController,\n logger: this.logger,\n signal: params.signal,\n onStreamSuccess: (usage) => {\n this.breaker?.recordSuccess(model);\n releaseAtOpen?.(this.actualTokensFor(usage));\n },\n onStreamFailure: (normalized, usage) => {\n // Idle timeout is the one mid-stream failure that trips the\n // breaker: otherwise a provider that hangs after one chunk\n // would always record a success and never open it.\n if (normalized.type === 'timeout') {\n this.breaker?.recordFailure(model);\n }\n\n if (usage && normalized.type !== 'aborted') {\n this.reportUsageFailure(usage, normalized, attempt, true);\n }\n\n releaseAtOpen?.(this.actualTokensFor(usage));\n },\n finalize: (textAcc, wireToolCalls, usage) =>\n this.finalizeResponse(\n textAcc,\n wireToolCalls,\n params,\n useJson,\n model,\n usage,\n requestId,\n attempt,\n ),\n });\n\n // Ownership of `release` passes to `buildStreamResult` from here.\n release = undefined;\n\n return result;\n } finally {\n // Only reached if opening the stream itself threw; a successful\n // open hands `release` off above and leaves this a no-op.\n release?.();\n }\n }\n\n /**\n * Checks every `ToolCall` against the `tools` that were offered, catching\n * a hallucinated tool name and a duplicate call id before either reaches\n * the application's dispatch table, then runs each tool's\n * `argumentsSchema`, if present.\n *\n * Contract failures (unknown name, duplicate id) are collected across\n * every call and thrown together as one `type: 'validation'` error with\n * `issues: ToolIssue[]`, since retrying a request that already has these\n * errors cannot help (excluded from retry by `type`) and a caller fixing\n * them wants to see every one, not just the first. Schema failures keep\n * the original single-error, `type: 'validation'` shape rather than being\n * folded into the aggregate, since they're a distinct failure kind from\n * the contract failures above.\n */\n private validateToolCallArguments(\n toolCalls: { id: string; name: string; arguments: unknown }[],\n tools: NonNullable<CallParams<unknown>['tools']>,\n ): void {\n const known = new Map(tools.map((t) => [t.name, t]));\n const seenIds = new Set<string>();\n const toolIssues: ToolIssue[] = [];\n\n for (const call of toolCalls) {\n if (seenIds.has(call.id)) {\n toolIssues.push({ name: call.name, toolCallId: call.id, code: 'duplicate_tool_call_id' });\n }\n seenIds.add(call.id);\n\n if (!known.has(call.name)) {\n toolIssues.push({ name: call.name, toolCallId: call.id, code: 'unknown_tool' });\n }\n }\n\n if (toolIssues.length > 0) {\n const unknownTool = toolIssues.find((i) => i.code === 'unknown_tool');\n const primary = unknownTool\n ? `Model requested tool \"${unknownTool.name}\", which was not in the tools offered ([${[...known.keys()].join(', ')}]).`\n : `Duplicate tool call id \"${toolIssues[0]!.toolCallId}\" in the model's response.`;\n\n // Most responses hit exactly one issue. When there's more than one,\n // say so, since toolCalls[0]'s problem alone would otherwise read as\n // the whole story.\n const message =\n toolIssues.length > 1\n ? `${primary} (${toolIssues.length} tool call issues total, see error.issues.)`\n : primary;\n\n throw new LLMError(message, 'validation', {\n code: unknownTool ? 'unknown_tool' : 'duplicate_tool_call_id',\n issues: toolIssues,\n });\n }\n\n for (const call of toolCalls) {\n const definition = known.get(call.name);\n\n if (!definition?.argumentsSchema) continue;\n\n const result = definition.argumentsSchema.safeParse(call.arguments);\n\n if (!result.success) {\n throw new LLMError(\n `Arguments for tool call \"${call.name}\" failed validation`,\n 'validation',\n {\n issues: result.error,\n },\n );\n }\n }\n }\n\n /**\n * Runs `fn`, retrying with backoff according to `shouldRetry`. When\n * `attempts` is given, every failed attempt that is actually followed by\n * a retry is recorded, in order. This mirrors `LLMError.attempts`'s\n * contract: every attempt made before this error was thrown. The\n * terminal failure is never pushed since it isn't a prior attempt, it\n * is the error being thrown. `attempts` stays empty when nothing was\n * retried, so no separate bookkeeping is needed at the call sites.\n * Each failure is recorded as a snapshot (`LLMError.toSnapshot()`),\n * not the live `LLMError`, per `RetryAttempt`'s contract.\n */\n private async retryWithBackoff<T>(\n fn: (attempt: number) => Promise<T>,\n requestId: string,\n model: string,\n signal?: AbortSignal,\n onAttempt?: () => void,\n attempts?: RetryAttempt[],\n ): Promise<T> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n if (attempt > 0) {\n await this.recoverDelay(requestId, model, attempt, lastError, signal);\n }\n\n onAttempt?.();\n return await fn(attempt);\n } catch (error) {\n lastError = error;\n\n const willRetry = attempt < this.maxRetries && this.shouldRetry(error, signal);\n if (!willRetry) break;\n\n attempts?.push({ index: attempt, error: normalizeError(error, signal).toSnapshot() });\n }\n }\n\n throw lastError;\n }\n\n /**\n * Pulls `TokenUsage` out of a raw response, if the provider reported it.\n * Extraction doesn't depend on what happens to the response afterward, so\n * a malformed body can still yield usage if the provider's usage block\n * itself came through intact.\n */\n private extractUsage(\n response: Awaited<ReturnType<LLMClient['chat']['completions']['create']>>,\n requestId: string,\n model: string,\n ): TokenUsage | undefined {\n if (!response.usage) return undefined;\n\n return {\n promptTokens: response.usage.prompt_tokens ?? 0,\n completionTokens: response.usage.completion_tokens ?? 0,\n totalTokens: response.usage.total_tokens ?? 0,\n requestId,\n model,\n provider: this.providerName,\n usedFallback: this.isFallback,\n };\n }\n\n /**\n * The token count to reconcile the rate limiter against for a finished\n * attempt: `totalTokens` when reported, otherwise the sum of prompt and\n * completion tokens, matching `reportUsageFailure`'s own fallback below\n * for a hand-rolled client that reports the parts but omits the total.\n */\n private actualTokensFor(usage: TokenUsage | undefined): number | undefined {\n if (!usage) return undefined;\n return usage.totalTokens || usage.promptTokens + usage.completionTokens;\n }\n\n /** Reports token usage for a successful call, swallowing and logging any error `onUsage` throws. */\n private reportUsage(usage: TokenUsage | undefined): void {\n if (!usage || !this.onUsage) return;\n\n try {\n this.onUsage(usage);\n } catch (error) {\n this.logger.error('[VernLLM] onUsage failed', {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n }\n\n /**\n * Reports token usage spent on an attempt that then failed, so it isn't\n * dropped alongside the error. Covers any error thrown after usage\n * extraction, since all of them happen only after a response (real\n * spend) already arrived. Swallows and logs any error `onUsageFailure`\n * itself throws.\n */\n private reportUsageFailure(\n usage: TokenUsage,\n error: LLMError,\n attempt: number,\n terminal = false,\n ): void {\n // Falls back to promptTokens + completionTokens if totalTokens is 0\n // (e.g. a hand-rolled client that omits the total), so the log\n // doesn't understate real spend.\n const displayTokens = usage.totalTokens || usage.promptTokens + usage.completionTokens;\n\n // A mid-stream failure is terminal for that call (no further attempts\n // for this stream), unlike a stream-open failure where attempt N+1 may\n // still follow. Label them differently so the log doesn't imply a\n // retry that isn't coming.\n const attemptText = terminal\n ? 'mid-stream failure (terminal, no further attempts)'\n : `attempt ${attempt + 1}/${this.maxRetries + 1}`;\n\n this.logger.warn(\n `[VernLLM:${usage.requestId}] usage failure, ${attemptText}: ` +\n `type=${error.type} tokens=${displayTokens}`,\n );\n\n if (!this.onUsageFailure) return;\n\n try {\n this.onUsageFailure(usage, error);\n } catch (hookError) {\n this.logger.error('[VernLLM] onUsageFailure failed', {\n message: hookError instanceof Error ? hookError.message : 'unknown',\n });\n }\n }\n\n /** Parses response content as JSON and validates it against `schema` when supplied. */\n private parseAndValidate<T>(content: string, schema?: CallParams<T>['schema']): T {\n let parsed: unknown;\n\n try {\n parsed = this.parseJson(content);\n } catch {\n throw new LLMError('Invalid JSON response', 'parse');\n }\n\n if (parsed === null || parsed === undefined) {\n throw new LLMError('Invalid JSON response', 'parse');\n }\n\n if (!schema) return parsed as T;\n\n const result = schema.safeParse(parsed);\n\n if (!result.success) {\n throw new LLMError('Schema validation failed', 'validation', { issues: result.error });\n }\n\n return result.data;\n }\n\n /**\n * Waits out the backoff delay for a retry attempt, honoring a\n * Retry-After header on the failed attempt's error when present.\n * Both Retry-After and plain exponential backoff are capped at the same\n * max delay (see `DEFAULT_MAX_DELAY_MS` in `retry.utils.ts`).\n */\n private async recoverDelay(\n requestId: string,\n model: string,\n attempt: number,\n error: unknown,\n signal?: AbortSignal,\n ) {\n const retryAfterMs = extractRetryAfterMs(error);\n const delay = retryAfterMs ?? getBackoffDelay(this.baseDelayMs, attempt);\n const retryAfterHonored = retryAfterMs !== undefined;\n\n this.logger.warn(\n `[VernLLM:${requestId}] recovery attempt ${attempt}/${this.maxRetries}, waiting ${Math.ceil(delay)}ms` +\n (retryAfterHonored ? ' (honoring Retry-After)' : ''),\n );\n\n this.reportEvent({\n kind: 'retry',\n requestId,\n provider: this.providerName,\n model,\n attempt,\n maxRetries: this.maxRetries,\n delayMs: delay,\n retryAfterHonored,\n error: normalizeError(error, signal),\n });\n\n await waitForRetry(delay, signal);\n }\n\n /** Decides whether a failed attempt is worth retrying. */\n private shouldRetry(error: unknown, signal?: AbortSignal): boolean {\n if (signal?.aborted) return false;\n\n // `LLMError.retryable` already covers the deterministic cases: parse/\n // validation/invalid_params/aborted types, the tool contract codes,\n // and the local rate-limit codes. Only `nonRetryableStatus`, specific\n // to this call, isn't part of that general-purpose property.\n if (error instanceof LLMError && !error.retryable) return false;\n\n const status = extractStatus(error);\n\n return !(status !== undefined && this.nonRetryableStatus.includes(status));\n }\n\n /**\n * Decides whether a failed attempt should count toward the circuit\n * breaker's failure threshold. A model hallucinating a tool name,\n * reusing a call id, or a provider ignoring `toolChoice: 'none'` isn't\n * the provider being unhealthy, it's a model/provider response defect\n * that will very likely recur regardless of provider health, so it\n * shouldn't push a healthy provider's circuit toward opening. Same for\n * a caller-input bug or a local rate-limit rejection: neither ever\n * reached the provider at all. This is exactly what `LLMError.retryable`\n * already excludes, so this defers to it directly.\n */\n private countsTowardBreaker(error: LLMError): boolean {\n return error.retryable;\n }\n}\n","import type { Logger } from '../logger.js';\n\n/**\n * Wraps a `Logger` so a throwing implementation can never break the call\n * it's trying to describe. `logger` is user-supplied (`VernLLMOptions.logger`),\n * so a custom logger that ships to a file, Datadog, etc. can throw for\n * reasons unrelated to VernLLM. Wrap once at construction so every\n * downstream `this.logger.warn(...)` call stays as-is and is safe by\n * construction, instead of guarding each call site individually.\n */\nexport function createSafeLogger(logger: Logger): Logger {\n return {\n debug: safe(logger, 'debug'),\n warn: safe(logger, 'warn'),\n error: safe(logger, 'error'),\n };\n}\n\nfunction safe<M extends 'debug' | 'warn' | 'error'>(logger: Logger, method: M): Logger[M] {\n const fn = (logger[method] as (...args: Parameters<Logger[M]>) => unknown).bind(logger);\n\n return ((...args: Parameters<Logger[M]>) => {\n try {\n swallowRejection(fn(...args));\n } catch {\n // a broken logger must never break the call it's describing\n }\n }) as Logger[M];\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return typeof (value as PromiseLike<unknown>)?.then === 'function';\n}\n\nfunction swallowRejection(result: unknown): void {\n if (isPromiseLike(result)) {\n Promise.resolve(result).catch(() => {\n // a broken logger must never break the call it's describing\n });\n }\n}\n","export interface Logger {\n debug(message: string): void;\n warn(message: string): void;\n error(message: string, meta?: Record<string, unknown>): void;\n}\n\n/**\n * Default logger. `debug` is gated by the `debug` option on VernLLM\n * warn/error always fire since they indicate real problems (retries, cache failures)\n */\nexport class ConsoleLogger implements Logger {\n constructor(private debugEnabled: boolean) {}\n\n debug(message: string): void {\n if (this.debugEnabled) console.debug(message);\n }\n\n warn(message: string): void {\n console.warn(message);\n }\n\n error(message: string, meta?: Record<string, unknown>): void {\n console.error(message, meta ?? '');\n }\n}\n","import { LLMError } from './types/errors.js';\n\nimport type { LLMClient } from './types/client.js';\n\n/** The request shape sent to `LLMClient['chat']['completions']['create']`, used for token estimation. */\nexport type WireRequest = Parameters<LLMClient['chat']['completions']['create']>[0];\n\n/** Which configured bucket is currently blocking a call. */\nexport type RateLimitReason = 'concurrency' | 'rpm' | 'tpm';\n\nexport interface RateLimitOptions {\n /** Max requests per minute. Omit for unlimited. */\n requestsPerMinute?: number;\n /**\n * Max tokens per minute. Enforced against a pre-flight estimate, then\n * reconciled against reported usage once the call completes. Omit for\n * unlimited.\n */\n tokensPerMinute?: number;\n /** Max requests in flight at once. Default 0, meaning unlimited. */\n maxConcurrent?: number;\n /**\n * Max time a call may sit queued waiting for capacity, in ms. Exceeding\n * it throws rather than hanging forever. Default 30000. Pass 0 to wait\n * indefinitely.\n */\n maxQueueMs?: number;\n /** Max queued calls before new ones reject immediately instead of queueing. Default 0, unbounded. */\n maxQueueSize?: number;\n /**\n * Pre-flight token estimate for `tokensPerMinute`. Defaults to a\n * chars/4 heuristic over message content plus `max_tokens`.\n */\n estimateTokens?: (request: WireRequest) => number;\n}\n\nexport interface RateLimitAcquireResult {\n /**\n * Releases the concurrency slot this attempt held and reconciles the\n * token bucket against real usage, when `actualTokens` is supplied.\n * Idempotent: only the first call does anything. Must run in a\n * `finally` block so a slot is never leaked on a failed attempt.\n */\n release: (actualTokens?: number) => void;\n /** How long this attempt waited in queue before capacity was available. */\n waitedMs: number;\n /** Which bucket was blocking this attempt just before it cleared, if any wait happened. */\n reason?: RateLimitReason;\n}\n\n/** Default `estimateTokens`: chars/4 over every message's content, plus the requested `max_tokens`. */\nexport function defaultEstimateTokens(request: WireRequest): number {\n const messagesChars = request.messages.reduce((sum, message) => {\n const content = (message as { content?: unknown }).content;\n\n if (typeof content === 'string') return sum + content.length;\n if (content === undefined || content === null) return sum;\n\n try {\n return sum + JSON.stringify(content).length;\n } catch {\n return sum;\n }\n }, 0);\n\n return Math.ceil(messagesChars / 4) + (request.max_tokens ?? 0);\n}\n\n/**\n * A capacity that refills continuously. Used for requests per minute and\n * tokens per minute, where `refillPerMs` is `capacity / 60000`, and for\n * concurrency, where `refillPerMs` is 0 and every release calls\n * `give(1)` instead of relying on the clock.\n */\nclass TokenBucket {\n private available: number;\n private lastRefill = Date.now();\n\n constructor(\n private readonly capacity: number,\n private readonly refillPerMs: number,\n ) {\n this.available = capacity;\n }\n\n private refill(): void {\n if (this.refillPerMs === 0) return;\n\n const now = Date.now();\n const elapsedMs = now - this.lastRefill;\n\n // A backward clock adjustment (NTP correction, VM migration, etc.)\n // makes `elapsedMs` negative, which would otherwise reduce `available`\n // on the next line, rate-limiting harder than configured for no\n // real-world reason. Treat a negative elapsed time as no time having\n // passed instead: `available` just doesn't grow this tick, rather\n // than shrinking, and `lastRefill` still advances so a subsequent\n // forward-moving `now` measures from here, not from the skewed past.\n this.available = Math.min(\n this.capacity,\n this.available + Math.max(0, elapsedMs) * this.refillPerMs,\n );\n this.lastRefill = now;\n }\n\n /** Refills, then takes `amount` if available. Leaves the bucket untouched if it can't. */\n tryTake(amount: number): boolean {\n this.refill();\n\n if (this.available < amount) return false;\n\n this.available -= amount;\n return true;\n }\n\n /**\n * Refills, then reports how many ms until this bucket could supply\n * `amount`, assuming nothing else takes from it meanwhile. Returns 0 if\n * it already can, `Infinity` if it never will on its own (a\n * concurrency bucket, `refillPerMs === 0`, only frees via `give`).\n */\n msUntilAvailable(amount: number): number {\n this.refill();\n\n if (this.available >= amount) return 0;\n if (this.refillPerMs === 0) return Infinity;\n\n return (amount - this.available) / this.refillPerMs;\n }\n\n /**\n * Gives capacity back. Not floored at 0: a bad token-usage estimate can\n * push `available` negative, and it self-corrects on the next refill\n * rather than being clamped away immediately. Only ceilinged at\n * `capacity`, so a give can never overfill the bucket.\n */\n give(amount: number): void {\n this.available = Math.min(this.capacity, this.available + amount);\n }\n\n /** The bucket's ceiling, e.g. so a request that could never fit can fail fast instead of queueing forever. */\n getCapacity(): number {\n return this.capacity;\n }\n}\n\n/**\n * `setTimeout` silently clamps any delay above this (~24.8 days) instead\n * of erroring, so an uncapped delay derived from a very small\n * `requestsPerMinute`/`tokensPerMinute` could wrap around to firing\n * almost immediately instead of waiting. Mirrors the same guard in\n * `withTimeout`/`withChunkIdleTimeout`.\n */\nconst MAX_WAKE_DELAY_MS = 2_147_483_647;\n\n/** One caller waiting for capacity, queued FIFO. */\ninterface Waiter {\n estimatedTokens: number;\n enqueuedAt: number;\n /** Reason recorded the last time this waiter was checked and found still blocked. */\n lastReason?: RateLimitReason;\n resolve: (result: RateLimitAcquireResult) => void;\n reject: (error: unknown) => void;\n}\n\n/**\n * Per-target rate limiter. Up to three buckets (requests/min, tokens/min,\n * concurrency) behind one FIFO queue, so a large call isn't starved by a\n * stream of small ones. Any bucket omitted from `options` has infinite\n * capacity and never blocks.\n */\nexport class RateLimiter {\n private readonly requests?: TokenBucket;\n private readonly tokens?: TokenBucket;\n private readonly concurrency?: TokenBucket;\n\n private readonly maxQueueMs: number;\n private readonly maxQueueSize: number;\n private readonly estimateTokensFn: (request: WireRequest) => number;\n\n private readonly queue: Waiter[] = [];\n\n /**\n * A single scheduled re-check for the head of the queue when it's\n * blocked on a bucket that refills on its own clock (rpm/tpm), so a\n * queue that nobody calls `acquire`/`release` on again isn't stuck\n * forever waiting for an external trigger to re-drain it. Not needed\n * for a concurrency block, which only clears via `release`.\n */\n private wakeTimer?: ReturnType<typeof setTimeout>;\n\n constructor(options: RateLimitOptions) {\n if (options.requestsPerMinute) {\n this.requests = new TokenBucket(\n options.requestsPerMinute,\n options.requestsPerMinute / 60_000,\n );\n }\n\n if (options.tokensPerMinute) {\n this.tokens = new TokenBucket(options.tokensPerMinute, options.tokensPerMinute / 60_000);\n }\n\n if (options.maxConcurrent) {\n this.concurrency = new TokenBucket(options.maxConcurrent, 0);\n }\n\n this.maxQueueMs = options.maxQueueMs ?? 30_000;\n this.maxQueueSize = options.maxQueueSize ?? 0;\n this.estimateTokensFn = options.estimateTokens ?? defaultEstimateTokens;\n }\n\n /** Pre-flight token estimate for a request, per the configured (or default) heuristic. */\n estimate(request: WireRequest): number {\n return this.estimateTokensFn(request);\n }\n\n /**\n * Waits for capacity in every configured bucket, then takes from each.\n * The returned `release` gives the concurrency slot back and reconciles\n * the token bucket against real usage; it must run in a `finally` block.\n */\n async acquire(estimatedTokens: number, signal?: AbortSignal): Promise<RateLimitAcquireResult> {\n if (signal?.aborted) {\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n // Guards `estimatedTokens` even on this directly-exported entry point\n // (not just the `VernLLM.executeCall`/`executeStreamCall` call sites):\n // an unchecked NaN or negative value would poison a bucket's\n // `available` permanently, since `NaN < amount` is always false and\n // would make `tryTake` wrongly report success forever after.\n if (!Number.isFinite(estimatedTokens) || estimatedTokens < 0) {\n throw new LLMError(\n `estimatedTokens must be a finite, non-negative number, got ${String(estimatedTokens)}`,\n 'invalid_params',\n );\n }\n\n // A request over the bucket's own ceiling can never be satisfied by\n // any amount of waiting, refill included, so failing fast here also\n // avoids permanently stalling every waiter queued behind it in FIFO.\n if (this.tokens && estimatedTokens > this.tokens.getCapacity()) {\n throw new LLMError(\n `estimatedTokens (${estimatedTokens}) exceeds the configured tokensPerMinute capacity (${this.tokens.getCapacity()}); this call could never acquire capacity.`,\n 'rate_limited',\n { code: 'rate_limit_capacity_exceeded' },\n );\n }\n\n // Fast path: nothing already queued, so try to go straight through\n // rather than paying queue bookkeeping for the common, uncontended case.\n if (this.queue.length === 0) {\n const attempt = this.tryAcquireBuckets(estimatedTokens);\n\n if (attempt.ok) {\n return { release: this.makeRelease(estimatedTokens), waitedMs: 0 };\n }\n\n // No maxQueueSize check needed here: the queue is empty (this\n // branch's own condition), so enqueueing this one waiter can never\n // exceed any maxQueueSize > 0. The check below only becomes\n // reachable once the queue is non-empty.\n return this.enqueue(estimatedTokens, attempt.reason, signal);\n }\n\n if (this.maxQueueSize > 0 && this.queue.length >= this.maxQueueSize) {\n throw this.queueFullError();\n }\n\n return this.enqueue(estimatedTokens, undefined, signal);\n }\n\n private queueFullError(): LLMError {\n return new LLMError('Rate limit queue is full', 'rate_limited', {\n code: 'rate_limit_queue_full',\n });\n }\n\n private enqueue(\n estimatedTokens: number,\n initialReason: RateLimitReason | undefined,\n signal?: AbortSignal,\n ): Promise<RateLimitAcquireResult> {\n return new Promise<RateLimitAcquireResult>((resolvePromise, rejectPromise) => {\n const waiter: Waiter = {\n estimatedTokens,\n enqueuedAt: Date.now(),\n lastReason: initialReason,\n resolve: (result) => {\n cleanup();\n resolvePromise(result);\n },\n reject: (error) => {\n cleanup();\n\n // The removed waiter may have been the head a wakeTimer was\n // scheduled around (or, for a concurrency block, the head with\n // no timer scheduled at all). Either way, re-drain immediately\n // so a successor with different requirements is evaluated now\n // instead of waiting on a stale timer or an unrelated\n // acquire/release call to trigger it.\n if (this.wakeTimer) {\n clearTimeout(this.wakeTimer);\n this.wakeTimer = undefined;\n }\n this.drain();\n\n rejectPromise(error);\n },\n };\n\n let queueTimer: ReturnType<typeof setTimeout> | undefined;\n\n const onAbort = () => {\n waiter.reject(new LLMError('LLM request aborted', 'aborted'));\n };\n\n const cleanup = () => {\n if (queueTimer) clearTimeout(queueTimer);\n signal?.removeEventListener('abort', onAbort);\n\n const index = this.queue.indexOf(waiter);\n if (index !== -1) this.queue.splice(index, 1);\n };\n\n if (this.maxQueueMs > 0) {\n queueTimer = setTimeout(() => {\n waiter.reject(\n new LLMError(\n 'Rate limit queue timed out before capacity was available',\n 'rate_limited',\n {\n code: 'rate_limit_queue_timeout',\n },\n ),\n );\n }, this.maxQueueMs);\n }\n\n signal?.addEventListener('abort', onAbort, { once: true });\n\n this.queue.push(waiter);\n this.drain();\n });\n }\n\n /**\n * Checks and takes from every configured bucket as one atomic unit: if\n * any bucket lacks capacity, whatever was already taken from the\n * earlier ones in this attempt is rolled back before reporting which\n * bucket blocked.\n */\n private tryAcquireBuckets(\n estimatedTokens: number,\n ): { ok: true } | { ok: false; reason: RateLimitReason } {\n const taken: Array<{ bucket: TokenBucket; amount: number }> = [];\n\n const take = (bucket: TokenBucket | undefined, amount: number) => {\n if (!bucket) return true;\n if (!bucket.tryTake(amount)) return false;\n\n taken.push({ bucket, amount });\n return true;\n };\n\n if (!take(this.concurrency, 1)) {\n return { ok: false, reason: 'concurrency' };\n }\n\n if (!take(this.requests, 1)) {\n for (const entry of taken) entry.bucket.give(entry.amount);\n return { ok: false, reason: 'rpm' };\n }\n\n if (!take(this.tokens, estimatedTokens)) {\n for (const entry of taken) entry.bucket.give(entry.amount);\n return { ok: false, reason: 'tpm' };\n }\n\n return { ok: true };\n }\n\n /** Drains the queue head first. Stops at the first waiter that still can't proceed, so no one is starved out of turn. */\n private drain(): void {\n while (this.queue.length > 0) {\n const waiter = this.queue[0] as Waiter;\n const attempt = this.tryAcquireBuckets(waiter.estimatedTokens);\n\n if (!attempt.ok) {\n waiter.lastReason = attempt.reason;\n this.scheduleWake(attempt.reason, waiter.estimatedTokens);\n return;\n }\n\n const waitedMs = Date.now() - waiter.enqueuedAt;\n\n waiter.resolve({\n release: this.makeRelease(waiter.estimatedTokens),\n waitedMs,\n reason: waiter.lastReason,\n });\n }\n }\n\n /**\n * Schedules a one-shot re-check of the queue for whenever the bucket\n * that's currently blocking the head waiter should next have enough\n * capacity. A no-op for a concurrency block (only `release` can clear\n * that) or while a wake is already pending.\n */\n private scheduleWake(reason: RateLimitReason, estimatedTokens: number): void {\n if (this.wakeTimer) return;\n\n const ms =\n reason === 'rpm'\n ? this.requests?.msUntilAvailable(1)\n : reason === 'tpm'\n ? this.tokens?.msUntilAvailable(estimatedTokens)\n : undefined;\n\n if (ms === undefined || !Number.isFinite(ms)) return;\n\n // Capped, not just clamped-by-omission: `drain()` re-derives the\n // real remaining wait from live bucket state on every firing (it\n // doesn't trust the delay that got it there), so a wake that fires\n // early because the true wait exceeded the cap just re-schedules\n // correctly from where the bucket actually is, rather than looping\n // on a delay that never shrinks.\n const delay = Math.min(Math.max(1, Math.ceil(ms)), MAX_WAKE_DELAY_MS);\n\n this.wakeTimer = setTimeout(() => {\n this.wakeTimer = undefined;\n this.drain();\n }, delay);\n }\n\n /**\n * Builds the one-shot release closure for an acquired slot. Only the\n * concurrency bucket is given back on release; the requests-per-minute\n * bucket is a real spend that only recovers via its own refill, and the\n * tokens bucket is reconciled against `actualTokens` rather than fully\n * refunded, since real tokens really were spent.\n */\n private makeRelease(estimatedTokens: number): (actualTokens?: number) => void {\n let released = false;\n\n return (actualTokens?: number) => {\n if (released) return;\n released = true;\n\n this.concurrency?.give(1);\n\n // An invalid `actualTokens` (e.g. NaN from a malformed usage\n // report) must not reach `give`: `Math.min(capacity, available +\n // NaN)` is NaN, and a NaN `available` poisons every future\n // `tryTake` on that bucket (any comparison against NaN is false,\n // so it would look permanently under capacity and rate limiting\n // would silently stop happening). Falling back to no reconciliation\n // at least keeps the estimated debit, the safe direction to err.\n if (this.tokens && actualTokens !== undefined && Number.isFinite(actualTokens)) {\n this.tokens.give(estimatedTokens - actualTokens);\n }\n\n this.drain();\n };\n }\n}\n","import { randomUUID } from 'crypto';\n\nimport { CacheOrchestrator } from './internal/cache/cacheOrchestrator.js';\nimport { buildCircuitBreaker, makeEventReporter } from './internal/circuitBreaker.utils.js';\nimport { CallExecutor } from './internal/execution/callExecutor.js';\nimport { normalizeError } from './internal/execution/errors.utils.js';\nimport { withReservedUsage, withReservedUsageForStream } from './internal/execution/usage.utils.js';\nimport { createSafeLogger } from './internal/logger.utils.js';\nimport { ConsoleLogger, type Logger } from './logger.js';\nimport { RateLimiter } from './rateLimit.js';\nimport {\n InMemoryCacheAdapter,\n LLMError,\n FallbackExhaustedError,\n defaultFallbackOn,\n type CachedCallParams,\n type CachedStreamCallParams,\n type CachedStreamToolCallParams,\n type CachedToolCallParams,\n type CallParams,\n type CallWithToolsResult,\n type ContentResult,\n type FallbackAttempt,\n type FallbackOn,\n type FallbackTarget,\n type StreamCallResult,\n type TargetCircuitState,\n type CircuitTarget,\n type StreamEnabledCallParams,\n type ToolEnabledCallParams,\n type ToolsDisabledCallParams,\n type VernLLMEvent,\n type VernLLMOptions,\n} from './types/index.js';\n\nimport type { CircuitState } from './circuitBreaker.js';\nimport type { InternalCacheParams } from './internal/cache/cache.utils.js';\n\n/**\n * A resilient layer around an LLM chat completions client. This is VernLLM!\n *\n * Adds retry with backoff and jitter, per-attempt timeouts, an optional\n * circuit breaker, JSON parsing with optional schema validation, usage\n * tracking, and an optional response cache. All configurable, all opt-in\n * beyond sensible defaults.\n */\nexport class VernLLM {\n private readonly logger: Logger;\n\n /**\n * One `CallExecutor` per provider target: index 0 is the primary,\n * everything after it is a `fallback` target, in the order declared.\n * Each owns its own request building, retry/timeout, circuit breaker,\n * and rate limiter. `call()` walks this array in `runFallbackChain`,\n * moving to the next entry only when `fallbackOn` says to.\n */\n private readonly executors: CallExecutor[];\n\n /** Decides whether a failed target is followed by the next one or the chain stops. See `VernLLMOptions['fallbackOn']`. */\n private readonly fallbackOn: FallbackOn;\n\n /** Reports a `'fallback'` event when the chain moves to the next target. Shared `onEvent` plumbing, same as every executor's. */\n private readonly reportEvent: (event: VernLLMEvent) => void;\n\n /**\n * Owns cache key resolution, cache reads/writes, and in-flight\n * coalescing for `cachedCall()`. Independent of `executor`: it only\n * ever calls back into `this.call()` as an opaque function.\n */\n private readonly cacheOrchestrator: CacheOrchestrator;\n\n /**\n * @param options Client, model, and tunables. Defaults: `maxRetries` 1,\n * `timeoutMs` 25000, `baseDelayMs` 500, `defaultMaxTokens` 1000,\n * `defaultTemperature` 0.2, `cache` an in-memory adapter,\n * `nonRetryableStatus` `[400, 401, 403, 404, 422]`, `debug` false.\n */\n constructor(options: VernLLMOptions) {\n this.logger = createSafeLogger(options.logger ?? new ConsoleLogger(options.debug ?? false));\n\n const providerName = options.name ?? 'primary';\n\n this.cacheOrchestrator = new CacheOrchestrator(\n options.cache ?? new InMemoryCacheAdapter(),\n this.logger,\n );\n\n this.fallbackOn = options.fallbackOn ?? defaultFallbackOn;\n this.reportEvent = makeEventReporter(options.onEvent, this.logger);\n\n // The primary target's shared knobs, resolved once here rather than\n // inline in the retry-tunable default below, since fallback targets\n // that omit a field inherit this resolved value, not the raw\n // (possibly-undefined) option.\n const primaryDefaultTemperature =\n options.defaultTemperature === undefined ? 0.2 : options.defaultTemperature;\n\n // The primary, shaped like a `FallbackTarget` so it walks the same\n // build loop as every declared fallback target below. Its own\n // `circuitBreaker`/`rateLimit` are read directly off `options`\n // instead of this list, since only the primary carries them at the\n // top level (a `FallbackTarget`'s copies are genuinely independent,\n // never inherited, see `FallbackTarget`'s docs).\n const primaryTarget: FallbackTarget = {\n client: options.client,\n model: options.model,\n name: providerName,\n maxRetries: options.maxRetries,\n timeoutMs: options.timeoutMs,\n chunkIdleTimeoutMs: options.chunkIdleTimeoutMs,\n baseDelayMs: options.baseDelayMs,\n defaultMaxTokens: options.defaultMaxTokens,\n defaultTemperature: primaryDefaultTemperature,\n nonRetryableStatus: options.nonRetryableStatus,\n circuitBreaker: options.circuitBreaker,\n rateLimit: options.rateLimit,\n };\n\n const declaredFallbacks: FallbackTarget[] = Array.isArray(options.fallback)\n ? options.fallback\n : options.fallback\n ? [options.fallback]\n : [];\n\n const targets = [primaryTarget, ...declaredFallbacks];\n\n this.executors = targets.map((target, i) => {\n const isFallback = i > 0;\n // `-1` for the primary, matching `FallbackAttempt.index`.\n const name = target.name ?? (isFallback ? `fallback[${i - 1}]` : providerName);\n\n // Built before the executor: onStateChange fires from inside the\n // breaker itself, which the executor is merely handed a reference to.\n const breaker = buildCircuitBreaker(\n target.circuitBreaker,\n name,\n target.model,\n options.onEvent,\n this.logger,\n );\n\n return new CallExecutor(name, target.client, target.model, {\n maxRetries: target.maxRetries ?? options.maxRetries ?? 1,\n timeoutMs: target.timeoutMs ?? options.timeoutMs ?? 25_000,\n chunkIdleTimeoutMs: target.chunkIdleTimeoutMs ?? options.chunkIdleTimeoutMs ?? 30_000,\n baseDelayMs: target.baseDelayMs ?? options.baseDelayMs ?? 500,\n defaultMaxTokens: target.defaultMaxTokens ?? options.defaultMaxTokens ?? 1000,\n defaultTemperature:\n target.defaultTemperature === undefined\n ? primaryDefaultTemperature\n : target.defaultTemperature,\n nonRetryableStatus: target.nonRetryableStatus ??\n options.nonRetryableStatus ?? [400, 401, 403, 404, 422],\n parseJson: options.parseJson,\n logger: this.logger,\n redact: options.redact,\n onUsage: options.onUsage,\n onUsageFailure: options.onUsageFailure,\n onEvent: options.onEvent,\n breaker,\n limiter: target.rateLimit ? new RateLimiter(target.rateLimit) : undefined,\n isFallback,\n });\n });\n }\n\n /** Logs a failed refundUsage attempt via the configured logger. */\n private logRefundError(logMessage: string, error: unknown): void {\n this.logger.error(logMessage, {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n\n /**\n * Walks `this.executors` in order, running `attempt` against each until\n * one succeeds or every target has failed. `run` on a lone target\n * (no `fallback` configured) throws exactly what it throws today: the\n * loop's single iteration path is unchanged from pre-fallback behavior.\n *\n * For streaming, `attempt` is `executor.runStream`, whose own retries\n * only cover *opening* the stream (see `CallExecutor.runStream`). A\n * mid-stream failure surfaces through `finalResult` after this function\n * has already returned, so it's never seen here and never falls over,\n * per the streaming limitation: splicing a second model's output into a\n * response the consumer has already partially rendered would corrupt\n * it.\n */\n private async runFallbackChain<R>(\n params: Pick<CallParams<unknown>, 'model' | 'signal'>,\n requestId: string,\n attempt: (executor: CallExecutor, onAttempt: () => void) => Promise<R>,\n skipBreakerCheckForFirst = false,\n ): Promise<{ result: R; executor: CallExecutor; index: number; attemptCount: number }> {\n const attempts: FallbackAttempt[] = [];\n\n for (let i = 0; i < this.executors.length; i++) {\n const executor = this.executors[i]!;\n const startedAt = Date.now();\n let attemptCount = 0;\n\n try {\n // Already checked once, before usage was reserved, when this is\n // the sole target (see `call()`). `assertClosed` claims a\n // half-open trial slot as a side effect on a non-throwing call,\n // so it must run exactly once per logical call: checking it\n // again here for the same executor could either falsely see\n // \"trial already in flight\" (from the check that just claimed\n // it) or double-claim a slot no concurrent caller actually has.\n if (!(i === 0 && skipBreakerCheckForFirst)) {\n executor.assertBreakerClosed(params.model);\n }\n\n const result = await attempt(executor, () => {\n attemptCount += 1;\n });\n return { result, executor, index: i, attemptCount };\n } catch (error) {\n const normalized = normalizeError(error, params.signal);\n\n attempts.push({\n index: i - 1,\n provider: executor.providerName,\n model: params.model ?? executor.model,\n // `.toSnapshot()`: this target's own `attempts` (from its own\n // retries, already snapshots per `CallExecutor`) come along\n // for free since `toSnapshot()` copies them as-is.\n error: normalized.toSnapshot(),\n });\n\n const isLast = i === this.executors.length - 1;\n // Always consult fallbackOn, including on the last target, so it\n // sees every failure and callers who log or count from inside it\n // get a complete picture. The chain still stops once the last\n // target fails regardless of what fallbackOn returns: there is no\n // next executor to fall over to.\n const policyDecision = this.fallbackOn(normalized, { isLastTarget: isLast });\n const decision = isLast ? 'stop' : policyDecision;\n\n if (decision === 'stop') {\n // A lone target (or a chain that stopped on its first failure)\n // throws its own error, unchanged from pre-fallback behavior.\n throw attempts.length > 1 ? new FallbackExhaustedError(attempts) : normalized;\n }\n\n const next = this.executors[i + 1]!;\n\n this.reportEvent({\n kind: 'fallback',\n requestId,\n from: executor.providerName,\n to: next.providerName,\n fromIndex: i - 1,\n toIndex: i,\n error: normalized,\n elapsedMs: Date.now() - startedAt,\n });\n }\n }\n\n // Unreachable: the loop above always either returns or throws before\n // running out of targets (the last iteration's `isLast` forces a\n // throw). Kept only to satisfy the return type.\n throw new LLMError('No provider targets configured', 'invalid_params');\n }\n\n /**\n * Makes a single logical LLM call, retrying on failure per the configured\n * policy. Fails fast if the breaker is open or the signal is already\n * aborted. Rejects with a normalized LLMError on exhausted retries.\n *\n * When `tools` is set, returns a `CallWithToolsResult<T>` instead of `T`:\n * `{ type: 'content', content }` or `{ type: 'tool_calls', toolCalls,\n * content? }`. VernLLM never executes tools; run them yourself and\n * continue via `history` (see `ConversationTurn`). Mutually exclusive\n * with `jsonSchema`/`schema`.\n *\n * TypeScript only picks the tools-aware overload when `tools` is\n * statically present on `params`. If set conditionally on a plain\n * `CallParams<T>`, use `isToolCallResult()` to check the shape at\n * runtime instead. See the Tool Calling docs for details.\n *\n * The same static-vs-dynamic caveat applies to `stream`: TypeScript only\n * selects the streaming overload (returning `StreamCallResult<...>`) when\n * `stream: true` is statically present on `params`. A `stream` value set\n * conditionally on a plain `CallParams<T>` still resolves to `Promise<T>`\n * (or `Promise<CallWithToolsResult<T>>`) at the type level even though\n * the actual runtime result is the `{ chunks, finalResult }` streaming\n * shape whenever `stream` evaluates to `true`, callers doing this should\n * narrow/cast accordingly rather than relying on the static return type.\n *\n * @param params System/user content plus per-call overrides. See `CallParams`.\n * @returns Without `tools` or `stream`: the parsed response, or raw\n * string if `jsonMode` is false. With `tools`: a `CallWithToolsResult<T>`,\n * narrowed to `ContentResult<T>` when `toolChoice: 'none'` is set, since\n * the model is then structurally barred from returning a `tool_calls`\n * result. With `stream: true` (statically): a `{ chunks, finalResult }`\n * `StreamCallResult`, `finalResult` resolving to whichever of the above\n * shapes applies once the stream completes. See `StreamCallResult`.\n */\n async call<T = unknown>(\n params: StreamEnabledCallParams<T> & ToolsDisabledCallParams<T>,\n ): Promise<StreamCallResult<ContentResult<T>>>;\n\n async call<T = unknown>(\n params: StreamEnabledCallParams<T> & ToolEnabledCallParams<T>,\n ): Promise<StreamCallResult<CallWithToolsResult<T>>>;\n\n async call<T = unknown>(params: StreamEnabledCallParams<T>): Promise<StreamCallResult<T>>;\n\n async call<T = unknown>(params: ToolsDisabledCallParams<T>): Promise<ContentResult<T>>;\n\n async call<T = unknown>(params: ToolEnabledCallParams<T>): Promise<CallWithToolsResult<T>>;\n\n async call<T = unknown>(params: CallParams<T>): Promise<T>;\n\n async call<T = unknown>(\n params: CallParams<T>,\n ): Promise<T | CallWithToolsResult<T> | StreamCallResult<T | CallWithToolsResult<T>>> {\n if (params.signal?.aborted) {\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n const requestId = params.requestId ?? randomUUID();\n\n // A lone target (no `fallback` configured) keeps the exact pre-fallback\n // contract: the breaker is checked once, up front, before usage is\n // reserved, so a call that's definitely blocked never pays a\n // reserve-then-refund round trip. This can only be hoisted out of the\n // chain for the sole-target case: `assertBreakerClosed` claims a\n // half-open trial slot as a side effect, which must happen exactly\n // once per logical call, so with more than one target the check has\n // to stay inside `runFallbackChain`, where an open primary is just\n // another target failure that `fallbackOn` can fall over from.\n const soleTarget = this.executors.length === 1;\n\n if (soleTarget) {\n this.executors[0]!.assertBreakerClosed(params.model);\n }\n\n if (params.stream) {\n // Same breaker/logging treatment as non-streaming, applied around\n // opening the stream; mid-stream failures are handled separately\n // inside the executor and never fall over (see `runFallbackChain`).\n // Usage refund/report is deferred onto finalResult, since call()\n // must return { chunks, finalResult } before the real outcome is\n // known, which is also why `params.meta` isn't populated for\n // streaming calls.\n return withReservedUsageForStream(\n params,\n async () => {\n const { result } = await this.runFallbackChain(\n params,\n requestId,\n (executor, onAttempt) => executor.runStream(params, requestId, onAttempt),\n soleTarget,\n );\n return result;\n },\n params.signal,\n (logMessage, error) => this.logRefundError(logMessage, error),\n );\n }\n\n return withReservedUsage(\n params,\n false,\n async () => {\n const { result, executor, index, attemptCount } = await this.runFallbackChain(\n params,\n requestId,\n (target, onAttempt) => target.run(params, requestId, onAttempt),\n soleTarget,\n );\n\n if (params.meta) {\n params.meta.current = {\n provider: executor.providerName,\n model: params.model ?? executor.model,\n fallbackIndex: index - 1,\n usedFallback: index > 0,\n attempts: attemptCount,\n };\n }\n\n return result;\n },\n params.signal,\n (logMessage, error) => this.logRefundError(logMessage, error),\n );\n }\n\n /**\n * Thin delegator kept private on `VernLLM` (rather than only existing on\n * `CacheOrchestrator`) since it's the one caching primitive exercised\n * directly by white-box tests, independent of the public `cachedCall()`\n * surface.\n */\n private runCached<T>(params: InternalCacheParams<T>) {\n return this.cacheOrchestrator.runCached(params);\n }\n\n /**\n * Removes a cached response by key when the configured cache adapter\n * supports deletion. Cache invalidation is the caller's responsibility;\n * only the application knows when cached data is stale.\n *\n * @param key The raw cache key (resolved through the adapter's\n * `resolveKey`, if any, before deletion).\n */\n async deleteCache(key: string): Promise<void> {\n await this.cacheOrchestrator.deleteCache(key);\n }\n\n /**\n * Cache wrapper composing `call` + caching, so cached LLM calls\n * automatically get retry/timeout/circuit-breaker behavior. `reserveUsage`/\n * `refundUsage` are read from the top-level params only. Concurrent misses\n * for the same `cacheKey` share a single in-flight call, avoiding cache\n * stampedes. Supports `stream: true` and `tools` in any combination.\n *\n * When `call.tools` is set, this caches the whole `CallWithToolsResult`,\n * including `tool_calls` results, not just final answers. Whether\n * that's appropriate depends on the tool: caching \"the model decided to\n * call get_weather\" is usually fine to reuse briefly, but caching a\n * decision made under permissions or account state that can change\n * between calls is not. Use a short `ttl` or a separate `cacheKey` for\n * such tools if this distinction matters.\n *\n * There is no public way to cache an arbitrary non-LLM function through\n * `VernLLM`. This method always composes with `call()`. For\n * general-purpose caching unrelated to an LLM call, use a dedicated\n * caching library at the application level instead.\n *\n * @param params `cacheKey`, `ttl`, and optional\n * `reserveUsage`/`refundUsage`/`signal`, plus `call`, the `CallParams`\n * (optionally with `tools` and/or `stream`) to pass through to\n * `this.call(...)`. The top-level `signal` governs the cached operation\n * and its usage hooks only; to also abort the underlying provider\n * request, set `signal` inside `call`.\n * @returns The cached value on a hit, or the freshly-called result on a miss.\n */\n async cachedCall<T>(\n params: CachedStreamToolCallParams<T>,\n ): Promise<StreamCallResult<CallWithToolsResult<T>>>;\n\n async cachedCall<T>(params: CachedStreamCallParams<T>): Promise<StreamCallResult<T>>;\n\n async cachedCall<T>(params: CachedToolCallParams<T>): Promise<CallWithToolsResult<T>>;\n\n async cachedCall<T>(params: CachedCallParams<T>): Promise<T>;\n\n async cachedCall<T>(\n params:\n | CachedCallParams<T>\n | CachedToolCallParams<T>\n | CachedStreamCallParams<T>\n | CachedStreamToolCallParams<T>,\n ): Promise<T | CallWithToolsResult<T> | StreamCallResult<T | CallWithToolsResult<T>>> {\n const { call: callParams, ...cacheParams } = params;\n\n // `callParams`'s type no longer includes reserveUsage/refundUsage (see\n // CachedCallParams et al.): a well-typed caller can't reach this branch\n // at all, TypeScript rejects it at the call site instead. This check is\n // a defense-in-depth backstop for callers that bypass the type system\n // (plain JS, or an `as any` cast), and now throws instead of silently\n // ignoring the hooks and continuing: reserveUsage/refundUsage exist as\n // a cost-control safety mechanism, so silently dropping them fails\n // open, not safe, which a warning callers may never see doesn't fix.\n const restCallParams = callParams as CallParams<T>;\n\n if (restCallParams.reserveUsage || restCallParams.refundUsage) {\n throw new LLMError(\n '`reserveUsage`/`refundUsage` were set inside `call`, where cachedCall ignores them. Move them ' +\n 'to the top level of the cachedCall() params, alongside cacheKey/ttl, instead.',\n 'invalid_params',\n );\n }\n\n if (restCallParams.stream) {\n const streamParams = restCallParams as StreamEnabledCallParams<T>;\n\n return this.cacheOrchestrator.runCachedStream(\n {\n ...cacheParams,\n openStream: () => this.call(streamParams),\n },\n Boolean(restCallParams.tools),\n );\n }\n\n return this.runCached({\n ...cacheParams,\n fn: () => this.call(restCallParams),\n });\n }\n\n /**\n * @param target.index Which target to read. Defaults to the primary.\n * @param target.model Which model bucket to read, if the target isolates by model.\n * @returns The breaker state, or `undefined` if that target has no breaker.\n * @throws {RangeError} If `target.index` names no target. Lets a real\n * target with no breaker (`undefined`) stay distinguishable from a\n * target that doesn't exist.\n */\n getCircuitState(target?: CircuitTarget): CircuitState | undefined {\n const executor = this.resolveExecutor(target?.index ?? 0, 'getCircuitState');\n this.warnIfModelUnsupported(executor.isolateByModel, target?.model, 'getCircuitState');\n\n return executor.getCircuitState(target?.model ?? executor.model);\n }\n\n /**\n * @param model Which model bucket to read, for targets that isolate by model.\n * @returns Every target's state, in chain order.\n */\n getCircuitStates(model?: string): TargetCircuitState[] {\n return this.executors.map((executor, index) => ({\n provider: executor.providerName,\n index,\n isFallback: index > 0,\n isolateByModel: executor.isolateByModel,\n state: executor.getCircuitState(model ?? executor.model),\n }));\n }\n\n /**\n * Manually opens a target's breaker, e.g. to pull a provider out of\n * rotation ahead of known maintenance instead of waiting for it to fail.\n *\n * @param target.index Which target to open. Defaults to the primary.\n * @param target.model Which model bucket to open, if the target isolates by model.\n * @throws {RangeError} If `target.index` names no target.\n */\n openCircuit(target?: CircuitTarget): void {\n const executor = this.resolveExecutor(target?.index ?? 0, 'openCircuit');\n this.warnIfModelUnsupported(executor.isolateByModel, target?.model, 'openCircuit');\n executor.openCircuit(target?.model ?? executor.model);\n }\n\n /**\n * Manually closes a target's breaker, e.g. once a provider is confirmed\n * healthy again without waiting out the cooldown.\n *\n * @param target.index Which target to close. Defaults to the primary.\n * @param target.model Which model bucket to close, if the target isolates by model.\n * @throws {RangeError} If `target.index` names no target.\n */\n closeCircuit(target?: CircuitTarget): void {\n const executor = this.resolveExecutor(target?.index ?? 0, 'closeCircuit');\n this.warnIfModelUnsupported(executor.isolateByModel, target?.model, 'closeCircuit');\n executor.closeCircuit(target?.model ?? executor.model);\n }\n\n /** Resolves a target index so every circuit-breaker method agrees on what counts as valid. */\n private resolveExecutor(index: number, caller: string): CallExecutor {\n const executor = this.executors[index];\n\n if (!executor) {\n throw new RangeError(\n `${caller}: no target at index ${index} (chain has ${this.executors.length} target${this.executors.length === 1 ? '' : 's'})`,\n );\n }\n\n return executor;\n }\n\n /** Warns when `model` can't do anything on this target, so it's never silently ignored. */\n private warnIfModelUnsupported(\n isolateByModel: boolean,\n model: string | undefined,\n caller: string,\n ): void {\n if (model !== undefined && !isolateByModel) {\n this.logger.warn(\n `[VernLLM] ${caller}: \\`model: '${model}'\\` has no effect here. This target's circuitBreaker doesn't have isolateByModel on, so it only tracks one shared circuit regardless of \\`model\\`. Omit \\`model\\`, or set \\`circuitBreaker.isolateByModel: true\\` on this target if per-model tracking is what you want.`,\n );\n }\n }\n}\n","import { LLMError } from '../../types/errors.js';\n\n/**\n * Parses a Server-Sent-Events byte/text stream into the JSON payload of\n * each `data:` frame, in arrival order. Generic over transport: works with\n * anything that hands back progressively-arriving `Uint8Array` or `string`\n * chunks via async iteration: native `fetch`'s `response.body` (wrapped\n * to be iterable, see `webStreamToAsyncIterable` in `fetch.ts`), axios's\n * Node `Readable` (already async-iterable, no wrapping needed), etc, so\n * this framing layer doesn't care which transport produced the bytes.\n *\n * Follows the SSE spec's frame-delimiting rules closely enough for LLM\n * streaming responses: frames are separated by a blank line, each frame\n * may carry one or more `data:` lines (joined with `\\n` per spec when\n * there's more than one), `:`-prefixed lines are comments and ignored, and\n * other SSE fields (`event:`, `id:`, `retry:`) are ignored since VernLLM\n * only needs the payload. A frame whose data is exactly `[DONE]` (the\n * sentinel several providers, notably OpenAI, send to mark stream end)\n * ends iteration without yielding it.\n *\n * Line endings: `\\r\\n` and bare `\\r` (both legal per the SSE spec, alongside `\\n`) are normalized\n * to `\\n` before frame splitting. A `\\r` at the very end of the currently-buffered text is left\n * alone until either more text arrives (in case it's the first half of a split `\\r\\n` pair) or the\n * stream ends, so a `\\r\\n` pair split across two transport chunks is never misread as two blank\n * lines.\n *\n * Malformed JSON in a frame throws `LLMError('parse')`, consistent with\n * how malformed JSON is handled elsewhere in VernLLM.\n */\nexport async function* parseSseStream(\n source: AsyncIterable<Uint8Array | string>,\n): AsyncGenerator<unknown> {\n // `fatal: true` makes invalid UTF-8 throw instead of silently decoding\n // to U+FFFD replacement characters, which could otherwise land inside a\n // JSON string and either corrupt it unnoticeably or, worse, still parse\n // as syntactically valid JSON with silently-wrong content.\n const decoder = new TextDecoder('utf-8', { fatal: true });\n let buffer = '';\n\n for await (const chunk of source) {\n let text: string;\n\n try {\n text = typeof chunk === 'string' ? chunk : decoder.decode(chunk, { stream: true });\n } catch (cause) {\n throw new LLMError('Invalid UTF-8 in SSE stream', 'parse', { cause });\n }\n\n // Normalized against the whole buffer, not just the newly-arrived\n // chunk: a `\\r\\n` delimiter can straddle a chunk boundary (one chunk\n // ending in `\\r`, the next starting with `\\n`), and normalizing only\n // the new text would miss that split pair. A bare trailing `\\r` (not\n // followed by anything yet) is left as-is for the same reason; it's\n // converted once either more text or end-of-stream resolves whether\n // it was standalone or the start of a split `\\r\\n`.\n buffer = (buffer + text).replace(/\\r\\n/g, '\\n').replace(/\\r(?!$)/g, '\\n');\n\n let boundary = buffer.indexOf('\\n\\n');\n\n while (boundary !== -1) {\n const frame = buffer.slice(0, boundary);\n\n buffer = buffer.slice(boundary + 2);\n\n const event = parseSseFrame(frame);\n\n if (event === DONE) return;\n if (event !== NO_DATA) yield event;\n\n boundary = buffer.indexOf('\\n\\n');\n }\n }\n\n // Flush any bytes TextDecoder held back mid-decode, so a truncated\n // multi-byte char surfaces as a parse error instead of silently\n // vanishing (and possibly leaving behind valid-looking, wrong JSON).\n try {\n buffer += decoder.decode();\n } catch (cause) {\n throw new LLMError('Invalid UTF-8 in SSE stream', 'parse', { cause });\n }\n\n // The stream has ended, so a trailing `\\r` still held back above (it\n // could have been the start of a split `\\r\\n` pair) can only be a bare\n // CR line ending now. Normalize it and re-check for any frame boundary\n // it just completed.\n buffer = buffer.replace(/\\r$/, '\\n');\n\n let boundary = buffer.indexOf('\\n\\n');\n\n while (boundary !== -1) {\n const frame = buffer.slice(0, boundary);\n\n buffer = buffer.slice(boundary + 2);\n\n const event = parseSseFrame(frame);\n\n if (event === DONE) return;\n if (event !== NO_DATA) yield event;\n\n boundary = buffer.indexOf('\\n\\n');\n }\n\n // Flush a final frame that arrived without a trailing blank line: some\n // servers close the connection right after the last `data:` line instead\n // of sending one more `\\n\\n` first.\n const trailing = buffer.trim();\n\n if (trailing) {\n const event = parseSseFrame(trailing);\n\n if (event !== DONE && event !== NO_DATA) yield event;\n }\n}\n\nconst DONE = Symbol('sse-stream-done');\nconst NO_DATA = Symbol('sse-frame-no-data');\n\n/**\n * Sentinel yielded by `parseSseStream` for a comment-only frame (no\n * `data:` payload), the mechanism providers use for SSE keep-alive\n * pings. Exported so a consumer (e.g. `fromFetch`) can react to \"still\n * alive\" separately from a genuinely empty frame (`NO_DATA`, kept internal).\n */\nexport const SSE_PING = Symbol('sse-frame-ping');\n\n/** Extracts and JSON-parses the `data:` payload of one SSE frame (the text between two blank lines). */\nfunction parseSseFrame(frame: string): unknown {\n const dataLines: string[] = [];\n let sawComment = false;\n\n for (const line of frame.split('\\n')) {\n if (line.startsWith(':')) {\n sawComment = true; // comment line, also used as a keep-alive ping\n continue;\n }\n if (!line.startsWith('data:')) continue; // ignore event:/id:/retry:/blank lines\n\n // A single space after the colon is stripped per the SSE spec; further\n // leading whitespace is preserved as part of the payload.\n dataLines.push(line.startsWith('data: ') ? line.slice(6) : line.slice(5));\n }\n\n if (!dataLines.length) return sawComment ? SSE_PING : NO_DATA;\n\n const data = dataLines.join('\\n');\n\n if (data === '[DONE]') return DONE;\n\n try {\n return JSON.parse(data);\n } catch (cause) {\n throw new LLMError(`Invalid JSON in SSE frame: ${data.slice(0, 200)}`, 'parse', {\n cause,\n code: 'stream_frame_invalid',\n });\n }\n}\n","import { LLMError } from '../../types/index.js';\n\n/**\n * MIME types accepted for `ImageBlock.mimeType` across all adapters. This is\n * the intersection of what Anthropic, Gemini, OpenAI-compatible, and Bedrock\n * Converse all natively support, so a `ContentBlock[]` that validates for\n * one provider validates for all of them.\n */\nexport const SUPPORTED_IMAGE_MIME_TYPES = [\n 'image/png',\n 'image/jpeg',\n 'image/gif',\n 'image/webp',\n] as const;\n\nexport type SupportedImageMimeType = (typeof SUPPORTED_IMAGE_MIME_TYPES)[number];\n\n/**\n * Validates an `ImageBlock.mimeType` against the shared supported set.\n * Throws a non-retryable `LLMError('invalid_params')`, since an unsupported\n * mimeType is a bug in the caller's own input, deterministic before any\n * request is built, the same class of failure as every other check in\n * `RequestBuilder`.\n */\nexport function assertSupportedImageMimeType(mimeType: string): SupportedImageMimeType {\n if ((SUPPORTED_IMAGE_MIME_TYPES as readonly string[]).includes(mimeType)) {\n return mimeType as SupportedImageMimeType;\n }\n\n throw new LLMError(\n `Unsupported image mimeType \"${mimeType}\": expected one of ${SUPPORTED_IMAGE_MIME_TYPES.join(', ')}`,\n 'invalid_params',\n );\n}\n","/**\n * A static allow-list or predicate naming which models support native,\n * schema-constrained output as its own request field — Anthropic's\n * `output_config.format`, Bedrock's `outputConfig.textFormat` — separate\n * from `tools`/`tool_choice`, so it can be combined with real,\n * caller-supplied `tools` in the same request.\n *\n * There is no built-in default list here. Which models support this is\n * Anthropic's and Bedrock's call to make, not this package's, and it\n * changes over time; hardcoding a guessed list would risk silently\n * routing a request onto a field a given model doesn't actually support,\n * trading a clear `LLMError('validation')` for a confusing error from the\n * provider instead. So this is opt-in: pass the model IDs you've verified\n * against the provider's own docs (or a predicate). Left unset, no model\n * is treated as native-capable, `jsonSchema` keeps using the older\n * forced-single-tool-call emulation, and `tools` + `jsonSchema` together\n * is rejected, exactly this package's behavior before native support was\n * added.\n */\nexport type ModelCapabilityOverride = string[] | ((model: string) => boolean);\n\n/** Resolves whether `model` is covered by a caller-supplied allow-list/predicate. */\nexport function supportsNativeStructuredOutput(\n model: string,\n override?: ModelCapabilityOverride,\n): boolean {\n if (!override) return false;\n\n return Array.isArray(override) ? override.includes(model) : override(model);\n}\n","import {\n LLMError,\n type ContentBlock,\n type LLMClient,\n type WireStreamChunk,\n type WireToolCall,\n} from '../types/index.js';\nimport {\n assertSupportedImageMimeType,\n type SupportedImageMimeType,\n} from './internal/imageFormat.js';\nimport {\n supportsNativeStructuredOutput,\n type ModelCapabilityOverride,\n} from './internal/nativeStructuredOutput.js';\n\n/** Anthropic's native per-block content shape for a message. */\ntype AnthropicContentBlock =\n | { type: 'text'; text: string }\n | {\n type: 'image';\n source: { type: 'base64'; media_type: SupportedImageMimeType; data: string };\n }\n | { type: 'tool_use'; id: string; name: string; input: unknown }\n | { type: 'tool_result'; tool_use_id: string; content: string; is_error?: boolean };\n\n/** Minimal structural type for the Anthropic SDK's `messages.create` */\nexport interface AnthropicClient {\n messages: {\n create(\n params: {\n model: string;\n max_tokens: number;\n temperature?: number;\n system?: string;\n messages: Array<{ role: 'user' | 'assistant'; content: string | AnthropicContentBlock[] }>;\n tools?: Array<{\n name: string;\n description?: string;\n // The real Anthropic SDK's `Tool.input_schema` requires the\n // literal `type: 'object'` (VernLLM's own public `tools` API\n // accepts freeform JSON Schema, so this is narrower than that);\n // see the two call sites below for how a caller's schema is\n // asserted into this shape.\n input_schema: { type: 'object'; [key: string]: unknown };\n strict?: boolean;\n }>;\n tool_choice?:\n | { type: 'auto' }\n | { type: 'any' }\n | { type: 'none' }\n | { type: 'tool'; name: string };\n /**\n * Native, schema-constrained output: a separate request field from\n * `tools`/`tool_choice`, so it can be sent alongside real tool\n * calls. Only built by this adapter for models covered by\n * `nativeStructuredOutputModels` (opt-in, see\n * `AnthropicAdapterOptions`); other models keep getting\n * `jsonSchema` emulated as a forced single tool call, the\n * pre-existing behavior.\n *\n * Matches the real Anthropic API's `output_config.format` shape\n * exactly: just `type` and `schema`, no `name`/`description`/\n * `strict`. Those three exist on VernLLM's own `jsonSchema` API\n * (and are still forwarded on the legacy forced-tool-call path,\n * where they're real `Tool` fields), but the native structured-\n * output endpoint has no equivalent for any of them.\n */\n output_config?: {\n format: {\n type: 'json_schema';\n schema: Record<string, unknown>;\n };\n };\n },\n options: { signal: AbortSignal },\n ): Promise<{\n content: Array<{ type: string; text?: string; id?: string; name?: string; input?: unknown }>;\n usage?: { input_tokens?: number; output_tokens?: number };\n }>;\n };\n}\n\n/**\n * Translates a VernLLM `ContentBlock[]` (our provider-agnostic multimodal\n * shape) into Anthropic's native content-block array: text blocks pass\n * through as-is, image blocks become `{ type: 'image', source: { type:\n * 'base64', media_type, data } }`.\n */\nfunction toAnthropicContent(blocks: ContentBlock[]): AnthropicContentBlock[] {\n return blocks.map((block) =>\n block.type === 'image'\n ? {\n type: 'image',\n source: {\n type: 'base64',\n media_type: assertSupportedImageMimeType(block.mimeType),\n data: block.data,\n },\n }\n : { type: 'text', text: block.text },\n );\n}\n\n/**\n * Asserts a caller-supplied JSON Schema is an object schema before it's\n * used as Anthropic's `Tool.input_schema`, which (like every other\n * provider's function-calling API) requires `type: 'object'`. VernLLM's own\n * public `tools`/`jsonSchema` APIs accept freeform `Record<string,\n * unknown>` JSON Schema, so nothing upstream guarantees this at compile\n * time; this is the runtime check that stands in for that, so a schema\n * missing (or mistyping) `type: 'object'` fails loudly and immediately\n * instead of being silently forwarded to Anthropic malformed.\n */\nfunction assertObjectSchema(\n schema: Record<string, unknown>,\n toolName: string,\n): { type: 'object'; [key: string]: unknown } {\n if (schema.type !== 'object') {\n throw new LLMError(\n `Tool \"${toolName}\"'s schema must have \"type\": \"object\" (Anthropic requires object-shaped tool parameters).`,\n 'validation',\n );\n }\n\n return schema as { type: 'object'; [key: string]: unknown };\n}\n\n/**\n * Translates VernLLM's OpenAI-shaped wire `tool_choice` into Anthropic's\n * `{ type: 'auto' | 'any' | 'none' | 'tool', name? }` shape. `'required'`\n * maps to `'any'` (Anthropic's \"must call some tool\" equivalent).\n */\nfunction toAnthropicToolChoice(\n toolChoice: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice'],\n):\n | { type: 'auto' }\n | { type: 'any' }\n | { type: 'none' }\n | { type: 'tool'; name: string }\n | undefined {\n if (!toolChoice || toolChoice === 'auto') return { type: 'auto' };\n if (toolChoice === 'none') return { type: 'none' };\n if (toolChoice === 'required') return { type: 'any' };\n\n return { type: 'tool', name: toolChoice.function.name };\n}\n\n/** One SSE event of an Anthropic `messages.create({ stream: true })` stream. */\ntype AnthropicStreamEvent =\n | { type: 'message_start'; message: { usage?: { input_tokens?: number } } }\n | {\n type: 'content_block_start';\n index: number;\n content_block: { type: string; id?: string; name?: string };\n }\n | {\n type: 'content_block_delta';\n index: number;\n delta:\n | { type: 'text_delta'; text: string }\n | { type: 'input_json_delta'; partial_json: string };\n }\n | { type: 'content_block_stop'; index: number }\n | { type: 'message_delta'; usage?: { output_tokens?: number } }\n | { type: 'message_stop' }\n // Keep-alive event during long streams (e.g. extended thinking). Modeled\n // so the event switch has somewhere to route it. See createStream.\n | { type: 'ping' };\n\ntype AnthropicRequestBody = Parameters<AnthropicClient['messages']['create']>[0];\n\n/**\n * Maps VernLLM's OpenAI-shaped wire `tools`/`tool_choice` into Anthropic's\n * `tools`/`tool_choice` shape. Shared by the two call sites that build real\n * (non-schema-forced) tool definitions: the plain tools-only branch, and\n * the native-structured-output branch, which sends real tools alongside\n * `output_config` rather than instead of it.\n */\nfunction buildAnthropicTools(\n tools: NonNullable<Parameters<LLMClient['chat']['completions']['create']>[0]['tools']>,\n toolChoiceParam: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice'],\n): {\n tools: NonNullable<Parameters<AnthropicClient['messages']['create']>[0]['tools']>;\n toolChoice: Parameters<AnthropicClient['messages']['create']>[0]['tool_choice'];\n} {\n return {\n tools: tools.map((t) => ({\n name: t.function.name,\n description: t.function.description,\n // Tool parameters are always object schemas in practice (every\n // provider's function-calling API requires it).\n input_schema: assertObjectSchema(t.function.parameters, t.function.name),\n })),\n toolChoice: toAnthropicToolChoice(toolChoiceParam),\n };\n}\n\n/**\n * Builds the Anthropic-shaped request body from VernLLM's wire params,\n * shared between `create` and `createStream` so both go through identical\n * translation (system prompt, message shaping, and the jsonSchema →\n * forced-single-tool mapping all happen exactly once, not once per entry\n * point).\n *\n * Returns `toolName` alongside the body: when set, the model was forced to\n * call a single synthetic tool standing in for `jsonSchema` output (the\n * legacy path, for models without native structured-output support), and\n * both `create` and `createStream` need to know this so they can unwrap\n * that tool call back into plain text content instead of treating it like\n * a real tool call. On the native path (model supports `output_config`),\n * `toolName` is `undefined`: the schema-conforming JSON already arrives as\n * ordinary text content, nothing to unwrap, and any real tool calls in\n * `params.tools` are left for the normal, non-forced tool-call handling\n * both `create` and `createStream` already do when `toolName` is unset.\n */\nfunction buildAnthropicRequestBody(\n params: Parameters<LLMClient['chat']['completions']['create']>[0],\n nativeStructuredOutputModels?: ModelCapabilityOverride,\n): { body: AnthropicRequestBody; toolName: string | undefined } {\n const systemMessage = params.messages.find((m) => m.role === 'system');\n\n // Keep user, assistant, and tool turns, in order. Anthropic has no\n // separate 'tool' role: tool results travel as a user-role message\n // containing tool_result content blocks, and an assistant's tool\n // requests travel as tool_use content blocks on its own turn.\n const conversationMessages = params.messages.filter(\n (m) => m.role === 'user' || m.role === 'assistant' || m.role === 'tool',\n );\n\n const jsonSchema =\n params.response_format?.type === 'json_schema' ? params.response_format.json_schema : undefined;\n\n const schemaName = jsonSchema?.name.trim();\n\n if (jsonSchema && !schemaName) {\n throw new LLMError('json_schema.name must not be empty.', 'validation');\n }\n\n const isNative =\n Boolean(jsonSchema) &&\n supportsNativeStructuredOutput(params.model, nativeStructuredOutputModels);\n\n if (jsonSchema && params.tools?.length && !isNative) {\n throw new LLMError(\n `Anthropic model \"${params.model}\" is not covered by nativeStructuredOutputModels, so ` +\n '`jsonSchema` is emulated as a forced single tool call there, which collides with the ' +\n '`tools` you also provided. Either drop `tools` or `jsonSchema` for this call, or pass ' +\n \"this model in fromAnthropic's `nativeStructuredOutputModels` option once you've \" +\n \"confirmed it supports Anthropic's `output_config.format`.\",\n 'validation',\n );\n }\n\n let toolName: string | undefined;\n let jsonInstruction: string | undefined;\n let outputFormat: NonNullable<AnthropicRequestBody['output_config']>['format'] | undefined;\n let tools: NonNullable<Parameters<AnthropicClient['messages']['create']>[0]['tools']> | undefined;\n let toolChoice: Parameters<AnthropicClient['messages']['create']>[0]['tool_choice'];\n\n if (jsonSchema && isNative) {\n // Native path: the schema goes in its own request field, independent\n // of tools/tool_choice, so real tools (if any) are built exactly like\n // the tools-only branch below and sent alongside it.\n //\n // Only `type` and `schema` are sent: the real Anthropic API's\n // `output_config.format` has no `name`/`description`/`strict` fields,\n // unlike the legacy forced-tool-call path below, where those are real\n // `Tool` fields. `schemaName` is still required and validated above\n // (a caller-facing identifier, useful for logging/debugging on their\n // end), it just never reaches this particular wire request.\n outputFormat = { type: 'json_schema', schema: jsonSchema.schema };\n\n if (params.tools?.length) {\n ({ tools, toolChoice } = buildAnthropicTools(params.tools, params.tool_choice));\n }\n } else if (jsonSchema && schemaName) {\n // Legacy path: jsonSchema alone (or with tools, on a native model — see\n // above), on a model without native support, becomes a forced single\n // tool call, unchanged from before this adapter had a native path.\n const { schema, description, strict } = jsonSchema;\n\n toolName = schemaName;\n // VernLLM's public `jsonSchema` API accepts a freeform JSON Schema\n // object (`Record<string, unknown>`), not necessarily typed with a\n // literal `type: 'object'`, but tool/function parameters are always\n // object schemas in practice (every provider's function-calling API\n // requires it), so this assertion reflects that existing convention\n // rather than changing behavior.\n tools = [\n { name: toolName, description, input_schema: assertObjectSchema(schema, toolName), strict },\n ];\n toolChoice = { type: 'tool', name: toolName };\n } else if (params.response_format?.type === 'json_object') {\n // No schema to build a tool from, fall back to a prompt instruction.\n // This does not exclude real `tools`: `json_object` mode is just a\n // system-prompt nudge, not a request field that could collide with\n // `tools`/`tool_choice`, so both are set independently below.\n jsonInstruction = 'Respond with valid JSON only, no prose or markdown fences.';\n }\n\n if (!jsonSchema && params.tools?.length) {\n ({ tools, toolChoice } = buildAnthropicTools(params.tools, params.tool_choice));\n }\n\n // `reasoning_effort` (OpenAI o-series/gpt-5 style) has no direct Anthropic\n // equivalent. Claude's extended thinking uses a token budget, not a tier\n // string, so it's intentionally dropped here rather than guessed at.\n\n const system = [systemMessage?.content, jsonInstruction].filter(Boolean).join('\\n\\n');\n\n const body: AnthropicRequestBody = {\n model: params.model,\n max_tokens: params.max_tokens,\n ...(params.temperature !== undefined ? { temperature: params.temperature } : {}),\n system: system || undefined,\n messages: mergeConsecutiveToolResults(conversationMessages.map((m) => toAnthropicMessage(m))),\n ...(tools ? { tools, tool_choice: toolChoice } : {}),\n ...(outputFormat ? { output_config: { format: outputFormat } } : {}),\n };\n\n return { body, toolName };\n}\n\n/** Optional configuration for `fromAnthropic`. */\nexport interface AnthropicAdapterOptions {\n /**\n * Which models support native, schema-constrained output\n * (`output_config.format`), independent of `tools`/`tool_choice`, so it\n * can be combined with real `tools` in one request. Pass a static list\n * of model IDs (verified against Anthropic's own docs) or a predicate.\n *\n * There is no built-in default here (see `supportsNativeStructuredOutput`\n * for why). Left unset, every model uses the older forced-single-tool-\n * call emulation, and `tools` + `jsonSchema` together is rejected,\n * exactly this adapter's behavior before native support was added.\n */\n nativeStructuredOutputModels?: ModelCapabilityOverride;\n}\n\n/**\n * Wraps an Anthropic SDK client so it satisfies the same `LLMClient`\n * interface VernLLM uses for OpenAI/Groq.\n *\n * `response_format: json_schema`, on a model covered by\n * `options.nativeStructuredOutputModels`, is sent as `output_config.format`,\n * its own request field, independent of `tools`/`tool_choice`, so it can be\n * combined with real, caller-supplied `tools` in the same request. Only\n * `type` and `schema` are sent on this path, the real Anthropic API's\n * `output_config.format` has no `name`/`description`/`strict` fields.\n *\n * On any other model (the default, since `nativeStructuredOutputModels` is\n * opt-in), `response_format: json_schema` is mapped to Anthropic's forced\n * tool-use instead: a single tool is defined with `input_schema` set to\n * the caller's schema, `description` forwarded when provided, and `strict`\n * forwarded when set, and `tool_choice` forces the model to call it. This\n * legacy path cannot be combined with real `tools` (both would need the\n * same `tools`/`tool_choice` field), and a call that tries throws\n * `LLMError('validation')` before reaching the API. Provider-constrained\n * schema matching applies only when `strict: true` is forwarded and\n * supported.\n *\n * `response_format: json_object` (no schema to build a tool from) falls\n * back to a system-prompt instruction, since there's nothing to constrain\n * generation against. Unlike `jsonSchema`, this combines with real `tools`\n * freely on every model: it's a prompt nudge, not a request field, so\n * there's nothing for it to collide with.\n */\nexport function fromAnthropic(\n anthropicClient: AnthropicClient,\n options?: AnthropicAdapterOptions,\n): LLMClient {\n const nativeStructuredOutputModels = options?.nativeStructuredOutputModels;\n // The Anthropic SDK's `messages.create`, called with `stream: true`,\n // returns an AsyncIterable of `AnthropicStreamEvent` rather than\n // `AnthropicClient['messages']['create']`'s normal single-message return\n // type, hence `unknown` here and a cast at the call site, same\n // rationale as `fromOpenAICompatible`'s `rawCreate`: the wire contract,\n // not the SDK's own TS types, is what's actually relied on.\n const rawMessagesCreate = anthropicClient.messages.create.bind(\n anthropicClient.messages,\n ) as unknown as (\n params: unknown,\n options: { signal: AbortSignal },\n ) => Promise<unknown> | AsyncIterable<AnthropicStreamEvent>;\n\n return {\n chat: {\n completions: {\n async create(params, options) {\n const { body, toolName } = buildAnthropicRequestBody(\n params,\n nativeStructuredOutputModels,\n );\n\n const response = await anthropicClient.messages.create(body, options);\n\n let text: string;\n let wireToolCalls: WireToolCall[] | undefined;\n if (toolName) {\n const toolUse = response.content.find(\n (block) => block.type === 'tool_use' && block.name === toolName,\n );\n\n if (!toolUse) {\n throw new LLMError(\n `Anthropic did not return the required structured output tool \"${toolName}\".`,\n 'validation',\n );\n }\n\n if (\n !toolUse.input ||\n typeof toolUse.input !== 'object' ||\n Array.isArray(toolUse.input)\n ) {\n throw new LLMError(\n `Anthropic returned invalid structured output for tool \"${toolName}\". Expected an object.`,\n 'validation',\n );\n }\n\n text = JSON.stringify(toolUse.input);\n } else {\n text = response.content\n .filter((block) => block.type === 'text')\n .map((block) => block.text ?? '')\n .join('');\n\n const toolUses = response.content.filter((block) => block.type === 'tool_use');\n\n if (toolUses.length) {\n wireToolCalls = toolUses.map((block) => ({\n id: block.id!,\n type: 'function' as const,\n function: { name: block.name!, arguments: JSON.stringify(block.input ?? {}) },\n }));\n }\n }\n\n return {\n choices: [\n {\n message: { content: text, ...(wireToolCalls ? { tool_calls: wireToolCalls } : {}) },\n },\n ],\n usage: {\n prompt_tokens: response.usage?.input_tokens,\n completion_tokens: response.usage?.output_tokens,\n total_tokens:\n (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0),\n },\n };\n },\n\n async *createStream(params, options) {\n const { body, toolName } = buildAnthropicRequestBody(\n params,\n nativeStructuredOutputModels,\n );\n\n const stream = (await rawMessagesCreate(\n { ...body, stream: true },\n options,\n )) as AsyncIterable<AnthropicStreamEvent>;\n\n // Tracks which content-block index is which kind, since Anthropic\n // interleaves text and tool_use blocks under a shared `index`\n // sequence and later delta events only carry that index, not the\n // kind. `json-tool` is the forced single-tool-call standing in\n // for `jsonSchema` output (see `toolName` above): its\n // input_json_delta fragments are re-emitted as `text-delta`, not\n // `tool_call_delta`, so the accumulated result lands in\n // finalizeResponse's `content` path exactly like the\n // non-streaming `create` branch above unwraps it.\n const blockKinds = new Map<number, 'text' | 'tool_use' | 'json-tool'>();\n let inputTokens = 0;\n let sawJsonTool = false;\n\n for await (const event of stream) {\n if (event.type === 'message_start') {\n inputTokens = event.message.usage?.input_tokens ?? 0;\n } else if (event.type === 'content_block_start') {\n if (event.content_block.type === 'tool_use') {\n const kind = event.content_block.name === toolName ? 'json-tool' : 'tool_use';\n\n blockKinds.set(event.index, kind);\n\n if (kind === 'json-tool') {\n sawJsonTool = true;\n } else if (!toolName) {\n // Only surface tool_use blocks as tool_call_delta chunks\n // when there's no forced structured-output tool in play.\n // When `toolName` is set, any non-matching tool_use block\n // is unexpected (Anthropic forces exactly one tool), and\n // surfacing it would corrupt the caller's expectation of\n // receiving only the forced tool's JSON payload as text.\n yield {\n type: 'tool_call_delta',\n index: event.index,\n id: event.content_block.id,\n name: event.content_block.name,\n };\n }\n } else {\n blockKinds.set(event.index, 'text');\n }\n } else if (event.type === 'content_block_delta') {\n if (event.delta.type === 'text_delta') {\n // Only surfaced as real content when there's no forced\n // json-schema tool in play. When `toolName` is set, the\n // *only* content that should end up in the accumulated\n // text is the forced tool's own JSON payload (its\n // input_json_delta fragments, handled below), exactly\n // what the non-streaming `create` branch above does by\n // discarding every content block except the matching\n // tool_use one. Anthropic can still emit genuine text\n // blocks alongside a forced tool call (a model narrating\n // before calling it, say), and without this guard those\n // would get concatenated into the same buffer as the\n // tool's JSON, corrupting it.\n if (!toolName) {\n yield { type: 'text-delta', delta: event.delta.text };\n }\n } else if (event.delta.type === 'input_json_delta') {\n const kind = blockKinds.get(event.index);\n\n if (kind === 'json-tool') {\n yield { type: 'text-delta', delta: event.delta.partial_json };\n } else if (!toolName) {\n yield {\n type: 'tool_call_delta',\n index: event.index,\n argumentsDelta: event.delta.partial_json,\n };\n }\n }\n } else if (event.type === 'message_delta') {\n const outputTokens = event.usage?.output_tokens ?? 0;\n\n yield {\n type: 'usage',\n usage: {\n prompt_tokens: inputTokens,\n completion_tokens: outputTokens,\n total_tokens: inputTokens + outputTokens,\n },\n } satisfies WireStreamChunk;\n } else if (event.type === 'ping') {\n // Keep-alive with no content. Yielding it resets the\n // idle-timeout clock in VernLLM's stream loop.\n yield { type: 'ping' };\n }\n }\n\n if (toolName && !sawJsonTool) {\n throw new LLMError(\n `Anthropic did not return the required structured output tool \"${toolName}\".`,\n 'validation',\n );\n }\n },\n },\n },\n };\n}\n\n/**\n * Anthropic requires strict role alternation, so the per-wire-message\n * mapping above (one `{role:'user', content:[tool_result]}` per VernLLM\n * wire tool message) needs merging back together when an assistant turn\n * requested more than one tool: multiple consecutive user turns would\n * violate that alternation, and Anthropic's API rejects it outright. This\n * merges any run of tool-result-only user messages into one, with all\n * their tool_result blocks combined, the shape Anthropic expects for \"here\n * are the results of everything you just asked for.\"\n */\nfunction mergeConsecutiveToolResults(\n messages: { role: 'user' | 'assistant'; content: string | AnthropicContentBlock[] }[],\n): { role: 'user' | 'assistant'; content: string | AnthropicContentBlock[] }[] {\n const isToolResultOnly = (\n m: (typeof messages)[number],\n ): m is { role: 'user'; content: AnthropicContentBlock[] } =>\n m.role === 'user' &&\n Array.isArray(m.content) &&\n m.content.length > 0 &&\n m.content.every((b) => b.type === 'tool_result');\n\n const merged: (typeof messages)[number][] = [];\n\n for (const m of messages) {\n const prev = merged.at(-1);\n\n if (isToolResultOnly(m) && prev && isToolResultOnly(prev)) {\n prev.content.push(...m.content);\n } else {\n merged.push(m);\n }\n }\n\n return merged;\n}\n\n/**\n * Translates one VernLLM wire message (OpenAI-shaped: plain user/assistant\n * turns, an assistant turn with `tool_calls`, or a `tool` turn) into\n * Anthropic's `{ role: 'user' | 'assistant', content }` shape.\n */\nfunction toAnthropicMessage(\n m: Extract<\n Parameters<LLMClient['chat']['completions']['create']>[0]['messages'][number],\n { role: 'user' | 'assistant' | 'tool' }\n >,\n): { role: 'user' | 'assistant'; content: string | AnthropicContentBlock[] } {\n if (m.role === 'tool') {\n // Anthropic has no 'tool' role: results travel as a user turn with\n // tool_result blocks.\n return {\n role: 'user',\n content: [\n {\n type: 'tool_result',\n tool_use_id: m.tool_call_id,\n content: m.content,\n ...(m.is_error ? { is_error: true } : {}),\n },\n ],\n };\n }\n\n if (m.role === 'assistant' && m.tool_calls?.length) {\n const blocks: AnthropicContentBlock[] = [];\n\n if (m.content) blocks.push({ type: 'text', text: m.content });\n\n for (const tc of m.tool_calls) {\n let input: unknown;\n\n try {\n input = tc.function.arguments.trim() ? JSON.parse(tc.function.arguments) : {};\n } catch (cause) {\n throw new LLMError(\n `Assistant tool call \"${tc.function.name}\" (${tc.id}) has arguments that are not valid JSON.`,\n 'validation',\n { cause },\n );\n }\n\n if (input === null || Array.isArray(input) || typeof input !== 'object') {\n throw new LLMError(\n `Assistant tool call \"${tc.function.name}\" (${tc.id}) arguments must be a JSON object.`,\n 'validation',\n );\n }\n\n blocks.push({ type: 'tool_use', id: tc.id, name: tc.function.name, input });\n }\n\n return { role: 'assistant', content: blocks };\n }\n\n return {\n role: m.role,\n content: Array.isArray(m.content) ? toAnthropicContent(m.content) : (m.content ?? ''),\n };\n}\n","import {\n LLMError,\n type ContentBlock,\n type LLMClient,\n type WireStreamChunk,\n type WireToolCall,\n} from '../types/index.js';\nimport { assertSupportedImageMimeType } from './internal/imageFormat.js';\n\n/** Gemini's native per-part content shape for a `contents` entry. */\ntype GeminiPart =\n | { text: string }\n | { inlineData: { mimeType: string; data: string } }\n | { functionCall: { name: string; args: unknown } }\n | { functionResponse: { name: string; response: unknown } };\n\n/**\n * Structural type matching the real `@google/genai` SDK's `ai.models`\n * object: `generateContent`/`generateContentStream` both take a single\n * `{ model, contents, config }` argument (config carries\n * `systemInstruction`, `tools`, `toolConfig`, generation settings, and\n * `abortSignal` all together), matching the real SDK closely enough that\n * `fromGemini(ai.models)` works directly, e.g:\n *\n * ```ts\n * import { GoogleGenAI } from '@google/genai';\n * const ai = new GoogleGenAI({ apiKey: '...' });\n * const llm = new VernLLM({ client: fromGemini(ai.models), model: 'gemini-2.5-flash' });\n * ```\n */\nexport interface GeminiClient {\n generateContent(params: {\n model?: string;\n contents: Array<{ role: 'user' | 'model'; parts: GeminiPart[] }>;\n config?: {\n systemInstruction?: { parts: Array<{ text: string }> };\n temperature?: number;\n maxOutputTokens?: number;\n responseMimeType?: string;\n responseSchema?: Record<string, unknown>;\n tools?: Array<{\n functionDeclarations: Array<{\n name: string;\n description?: string;\n parameters: Record<string, unknown>;\n }>;\n }>;\n toolConfig?: {\n functionCallingConfig: {\n mode: 'AUTO' | 'ANY' | 'NONE';\n allowedFunctionNames?: string[];\n };\n };\n abortSignal?: AbortSignal;\n };\n }): Promise<{\n candidates?: Array<{\n content?: {\n parts?: Array<{ text?: string; functionCall?: { name: string; args: unknown } }>;\n };\n }>;\n usageMetadata?: {\n promptTokenCount?: number;\n candidatesTokenCount?: number;\n totalTokenCount?: number;\n };\n }>;\n\n /**\n * Optional. Required only for `stream: true` calls. Takes the same\n * request shape as `generateContent`. Matching the real SDK's own\n * `generateContentStream`, this resolves to an `AsyncIterable` (rather\n * than returning one synchronously) of partial responses, each chunk\n * holding the same `candidates[].content.parts[]` structure as\n * `generateContent`'s response, just incremental.\n */\n generateContentStream?(params: Parameters<GeminiClient['generateContent']>[0]): Promise<\n AsyncIterable<{\n candidates?: Array<{\n content?: {\n parts?: Array<{ text?: string; functionCall?: { name: string; args: unknown } }>;\n };\n }>;\n usageMetadata?: {\n promptTokenCount?: number;\n candidatesTokenCount?: number;\n totalTokenCount?: number;\n };\n }>\n >;\n}\n\n/**\n * Translates a VernLLM `ContentBlock[]` into Gemini's native `parts` array:\n * text blocks become `{ text }`, image blocks become inline data parts\n * (`{ inlineData: { mimeType, data } }`), Gemini's shape for embedding raw\n * base64 image bytes directly in the request.\n */\nfunction toGeminiParts(blocks: ContentBlock[]): GeminiPart[] {\n return blocks.map((block) =>\n block.type === 'image'\n ? { inlineData: { mimeType: assertSupportedImageMimeType(block.mimeType), data: block.data } }\n : { text: block.text },\n );\n}\n\n/** Maps VernLLM's OpenAI-shaped wire `tool_choice` onto Gemini's `functionCallingConfig`. */\nfunction toGeminiToolConfig(\n toolChoice: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice'],\n): NonNullable<\n NonNullable<Parameters<GeminiClient['generateContent']>[0]['config']>['toolConfig']\n> {\n if (!toolChoice || toolChoice === 'auto') {\n return { functionCallingConfig: { mode: 'AUTO' } };\n }\n if (toolChoice === 'none') {\n return { functionCallingConfig: { mode: 'NONE' } };\n }\n if (toolChoice === 'required') {\n return { functionCallingConfig: { mode: 'ANY' } };\n }\n\n return {\n functionCallingConfig: { mode: 'ANY', allowedFunctionNames: [toolChoice.function.name] },\n };\n}\n\n/**\n * Translates one VernLLM wire message into a Gemini `contents` entry.\n * Gemini has no separate 'tool' role: a prior assistant tool request\n * becomes a `'model'` turn with `functionCall` parts, and its result\n * becomes a `'user'` turn with `functionResponse` parts.\n */\nfunction toGeminiContent(\n m: Extract<\n Parameters<LLMClient['chat']['completions']['create']>[0]['messages'][number],\n { role: 'user' | 'assistant' | 'tool' }\n >,\n): { role: 'user' | 'model'; parts: GeminiPart[] } {\n if (m.role === 'tool') {\n // Gemini's functionResponse identifies the call by function *name*, and\n // the Gemini branch of this adapter sets wire tool_call ids equal to\n // the function name for exactly this reason, so tool_call_id here is\n // already the name Gemini expects.\n return {\n role: 'user',\n parts: [\n {\n functionResponse: {\n name: m.tool_call_id,\n response: parseToolResult(m.content),\n },\n },\n ],\n };\n }\n\n if (m.role === 'assistant' && m.tool_calls?.length) {\n const parts: GeminiPart[] = [];\n\n if (typeof m.content === 'string' && m.content) {\n parts.push({ text: m.content });\n }\n\n parts.push(\n ...m.tool_calls.map((tc) => ({\n functionCall: {\n name: tc.function.name,\n args: parseToolArguments(tc.function.arguments, tc.function.name),\n },\n })),\n );\n\n return {\n role: 'model',\n parts,\n };\n }\n\n return {\n role: m.role === 'assistant' ? 'model' : 'user',\n parts: Array.isArray(m.content) ? toGeminiParts(m.content) : [{ text: m.content ?? '' }],\n };\n}\n\nfunction parseToolArguments(text: string, toolName: string): Record<string, unknown> {\n let parsed: unknown;\n\n try {\n parsed = text.trim() ? JSON.parse(text) : {};\n } catch (cause) {\n throw new LLMError(`Tool call \"${toolName}\" arguments are not valid JSON.`, 'parse', {\n cause,\n code: 'tool_arguments_parse_failed',\n });\n }\n\n if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') {\n throw new LLMError(`Tool call \"${toolName}\" arguments must be a JSON object.`, 'validation');\n }\n\n return parsed as Record<string, unknown>;\n}\n\nfunction parseToolResult(text: string): unknown {\n try {\n return text.trim() ? JSON.parse(text) : '';\n } catch {\n return text;\n }\n}\n\n/**\n * Gemini expects the results of everything the model asked for in one turn\n * to arrive together as multiple `functionResponse` parts on a single\n * `'user'` entry, not as separate consecutive `'user'` entries. The\n * per-wire-message mapping above produces one `'user'` entry per VernLLM\n * wire tool message, so when an assistant turn requested more than one\n * tool, this merges the resulting run of functionResponse-only `'user'`\n * entries back into one.\n */\nfunction mergeConsecutiveFunctionResponses(\n contents: { role: 'user' | 'model'; parts: GeminiPart[] }[],\n): { role: 'user' | 'model'; parts: GeminiPart[] }[] {\n const isFunctionResponseOnly = (\n c: (typeof contents)[number],\n ): c is { role: 'user'; parts: GeminiPart[] } =>\n c.role === 'user' && c.parts.length > 0 && c.parts.every((p) => 'functionResponse' in p);\n\n const merged: (typeof contents)[number][] = [];\n\n for (const c of contents) {\n const prev = merged.at(-1);\n\n if (isFunctionResponseOnly(c) && prev && isFunctionResponseOnly(prev)) {\n prev.parts.push(...c.parts);\n } else {\n merged.push(c);\n }\n }\n\n return merged;\n}\n\ntype GeminiRequest = Parameters<GeminiClient['generateContent']>[0];\ntype GeminiConfig = NonNullable<GeminiRequest['config']>;\n\n/**\n * Builds the Gemini-shaped request from VernLLM's wire params, shared\n * between `create` and `createStream` so both go through identical\n * translation (contents shaping, `responseSchema`/`responseMimeType`\n * mapping, and tool/toolConfig translation all happen exactly once).\n * `abortSignal` is folded into `config` by the caller (`create`/\n * `createStream`), once the request options are available.\n */\nfunction buildGeminiRequest(\n params: Parameters<LLMClient['chat']['completions']['create']>[0],\n): GeminiRequest {\n const systemMessage = params.messages.find((m) => m.role === 'system');\n // Keep user, assistant, and tool turns, in order.\n const conversationMessages = params.messages.filter(\n (m) => m.role === 'user' || m.role === 'assistant' || m.role === 'tool',\n );\n\n const wantsJson = Boolean(params.response_format);\n const config: GeminiConfig = {\n ...(params.temperature !== undefined ? { temperature: params.temperature } : {}),\n maxOutputTokens: params.max_tokens,\n ...(systemMessage\n ? // System turns are always plain strings; only user turns can carry ContentBlock[]\n { systemInstruction: { parts: [{ text: systemMessage.content as string }] } }\n : {}),\n };\n\n if (wantsJson) {\n config.responseMimeType = 'application/json';\n }\n\n if (params.response_format?.type === 'json_schema') {\n const { schema, description } = params.response_format.json_schema;\n\n config.responseSchema = {\n ...schema,\n ...(description ? { description } : {}),\n };\n }\n\n if (params.tools?.length) {\n config.tools = [\n {\n functionDeclarations: params.tools.map((t) => ({\n name: t.function.name,\n description: t.function.description,\n parameters: t.function.parameters,\n })),\n },\n ];\n config.toolConfig = toGeminiToolConfig(params.tool_choice);\n }\n\n return {\n model: params.model,\n contents: mergeConsecutiveFunctionResponses(\n conversationMessages.map((m) => toGeminiContent(m)),\n ),\n config,\n };\n}\n\n/**\n * Wraps a Gemini client so it satisfies the `LLMClient` interface VernLLM\n * uses for OpenAI-compatible APIs. Gemini's shape differs on nearly every\n * axis: a `contents` array instead of `messages`, a separate\n * `systemInstruction` field instead of a `system` role message,\n * `generationConfig` instead of top-level `temperature`/`max_tokens`, and\n * native JSON Schema support via `responseMimeType: 'application/json'` +\n * `responseSchema`. `reasoning_effort` has no equivalent. Gemini's thinking\n * models use a token budget, not an effort tier, so it's dropped, same as\n * Anthropic.\n *\n * `tools` maps to Gemini's native `functionDeclarations`/`functionCall`;\n * `tool_choice` maps to `toolConfig.functionCallingConfig`. Gemini accepts\n * `responseSchema` and `tools` in the same request natively, so both are\n * set independently here and no special-casing is needed for the\n * combination, unlike `fromAnthropic`/`fromBedrock`.\n *\n * `createStream` calls `generateContentStream` (optional on `GeminiClient`\n *, required only if the caller sets `stream: true`) and translates each\n * partial response into `WireStreamChunk`s. Unlike OpenAI/Anthropic,\n * Gemini's own function-calling API doesn't stream tool-call arguments\n * incrementally: a `functionCall` part always arrives whole in one chunk,\n * so each one is emitted as a single, complete `tool_call_delta` (a\n * one-shot \"delta\" containing the full arguments) rather than accumulated\n * fragments, that's a real difference in the underlying API, not\n * something this adapter can smooth over. `usageMetadata` is (per Gemini's\n * own behavior) only reliably present on the last chunk, so the `usage`\n * `WireStreamChunk` is emitted once, after the stream completes, from\n * whichever chunk's `usageMetadata` was seen last.\n */\nexport function fromGemini(geminiClient: GeminiClient): LLMClient {\n return {\n chat: {\n completions: {\n async create(params, options) {\n const request = buildGeminiRequest(params);\n request.config = { ...request.config, abortSignal: options.signal };\n\n const response = await geminiClient.generateContent(request);\n\n const parts = response.candidates?.[0]?.content?.parts ?? [];\n const text = parts.map((p) => p.text ?? '').join('');\n const functionCalls = parts.filter((p) => p.functionCall);\n\n let wireToolCalls: WireToolCall[] | undefined;\n\n if (functionCalls.length) {\n wireToolCalls = functionCalls.map((p) => ({\n // Gemini's functionResponse correlates by function *name*, not\n // a call id (Gemini has no call-id concept at all), so the id\n // here is just the name. This means two calls to the *same*\n // tool within one turn can't be told apart when results come\n // back, a real limitation of Gemini's own function-calling\n // API, not something VernLLM can paper over.\n id: p.functionCall!.name,\n type: 'function' as const,\n function: {\n name: p.functionCall!.name,\n arguments: JSON.stringify(p.functionCall!.args ?? {}),\n },\n }));\n }\n\n return {\n choices: [\n {\n message: { content: text, ...(wireToolCalls ? { tool_calls: wireToolCalls } : {}) },\n },\n ],\n usage: {\n prompt_tokens: response.usageMetadata?.promptTokenCount,\n completion_tokens: response.usageMetadata?.candidatesTokenCount,\n total_tokens: response.usageMetadata?.totalTokenCount,\n },\n };\n },\n\n async *createStream(params, options) {\n if (!geminiClient.generateContentStream) {\n throw new LLMError(\n 'stream: true requires a Gemini client with generateContentStream',\n 'invalid_params',\n { code: 'unsupported_capability', issues: { capability: 'generateContentStream' } },\n );\n }\n\n const request = buildGeminiRequest(params);\n request.config = { ...request.config, abortSignal: options.signal };\n\n const stream = await geminiClient.generateContentStream(request);\n\n let toolCallIndex = 0;\n let lastUsage:\n | NonNullable<Awaited<ReturnType<GeminiClient['generateContent']>>['usageMetadata']>\n | undefined;\n\n for await (const chunk of stream) {\n const parts = chunk.candidates?.[0]?.content?.parts ?? [];\n\n for (const part of parts) {\n if (part.text) {\n yield { type: 'text-delta', delta: part.text };\n }\n\n if (part.functionCall) {\n yield {\n type: 'tool_call_delta',\n index: toolCallIndex,\n id: part.functionCall.name,\n name: part.functionCall.name,\n argumentsDelta: JSON.stringify(part.functionCall.args ?? {}),\n // INVARIANT: assumes Gemini always sends a complete\n // function-call args blob per part, no incremental\n // streaming, per current API behavior. There is no field\n // on this minimal structural type to derive this from.\n // If Gemini ever starts streaming args incrementally,\n // this becomes silently wrong. See the test titled\n // \"INVARIANT: hardcodes complete: true on\n // tool_call_delta\" in gemini.stream.unit.test.ts, which\n // exists specifically to catch that drift.\n complete: true,\n } satisfies WireStreamChunk;\n toolCallIndex++;\n }\n }\n\n if (chunk.usageMetadata) {\n lastUsage = chunk.usageMetadata;\n }\n }\n\n if (lastUsage) {\n yield {\n type: 'usage',\n usage: {\n prompt_tokens: lastUsage.promptTokenCount,\n completion_tokens: lastUsage.candidatesTokenCount,\n total_tokens: lastUsage.totalTokenCount,\n },\n };\n }\n },\n },\n },\n };\n}\n","import {\n LLMError,\n type ContentBlock,\n type LLMClient,\n type WireStreamChunk,\n type WireToolCall,\n} from '../types/index.js';\nimport { assertSupportedImageMimeType } from './internal/imageFormat.js';\nimport {\n supportsNativeStructuredOutput,\n type ModelCapabilityOverride,\n} from './internal/nativeStructuredOutput.js';\n\n/** Bedrock Converse's supported inline image formats. */\ntype BedrockImageFormat = 'png' | 'jpeg' | 'gif' | 'webp';\n\n/** Bedrock Converse's native per-block content shape for a message. */\ntype BedrockContentBlock =\n | { text: string }\n | { image: { format: BedrockImageFormat; source: { bytes: Uint8Array } } }\n | { toolUse: { toolUseId: string; name: string; input: unknown } }\n | {\n toolResult: {\n toolUseId: string;\n content: Array<{ text: string }>;\n status?: 'success' | 'error';\n };\n };\n\n/**\n * Minimal structural type matching AWS Bedrock's Converse API. This is\n * intentionally NOT `BedrockRuntimeClient` itself, the AWS SDK v3 client\n * exposes `.send(command)`, not a direct `.converse()` method, and pulling\n * in `@aws-sdk/client-bedrock-runtime` as a dependency just for its types\n * isn't worth it for a structural adapter. Wrap your client, e.g:\n *\n * ```ts\n * import { BedrockRuntimeClient, ConverseCommand } from '@aws-sdk/client-bedrock-runtime';\n * const client = new BedrockRuntimeClient({ region: 'us-east-1' });\n * const converseClient = {\n * converse: (params, options) =>\n * client.send(new ConverseCommand(params), { abortSignal: options.signal }),\n * };\n * ```\n */\nexport interface BedrockConverseClient {\n converse(\n params: {\n modelId: string;\n messages: Array<{ role: 'user' | 'assistant'; content: BedrockContentBlock[] }>;\n system?: Array<{ text: string }>;\n inferenceConfig?: { temperature?: number; maxTokens?: number };\n toolConfig?: {\n tools: Array<{\n toolSpec: {\n name: string;\n description?: string;\n inputSchema: { json: Record<string, unknown> };\n strict?: boolean;\n };\n }>;\n toolChoice?:\n | { tool: { name: string } }\n | { auto: Record<string, never> }\n | { any: Record<string, never> };\n };\n /**\n * Native, schema-constrained output: a separate request field from\n * `toolConfig`, so it can be sent alongside real tool calls. Only\n * built by this adapter for models covered by\n * `nativeStructuredOutputModels` (opt-in, see\n * `BedrockAdapterOptions`); other models keep getting `jsonSchema`\n * emulated as a forced single tool call via `toolConfig`, the\n * pre-existing behavior.\n *\n * Matches the real Bedrock Converse API's `outputConfig.textFormat`\n * shape exactly: the schema itself is nested one level deeper, under\n * `structure.jsonSchema`, not flat on `textFormat`, and `schema` is\n * a JSON-encoded *string*, not a parsed object, unlike every other\n * schema field this adapter builds (`toolSpec.inputSchema.json`\n * included). There is no `strict` field here, unlike `toolSpec`.\n */\n outputConfig?: {\n textFormat: {\n type: 'json_schema';\n structure: {\n jsonSchema: {\n schema: string;\n name?: string;\n description?: string;\n };\n };\n };\n };\n },\n options: { signal: AbortSignal },\n ): Promise<{\n output?: {\n message?: {\n content?: Array<{\n text?: string;\n toolUse?: { toolUseId?: string; name?: string; input?: unknown };\n }>;\n };\n };\n usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number };\n }>;\n\n /**\n * Optional. Required only for `stream: true` calls. Takes the same\n * request shape `converse` does, returning `{ stream }`, matching\n * `ConverseStreamCommand`'s real AWS SDK v3 output shape, an\n * `AsyncIterable` of incremental events under a `stream` property,\n * rather than the whole response being the iterable directly.\n */\n converseStream?(\n params: Parameters<BedrockConverseClient['converse']>[0],\n options: { signal: AbortSignal },\n ): Promise<{ stream: AsyncIterable<BedrockConverseStreamEvent> }>;\n}\n\n/**\n * One event of a Bedrock `ConverseStreamCommand` response's `stream`.\n * Content blocks (text or toolUse) are identified by `contentBlockIndex`,\n * Converse's own convention for correlating start/delta/stop events across\n * possibly-interleaved blocks, mirrored directly by VernLLM's\n * `tool_call_delta.index`.\n */\ntype BedrockConverseStreamEvent =\n | { messageStart: { role: 'assistant' } }\n | {\n contentBlockStart: {\n contentBlockIndex: number;\n start?: { toolUse?: { toolUseId?: string; name?: string } };\n };\n }\n | {\n contentBlockDelta: {\n contentBlockIndex: number;\n delta?: { text?: string } | { toolUse?: { input?: string } };\n };\n }\n | { contentBlockStop: { contentBlockIndex: number } }\n | { messageStop: { stopReason?: string } }\n | {\n metadata: {\n usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number };\n };\n }\n // The above are the happy-path events. Bedrock Converse streams can also\n // emit these as in-band exception events (not promise rejections), so\n // they need to be modeled and handled explicitly below, an unmatched\n // event previously fell through the if/else chain silently, either\n // truncating output or leaving the stream hanging until an unrelated\n // idle timeout fired.\n | { internalServerException: { message?: string } }\n | { modelStreamErrorException: { message?: string; originalStatusCode?: number } }\n | { validationException: { message?: string } }\n | { throttlingException: { message?: string } }\n | { serviceUnavailableException: { message?: string } };\n\n/** Maps a `ContentBlock` image MIME type, already validated, to Converse's `format` enum. */\nfunction toBedrockImageFormat(mimeType: string): BedrockImageFormat {\n switch (assertSupportedImageMimeType(mimeType)) {\n case 'image/png':\n return 'png';\n case 'image/jpeg':\n return 'jpeg';\n case 'image/gif':\n return 'gif';\n case 'image/webp':\n return 'webp';\n }\n}\n\n/**\n * Decodes base64 image data into the raw `Uint8Array` bytes Converse's\n * `image.source.bytes` expects (unlike Anthropic/Gemini/OpenAI, which all\n * take base64 strings directly). Uses `Buffer`, since this adapter, like\n * the rest of the package, targets Node.\n */\nfunction decodeBase64(data: string): Uint8Array {\n return new Uint8Array(Buffer.from(data, 'base64'));\n}\n\n/**\n * Translates a VernLLM `ContentBlock[]` into Converse's native content-block\n * array: text blocks pass through as `{ text }`, image blocks become\n * `{ image: { format, source: { bytes } } }` with the base64 payload decoded\n * to raw bytes, since Converse doesn't accept base64 strings directly.\n */\nfunction toBedrockContent(blocks: ContentBlock[]): BedrockContentBlock[] {\n return blocks.map((block) =>\n block.type === 'image'\n ? {\n image: {\n format: toBedrockImageFormat(block.mimeType),\n source: { bytes: decodeBase64(block.data) },\n },\n }\n : { text: block.text },\n );\n}\n\n/**\n * Optional configuration for `fromBedrock`.\n */\nexport interface BedrockAdapterOptions {\n /**\n * Optional preflight check for tool-use support, needed whenever a\n * `jsonSchema` call ends up sending Converse `toolConfig` — either the\n * legacy forced-single-tool-call emulation, or real `tools` sent\n * alongside native structured output (`outputConfig`). VernLLM never\n * guesses capability from a failed call's error message (AWS's error\n * text isn't a documented, stable contract), so this is opt-in: pass\n * either a static list of tool-use-capable model IDs, or a predicate\n * function, and VernLLM will reject unsupported models with a clear\n * `LLMError('validation')` *before* dispatching the request, instead of\n * on the wire.\n *\n * Left unset (default), no preflight check runs, and a `jsonSchema` call\n * to an unsupported model surfaces Bedrock's raw `converse` error as-is.\n */\n toolUseSupportedModels?: string[] | ((modelId: string) => boolean);\n\n /**\n * Which models support native, schema-constrained output\n * (`outputConfig.textFormat`), independent of `toolConfig`, so it can be\n * combined with real `tools` in one request. Pass a static list of\n * model IDs (verified against Bedrock's own docs) or a predicate.\n *\n * There is no built-in default here (see `supportsNativeStructuredOutput`\n * for why). Left unset, every model uses the older forced-single-tool-\n * call emulation via `toolConfig`, and `tools` + `jsonSchema` together is\n * rejected, exactly this adapter's behavior before native support was\n * added.\n */\n nativeStructuredOutputModels?: ModelCapabilityOverride;\n}\n\ntype BedrockRequest = Parameters<BedrockConverseClient['converse']>[0];\n\n/**\n * Maps VernLLM's OpenAI-shaped wire `tools`/`tool_choice` into Converse's\n * `toolConfig` shape. Shared by the two call sites that build real\n * (non-schema-forced) tool definitions: the plain tools-only branch, and\n * the native-structured-output branch, which sends real tools alongside\n * `outputConfig` rather than instead of it.\n */\nfunction buildBedrockToolConfig(\n tools: NonNullable<Parameters<LLMClient['chat']['completions']['create']>[0]['tools']>,\n toolChoiceParam: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice'],\n): NonNullable<BedrockRequest['toolConfig']> {\n return {\n tools: tools.map((t) => ({\n toolSpec: {\n name: t.function.name,\n description: t.function.description,\n inputSchema: { json: t.function.parameters },\n },\n })),\n toolChoice: toBedrockToolChoice(toolChoiceParam),\n };\n}\n\n/**\n * Builds the Converse-shaped request from VernLLM's wire params, shared\n * between `create` and `createStream` so both go through identical\n * translation (system prompt, message shaping, the jsonSchema →\n * forced-single-tool mapping, and the `toolUseSupportedModels` preflight\n * check all happen exactly once).\n *\n * Returns `toolName` alongside the request: when set, the model was forced\n * to call a single synthetic tool standing in for `jsonSchema` output (the\n * legacy path, for models not covered by `nativeStructuredOutputModels`),\n * and both `create` and `createStream` need to know this so they can\n * unwrap that tool call back into plain text content instead of treating\n * it like a real tool call. On the native path (model covered by\n * `nativeStructuredOutputModels`), `toolName` is `undefined`: the\n * schema-conforming JSON already arrives as ordinary text content, nothing\n * to unwrap, and any real tool calls in `params.tools` are left for the\n * normal, non-forced tool-call handling both `create` and `createStream`\n * already do when `toolName` is unset.\n */\nfunction buildBedrockRequest(\n params: Parameters<LLMClient['chat']['completions']['create']>[0],\n toolUseSupportedModels: BedrockAdapterOptions['toolUseSupportedModels'],\n nativeStructuredOutputModels: BedrockAdapterOptions['nativeStructuredOutputModels'],\n): { request: BedrockRequest; toolName: string | undefined } {\n const systemMessage = params.messages.find((m) => m.role === 'system');\n\n // Keep user, assistant, and tool turns, in order. Converse has no\n // separate 'tool' role: tool results travel as a user-role message\n // with toolResult content blocks, and an assistant's tool\n // requests travel as toolUse content blocks on its own turn.\n const conversationMessages = params.messages.filter(\n (m) => m.role === 'user' || m.role === 'assistant' || m.role === 'tool',\n );\n\n const jsonSchema =\n params.response_format?.type === 'json_schema' ? params.response_format.json_schema : undefined;\n\n const schemaName = jsonSchema?.name.trim();\n\n if (jsonSchema && !schemaName) {\n throw new LLMError('json_schema.name must not be empty.', 'validation');\n }\n\n const isNative =\n Boolean(jsonSchema) &&\n supportsNativeStructuredOutput(params.model, nativeStructuredOutputModels);\n\n if (jsonSchema && params.tools?.length && !isNative) {\n throw new LLMError(\n `Bedrock model \"${params.model}\" is not covered by nativeStructuredOutputModels, so ` +\n '`jsonSchema` is emulated as a forced single tool call there (via `toolConfig`), which ' +\n 'collides with the `tools` you also provided. Either drop `tools` or `jsonSchema` for this ' +\n \"call, or pass this model in fromBedrock's `nativeStructuredOutputModels` option once \" +\n \"you've confirmed it supports Converse's `outputConfig.textFormat`.\",\n 'validation',\n );\n }\n\n let toolName: string | undefined;\n let jsonInstruction: string | undefined;\n let toolConfig: NonNullable<BedrockRequest['toolConfig']> | undefined;\n let outputConfig: NonNullable<BedrockRequest['outputConfig']> | undefined;\n\n if (jsonSchema && isNative) {\n // Native path: the schema goes in its own request field, independent\n // of toolConfig, so real tools (if any) are built exactly like the\n // tools-only branch below and sent alongside it.\n //\n // Unlike every other schema this adapter builds, the real Bedrock\n // Converse API requires `schema` here as a JSON-encoded *string*, not\n // a parsed object, nested under `structure.jsonSchema` rather than\n // flat on `textFormat`. There is no `strict` field on this path,\n // unlike `toolSpec`.\n const { schema, description } = jsonSchema;\n\n outputConfig = {\n textFormat: {\n type: 'json_schema',\n structure: {\n jsonSchema: { schema: JSON.stringify(schema), name: schemaName, description },\n },\n },\n };\n } else if (jsonSchema && schemaName) {\n // Legacy path: jsonSchema alone (or with tools, on a native model;\n // see above), on a model without native support, becomes a forced\n // single tool call via toolConfig, unchanged from before this adapter\n // had a native path.\n const { schema, description, strict } = jsonSchema;\n\n toolName = schemaName;\n toolConfig = {\n tools: [{ toolSpec: { name: toolName, description, inputSchema: { json: schema }, strict } }],\n toolChoice: { tool: { name: toolName } },\n };\n } else if (params.response_format?.type === 'json_object') {\n // No schema to build a tool from, fall back to a prompt instruction.\n // This does not exclude real `tools`: `json_object` mode is just a\n // system-prompt nudge, not a request field that could collide with\n // `toolConfig`, so both are set independently below.\n jsonInstruction = 'Respond with valid JSON only, no prose or markdown fences.';\n }\n\n // Runs after the branch chain, unified across every case that still\n // wants real tools built: the native-with-tools case above (toolConfig\n // still unset there, only outputConfig is), and the no-jsonSchema-at-all\n // case. `toolName` is the one signal that distinguishes them from the\n // legacy forced-single-tool-call branch, which builds its own toolConfig\n // inline and must not be overwritten here.\n if (params.tools?.length && !toolName) {\n toolConfig = buildBedrockToolConfig(params.tools, params.tool_choice);\n }\n\n // Runs whenever a jsonSchema call actually ends up sending toolConfig,\n // whether that's the legacy forced-single-tool-call path, or the native\n // path with real `tools` also present (native structured output doesn't\n // need Converse tool-use support, but real tools alongside it still do).\n if (jsonSchema && toolConfig && toolUseSupportedModels) {\n const isSupported = Array.isArray(toolUseSupportedModels)\n ? toolUseSupportedModels.includes(params.model)\n : toolUseSupportedModels(params.model);\n\n if (!isSupported) {\n throw new LLMError(\n `Bedrock model \"${params.model}\" is not listed in toolUseSupportedModels, but this call ` +\n 'requires Converse tool use (either jsonSchema emulated as a forced tool call, or real ' +\n '`tools` sent alongside native structured output).',\n 'invalid_params',\n { code: 'unsupported_capability', issues: { capability: 'toolUseSupportedModels' } },\n );\n }\n }\n\n const systemParts = [systemMessage?.content, jsonInstruction].filter((s): s is string =>\n Boolean(s),\n );\n\n const request: BedrockRequest = {\n modelId: params.model,\n messages: mergeConsecutiveToolResults(conversationMessages.map((m) => toBedrockMessage(m))),\n system: systemParts.length ? systemParts.map((text) => ({ text })) : undefined,\n inferenceConfig: {\n ...(params.temperature !== undefined ? { temperature: params.temperature } : {}),\n maxTokens: params.max_tokens,\n },\n ...(toolConfig ? { toolConfig } : {}),\n ...(outputConfig ? { outputConfig } : {}),\n };\n\n return { request, toolName };\n}\n\n/**\n * Wraps a Bedrock Converse-API client so it satisfies the `LLMClient`\n * interface VernLLM uses for OpenAI/Groq. The Converse API is unified\n * across Bedrock's model families (Anthropic, Titan, Llama, Mistral, etc.),\n * so unlike raw per-model Bedrock invocation, this one adapter works\n * regardless of which underlying model `modelId` points at, as long as\n * that model supports Converse (most current-generation ones do)\n *\n * `response_format: json_schema`, on a model covered by\n * `options.nativeStructuredOutputModels` (opt-in, unset by default), is\n * sent as `outputConfig.textFormat`, its own request field, independent of\n * `toolConfig`, so it can be combined with real, caller-supplied `tools`\n * in the same request. Matches the real Converse API's shape exactly: the\n * schema is nested under `structure.jsonSchema` and JSON-encoded as a\n * string, not the parsed object `toolConfig`'s tool schemas use, and there\n * is no `strict` field on this path.\n *\n * On any other model (the default), `response_format: json_schema` is\n * mapped to Converse's `toolConfig` instead: a single tool is defined from\n * the schema, description, and strictness settings, and `toolChoice`\n * forces the model to call it. This legacy path cannot be combined with\n * real `tools` (both would need the same `toolConfig`), and a call that\n * tries throws `LLMError('validation')` before reaching the API.\n * Provider-constrained schema matching applies only when `strict: true` is\n * forwarded and supported. Native tool support varies by model family;\n * pass `toolUseSupportedModels` to preflight-check it (see\n * `BedrockAdapterOptions`), otherwise a `jsonSchema` call to an\n * unsupported model surfaces Bedrock's raw error unchanged.\n *\n * `response_format: json_object` (no schema to build a tool from) and\n * `reasoning_effort` (no Converse equivalent) fall back to a system-prompt\n * instruction and are dropped respectively. Unlike `jsonSchema`,\n * `json_object` combines with real `tools` freely on every model: it's a\n * prompt nudge, not a request field, so there's nothing for it to collide\n * with.\n *\n * `tools` alone maps to Converse's native `toolConfig`/`toolUse`/\n * `toolResult`; `tool_choice` maps to `toolConfig.toolChoice`.\n *\n * `createStream` calls `converseStream` (optional on `BedrockConverseClient`\n *, required only if the caller sets `stream: true`) and translates its\n * `contentBlockStart`/`contentBlockDelta`/`metadata` events into\n * `WireStreamChunk`s. Content blocks are tracked by `contentBlockIndex`,\n * same as `fromAnthropic`'s block-index tracking (Converse's streaming\n * shape is structurally close to Anthropic's own, both being tool-use-aware\n * content-block streams), including the same `json-tool` unwrapping: a\n * `jsonSchema`-forced tool's `toolUse.input` deltas are re-emitted as\n * `text-delta`, not `tool_call_delta`, so the accumulated result lands in\n * `finalizeResponse`'s `content` path exactly like the non-streaming\n * `create` branch above unwraps it.\n */\nexport function fromBedrock(\n bedrockClient: BedrockConverseClient,\n options?: BedrockAdapterOptions,\n): LLMClient {\n const toolUseSupportedModels = options?.toolUseSupportedModels;\n const nativeStructuredOutputModels = options?.nativeStructuredOutputModels;\n\n return {\n chat: {\n completions: {\n async create(params, requestOptions) {\n const { request, toolName } = buildBedrockRequest(\n params,\n toolUseSupportedModels,\n nativeStructuredOutputModels,\n );\n\n const response = await bedrockClient.converse(request, requestOptions);\n\n let text: string;\n let wireToolCalls: WireToolCall[] | undefined;\n\n if (toolName) {\n // Forced tool-use: the schema-conforming payload arrives as the\n // toolUse content block's already-parsed `input`, not as text.\n // Re-serialize it to JSON so it flows through the same\n // string-content contract every other adapter uses.\n const toolUseBlock = response.output?.message?.content?.find(\n (block) => block.toolUse?.name === toolName,\n );\n\n text = toolUseBlock?.toolUse ? JSON.stringify(toolUseBlock.toolUse.input) : '';\n } else {\n const blocks = response.output?.message?.content ?? [];\n\n text = blocks.map((c) => c.text ?? '').join('');\n\n const toolUses = blocks.filter(\n (\n block,\n ): block is { toolUse: { toolUseId?: string; name?: string; input?: unknown } } =>\n Boolean(block.toolUse),\n );\n\n if (toolUses.length) {\n wireToolCalls = toolUses.map((block, i) => {\n const toolUse = block.toolUse;\n\n if (!toolUse.name) {\n throw new LLMError(\n `Bedrock returned a toolUse block without a name at index ${i}.`,\n 'validation',\n );\n }\n\n return {\n id: toolUse.toolUseId ?? `${toolUse.name}_${i}`,\n type: 'function' as const,\n function: {\n name: toolUse.name,\n arguments: JSON.stringify(toolUse.input ?? {}),\n },\n };\n });\n }\n }\n\n return {\n choices: [\n {\n message: { content: text, ...(wireToolCalls ? { tool_calls: wireToolCalls } : {}) },\n },\n ],\n usage: {\n prompt_tokens: response.usage?.inputTokens,\n completion_tokens: response.usage?.outputTokens,\n total_tokens: response.usage?.totalTokens,\n },\n };\n },\n\n async *createStream(params, requestOptions) {\n if (!bedrockClient.converseStream) {\n throw new LLMError(\n 'stream: true requires a Bedrock client with converseStream',\n 'invalid_params',\n { code: 'unsupported_capability', issues: { capability: 'converseStream' } },\n );\n }\n\n const { request, toolName } = buildBedrockRequest(\n params,\n toolUseSupportedModels,\n nativeStructuredOutputModels,\n );\n\n const { stream } = await bedrockClient.converseStream(request, requestOptions);\n\n const blockKinds = new Map<number, 'text' | 'tool_use' | 'json-tool'>();\n\n for await (const event of stream) {\n if ('contentBlockStart' in event) {\n const { contentBlockIndex, start } = event.contentBlockStart;\n\n if (start?.toolUse) {\n const kind = start.toolUse.name === toolName ? 'json-tool' : 'tool_use';\n\n blockKinds.set(contentBlockIndex, kind);\n\n if (kind === 'tool_use' && !toolName) {\n yield {\n type: 'tool_call_delta',\n index: contentBlockIndex,\n id: start.toolUse.toolUseId,\n name: start.toolUse.name,\n };\n }\n } else {\n blockKinds.set(contentBlockIndex, 'text');\n }\n } else if ('contentBlockDelta' in event) {\n const { contentBlockIndex, delta } = event.contentBlockDelta;\n\n // Only surfaced as real content when there's no forced\n // json-schema tool in play, see the identical guard (and\n // its full rationale) in `fromAnthropic`'s `createStream`.\n // Converse's streaming shape is structurally close enough to\n // Anthropic's own that the same corruption risk applies: a\n // genuine text block alongside a forced tool call would\n // otherwise get concatenated into the same buffer as the\n // tool's JSON payload.\n if (delta && 'text' in delta && delta.text !== undefined && !toolName) {\n yield { type: 'text-delta', delta: delta.text };\n } else if (delta && 'toolUse' in delta && delta.toolUse?.input !== undefined) {\n const kind = blockKinds.get(contentBlockIndex);\n\n if (kind === 'json-tool') {\n yield { type: 'text-delta', delta: delta.toolUse.input };\n } else if (!toolName) {\n yield {\n type: 'tool_call_delta',\n index: contentBlockIndex,\n argumentsDelta: delta.toolUse.input,\n } satisfies WireStreamChunk;\n }\n }\n } else if ('metadata' in event && event.metadata.usage) {\n yield {\n type: 'usage',\n usage: {\n prompt_tokens: event.metadata.usage.inputTokens,\n completion_tokens: event.metadata.usage.outputTokens,\n total_tokens: event.metadata.usage.totalTokens,\n },\n };\n } else if ('throttlingException' in event) {\n throw new LLMError(\n event.throttlingException.message ?? 'Bedrock throttled the request mid-stream',\n 'api',\n { status: 429, code: 'provider_rate_limited' },\n );\n } else if ('validationException' in event) {\n throw new LLMError(\n event.validationException.message ?? 'Bedrock rejected the request mid-stream',\n 'validation',\n );\n } else if (\n 'internalServerException' in event ||\n 'serviceUnavailableException' in event ||\n 'modelStreamErrorException' in event\n ) {\n const detail =\n ('internalServerException' in event && event.internalServerException.message) ||\n ('serviceUnavailableException' in event &&\n event.serviceUnavailableException.message) ||\n ('modelStreamErrorException' in event && event.modelStreamErrorException.message) ||\n 'Bedrock reported a mid-stream error';\n\n const status =\n ('modelStreamErrorException' in event &&\n event.modelStreamErrorException.originalStatusCode) ||\n ('serviceUnavailableException' in event && 503) ||\n 500;\n\n throw new LLMError(detail, 'api', {\n status,\n code: status >= 500 ? 'server_error' : undefined,\n });\n }\n }\n },\n },\n },\n };\n}\n\n/** Maps VernLLM's OpenAI-shaped wire `tool_choice` onto Converse's `toolChoice`. */\nfunction toBedrockToolChoice(\n toolChoice: Parameters<LLMClient['chat']['completions']['create']>[0]['tool_choice'],\n): NonNullable<Parameters<BedrockConverseClient['converse']>[0]['toolConfig']>['toolChoice'] {\n if (!toolChoice || toolChoice === 'auto') return { auto: {} };\n if (toolChoice === 'required') return { any: {} };\n\n if (toolChoice === 'none') {\n // Converse's toolConfig.toolChoice has no 'none' option. The only way\n // to guarantee no tool use is to omit toolConfig.tools entirely, which\n // isn't an option here since tools were explicitly requested. Silently\n // falling back to 'auto' would let the model call tools despite the\n // caller explicitly asking it not to, so this fails loudly instead.\n throw new LLMError(\n \"'none' is not supported by fromBedrock: Bedrock Converse has no \" +\n '`tool_choice` equivalent to forbidding tool use while tools are still offered. Omit ' +\n '`tools` entirely for this call instead.',\n 'invalid_params',\n { code: 'unsupported_capability', issues: { capability: \"toolChoice: 'none'\" } },\n );\n }\n\n return { tool: { name: toolChoice.function.name } };\n}\n/**\n * Translates one VernLLM wire message into Converse's\n * `{ role: 'user' | 'assistant', content }` shape.\n */\nfunction toBedrockMessage(\n m: Extract<\n Parameters<LLMClient['chat']['completions']['create']>[0]['messages'][number],\n { role: 'user' | 'assistant' | 'tool' }\n >,\n): { role: 'user' | 'assistant'; content: BedrockContentBlock[] } {\n if (m.role === 'tool') {\n return {\n role: 'user',\n content: [\n {\n toolResult: {\n toolUseId: m.tool_call_id,\n content: [{ text: m.content }],\n status: m.is_error ? 'error' : 'success',\n },\n },\n ],\n };\n }\n\n if (m.role === 'assistant' && m.tool_calls?.length) {\n const blocks: BedrockContentBlock[] = [];\n\n if (m.content) blocks.push({ text: m.content });\n\n for (const tc of m.tool_calls) {\n let input: unknown;\n\n if (!tc.function.arguments.trim()) {\n input = {};\n } else {\n try {\n input = JSON.parse(tc.function.arguments);\n } catch (cause) {\n throw new LLMError(\n `Assistant tool call \"${tc.function.name}\" (${tc.id}) has arguments that are not valid JSON.`,\n 'validation',\n { cause },\n );\n }\n }\n\n blocks.push({ toolUse: { toolUseId: tc.id, name: tc.function.name, input } });\n }\n\n return { role: 'assistant', content: blocks };\n }\n\n return {\n role: m.role,\n content: Array.isArray(m.content) ? toBedrockContent(m.content) : [{ text: m.content ?? '' }],\n };\n}\n\n/**\n * Converse expects the results of everything the model asked for in one\n * turn to arrive together as multiple `toolResult` content blocks on a\n * single `'user'` message, not as separate consecutive `'user'` messages.\n * The per-wire-message mapping above produces one `'user'` message per\n * VernLLM wire tool message, so when an assistant turn requested more than\n * one tool, this merges the resulting run of toolResult-only `'user'`\n * messages back into one.\n */\nfunction mergeConsecutiveToolResults(\n messages: { role: 'user' | 'assistant'; content: BedrockContentBlock[] }[],\n): { role: 'user' | 'assistant'; content: BedrockContentBlock[] }[] {\n const isToolResultOnly = (\n m: (typeof messages)[number],\n ): m is { role: 'user'; content: BedrockContentBlock[] } =>\n m.role === 'user' && m.content.length > 0 && m.content.every((b) => 'toolResult' in b);\n\n const merged: (typeof messages)[number][] = [];\n\n for (const m of messages) {\n const prev = merged.at(-1);\n\n if (isToolResultOnly(m) && prev && isToolResultOnly(prev)) {\n prev.content.push(...m.content);\n } else {\n merged.push(m);\n }\n }\n\n return merged;\n}\n","import {\n LLMError,\n type LLMClient,\n type WireStreamChunk,\n type WireToolCall,\n} from '../types/index.js';\nimport { parseSseStream, SSE_PING } from './internal/sse.js';\n\n/** The chat-completion-shaped request VernLLM builds internally */\ntype ChatRequest = Parameters<LLMClient['chat']['completions']['create']>[0];\n\n/**\n * The minimal shape the fetch adapter needs from a response object.\n * Native `fetch`'s `Response` satisfies this, but so do wrappers around\n * `axios`, `node-fetch`, `undici`, etc, which makes `request` swappable\n * without forcing consumers to polyfill the full `Response` interface\n */\nexport interface ResponseLike {\n ok: boolean;\n status: number;\n headers: {\n get(name: string): string | null;\n };\n text(): Promise<string>;\n json(): Promise<unknown>;\n}\n\n/** A fetch-compatible request function; defaults to native `fetch` */\nexport type RequestLike = (\n url: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body?: string;\n signal?: AbortSignal;\n },\n) => Promise<ResponseLike>;\n\n/**\n * A streaming-capable request function. Unlike `RequestLike`, which returns\n * a fully-buffered `ResponseLike`, this resolves to an `AsyncIterable` of\n * progressively-arriving chunks, the common ground across transports:\n * native `fetch`'s `response.body` (wrapped to be iterable; see\n * `webStreamToAsyncIterable` below), axios's Node `Readable` in\n * `responseType: 'stream'` mode (already async-iterable, no wrapping\n * needed), `node-fetch`, `undici`, etc, all satisfy this with little or no\n * glue code. Defaults to native `fetch`.\n */\nexport type StreamRequestLike = (\n url: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body?: string;\n signal?: AbortSignal;\n },\n) => Promise<AsyncIterable<Uint8Array | string>>;\n\nexport interface FetchAdapterConfig {\n /** Endpoint URL, or a function of the request in case it depends on model/params */\n url: string | ((params: ChatRequest) => string);\n /** Static headers, or a function (sync or async) for things like refreshed auth tokens */\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n /** HTTP method. Default 'POST' */\n method?: string;\n /**\n * The function used to make the HTTP request. Defaults to native `fetch`.\n * Swap in `axios`, `node-fetch`, or any other transport, as long as it\n * resolves to a `ResponseLike` object\n */\n request?: RequestLike;\n /** Maps VernLLMs internal chat-completion request into the providers raw request body */\n mapRequest: (params: ChatRequest) => unknown;\n /**\n * Maps the providers raw JSON response into `{ content, usage?, toolCalls? }`\n * `content` is the assistants text (JSON string when JSON mode was requested).\n * `content` may be empty/omitted when the model responded with only tool\n * calls and no text.\n *\n * `toolCalls`, when the model requested one or more tools, is the list of\n * calls as flat `{ id, name, arguments }` entries (matching this config's\n * own `toolCalls?: Array<{ id: string; name: string; arguments: string }>`\n * return type below), each entry's `arguments` already JSON-*encoded* as a\n * string (not the parsed object), mirroring the wire format every\n * OpenAI-compatible provider uses. `fromFetch` itself converts these into\n * `WireToolCall`'s `type`/`function`-wrapped shape before returning them\n * from `create`. VernLLM parses (and validates, if `argumentsSchema` was\n * set) the arguments string internally, mapResponse doesn't need to do\n * that itself.\n */\n mapResponse: (json: unknown) => {\n content?: string;\n usage?: { promptTokens?: number; completionTokens?: number; totalTokens?: number };\n toolCalls?: Array<{ id: string; name: string; arguments: string }>;\n };\n\n /**\n * Optional. Required only for `stream: true` calls. The function used to\n * open a streaming HTTP request. Takes the same request shape as\n * `request`, but resolves to an `AsyncIterable` of progressively-arriving\n * `Uint8Array` or `string` chunks instead of a buffered `ResponseLike`.\n * Defaults to native `fetch`.\n */\n requestStream?: StreamRequestLike;\n\n /**\n * Optional. How the raw stream bytes are split into individual event\n * payloads. Defaults to Server-Sent Events framing (`data: ...` blocks\n * separated by a blank line, `[DONE]` sentinel honored, see\n * `parseSseStream`), which covers the large majority of LLM providers'\n * streaming HTTP endpoints. Override this for a provider that frames its\n * stream differently, e.g. newline-delimited JSON (NDJSON) with no SSE\n * envelope.\n */\n parseStreamFrames?: (chunks: AsyncIterable<Uint8Array | string>) => AsyncIterable<unknown>;\n\n /**\n * Optional. Required only for `stream: true` calls. Maps one parsed\n * stream event (already extracted from its frame by `parseStreamFrames`)\n * into zero, one, or more `WireStreamChunk`s, mirrors `mapResponse`'s\n * role for the non-streaming path, just per-event instead of once for\n * the whole body. Return `undefined` to skip an event that carries\n * nothing VernLLM needs (e.g. a provider's keep-alive ping). Configs\n * that don't implement this make `stream: true` throw a clear\n * `LLMError('validation')` rather than a confusing runtime failure or a\n * silently empty stream.\n */\n mapStreamEvent?: (event: unknown) => WireStreamChunk | WireStreamChunk[] | undefined;\n}\n\n/**\n * Wraps a WHATWG `ReadableStream` (what `response.body` is) so it can be\n * consumed with `for await`. Implemented via `getReader()` rather than\n * relying on `ReadableStream` having a native `Symbol.asyncIterator`,\n * that support varies across runtimes/versions, and this works everywhere\n * a `ReadableStream` does.\n */\nasync function* webStreamToAsyncIterable(\n stream: ReadableStream<Uint8Array>,\n): AsyncGenerator<Uint8Array> {\n const reader = stream.getReader();\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n\n if (done) return;\n if (value) yield value;\n }\n } finally {\n // Cancel before releasing the lock so an early-terminated or\n // downstream-failed consumer still tells the underlying source to\n // stop, instead of leaving it running with nothing left to read it.\n // An already-errored stream rejects cancel(); that rejection isn't\n // useful to the caller here, so it's swallowed.\n try {\n await reader.cancel();\n } catch {\n // Ignore: the stream may already be errored/closed.\n }\n\n reader.releaseLock();\n }\n}\n\n/** Default `requestStream`: native `fetch`, with the same error/`.status` contract non-streaming errors get. */\nasync function defaultRequestStream(\n url: string,\n init: { method: string; headers: Record<string, string>; body?: string; signal?: AbortSignal },\n): Promise<AsyncIterable<Uint8Array | string>> {\n const res = await fetch(url, init);\n\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n const err = new Error(\n `Fetch adapter stream request failed (${res.status}): ${body.slice(0, 500)}`,\n ) as Error & { status?: number; headers?: ResponseLike['headers'] };\n\n err.status = res.status;\n err.headers = res.headers;\n\n throw err;\n }\n\n if (!res.body) {\n throw new Error('Fetch adapter stream request received a response with no body.');\n }\n\n return webStreamToAsyncIterable(res.body);\n}\n\n/** Builds the shared `{ method, headers, body? }` request-init for both `create` and `createStream`. */\nasync function buildRequestInit(\n config: FetchAdapterConfig,\n params: ChatRequest,\n requestBody: unknown,\n): Promise<{ url: string; method: string; headers: Record<string, string>; body?: string }> {\n const url = typeof config.url === 'function' ? config.url(params) : config.url;\n const headers = typeof config.headers === 'function' ? await config.headers() : config.headers;\n const method = config.method ?? 'POST';\n\n // GET/HEAD requests can't carry a body, so skip both the body and\n // the Content-Type header for them rather than sending a body a\n // server may reject\n const supportsBody = !['GET', 'HEAD'].includes(method.toUpperCase());\n\n return {\n url,\n method,\n headers: supportsBody ? { 'Content-Type': 'application/json', ...headers } : { ...headers },\n ...(supportsBody ? { body: JSON.stringify(requestBody) } : {}),\n };\n}\n\n/**\n * A fetch-based escape hatch for providers with no SDK, or where pulling one\n * in isnt worth it. You supply the URL, headers, and two small mapping\n * functions; this handles the HTTP call and slots the result into the same\n * `LLMClient` shape every other adapter produces, so retries, timeouts,\n * the circuit breaker, and JSON/schema handling all still work unmodified\n *\n * Non-2xx responses throw an error with `.status` set to the HTTP status\n * code, so VernLLMs `nonRetryableStatus` handling (e.g. failing fast on\n * 401/403) applies here too\n *\n * Tool calling works the same way as every other adapter: `mapRequest`\n * receives the full `ChatRequest`, including `tools`/`toolChoice`, so it can\n * translate them into whatever shape the provider's wire format expects\n * (typically an OpenAI-`function`-wrapped `tools` array plus a `tool_choice`\n * field). On the way back, `mapResponse` may return a `toolCalls` array\n * (id/name/JSON-encoded-arguments-string per call) alongside or instead of\n * `content`; VernLLM parses and (if `argumentsSchema` was set) validates\n * those arguments the same way it does for every other adapter. For\n * `stream: true`, tool-call deltas go through the existing\n * `mapStreamEvent` seam via `WireStreamChunk`'s `tool_call_delta` variant,\n * no separate config is needed for streaming vs non-streaming tool calls.\n *\n\n * `createStream` requires `mapStreamEvent` (there's no non-streaming\n * response to fall back on, unlike the other three optional streaming\n * seams). It opens the request via `requestStream` (defaults to native\n * `fetch`), splits the raw bytes into individual events via\n * `parseStreamFrames` (defaults to SSE framing, see `parseSseStream`),\n * and translates each event into `WireStreamChunk`(s) via\n * `mapStreamEvent`. Both seams are overridable per-config for providers\n * that don't fit the SSE-over-fetch default. If a custom `request`\n * transport is configured, `requestStream` must be configured too,\n * `requestStream` never silently falls back to `request` (see\n * `createStream`'s own comment for why), so a `stream: true` call with\n * `request` set but no `requestStream` throws a clear\n * `LLMError('validation')` instead of quietly using unrelated native\n * `fetch`.\n */\nexport function fromFetch(config: FetchAdapterConfig): LLMClient {\n return {\n chat: {\n completions: {\n async create(params, options) {\n const { url, method, headers, body } = await buildRequestInit(\n config,\n params,\n config.mapRequest(params),\n );\n const request = config.request ?? fetch;\n\n const res = await request(url, { method, headers, body, signal: options.signal });\n\n if (!res.ok) {\n const responseBody = await res.text().catch(() => '');\n const err = new Error(\n `Fetch adapter request failed (${res.status}): ${responseBody.slice(0, 500)}`,\n ) as Error & { status?: number; headers?: ResponseLike['headers'] };\n err.status = res.status;\n // Attach headers so downstream retry logic (e.g. rate-limit\n // handling) can read things like `Retry-After`\n err.headers = res.headers;\n throw err;\n }\n\n const json = await res.json();\n const { content, usage, toolCalls } = config.mapResponse(json);\n\n // `.length` guard, not just truthiness: an empty array is\n // semantically \"no tool calls\", same as `undefined`. Kept\n // explicit here rather than relying on downstream consumers\n // (e.g. `finalizeResponse`'s `wireToolCalls?.length` check) to\n // treat `[]` as absent, so this stays correct even if that\n // convention ever changes.\n const wireToolCalls: WireToolCall[] | undefined = toolCalls?.length\n ? toolCalls.map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: { name: tc.name, arguments: tc.arguments },\n }))\n : undefined;\n\n return {\n choices: [\n {\n message: {\n content,\n ...(wireToolCalls ? { tool_calls: wireToolCalls } : {}),\n },\n },\n ],\n usage: usage\n ? {\n prompt_tokens: usage.promptTokens,\n completion_tokens: usage.completionTokens,\n total_tokens: usage.totalTokens,\n }\n : undefined,\n };\n },\n\n async *createStream(params, options) {\n if (!config.mapStreamEvent) {\n throw new LLMError(\n 'stream: true requires mapStreamEvent to be configured on fromFetch',\n 'invalid_params',\n { code: 'unsupported_capability', issues: { capability: 'mapStreamEvent' } },\n );\n }\n\n // A custom `request` transport (proxying, special auth, test\n // mocking, etc.) is silently irrelevant to streaming unless the\n // caller *also* configures `requestStream`, `requestStream`\n // defaults to plain native `fetch`, not to `config.request`,\n // since `RequestLike`'s buffered `ResponseLike` has no way to\n // expose a byte stream generically. Falling back to native\n // `fetch` anyway would be a surprising, easy-to-miss divergence\n // (bypassing whatever `request` was there for, a proxy, custom\n // auth, or a test's mocked transport, and potentially hitting\n // the real network). Failing loudly here instead of guessing.\n if (config.request && !config.requestStream) {\n throw new LLMError(\n '`stream: true` requires `requestStream` to be configured on fromFetch when a ' +\n 'custom `request` transport is set. `requestStream` does not fall back to ' +\n \"`request` (it needs an async-iterable byte stream, which `RequestLike`'s \" +\n 'buffered `ResponseLike` has no way to provide), without it, `stream: true` ' +\n 'would silently use plain native `fetch` instead of your configured transport. ' +\n 'Add a `requestStream` that opens the same connection your `request` does, or ' +\n 'omit `request` if native `fetch` is fine for both.',\n 'invalid_params',\n { code: 'unsupported_capability', issues: { capability: 'requestStream' } },\n );\n }\n\n const { url, method, headers, body } = await buildRequestInit(\n config,\n params,\n config.mapRequest(params),\n );\n const requestStream = config.requestStream ?? defaultRequestStream;\n const parseFrames = config.parseStreamFrames ?? parseSseStream;\n\n const byteStream = await requestStream(url, {\n method,\n headers,\n body,\n signal: options.signal,\n });\n\n for await (const event of parseFrames(byteStream)) {\n // Only the default parseSseStream produces this sentinel (an\n // SSE comment line used as a keep-alive ping). Handled here,\n // before mapStreamEvent, since provider-specific mapping\n // shouldn't need to know about SSE framing internals.\n if (event === SSE_PING) {\n yield { type: 'ping' };\n continue;\n }\n\n const wireChunks = config.mapStreamEvent(event);\n\n if (!wireChunks) continue;\n\n if (Array.isArray(wireChunks)) {\n yield* wireChunks;\n } else {\n yield wireChunks;\n }\n }\n },\n },\n },\n };\n}\n","import { assertSupportedImageMimeType } from './internal/imageFormat.js';\n\nimport type { ContentBlock, LLMClient, WireStreamChunk } from '../types/index.js';\n\n/** OpenAI's native per-part content shape for a user message. */\ntype OpenAIContentPart =\n | { type: 'text'; text: string }\n | { type: 'image_url'; image_url: { url: string } };\n\n/**\n * Translates a VernLLM `ContentBlock[]` into OpenAI's wire-level content\n * array. Text blocks become `{ type: 'text', text }`; image blocks become\n * `{ type: 'image_url', image_url: { url } }` with the base64 payload\n * inlined as a `data:` URL, since our `ContentBlock` shape (`{ type:\n * 'image', data, mimeType }`) is provider-agnostic and doesn't itself match\n * OpenAI's wire format.\n */\nfunction toOpenAIContent(blocks: ContentBlock[]): OpenAIContentPart[] {\n return blocks.map((block) =>\n block.type === 'image'\n ? {\n type: 'image_url',\n image_url: {\n url: `data:${assertSupportedImageMimeType(block.mimeType)};base64,${block.data}`,\n },\n }\n : { type: 'text', text: block.text },\n );\n}\n\n/** One chunk of an OpenAI-shaped `chat.completions.create({ stream: true })` SSE stream. */\ninterface OpenAIStreamChunk {\n choices?: Array<{\n delta?: {\n content?: string | null;\n tool_calls?: Array<{\n index: number;\n id?: string;\n function?: { name?: string; arguments?: string };\n }>;\n };\n }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number };\n}\n\n/**\n * Translates VernLLM's provider-agnostic `messages` (the one part of a\n * request that isn't a pure passthrough for OpenAI-compatible clients) into\n * OpenAI's native wire shape. Shared between `create` and `createStream` so\n * both go through identical message translation.\n */\nfunction toOpenAIMessages(\n params: Parameters<LLMClient['chat']['completions']['create']>[0],\n): unknown[] {\n return params.messages.map((m) => {\n if (m.role === 'user' && Array.isArray(m.content)) {\n return { ...m, content: toOpenAIContent(m.content) };\n }\n\n if (m.role === 'tool') {\n const { is_error: _isError, ...openAIToolMessage } = m;\n return openAIToolMessage;\n }\n\n return m;\n });\n}\n\n/**\n * Translates one OpenAI-shaped SSE chunk into zero or more `WireStreamChunk`s.\n * A single chunk can carry a text delta, one or more tool-call argument\n * deltas (each keyed by `index`, OpenAI's own convention for streaming\n * parallel tool calls, mirrored directly by VernLLM's `tool_call_delta`\n * shape so accumulation composes without translation), and/or a final\n * usage block (present only when `stream_options.include_usage` is set,\n * which this adapter always sets).\n */\nfunction* toWireStreamChunks(chunk: OpenAIStreamChunk): Generator<WireStreamChunk> {\n const delta = chunk.choices?.[0]?.delta;\n\n if (delta?.content) {\n yield { type: 'text-delta', delta: delta.content };\n }\n\n if (delta?.tool_calls?.length) {\n for (const toolCall of delta.tool_calls) {\n yield {\n type: 'tool_call_delta',\n index: toolCall.index,\n id: toolCall.id,\n name: toolCall.function?.name,\n argumentsDelta: toolCall.function?.arguments,\n };\n }\n }\n\n if (chunk.usage) {\n yield { type: 'usage', usage: chunk.usage };\n }\n}\n\n/**\n * Adapter for any SDK/client whose `chat.completions.create` already\n * matches the OpenAI wire format: this covers most hosted inference\n * providers, since \"OpenAI-compatible\" is a de facto standard for chat\n * completion APIs. Almost everything passes straight through untouched,\n * this exists purely so call sites read clearly (`fromMistral(client)` vs\n * handing a Mistral client to something typed for OpenAI) and so a real\n * transformation could be added later, per-provider, without a breaking\n * change.\n *\n * The one thing that isn't a pure passthrough: a `ContentBlock[]`\n * `userContent` is translated into OpenAI's native `image_url` content-part\n * shape, since VernLLM's `ContentBlock` is intentionally provider-agnostic\n * rather than a copy of any one provider's wire format.\n *\n * Not every SDKs own TypeScript types line up exactly with `LLMClient`\n * (extra fields, stricter unions, etc.), so this takes `unknown` and casts:\n * the actual compatibility contract is the JSON each provider sends and\n * receives over the wire, not the SDKs TS types.\n *\n * `createStream` is implemented by calling the same underlying\n * `chat.completions.create` with `stream: true` (and, for providers that\n * support it, `stream_options: { include_usage: true }`, so a final usage\n * block arrives), the OpenAI SDK, and every OpenAI-compatible client\n * modeled on it, returns an `AsyncIterable` of SSE chunks instead of a\n * single completion object when `stream: true` is set. Each chunk is\n * translated into `WireStreamChunk`(s) via `toWireStreamChunks`.\n *\n * Note on long-running reasoning models: this adapter consumes the\n * underlying SDK's already-parsed stream rather than raw SSE bytes, so\n * unlike `fromFetch`/`fromAnthropic` it cannot see comment-only keep-alive\n * ping frames. Combined with `chunkIdleTimeoutMs`'s 30 second default and\n * `reasoningEffort` (documented to have long silent gaps for o-series and\n * similar models), a long-running reasoning call on this adapter can trip\n * the idle timeout even though the provider is still working. Raise or\n * disable `chunkIdleTimeoutMs` per call for those routes, see `CallParams`.\n */\nexport interface OpenAICompatibleAdapterOptions {\n /**\n * Whether the provider supports `stream_options.include_usage`. Not\n * every \"OpenAI-compatible\" provider is guaranteed to, so this defaults\n * to `true` (matching OpenAI, Groq, Mistral, and most others observed)\n * and should be set to `false` for a provider verified not to support\n * it. When `false`, `stream_options` is omitted entirely and no usage\n * block will arrive on the stream; callers relying on streamed `usage`\n * with such a provider won't get one.\n */\n supportsStreamUsage?: boolean;\n}\n\nexport function fromOpenAICompatible(\n client: unknown,\n options: OpenAICompatibleAdapterOptions = {},\n): LLMClient {\n const raw = client as LLMClient;\n const { supportsStreamUsage = true } = options;\n\n // The underlying client's `create`, called with `stream: true`, returns\n // an AsyncIterable of `OpenAIStreamChunk` rather than\n // `LLMClient['create']`'s normal single-completion return type, hence\n // `unknown` here and a cast at the call site, same rationale as casting\n // the whole client above: the wire contract, not the SDK's own TS types,\n // is what's actually being relied on.\n const rawCreate = raw.chat.completions.create.bind(raw.chat.completions) as unknown as (\n params: unknown,\n options: { signal: AbortSignal },\n ) => Promise<unknown> | AsyncIterable<OpenAIStreamChunk>;\n\n return {\n chat: {\n completions: {\n async create(params, options) {\n const messages = toOpenAIMessages(params);\n\n return raw.chat.completions.create(\n { ...params, messages } as Parameters<LLMClient['chat']['completions']['create']>[0],\n options,\n );\n },\n\n async *createStream(params, options) {\n const messages = toOpenAIMessages(params);\n\n const stream = (await rawCreate(\n {\n ...params,\n messages,\n stream: true,\n ...(supportsStreamUsage ? { stream_options: { include_usage: true } } : {}),\n },\n options,\n )) as AsyncIterable<OpenAIStreamChunk>;\n\n for await (const chunk of stream) {\n yield* toWireStreamChunks(chunk);\n }\n },\n },\n },\n };\n}\n\n// LLM aliases\n\n/**\n * Named alias for the OpenAI SDK itself. A raw `new OpenAI(...)` instance\n * structurally matches most of `LLMClient`, but newer `openai` SDK major\n * versions have widened `ChatCompletionContentPart` (e.g. adding a `file`\n * variant) in ways that no longer structurally satisfy VernLLM's\n * provider-agnostic `ContentBlock[]` on `userContent`, so passing the SDK\n * instance directly can fail to typecheck depending on the installed\n * `openai` version. Wrapping with `fromOpenAI()` (a plain alias of\n * `fromOpenAICompatible()`) sidesteps that by translating through\n * `unknown` at the boundary, and also picks up multimodal image\n * translation and `createStream` wiring that a raw client doesn't have.\n * See Migration Notes for details.\n */\nexport const fromOpenAI = fromOpenAICompatible;\n\n/** Groqs SDK matches the OpenAI wire format */\nexport const fromGroq = fromOpenAICompatible;\n\n/**\n * Mistrals `chat.completions`-shaped client (or their OpenAI-compat\n * endpoint). Mistral supports `stream_options.include_usage` (added after\n * an earlier period where it returned a 422 for unrecognized fields, per\n * Mistral's changelog and streaming docs), so this is a plain alias like\n * the others, `supportsStreamUsage` defaults to `true`.\n */\nexport const fromMistral = fromOpenAICompatible;\n\n/** DeepSeeks API is OpenAI-compatible */\nexport const fromDeepSeek = fromOpenAICompatible;\n\n/** Cerebras inference API is OpenAI-compatible */\nexport const fromCerebras = fromOpenAICompatible;\n\n/** Together AIs API is OpenAI-compatible */\nexport const fromTogether = fromOpenAICompatible;\n\n/** Fireworks AIs API is OpenAI-compatible */\nexport const fromFireworks = fromOpenAICompatible;\n\n/**\n * Ollama exposes an OpenAI-compatible endpoint at `/v1/chat/completions`\n * (as opposed to its native `/api/chat` format, which differs). Point an\n * OpenAI SDK instances `baseURL` at your Ollama server and pass it here:\n * this does not talk to Ollamas native API directly.\n */\nexport const fromOllama = fromOpenAICompatible;\n\n/** OpenRouter's API is OpenAI-compatible */\nexport const fromOpenRouter = fromOpenAICompatible;\n\n/** Perplexity's API is OpenAI-compatible */\nexport const fromPerplexity = fromOpenAICompatible;\n\n/** DeepInfra's API is OpenAI-compatible */\nexport const fromDeepInfra = fromOpenAICompatible;\n\n/** Novita's API is OpenAI-compatible */\nexport const fromNovita = fromOpenAICompatible;\n\n/** Hyperbolic's API is OpenAI-compatible */\nexport const fromHyperbolic = fromOpenAICompatible;\n\n/** Moonshot's (Kimi) API is OpenAI-compatible */\nexport const fromMoonshot = fromOpenAICompatible;\n\n/** Zhipu's (GLM) API is OpenAI-compatible */\nexport const fromZhipu = fromOpenAICompatible;\n\n/**\n * LM Studio exposes an OpenAI-compatible endpoint at `/v1/chat/completions`.\n * Point an OpenAI SDK instance's `baseURL` at your local LM Studio server.\n */\nexport const fromLMStudio = fromOpenAICompatible;\n\n/**\n * vLLM's OpenAI-compatible server mode exposes `/v1/chat/completions`.\n * Point an OpenAI SDK instance's `baseURL` at your vLLM server.\n */\nexport const fromVLLM = fromOpenAICompatible;\n\n/** xAI's Grok API is OpenAI-compatible */\nexport const fromXAI = fromOpenAICompatible;\n\n/** NVIDIA NIM's hosted and self-hosted endpoints are OpenAI-compatible */\nexport const fromNvidiaNIM = fromOpenAICompatible;\n\n/** Vercel AI Gateway is OpenAI-compatible */\nexport const fromVercelAIGateway = fromOpenAICompatible;\n\n/** Cloudflare Workers AI exposes an OpenAI-compatible endpoint */\nexport const fromCloudflareWorkersAI = fromOpenAICompatible;\n\n/** Nebius AI Studio is OpenAI-compatible */\nexport const fromNebius = fromOpenAICompatible;\n\n/** SambaNova Cloud's API is OpenAI-compatible */\nexport const fromSambaNova = fromOpenAICompatible;\n\n/** Baseten's model hosting exposes an OpenAI-compatible endpoint */\nexport const fromBaseten = fromOpenAICompatible;\n\n/** Featherless AI's API is OpenAI-compatible */\nexport const fromFeatherless = fromOpenAICompatible;\n\n/** Friendli AI's serving endpoint is OpenAI-compatible */\nexport const fromFriendli = fromOpenAICompatible;\n\n/** SiliconFlow's API is OpenAI-compatible */\nexport const fromSiliconFlow = fromOpenAICompatible;\n\n/** Parasail's inference API is OpenAI-compatible */\nexport const fromParasail = fromOpenAICompatible;\n\n/** StepFun's API is OpenAI-compatible */\nexport const fromStepFun = fromOpenAICompatible;\n\n/** MiniMax's API is OpenAI-compatible */\nexport const fromMiniMax = fromOpenAICompatible;\n\n/** Lambda Labs' Inference API is OpenAI-compatible */\nexport const fromLambdaLabs = fromOpenAICompatible;\n\n/** Snowflake Cortex's LLM endpoint is OpenAI-compatible */\nexport const fromSnowflakeCortex = fromOpenAICompatible;\n\n/** Anyscale Endpoints' API is OpenAI-compatible */\nexport const fromAnyscale = fromOpenAICompatible;\n\n/** Lepton AI's inference API is OpenAI-compatible */\nexport const fromLepton = fromOpenAICompatible;\n\n/** Inference.net's API is OpenAI-compatible */\nexport const fromInferenceNet = fromOpenAICompatible;\n\n/** Infermatic's API is OpenAI-compatible */\nexport const fromInfermatic = fromOpenAICompatible;\n\n/** AtlasCloud's inference API is OpenAI-compatible */\nexport const fromAtlasCloud = fromOpenAICompatible;\n\n/** 01.AI's (Yi models) API is OpenAI-compatible */\nexport const from01AI = fromOpenAICompatible;\n"],"mappings":"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"}
|