vern-llm 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,19 +64,6 @@ const result = await llm.call({
64
64
 
65
65
  See the [docs](https://vernllm.vercel.app) for adapter setup, caching, the circuit breaker, and structured output in depth.
66
66
 
67
- ## Development
68
-
69
- ```bash
70
- pnpm install
71
- pnpm run build # tsdown → dist (ESM + CJS + types)
72
- pnpm run typecheck # tsc --noEmit on src, since tsdown doesn't fully type-check
73
- pnpm run test # vitest run
74
- pnpm run test:coverage # vitest run --coverage (v8 provider)
75
- pnpm run changeset # record a change for the next release
76
- ```
77
-
78
- Tests live in `tests/`, mirroring `src/`, and cover retry/backoff/timeout/abort/schema/model-override/usage behavior, the circuit breaker (unit + integration), caching, the injectable logger, and every provider adapter's request/response translation against a fake client — no real API calls anywhere in the suite.
79
-
80
67
  ## License
81
68
 
82
69
  [MIT](https://github.com/LakBud/vernLLM/blob/main/LICENSE.md) © LakBud
package/dist/index.cjs CHANGED
@@ -410,15 +410,16 @@ var VernLLM = class {
410
410
  this.breaker?.assertClosed();
411
411
  if (params.signal?.aborted) throw new LLMError("LLM request aborted", "aborted");
412
412
  const requestId = params.requestId ?? (0, crypto.randomUUID)();
413
- try {
414
- const result = await this.retryWithBackoff(() => this.executeCall(params, requestId), requestId, params.signal);
415
- return result;
416
- } catch (error) {
417
- this.breaker?.recordFailure();
418
- const normalized = this.normalizeError(error, params.signal);
419
- this.logger.debug(`[vern:${requestId}] error:\n${describeError(error)}`);
420
- throw normalized;
421
- }
413
+ return this.withReservedUsage(params, false, async () => {
414
+ try {
415
+ return await this.retryWithBackoff(() => this.executeCall(params, requestId), requestId, params.signal);
416
+ } catch (error) {
417
+ const normalized = this.normalizeError(error, params.signal);
418
+ if (normalized.type !== "validation" && normalized.type !== "parse" && normalized.type !== "aborted") this.breaker?.recordFailure();
419
+ this.logger.debug(`[vern:${requestId}] error:\n${describeError(error)}`);
420
+ throw normalized;
421
+ }
422
+ }, params.signal);
422
423
  }
423
424
  /**
424
425
  * Runs `fn`, retrying with backoff according to `shouldRetry` policy
@@ -463,9 +464,13 @@ var VernLLM = class {
463
464
  if (!content) throw new LLMError("Empty LLM response", "api");
464
465
  this.logger.debug(`[vern:${requestId}] output:\n${content.slice(0, 800)}`);
465
466
  this.recordUsage(response, requestId, model);
467
+ if (!useJson) {
468
+ this.breaker?.recordSuccess();
469
+ return content;
470
+ }
471
+ const result = this.parseAndValidate(content, params.schema);
466
472
  this.breaker?.recordSuccess();
467
- if (!useJson) return content;
468
- return this.parseAndValidate(content, params.schema);
473
+ return result;
469
474
  }
470
475
  /**
471
476
  * Anthropic and Gemini both require strict user/assistant alternation
@@ -546,16 +551,24 @@ var VernLLM = class {
546
551
  * Reports token usage to the caller supplied onUsage callback, when
547
552
  * both a callback was configured and the provider actually returned
548
553
  * usage data on this response. A no op otherwise.
554
+ *
555
+ * A throwing onUsage callback is logged and swallowed rather than
556
+ * propagated, so a broken billing/metrics hook can't fail or retrigger
557
+ * retries on an otherwise-successful call.
549
558
  */
550
559
  recordUsage(response, requestId, model) {
551
560
  if (!response.usage || !this.onUsage) return;
552
- this.onUsage({
553
- promptTokens: response.usage.prompt_tokens ?? 0,
554
- completionTokens: response.usage.completion_tokens ?? 0,
555
- totalTokens: response.usage.total_tokens ?? 0,
556
- requestId,
557
- model
558
- });
561
+ try {
562
+ this.onUsage({
563
+ promptTokens: response.usage.prompt_tokens ?? 0,
564
+ completionTokens: response.usage.completion_tokens ?? 0,
565
+ totalTokens: response.usage.total_tokens ?? 0,
566
+ requestId,
567
+ model
568
+ });
569
+ } catch (error) {
570
+ this.logger.error("[VernLLM] onUsage failed", { message: error instanceof Error ? error.message : "unknown" });
571
+ }
559
572
  }
560
573
  /**
561
574
  * Parses the raw response content as JSON and, when a schema is
@@ -629,19 +642,12 @@ var VernLLM = class {
629
642
  if (cached.hit) return cached.value;
630
643
  const existing = this.inFlight.get(params.cacheKey);
631
644
  const coalesced = existing !== void 0;
632
- const resultPromise = existing ?? this.registerTrigger(params, coalesced);
633
- if (coalesced) return this.withRefundOnFailure(params, coalesced, async () => {
634
- await params.reserveUsage?.({ coalesced });
635
- return resultPromise;
636
- });
637
- return this.withRefundOnFailure(params, coalesced, () => resultPromise);
645
+ if (coalesced) return this.withReservedUsage(params, coalesced, () => existing, params.signal);
646
+ return this.registerTrigger(params, coalesced);
638
647
  }
639
648
  /** Starts the shared fn() call for a cache miss, reserving usage first, and registers it in the in-flight map until it settles */
640
649
  registerTrigger(params, coalesced) {
641
- const resultPromise = (async () => {
642
- await params.reserveUsage?.({ coalesced });
643
- return this.runAndCache(params);
644
- })();
650
+ const resultPromise = this.withReservedUsage(params, coalesced, () => this.runAndCache(params), params.signal);
645
651
  this.inFlight.set(params.cacheKey, resultPromise);
646
652
  resultPromise.catch(() => {}).finally(() => {
647
653
  this.inFlight.delete(params.cacheKey);
@@ -658,13 +664,48 @@ var VernLLM = class {
658
664
  }
659
665
  return result;
660
666
  }
661
- /** Awaits `run`, calling this caller's own refundUsage (tagged with whether it was coalesced) if it rejects, then rethrows the original error */
662
- async withRefundOnFailure(params, coalesced, run) {
667
+ /**
668
+ * Runs `getResult` after reserving usage, if a `reserveUsage` hook was
669
+ * provided. `refundUsage` fires only if a reservation was actually made,
670
+ * i.e. `reserveUsage` was provided and it resolved successfully. If
671
+ * `reserveUsage` is omitted entirely, or if it throws, there is nothing to
672
+ * refund, so `refundUsage` is not invoked in either case.
673
+ *
674
+ * Shared by `cachedCall()`/`registerTrigger()` and `call()`, so it only
675
+ * depends on the usage hooks rather than LLM request concerns.
676
+ */
677
+ async withReservedUsage(params, coalesced, getResult, signal) {
678
+ let reserved = false;
663
679
  try {
664
- return await run();
680
+ if (params.reserveUsage) {
681
+ await params.reserveUsage({
682
+ coalesced,
683
+ signal
684
+ });
685
+ reserved = true;
686
+ }
665
687
  } catch (error) {
666
- try {
667
- await params.refundUsage?.({ coalesced });
688
+ throw new LLMError(error instanceof Error ? error.message : "Usage reservation failed", "quota_exceeded", void 0, void 0, error);
689
+ }
690
+ if (signal?.aborted) {
691
+ if (reserved) try {
692
+ await params.refundUsage?.({
693
+ coalesced,
694
+ signal
695
+ });
696
+ } catch (refundError) {
697
+ this.logger.error("[VernLLM] refundUsage failed after abort", { message: refundError instanceof Error ? refundError.message : "unknown" });
698
+ }
699
+ throw new LLMError("LLM request aborted", "aborted");
700
+ }
701
+ try {
702
+ return await getResult();
703
+ } catch (error) {
704
+ if (reserved) try {
705
+ await params.refundUsage?.({
706
+ coalesced,
707
+ signal
708
+ });
668
709
  } catch (refundError) {
669
710
  this.logger.error("[VernLLM] refundUsage failed", { message: refundError instanceof Error ? refundError.message : "unknown" });
670
711
  }
@@ -674,13 +715,20 @@ var VernLLM = class {
674
715
  /**
675
716
  * Convenience wrapper composing `call` + `cachedCall`, so cached LLM calls
676
717
  * automatically get retry/timeout/circuit-breaker behavior without callers
677
- * having to remember to wire `fn: () => this.call(...)` themselves
718
+ * having to remember to wire `fn: () => this.call(...)` themselves.
719
+ *
720
+ * `reserveUsage`/`refundUsage` are read from the top-level params only — if
721
+ * `call` also sets them, they're ignored, since `call()` now performs its
722
+ * own reservation. Honoring both would reserve/refund twice per logical
723
+ * cachedLLMCall (once via cachedCall's wrapping, once via the inner call()).
678
724
  */
679
725
  async cachedLLMCall(params) {
680
726
  const { call: callParams,...cacheParams } = params;
727
+ const { reserveUsage: _innerReserveUsage, refundUsage: _innerRefundUsage,...restCallParams } = callParams;
728
+ if (_innerReserveUsage || _innerRefundUsage) this.logger.warn("[VernLLM] reserveUsage/refundUsage on `call` are ignored by cachedLLMCall; set them at the top level instead.");
681
729
  return this.cachedCall({
682
730
  ...cacheParams,
683
- fn: () => this.call(callParams)
731
+ fn: () => this.call(restCallParams)
684
732
  });
685
733
  }
686
734
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["message: string","type: LLMErrorType","status?: number","issues?: unknown","cause?: unknown","retryAfterMs?: number","err: unknown","options: CircuitBreakerOptions","content: string","err: unknown","value: unknown","fn: (signal: AbortSignal) => Promise<T>","timeoutMs: number","externalSignal?: AbortSignal","baseDelayMs: number","attempt: number","delay: number","signal?: AbortSignal","debugEnabled: boolean","message: string","meta?: Record<string, unknown>","key: string","value: T","ttl: number","options: VernLLMOptions","params: CallParams<T>","fn: () => Promise<T>","requestId: string","signal?: AbortSignal","lastError: unknown","error: unknown","history: ConversationTurn[]","previousRole: 'user' | 'assistant' | undefined","jsonSchema: CallParams<unknown>['jsonSchema']","useJson: boolean","response: Awaited<ReturnType<LLMClient['chat']['completions']['create']>>","model: string","content: string","schema?: CallParams<T>['schema']","parsed: unknown","attempt: number","key: string","params: CachedCallParams<T>","coalesced: boolean","run: () => Promise<T>","params: Omit<CachedCallParams<T>, 'fn'> & { call: CallParams<T> }","mimeType: string","blocks: ContentBlock[]","anthropicClient: AnthropicClient","jsonInstruction: string | undefined","tools:\n | NonNullable<Parameters<AnthropicClient['messages']['create']>[0]['tools']>\n | undefined","text: string","blocks: ContentBlock[]","geminiClient: GeminiClient","generationConfig: NonNullable<\n Parameters<GeminiClient['generateContent']>[0]['generationConfig']\n >","mimeType: string","data: string","blocks: ContentBlock[]","bedrockClient: BedrockConverseClient","jsonInstruction: string | undefined","toolConfig:\n | NonNullable<Parameters<BedrockConverseClient['converse']>[0]['toolConfig']>\n | undefined","text","text: string","config: FetchAdapterConfig","blocks: ContentBlock[]","client: unknown"],"sources":["../src/types/errors.ts","../src/circuitBreaker.ts","../src/internal/vernLLM.utils.ts","../src/logger.ts","../src/types/cache.ts","../src/vernLLM.ts","../src/internal/imageFormat.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 | 'unknown'\n | 'aborted';\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 ) {\n super(message);\n this.name = 'LLMError';\n }\n}\n\nexport function isLLMError(err: unknown): err is LLMError {\n return err instanceof LLMError;\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\ntype CircuitState = 'closed' | 'open' | 'half-open';\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 state: CircuitState = 'closed';\n private consecutiveFailures = 0;\n private openedAt = 0;\n private threshold: number;\n private cooldownMs: 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 private trialInFlight = false;\n\n constructor(options: CircuitBreakerOptions = {}) {\n this.threshold = options.threshold ?? 5;\n this.cooldownMs = options.cooldownMs ?? 30_000;\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(): void {\n if (this.state === 'closed') return;\n\n if (this.state === 'open') {\n const elapsed = Date.now() - this.openedAt;\n if (elapsed < this.cooldownMs) {\n throw new LLMError(\n `Circuit open — provider has failed ${this.consecutiveFailures} times in a row. Retry in ${Math.ceil((this.cooldownMs - elapsed) / 1000)}s.`,\n 'circuit_open',\n );\n }\n\n this.state = 'half-open';\n this.trialInFlight = true;\n return;\n }\n\n // state === 'half-open'\n if (this.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 this.trialInFlight = true;\n }\n\n recordSuccess(): void {\n this.consecutiveFailures = 0;\n this.state = 'closed';\n this.trialInFlight = false;\n }\n\n recordFailure(): void {\n this.consecutiveFailures += 1;\n this.trialInFlight = false;\n\n if (this.state === 'half-open') {\n // Trial call failed: reopen and reset the cooldown window.\n this.state = 'open';\n this.openedAt = Date.now();\n return;\n }\n\n if (this.consecutiveFailures >= this.threshold) {\n this.state = 'open';\n this.openedAt = Date.now();\n }\n }\n\n getState(): CircuitState {\n return this.state;\n }\n}\n","import { LLMError } from '../types/errors.js';\n\nexport function defaultParseJson(content: string): unknown {\n try {\n return JSON.parse(content);\n } catch {\n return undefined;\n }\n}\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 * 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 };\n\n if (typeof error.status === 'number') return error.status;\n if (typeof error.statusCode === 'number') return error.statusCode;\n\n return undefined;\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 * 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 */\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 timer = setTimeout(() => {\n controller.abort();\n }, timeoutMs);\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 * 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 * 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 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","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","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}\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","import { randomUUID } from 'crypto';\n\nimport { CircuitBreaker } from './circuitBreaker.js';\nimport {\n defaultParseJson,\n extractStatus,\n extractRetryAfterMs,\n withTimeout,\n getBackoffDelay,\n waitForRetry,\n describeError,\n} from './internal/vernLLM.utils.js';\nimport { ConsoleLogger, type Logger } from './logger.js';\nimport {\n InMemoryCacheAdapter,\n LLMError,\n type CacheAdapter,\n type CachedCallParams,\n type CallParams,\n type ConversationTurn,\n type LLMClient,\n type VernLLMOptions,\n} from './types/index.js';\n\n/**\n * A resilient wrapper around an LLM chat completions client, this is VernLLM!\n *\n * Adds retry with exponential backoff and jitter, per-attempt timeouts,\n * an optional circuit breaker, JSON parsing with optional schema\n * validation, usage tracking, and an optional response cache, all\n * configurable, all opt-in beyond sensible defaults\n */\nexport class VernLLM {\n private readonly client: LLMClient;\n private readonly model: string;\n\n private readonly maxRetries: number;\n private readonly timeoutMs: number;\n private readonly baseDelayMs: number;\n private readonly defaultMaxTokens: number;\n\n private readonly cache: CacheAdapter<unknown>;\n private readonly nonRetryableStatus: number[];\n\n private readonly inFlight = new Map<string, Promise<unknown>>();\n\n private readonly parseJson: (content: string) => unknown;\n private readonly onUsage?: VernLLMOptions['onUsage'];\n\n private readonly logger: Logger;\n private readonly breaker?: CircuitBreaker;\n\n /**\n * @param options: Client, model, and all tunables (retries, timeout,\n * backoff, cache, circuit breaker, logger, etc). See VernLLMOptions in `types.ts`\n * for individual defaults\n */\n constructor(options: VernLLMOptions) {\n this.client = options.client;\n this.model = options.model;\n\n const retryConfig = this.resolveRetryConfig(options);\n this.maxRetries = retryConfig.maxRetries;\n this.timeoutMs = retryConfig.timeoutMs;\n this.baseDelayMs = retryConfig.baseDelayMs;\n this.defaultMaxTokens = retryConfig.defaultMaxTokens;\n\n this.cache = options.cache ?? new InMemoryCacheAdapter();\n this.nonRetryableStatus = options.nonRetryableStatus ?? [400, 401, 403, 404, 422];\n\n this.parseJson = options.parseJson ?? defaultParseJson;\n this.onUsage = options.onUsage;\n\n this.logger = this.resolveLogger(options);\n this.breaker = this.resolveCircuitBreaker(options);\n }\n\n /**\n * Resolves retry/timeout/token defaults from the given options,\n * falling back to the librarys built-in defaults for anything unset\n */\n private resolveRetryConfig(options: VernLLMOptions) {\n return {\n maxRetries: options.maxRetries ?? 1,\n timeoutMs: options.timeoutMs ?? 25_000,\n baseDelayMs: options.baseDelayMs ?? 500,\n defaultMaxTokens: options.defaultMaxTokens ?? 1000,\n };\n }\n\n /**\n * Returns the caller supplied logger, or a console-based logger whose\n * debug output is gated by the `debug` option (defaulting to off,\n * so response content isn't unintentionally written to logs)\n */\n private resolveLogger(options: VernLLMOptions): Logger {\n return options.logger ?? new ConsoleLogger(options.debug ?? false);\n }\n\n /**\n * Builds a circuit breaker if `circuitBreaker` is truthy on the\n * options. Passing `true` uses default thresholds, passing an options\n * object tunes them. Returns undefined when the breaker is disabled\n */\n private resolveCircuitBreaker(options: VernLLMOptions): CircuitBreaker | undefined {\n if (!options.circuitBreaker) return undefined;\n\n return new CircuitBreaker(options.circuitBreaker === true ? undefined : options.circuitBreaker);\n }\n\n /**\n * Makes a single logical LLM call, transparently retrying on failure\n * according to the configured retry policy\n *\n * Fails fast if the circuit breaker is open or the signal is already\n * aborted, before any request is dispatched. On exhausting all\n * retries, records a circuit breaker failure and rejects with a\n * normalized LLMError\n *\n * @param params : System/user content plus per call overrides\n * (model, temperature, jsonMode, schema, signal, etc)\n * @returns The parsed and optionally schema-validated response, or\n * the raw string content when jsonMode is disabled\n */\n async call<T = unknown>(params: CallParams<T>): Promise<T> {\n this.breaker?.assertClosed();\n\n if (params.signal?.aborted) {\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n const requestId = params.requestId ?? randomUUID();\n\n try {\n const result = await this.retryWithBackoff(\n () => this.executeCall(params, requestId),\n requestId,\n params.signal,\n );\n\n return result;\n } catch (error) {\n this.breaker?.recordFailure();\n const normalized = this.normalizeError(error, params.signal);\n this.logger.debug(`[vern:${requestId}] error:\\n${describeError(error)}`);\n throw normalized;\n }\n }\n\n /**\n * Runs `fn`, retrying with backoff according to `shouldRetry` policy\n * Purely mechanical: knows nothing about LLM specifics beyond the retry\n * predicate, so its testable independent of request/response shaping\n */\n private async retryWithBackoff<T>(\n fn: () => Promise<T>,\n requestId: string,\n signal?: AbortSignal,\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, attempt, lastError, signal);\n }\n\n return await fn();\n } catch (error) {\n lastError = error;\n\n if (!this.shouldRetry(error, signal)) {\n break;\n }\n }\n }\n\n throw lastError;\n }\n\n /**\n * Converts any thrown value into a well-typed LLMError for the public\n * API surface. Preserves an existing LLMError as is, reports aborted\n * signals as such, classifies errors carrying an http status as\n * type api, and otherwise falls back to a generic unknown error.\n */\n private 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 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', status, undefined, error, retryAfterMs);\n }\n\n return new LLMError('LLM request failed', 'unknown', undefined, undefined, error, retryAfterMs);\n }\n\n /**\n * Performs a single attempt: builds the request, dispatches it with a\n * timeout, and shapes the response. Throws on an empty response so\n * the retry loop treats it like any other transient failure. Records\n * usage and a circuit breaker success before returning\n */\n private async executeCall<T>(params: CallParams<T>, requestId: string): Promise<T> {\n const { useJson, model, request } = this.buildRequestPayload(params);\n\n const response = await withTimeout(\n (attemptSignal) => this.client.chat.completions.create(request, { signal: attemptSignal }),\n this.timeoutMs,\n params.signal,\n );\n\n const content = response.choices?.[0]?.message?.content?.trim();\n\n if (!content) {\n throw new LLMError('Empty LLM response', 'api');\n }\n\n this.logger.debug(`[vern:${requestId}] output:\\n${content.slice(0, 800)}`);\n\n this.recordUsage(response, requestId, model);\n\n this.breaker?.recordSuccess();\n\n if (!useJson) {\n return content as T;\n }\n\n return this.parseAndValidate(content, params.schema);\n }\n\n /**\n * Anthropic and Gemini both require strict user/assistant alternation\n * (and reject or silently mishandle two consecutive same-role turns), so\n * this validates `history` up front rather than letting a malformed\n * request surface as a confusing provider-side error. Thrown as a\n * validation LLMError, which `shouldRetry` never retries, since retrying\n * the same malformed input can't succeed\n */\n private validateHistory(history: ConversationTurn[]): void {\n let previousRole: 'user' | 'assistant' | undefined;\n\n for (const [index, turn] of history.entries()) {\n if (turn.role !== 'user' && turn.role !== 'assistant') {\n throw new LLMError(\n `Invalid history[${index}].role \"${turn.role}\": must be \"user\" or \"assistant\"`,\n 'validation',\n );\n }\n\n if (turn.role === previousRole) {\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 previousRole = turn.role;\n }\n\n if (previousRole === 'user') {\n throw new LLMError(\n 'The last entry in history is a \"user\" turn, which would collide with the current userContent turn. history must end with an \"assistant\" turn (or be empty).',\n 'validation',\n );\n }\n }\n\n /**\n * Applies per call defaults and shapes the params into the request\n * object expected by the underlying client, including the resolved\n * response format. Also returns whether JSON parsing should be\n * applied to the response and which model was ultimately used\n */\n private buildRequestPayload<T>(params: CallParams<T>) {\n const {\n systemPrompt,\n userContent,\n history = [],\n temperature = 0.2,\n jsonMode = true,\n maxTokens = this.defaultMaxTokens,\n model = this.model,\n reasoningEffort,\n jsonSchema,\n } = params;\n\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,\n max_tokens: maxTokens,\n ...(responseFormat ? { response_format: responseFormat } : {}),\n ...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),\n messages: [\n ...(systemPrompt ? [{ role: 'system' as const, content: systemPrompt }] : []),\n ...history.map((turn) => ({ role: turn.role, content: turn.content })),\n { role: 'user' as const, content: userContent },\n ],\n };\n\n return { useJson, model, request };\n }\n\n /**\n * Chooses the response format to send to the provider. A provider\n * native json schema takes priority when supplied, constraining\n * generation directly, otherwise falls back to the looser json\n * object mode when JSON output is requested, or no format at all\n * 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 /**\n * Reports token usage to the caller supplied onUsage callback, when\n * both a callback was configured and the provider actually returned\n * usage data on this response. A no op otherwise.\n */\n private recordUsage(\n response: Awaited<ReturnType<LLMClient['chat']['completions']['create']>>,\n requestId: string,\n model: string,\n ): void {\n if (!response.usage || !this.onUsage) return;\n\n this.onUsage({\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 });\n }\n\n /**\n * Parses the raw response content as JSON and, when a schema is\n * supplied, validates the parsed value against it. Throws a parse\n * type LLMError on malformed JSON and a validation type LLMError,\n * carrying the schemas issues, on a failed validation\n */\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) {\n return parsed as T;\n }\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 given retry attempt, logging the\n * attempt for observability before the wait begins. Honors a\n * Retry-After header on the failed attempt's error when present\n * (capped at the same maxDelayMs as backoff), otherwise falls back to\n * exponential backoff exactly as before. Rejects early if the signal\n * aborts during the wait\n */\n private async recoverDelay(\n requestId: 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\n this.logger.warn(\n `[vern:${requestId}] recovery attempt ${attempt}/${this.maxRetries}, waiting ${delay}ms` +\n (retryAfterMs !== undefined ? ' (honoring Retry-After)' : ''),\n );\n\n await waitForRetry(delay, signal);\n }\n\n /**\n * Decides whether a failed attempt is worth retrying. Never retries\n * once the signal has aborted, never retries a parse or validation\n * failure since those stem from the response content rather than a\n * transient fault, and never retries a status code the caller has\n * marked as non retryable. Retries everything else\n */\n private shouldRetry(error: unknown, signal?: AbortSignal): boolean {\n if (signal?.aborted) {\n return false;\n }\n\n if (error instanceof LLMError && (error.type === 'parse' || error.type === 'validation')) {\n return false;\n }\n\n const status = extractStatus(error);\n\n if (status !== undefined && this.nonRetryableStatus.includes(status)) {\n return false;\n }\n\n return true;\n }\n\n /**\n * Removes a cached response by key when the configured cache adapter\n * supports deletion.\n *\n * Cache invalidation remains the responsibility of the caller because\n * only the application knows when cached data is stale.\n */\n async deleteCache(key: string): Promise<void> {\n if (!this.cache.delete) {\n return;\n }\n\n await this.cache.delete(key);\n }\n\n /**\n * Cache wrapper around caller-supplied logic. `params.fn` should invoke\n * `this.call(...)` (see `cachedLLMCall`); retry/timeout handling is left\n * to the caller.\n *\n * Concurrent misses for the same `cacheKey` share a single in-flight call,\n * avoiding cache stampedes. Each caller still receives its own\n * `reserveUsage`/`refundUsage` callbacks with coalescing metadata.\n */\n async cachedCall<T>(params: CachedCallParams<T>): Promise<T> {\n const cached = await this.cache.get(params.cacheKey);\n\n if (cached.hit) {\n return cached.value as T;\n }\n\n const existing = this.inFlight.get(params.cacheKey) as Promise<T> | undefined;\n const coalesced = existing !== undefined;\n const resultPromise = existing ?? this.registerTrigger(params, coalesced);\n\n if (coalesced) {\n return this.withRefundOnFailure(params, coalesced, async () => {\n await params.reserveUsage?.({ coalesced });\n return resultPromise;\n });\n }\n\n return this.withRefundOnFailure(params, coalesced, () => resultPromise);\n }\n\n /** Starts the shared fn() call for a cache miss, reserving usage first, and registers it in the in-flight map until it settles */\n private registerTrigger<T>(params: CachedCallParams<T>, coalesced: boolean): Promise<T> {\n const resultPromise = (async () => {\n await params.reserveUsage?.({ coalesced });\n return this.runAndCache(params);\n })();\n\n this.inFlight.set(params.cacheKey, resultPromise);\n // Cleanup runs regardless of outcome\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. Only ever called once per cacheKey per in-flight window, from registerTrigger */\n private async runAndCache<T>(params: CachedCallParams<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 /** Awaits `run`, calling this caller's own refundUsage (tagged with whether it was coalesced) if it rejects, then rethrows the original error */\n private async withRefundOnFailure<T>(\n params: CachedCallParams<T>,\n coalesced: boolean,\n run: () => Promise<T>,\n ): Promise<T> {\n try {\n return await run();\n } catch (error) {\n try {\n await params.refundUsage?.({ coalesced });\n } catch (refundError) {\n this.logger.error('[VernLLM] refundUsage failed', {\n message: refundError instanceof Error ? refundError.message : 'unknown',\n });\n }\n\n throw error;\n }\n }\n\n /**\n * Convenience wrapper composing `call` + `cachedCall`, so cached LLM calls\n * automatically get retry/timeout/circuit-breaker behavior without callers\n * having to remember to wire `fn: () => this.call(...)` themselves\n */\n async cachedLLMCall<T>(\n params: Omit<CachedCallParams<T>, 'fn'> & { call: CallParams<T> },\n ): Promise<T> {\n const { call: callParams, ...cacheParams } = params;\n\n return this.cachedCall({\n ...cacheParams,\n fn: () => this.call(callParams),\n });\n }\n\n /**\n * Returns the current circuit breaker state, or undefined when no\n * circuit breaker was configured on this instance\n */\n getCircuitState() {\n return this.breaker?.getState();\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","import { assertSupportedImageMimeType } from '../internal/imageFormat.js';\n\nimport type { ContentBlock, LLMClient } from '../types/index.js';\n\n/** Anthropic's native per-block content shape for a message. */\ntype AnthropicContentBlock =\n | { type: 'text'; text: string }\n | { type: 'image'; source: { type: 'base64'; media_type: string; data: string } };\n\n/** Minimal structural type for the Anthropic SDKs `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 input_schema: Record<string, unknown>;\n }>;\n tool_choice?: { type: 'tool'; name: string };\n },\n options: { signal: AbortSignal },\n ): Promise<{\n content: Array<{ type: string; text?: 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 * Wraps an Anthropic SDK client so it satisfies the same `LLMClient`\n * interface VernLLM uses for OpenAI/Groq.\n *\n * `response_format: json_schema` is mapped to Anthropics forced tool-use:\n * a single tool is defined with `input_schema` set to the caller's schema,\n * and `tool_choice` forces the model to call it, so the output is\n * provider-constrained to match the schema rather than merely instructed\n * to via prompt text (the same guarantee OpenAIs native `json_schema` mode\n * gives, built on Anthropics tool-calling primitive instead).\n *\n * `response_format: json_object` (no schema to build a tool from) falls\n * back to a system-prompt instruction, since theres nothing to constrain\n * generation against.\n */\nexport function fromAnthropic(anthropicClient: AnthropicClient): LLMClient {\n return {\n chat: {\n completions: {\n async create(params, options) {\n const systemMessage = params.messages.find((m) => m.role === 'system');\n // Keep both user and assistant turns, in order, so multi-turn history\n // survives instead of collapsing to consecutive user messages.\n const conversationMessages = params.messages.filter(\n (m): m is typeof m & { role: 'user' | 'assistant' } =>\n m.role === 'user' || m.role === 'assistant',\n );\n\n const toolName =\n params.response_format?.type === 'json_schema'\n ? params.response_format.json_schema.name\n : undefined;\n\n let jsonInstruction: string | undefined;\n let tools:\n | NonNullable<Parameters<AnthropicClient['messages']['create']>[0]['tools']>\n | undefined;\n\n if (params.response_format?.type === 'json_schema' && toolName) {\n const { schema, description } = params.response_format.json_schema;\n tools = [{ name: toolName, description, input_schema: schema }];\n } else if (params.response_format?.type === 'json_object') {\n // No schema to build a tool from, fall back to a prompt instruction\n jsonInstruction = 'Respond with valid JSON only, no prose or markdown fences.';\n }\n\n // `reasoning_effort` (OpenAI o-series/gpt-5 style) has no direct Anthropic\n // equivalent, Claudes extended thinking uses a token budget, not a tier\n // string, so its intentionally dropped here rather than guessed at.\n\n const system = [systemMessage?.content, jsonInstruction].filter(Boolean).join('\\n\\n');\n\n const response = await anthropicClient.messages.create(\n {\n model: params.model,\n max_tokens: params.max_tokens,\n temperature: params.temperature,\n system: system || undefined,\n messages: conversationMessages.map((m) => ({\n role: m.role,\n content: Array.isArray(m.content) ? toAnthropicContent(m.content) : m.content,\n })),\n ...(tools ? { tools, tool_choice: { type: 'tool' as const, name: toolName! } } : {}),\n },\n options,\n );\n\n let text: string;\n if (toolName) {\n // Forced tool-use: the schema-conforming payload arrives as the\n // tool_use block's already-parsed `input`, not as text. Re-serialize\n // it to JSON so it flows through the same string-content contract\n // every other adapter uses (VernLLM JSON.parses the content itself).\n const toolUse = response.content.find(\n (block) => block.type === 'tool_use' && block.name === toolName,\n );\n text = toolUse ? JSON.stringify(toolUse.input) : '';\n } else {\n text = response.content.find((block) => block.type === 'text')?.text ?? '';\n }\n\n return {\n choices: [{ message: { content: text } }],\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 },\n };\n}\n","import { assertSupportedImageMimeType } from '../internal/imageFormat.js';\n\nimport type { ContentBlock, LLMClient } from '../types/index.js';\n\n/** Gemini's native per-part content shape for a `contents` entry. */\ntype GeminiPart = { text: string } | { inlineData: { mimeType: string; data: string } };\n\n/**\n * Minimal structural type for Geminis `generateContent`, matching both the\n * legacy `@google/generative-ai` SDKs `model.generateContent(...)` and the\n * newer `@google/genai` SDKs `ai.models.generateContent({ model, ... })`\n * closely enough to adapt either — pass whichever `.generateContent` you have.\n */\nexport interface GeminiClient {\n generateContent(\n params: {\n model?: string;\n contents: Array<{ role: 'user' | 'model'; parts: GeminiPart[] }>;\n systemInstruction?: { parts: Array<{ text: string }> };\n generationConfig?: {\n temperature?: number;\n maxOutputTokens?: number;\n responseMimeType?: string;\n responseSchema?: Record<string, unknown>;\n };\n },\n options: { signal: AbortSignal },\n ): Promise<{\n candidates?: Array<{ content?: { parts?: Array<{ text?: string }> } }>;\n usageMetadata?: {\n promptTokenCount?: number;\n candidatesTokenCount?: number;\n totalTokenCount?: number;\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 } }`), Geminis 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/**\n * Wraps a Gemini client so it satisfies the `LLMClient` interface VernLLM\n * uses for OpenAI/Groq. Geminis shape differs on nearly every axis: a\n * `contents` array instead of `messages`, a separate `systemInstruction`\n * field instead of a `system` role message, `generationConfig` instead of\n * top-level `temperature`/`max_tokens`, and native JSON Schema support via\n * `responseMimeType: 'application/json'` + `responseSchema` (so `jsonSchema`\n * is provider-enforced here, unlike the Anthropic adapters prompt-embedding\n * fallback). `reasoning_effort` has no equivalent. Geminis thinking models\n * use a token budget, not an effort tier, so its dropped, same as Anthropic.\n */\nexport function fromGemini(geminiClient: GeminiClient): LLMClient {\n return {\n chat: {\n completions: {\n async create(params, options) {\n const systemMessage = params.messages.find((m) => m.role === 'system');\n // Keep both user and assistant turns, in order. Gemini calls the\n // assistant role 'model' rather than 'assistant'.\n const conversationMessages = params.messages.filter(\n (m) => m.role === 'user' || m.role === 'assistant',\n );\n\n const wantsJson = Boolean(params.response_format);\n const generationConfig: NonNullable<\n Parameters<GeminiClient['generateContent']>[0]['generationConfig']\n > = {\n temperature: params.temperature,\n maxOutputTokens: params.max_tokens,\n };\n\n if (wantsJson) {\n generationConfig.responseMimeType = 'application/json';\n }\n if (params.response_format?.type === 'json_schema') {\n generationConfig.responseSchema = params.response_format.json_schema.schema;\n }\n\n const response = await geminiClient.generateContent(\n {\n model: params.model,\n contents: conversationMessages.map((m) => ({\n role: m.role === 'assistant' ? ('model' as const) : ('user' as const),\n parts: Array.isArray(m.content) ? toGeminiParts(m.content) : [{ text: m.content }],\n })),\n systemInstruction: systemMessage\n ? // System turns are always plain strings; only user turns can carry ContentBlock[]\n { parts: [{ text: systemMessage.content as string }] }\n : undefined,\n generationConfig,\n },\n options,\n );\n\n const text =\n response.candidates?.[0]?.content?.parts?.map((p) => p.text ?? '').join('') ?? '';\n\n return {\n choices: [{ message: { content: text } }],\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 },\n };\n}\n","import { assertSupportedImageMimeType } from '../internal/imageFormat.js';\n\nimport type { ContentBlock, LLMClient } from '../types/index.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\n/**\n * Minimal structural type matching AWS Bedrocks 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 };\n }>;\n toolChoice?: { tool: { name: string } };\n };\n },\n options: { signal: AbortSignal },\n ): Promise<{\n output?: {\n message?: {\n content?: Array<{ text?: string; toolUse?: { name?: string; input?: unknown } }>;\n };\n };\n usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number };\n }>;\n}\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 * 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 Bedrocks 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` is mapped to Converses `toolConfig`: a\n * single tool is defined from the schema and `toolChoice` forces the model\n * to call it, constraining output at generation time rather than merely\n * instructing for it via prompt text. Native tool support varies by model\n * family (most current-generation ones support it via Converse; check your\n * specific `modelId` if a call fails with an unsupported-parameter error).\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.\n */\nexport function fromBedrock(bedrockClient: BedrockConverseClient): LLMClient {\n return {\n chat: {\n completions: {\n async create(params, options) {\n const systemMessage = params.messages.find((m) => m.role === 'system');\n // Keep both user and assistant turns, in order, so conversation\n // history survives instead of collapsing to consecutive user turns.\n const conversationMessages = params.messages.filter(\n (m): m is typeof m & { role: 'user' | 'assistant' } =>\n m.role === 'user' || m.role === 'assistant',\n );\n\n const toolName =\n params.response_format?.type === 'json_schema'\n ? params.response_format.json_schema.name\n : undefined;\n\n let jsonInstruction: string | undefined;\n let toolConfig:\n | NonNullable<Parameters<BedrockConverseClient['converse']>[0]['toolConfig']>\n | undefined;\n\n if (params.response_format?.type === 'json_schema' && toolName) {\n const { schema, description } = params.response_format.json_schema;\n toolConfig = {\n tools: [{ toolSpec: { name: toolName, description, inputSchema: { json: schema } } }],\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 jsonInstruction = 'Respond with valid JSON only, no prose or markdown fences.';\n }\n\n const systemParts = [systemMessage?.content, jsonInstruction].filter((s): s is string =>\n Boolean(s),\n );\n\n const response = await bedrockClient.converse(\n {\n modelId: params.model,\n messages: conversationMessages.map((m) => ({\n role: m.role,\n content: Array.isArray(m.content)\n ? toBedrockContent(m.content)\n : [{ text: m.content }],\n })),\n system: systemParts.length ? systemParts.map((text) => ({ text })) : undefined,\n inferenceConfig: {\n temperature: params.temperature,\n maxTokens: params.max_tokens,\n },\n ...(toolConfig ? { toolConfig } : {}),\n },\n options,\n );\n\n let text: string;\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 text = toolUseBlock?.toolUse ? JSON.stringify(toolUseBlock.toolUse.input) : '';\n } else {\n text = response.output?.message?.content?.map((c) => c.text ?? '').join('') ?? '';\n }\n\n return {\n choices: [{ message: { content: text } }],\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 },\n };\n}\n","import type { LLMClient } from '../types/index.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\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? }`\n * `content` is the assistants text (JSON string when JSON mode was requested)\n */\n mapResponse: (json: unknown) => {\n content: string;\n usage?: { promptTokens?: number; completionTokens?: number; totalTokens?: number };\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 */\nexport function fromFetch(config: FetchAdapterConfig): LLMClient {\n return {\n chat: {\n completions: {\n async create(params, options) {\n const url = typeof config.url === 'function' ? config.url(params) : config.url;\n const headers =\n typeof config.headers === 'function' ? await config.headers() : config.headers;\n const method = config.method ?? 'POST';\n const request = config.request ?? fetch;\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 const res = await request(url, {\n method,\n headers: supportsBody\n ? { 'Content-Type': 'application/json', ...headers }\n : { ...headers },\n ...(supportsBody ? { body: JSON.stringify(config.mapRequest(params)) } : {}),\n signal: options.signal,\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n const err = new Error(\n `Fetch adapter request failed (${res.status}): ${body.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 } = config.mapResponse(json);\n\n return {\n choices: [{ message: { content } }],\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 },\n };\n}\n","import { assertSupportedImageMimeType } from '../internal/imageFormat.js';\n\nimport type { ContentBlock, LLMClient } 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/**\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 */\nexport function fromOpenAICompatible(client: unknown): LLMClient {\n const raw = client as LLMClient;\n\n return {\n chat: {\n completions: {\n async create(params, options) {\n const messages = params.messages.map((m) =>\n m.role === 'user' && Array.isArray(m.content)\n ? { ...m, content: toOpenAIContent(m.content) }\n : m,\n );\n\n return raw.chat.completions.create(\n { ...params, messages } as Parameters<LLMClient['chat']['completions']['create']>[0],\n options,\n );\n },\n },\n },\n };\n}\n\n// LLM aliases\n\n/** Groqs SDK matches the OpenAI wire format */\nexport const fromGroq = fromOpenAICompatible;\n\n/** Mistrals `chat.completions`-shaped client (or their OpenAI-compat endpoint) */\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/** GitHub Models is OpenAI-compatible */\nexport const fromGitHubModels = 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/** kluster.ai's inference API is OpenAI-compatible */\nexport const fromKlusterAI = 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":["message: string","type: LLMErrorType","status?: number","issues?: unknown","cause?: unknown","retryAfterMs?: number","err: unknown","options: CircuitBreakerOptions","content: string","err: unknown","value: unknown","fn: (signal: AbortSignal) => Promise<T>","timeoutMs: number","externalSignal?: AbortSignal","baseDelayMs: number","attempt: number","delay: number","signal?: AbortSignal","debugEnabled: boolean","message: string","meta?: Record<string, unknown>","key: string","value: T","ttl: number","options: VernLLMOptions","params: CallParams<T>","fn: () => Promise<T>","requestId: string","signal?: AbortSignal","lastError: unknown","error: unknown","history: ConversationTurn[]","previousRole: 'user' | 'assistant' | undefined","jsonSchema: CallParams<unknown>['jsonSchema']","useJson: boolean","response: Awaited<ReturnType<LLMClient['chat']['completions']['create']>>","model: string","content: string","schema?: CallParams<T>['schema']","parsed: unknown","attempt: number","key: string","params: CachedCallParams<T>","coalesced: boolean","params: {\n reserveUsage?: ReserveUsage;\n refundUsage?: RefundUsage;\n }","getResult: () => Promise<T>","params: Omit<CachedCallParams<T>, 'fn'> & { call: CallParams<T> }","mimeType: string","blocks: ContentBlock[]","anthropicClient: AnthropicClient","jsonInstruction: string | undefined","tools:\n | NonNullable<Parameters<AnthropicClient['messages']['create']>[0]['tools']>\n | undefined","text: string","blocks: ContentBlock[]","geminiClient: GeminiClient","generationConfig: NonNullable<\n Parameters<GeminiClient['generateContent']>[0]['generationConfig']\n >","mimeType: string","data: string","blocks: ContentBlock[]","bedrockClient: BedrockConverseClient","jsonInstruction: string | undefined","toolConfig:\n | NonNullable<Parameters<BedrockConverseClient['converse']>[0]['toolConfig']>\n | undefined","text","text: string","config: FetchAdapterConfig","blocks: ContentBlock[]","client: unknown"],"sources":["../src/types/errors.ts","../src/circuitBreaker.ts","../src/internal/vernLLM.utils.ts","../src/logger.ts","../src/types/cache.ts","../src/vernLLM.ts","../src/internal/imageFormat.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\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 ) {\n super(message);\n this.name = 'LLMError';\n }\n}\n\nexport function isLLMError(err: unknown): err is LLMError {\n return err instanceof LLMError;\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\ntype CircuitState = 'closed' | 'open' | 'half-open';\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 state: CircuitState = 'closed';\n private consecutiveFailures = 0;\n private openedAt = 0;\n private threshold: number;\n private cooldownMs: 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 private trialInFlight = false;\n\n constructor(options: CircuitBreakerOptions = {}) {\n this.threshold = options.threshold ?? 5;\n this.cooldownMs = options.cooldownMs ?? 30_000;\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(): void {\n if (this.state === 'closed') return;\n\n if (this.state === 'open') {\n const elapsed = Date.now() - this.openedAt;\n if (elapsed < this.cooldownMs) {\n throw new LLMError(\n `Circuit open — provider has failed ${this.consecutiveFailures} times in a row. Retry in ${Math.ceil((this.cooldownMs - elapsed) / 1000)}s.`,\n 'circuit_open',\n );\n }\n\n this.state = 'half-open';\n this.trialInFlight = true;\n return;\n }\n\n // state === 'half-open'\n if (this.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 this.trialInFlight = true;\n }\n\n recordSuccess(): void {\n this.consecutiveFailures = 0;\n this.state = 'closed';\n this.trialInFlight = false;\n }\n\n recordFailure(): void {\n this.consecutiveFailures += 1;\n this.trialInFlight = false;\n\n if (this.state === 'half-open') {\n // Trial call failed: reopen and reset the cooldown window.\n this.state = 'open';\n this.openedAt = Date.now();\n return;\n }\n\n if (this.consecutiveFailures >= this.threshold) {\n this.state = 'open';\n this.openedAt = Date.now();\n }\n }\n\n getState(): CircuitState {\n return this.state;\n }\n}\n","import { LLMError } from '../types/errors.js';\n\nexport function defaultParseJson(content: string): unknown {\n try {\n return JSON.parse(content);\n } catch {\n return undefined;\n }\n}\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 * 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 };\n\n if (typeof error.status === 'number') return error.status;\n if (typeof error.statusCode === 'number') return error.statusCode;\n\n return undefined;\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 * 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 */\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 timer = setTimeout(() => {\n controller.abort();\n }, timeoutMs);\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 * 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 * 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 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","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","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}\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","import { randomUUID } from 'crypto';\n\nimport { CircuitBreaker } from './circuitBreaker.js';\nimport {\n defaultParseJson,\n extractStatus,\n extractRetryAfterMs,\n withTimeout,\n getBackoffDelay,\n waitForRetry,\n describeError,\n} from './internal/vernLLM.utils.js';\nimport { ConsoleLogger, type Logger } from './logger.js';\nimport {\n InMemoryCacheAdapter,\n LLMError,\n type CacheAdapter,\n type CachedCallParams,\n type CallParams,\n type ConversationTurn,\n type LLMClient,\n type RefundUsage,\n type ReserveUsage,\n type VernLLMOptions,\n} from './types/index.js';\n\n/**\n * A resilient wrapper around an LLM chat completions client, this is VernLLM!\n *\n * Adds retry with exponential backoff and jitter, per-attempt timeouts,\n * an optional circuit breaker, JSON parsing with optional schema\n * validation, usage tracking, and an optional response cache, all\n * configurable, all opt-in beyond sensible defaults\n */\nexport class VernLLM {\n private readonly client: LLMClient;\n private readonly model: string;\n\n private readonly maxRetries: number;\n private readonly timeoutMs: number;\n private readonly baseDelayMs: number;\n private readonly defaultMaxTokens: number;\n\n private readonly cache: CacheAdapter<unknown>;\n private readonly nonRetryableStatus: number[];\n\n private readonly inFlight = new Map<string, Promise<unknown>>();\n\n private readonly parseJson: (content: string) => unknown;\n private readonly onUsage?: VernLLMOptions['onUsage'];\n\n private readonly logger: Logger;\n private readonly breaker?: CircuitBreaker;\n\n /**\n * @param options: Client, model, and all tunables (retries, timeout,\n * backoff, cache, circuit breaker, logger, etc). See VernLLMOptions in `types.ts`\n * for individual defaults\n */\n constructor(options: VernLLMOptions) {\n this.client = options.client;\n this.model = options.model;\n\n const retryConfig = this.resolveRetryConfig(options);\n this.maxRetries = retryConfig.maxRetries;\n this.timeoutMs = retryConfig.timeoutMs;\n this.baseDelayMs = retryConfig.baseDelayMs;\n this.defaultMaxTokens = retryConfig.defaultMaxTokens;\n\n this.cache = options.cache ?? new InMemoryCacheAdapter();\n this.nonRetryableStatus = options.nonRetryableStatus ?? [400, 401, 403, 404, 422];\n\n this.parseJson = options.parseJson ?? defaultParseJson;\n this.onUsage = options.onUsage;\n\n this.logger = this.resolveLogger(options);\n this.breaker = this.resolveCircuitBreaker(options);\n }\n\n /**\n * Resolves retry/timeout/token defaults from the given options,\n * falling back to the librarys built-in defaults for anything unset\n */\n private resolveRetryConfig(options: VernLLMOptions) {\n return {\n maxRetries: options.maxRetries ?? 1,\n timeoutMs: options.timeoutMs ?? 25_000,\n baseDelayMs: options.baseDelayMs ?? 500,\n defaultMaxTokens: options.defaultMaxTokens ?? 1000,\n };\n }\n\n /**\n * Returns the caller supplied logger, or a console-based logger whose\n * debug output is gated by the `debug` option (defaulting to off,\n * so response content isn't unintentionally written to logs)\n */\n private resolveLogger(options: VernLLMOptions): Logger {\n return options.logger ?? new ConsoleLogger(options.debug ?? false);\n }\n\n /**\n * Builds a circuit breaker if `circuitBreaker` is truthy on the\n * options. Passing `true` uses default thresholds, passing an options\n * object tunes them. Returns undefined when the breaker is disabled\n */\n private resolveCircuitBreaker(options: VernLLMOptions): CircuitBreaker | undefined {\n if (!options.circuitBreaker) return undefined;\n\n return new CircuitBreaker(options.circuitBreaker === true ? undefined : options.circuitBreaker);\n }\n\n /**\n * Makes a single logical LLM call, transparently retrying on failure\n * according to the configured retry policy\n *\n * Fails fast if the circuit breaker is open or the signal is already\n * aborted, before any request is dispatched. On exhausting all\n * retries, records a circuit breaker failure and rejects with a\n * normalized LLMError\n *\n * @param params : System/user content plus per call overrides\n * (model, temperature, jsonMode, schema, signal, etc)\n * @returns The parsed and optionally schema-validated response, or\n * the raw string content when jsonMode is disabled\n */\n async call<T = unknown>(params: CallParams<T>): Promise<T> {\n this.breaker?.assertClosed();\n\n if (params.signal?.aborted) {\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n const requestId = params.requestId ?? randomUUID();\n\n return this.withReservedUsage(\n params,\n false,\n async () => {\n try {\n return await this.retryWithBackoff(\n () => this.executeCall(params, requestId),\n requestId,\n params.signal,\n );\n } catch (error) {\n const normalized = this.normalizeError(error, params.signal);\n\n if (\n normalized.type !== 'validation' &&\n normalized.type !== 'parse' &&\n normalized.type !== 'aborted'\n ) {\n this.breaker?.recordFailure();\n }\n\n this.logger.debug(`[vern:${requestId}] error:\\n${describeError(error)}`);\n\n throw normalized;\n }\n },\n params.signal,\n );\n }\n\n /**\n * Runs `fn`, retrying with backoff according to `shouldRetry` policy\n * Purely mechanical: knows nothing about LLM specifics beyond the retry\n * predicate, so its testable independent of request/response shaping\n */\n private async retryWithBackoff<T>(\n fn: () => Promise<T>,\n requestId: string,\n signal?: AbortSignal,\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, attempt, lastError, signal);\n }\n\n return await fn();\n } catch (error) {\n lastError = error;\n\n if (!this.shouldRetry(error, signal)) {\n break;\n }\n }\n }\n\n throw lastError;\n }\n\n /**\n * Converts any thrown value into a well-typed LLMError for the public\n * API surface. Preserves an existing LLMError as is, reports aborted\n * signals as such, classifies errors carrying an http status as\n * type api, and otherwise falls back to a generic unknown error.\n */\n private 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 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', status, undefined, error, retryAfterMs);\n }\n\n return new LLMError('LLM request failed', 'unknown', undefined, undefined, error, retryAfterMs);\n }\n\n /**\n * Performs a single attempt: builds the request, dispatches it with a\n * timeout, and shapes the response. Throws on an empty response so\n * the retry loop treats it like any other transient failure. Records\n * usage and a circuit breaker success before returning\n */\n private async executeCall<T>(params: CallParams<T>, requestId: string): Promise<T> {\n const { useJson, model, request } = this.buildRequestPayload(params);\n\n const response = await withTimeout(\n (attemptSignal) => this.client.chat.completions.create(request, { signal: attemptSignal }),\n this.timeoutMs,\n params.signal,\n );\n\n const content = response.choices?.[0]?.message?.content?.trim();\n\n if (!content) {\n throw new LLMError('Empty LLM response', 'api');\n }\n\n this.logger.debug(`[vern:${requestId}] output:\\n${content.slice(0, 800)}`);\n\n this.recordUsage(response, requestId, model);\n\n if (!useJson) {\n this.breaker?.recordSuccess();\n\n return content as T;\n }\n\n const result = this.parseAndValidate(content, params.schema);\n\n this.breaker?.recordSuccess();\n\n return result;\n }\n\n /**\n * Anthropic and Gemini both require strict user/assistant alternation\n * (and reject or silently mishandle two consecutive same-role turns), so\n * this validates `history` up front rather than letting a malformed\n * request surface as a confusing provider-side error. Thrown as a\n * validation LLMError, which `shouldRetry` never retries, since retrying\n * the same malformed input can't succeed\n */\n private validateHistory(history: ConversationTurn[]): void {\n let previousRole: 'user' | 'assistant' | undefined;\n\n for (const [index, turn] of history.entries()) {\n if (turn.role !== 'user' && turn.role !== 'assistant') {\n throw new LLMError(\n `Invalid history[${index}].role \"${turn.role}\": must be \"user\" or \"assistant\"`,\n 'validation',\n );\n }\n\n if (turn.role === previousRole) {\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 previousRole = turn.role;\n }\n\n if (previousRole === 'user') {\n throw new LLMError(\n 'The last entry in history is a \"user\" turn, which would collide with the current userContent turn. history must end with an \"assistant\" turn (or be empty).',\n 'validation',\n );\n }\n }\n\n /**\n * Applies per call defaults and shapes the params into the request\n * object expected by the underlying client, including the resolved\n * response format. Also returns whether JSON parsing should be\n * applied to the response and which model was ultimately used\n */\n private buildRequestPayload<T>(params: CallParams<T>) {\n const {\n systemPrompt,\n userContent,\n history = [],\n temperature = 0.2,\n jsonMode = true,\n maxTokens = this.defaultMaxTokens,\n model = this.model,\n reasoningEffort,\n jsonSchema,\n } = params;\n\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,\n max_tokens: maxTokens,\n ...(responseFormat ? { response_format: responseFormat } : {}),\n ...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),\n messages: [\n ...(systemPrompt ? [{ role: 'system' as const, content: systemPrompt }] : []),\n ...history.map((turn) => ({ role: turn.role, content: turn.content })),\n { role: 'user' as const, content: userContent },\n ],\n };\n\n return { useJson, model, request };\n }\n\n /**\n * Chooses the response format to send to the provider. A provider\n * native json schema takes priority when supplied, constraining\n * generation directly, otherwise falls back to the looser json\n * object mode when JSON output is requested, or no format at all\n * 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 /**\n * Reports token usage to the caller supplied onUsage callback, when\n * both a callback was configured and the provider actually returned\n * usage data on this response. A no op otherwise.\n *\n * A throwing onUsage callback is logged and swallowed rather than\n * propagated, so a broken billing/metrics hook can't fail or retrigger\n * retries on an otherwise-successful call.\n */\n private recordUsage(\n response: Awaited<ReturnType<LLMClient['chat']['completions']['create']>>,\n requestId: string,\n model: string,\n ): void {\n if (!response.usage || !this.onUsage) return;\n\n try {\n this.onUsage({\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 });\n } catch (error) {\n this.logger.error('[VernLLM] onUsage failed', {\n message: error instanceof Error ? error.message : 'unknown',\n });\n }\n }\n\n /**\n * Parses the raw response content as JSON and, when a schema is\n * supplied, validates the parsed value against it. Throws a parse\n * type LLMError on malformed JSON and a validation type LLMError,\n * carrying the schemas issues, on a failed validation\n */\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) {\n return parsed as T;\n }\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 given retry attempt, logging the\n * attempt for observability before the wait begins. Honors a\n * Retry-After header on the failed attempt's error when present\n * (capped at the same maxDelayMs as backoff), otherwise falls back to\n * exponential backoff exactly as before. Rejects early if the signal\n * aborts during the wait\n */\n private async recoverDelay(\n requestId: 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\n this.logger.warn(\n `[vern:${requestId}] recovery attempt ${attempt}/${this.maxRetries}, waiting ${delay}ms` +\n (retryAfterMs !== undefined ? ' (honoring Retry-After)' : ''),\n );\n\n await waitForRetry(delay, signal);\n }\n\n /**\n * Decides whether a failed attempt is worth retrying. Never retries\n * once the signal has aborted, never retries a parse or validation\n * failure since those stem from the response content rather than a\n * transient fault, and never retries a status code the caller has\n * marked as non retryable. Retries everything else\n */\n private shouldRetry(error: unknown, signal?: AbortSignal): boolean {\n if (signal?.aborted) {\n return false;\n }\n\n if (error instanceof LLMError && (error.type === 'parse' || error.type === 'validation')) {\n return false;\n }\n\n const status = extractStatus(error);\n\n if (status !== undefined && this.nonRetryableStatus.includes(status)) {\n return false;\n }\n\n return true;\n }\n\n /**\n * Removes a cached response by key when the configured cache adapter\n * supports deletion.\n *\n * Cache invalidation remains the responsibility of the caller because\n * only the application knows when cached data is stale.\n */\n async deleteCache(key: string): Promise<void> {\n if (!this.cache.delete) {\n return;\n }\n\n await this.cache.delete(key);\n }\n\n /**\n * Cache wrapper around caller-supplied logic. `params.fn` should invoke\n * `this.call(...)` (see `cachedLLMCall`); retry/timeout handling is left\n * to the caller.\n *\n * Concurrent misses for the same `cacheKey` share a single in-flight call,\n * avoiding cache stampedes. Each caller still receives its own\n * `reserveUsage`/`refundUsage` callbacks with coalescing metadata.\n */\n async cachedCall<T>(params: CachedCallParams<T>): Promise<T> {\n const cached = await this.cache.get(params.cacheKey);\n\n if (cached.hit) {\n return cached.value as T;\n }\n\n const existing = this.inFlight.get(params.cacheKey) as Promise<T> | undefined;\n const coalesced = existing !== undefined;\n\n if (coalesced) {\n return this.withReservedUsage(params, coalesced, () => existing, params.signal);\n }\n\n return this.registerTrigger(params, coalesced);\n }\n\n /** Starts the shared fn() call for a cache miss, reserving usage first, and registers it in the in-flight map until it settles */\n private registerTrigger<T>(params: CachedCallParams<T>, coalesced: boolean): Promise<T> {\n const resultPromise = this.withReservedUsage(\n params,\n coalesced,\n () => this.runAndCache(params),\n params.signal,\n );\n\n this.inFlight.set(params.cacheKey, resultPromise);\n\n // Cleanup runs regardless of outcome\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. Only ever called once per cacheKey per in-flight window, from registerTrigger */\n private async runAndCache<T>(params: CachedCallParams<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 * Runs `getResult` after reserving usage, if a `reserveUsage` hook was\n * provided. `refundUsage` fires only if a reservation was actually made,\n * i.e. `reserveUsage` was provided and it resolved successfully. If\n * `reserveUsage` is omitted entirely, or if it throws, there is nothing to\n * refund, so `refundUsage` is not invoked in either case.\n *\n * Shared by `cachedCall()`/`registerTrigger()` and `call()`, so it only\n * depends on the usage hooks rather than LLM request concerns.\n */\n private async withReservedUsage<T>(\n params: {\n reserveUsage?: ReserveUsage;\n refundUsage?: RefundUsage;\n },\n coalesced: boolean,\n getResult: () => Promise<T>,\n signal?: AbortSignal,\n ): Promise<T> {\n let reserved = false;\n\n try {\n if (params.reserveUsage) {\n await params.reserveUsage({ coalesced, signal });\n reserved = true;\n }\n } catch (error) {\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 if (signal?.aborted) {\n if (reserved) {\n try {\n await params.refundUsage?.({ coalesced, signal });\n } catch (refundError) {\n this.logger.error('[VernLLM] refundUsage failed after abort', {\n message: refundError instanceof Error ? refundError.message : 'unknown',\n });\n }\n }\n\n throw new LLMError('LLM request aborted', 'aborted');\n }\n\n try {\n return await getResult();\n } catch (error) {\n if (reserved) {\n try {\n await params.refundUsage?.({ coalesced, signal });\n } catch (refundError) {\n this.logger.error('[VernLLM] refundUsage failed', {\n message: refundError instanceof Error ? refundError.message : 'unknown',\n });\n }\n }\n\n throw error;\n }\n }\n\n /**\n * Convenience wrapper composing `call` + `cachedCall`, so cached LLM calls\n * automatically get retry/timeout/circuit-breaker behavior without callers\n * having to remember to wire `fn: () => this.call(...)` themselves.\n *\n * `reserveUsage`/`refundUsage` are read from the top-level params only — if\n * `call` also sets them, they're ignored, since `call()` now performs its\n * own reservation. Honoring both would reserve/refund twice per logical\n * cachedLLMCall (once via cachedCall's wrapping, once via the inner call()).\n */\n async cachedLLMCall<T>(\n params: Omit<CachedCallParams<T>, 'fn'> & { call: CallParams<T> },\n ): Promise<T> {\n const { call: callParams, ...cacheParams } = params;\n const {\n reserveUsage: _innerReserveUsage,\n refundUsage: _innerRefundUsage,\n ...restCallParams\n } = callParams;\n\n if (_innerReserveUsage || _innerRefundUsage) {\n this.logger.warn(\n '[VernLLM] reserveUsage/refundUsage on `call` are ignored by cachedLLMCall; set them at the top level instead.',\n );\n }\n\n return this.cachedCall({\n ...cacheParams,\n fn: () => this.call(restCallParams),\n });\n }\n\n /**\n * Returns the current circuit breaker state, or undefined when no\n * circuit breaker was configured on this instance\n */\n getCircuitState() {\n return this.breaker?.getState();\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","import { assertSupportedImageMimeType } from '../internal/imageFormat.js';\n\nimport type { ContentBlock, LLMClient } from '../types/index.js';\n\n/** Anthropic's native per-block content shape for a message. */\ntype AnthropicContentBlock =\n | { type: 'text'; text: string }\n | { type: 'image'; source: { type: 'base64'; media_type: string; data: string } };\n\n/** Minimal structural type for the Anthropic SDKs `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 input_schema: Record<string, unknown>;\n }>;\n tool_choice?: { type: 'tool'; name: string };\n },\n options: { signal: AbortSignal },\n ): Promise<{\n content: Array<{ type: string; text?: 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 * Wraps an Anthropic SDK client so it satisfies the same `LLMClient`\n * interface VernLLM uses for OpenAI/Groq.\n *\n * `response_format: json_schema` is mapped to Anthropics forced tool-use:\n * a single tool is defined with `input_schema` set to the caller's schema,\n * and `tool_choice` forces the model to call it, so the output is\n * provider-constrained to match the schema rather than merely instructed\n * to via prompt text (the same guarantee OpenAIs native `json_schema` mode\n * gives, built on Anthropics tool-calling primitive instead).\n *\n * `response_format: json_object` (no schema to build a tool from) falls\n * back to a system-prompt instruction, since theres nothing to constrain\n * generation against.\n */\nexport function fromAnthropic(anthropicClient: AnthropicClient): LLMClient {\n return {\n chat: {\n completions: {\n async create(params, options) {\n const systemMessage = params.messages.find((m) => m.role === 'system');\n // Keep both user and assistant turns, in order, so multi-turn history\n // survives instead of collapsing to consecutive user messages.\n const conversationMessages = params.messages.filter(\n (m): m is typeof m & { role: 'user' | 'assistant' } =>\n m.role === 'user' || m.role === 'assistant',\n );\n\n const toolName =\n params.response_format?.type === 'json_schema'\n ? params.response_format.json_schema.name\n : undefined;\n\n let jsonInstruction: string | undefined;\n let tools:\n | NonNullable<Parameters<AnthropicClient['messages']['create']>[0]['tools']>\n | undefined;\n\n if (params.response_format?.type === 'json_schema' && toolName) {\n const { schema, description } = params.response_format.json_schema;\n tools = [{ name: toolName, description, input_schema: schema }];\n } else if (params.response_format?.type === 'json_object') {\n // No schema to build a tool from, fall back to a prompt instruction\n jsonInstruction = 'Respond with valid JSON only, no prose or markdown fences.';\n }\n\n // `reasoning_effort` (OpenAI o-series/gpt-5 style) has no direct Anthropic\n // equivalent, Claudes extended thinking uses a token budget, not a tier\n // string, so its intentionally dropped here rather than guessed at.\n\n const system = [systemMessage?.content, jsonInstruction].filter(Boolean).join('\\n\\n');\n\n const response = await anthropicClient.messages.create(\n {\n model: params.model,\n max_tokens: params.max_tokens,\n temperature: params.temperature,\n system: system || undefined,\n messages: conversationMessages.map((m) => ({\n role: m.role,\n content: Array.isArray(m.content) ? toAnthropicContent(m.content) : m.content,\n })),\n ...(tools ? { tools, tool_choice: { type: 'tool' as const, name: toolName! } } : {}),\n },\n options,\n );\n\n let text: string;\n if (toolName) {\n // Forced tool-use: the schema-conforming payload arrives as the\n // tool_use block's already-parsed `input`, not as text. Re-serialize\n // it to JSON so it flows through the same string-content contract\n // every other adapter uses (VernLLM JSON.parses the content itself).\n const toolUse = response.content.find(\n (block) => block.type === 'tool_use' && block.name === toolName,\n );\n text = toolUse ? JSON.stringify(toolUse.input) : '';\n } else {\n text = response.content.find((block) => block.type === 'text')?.text ?? '';\n }\n\n return {\n choices: [{ message: { content: text } }],\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 },\n };\n}\n","import { assertSupportedImageMimeType } from '../internal/imageFormat.js';\n\nimport type { ContentBlock, LLMClient } from '../types/index.js';\n\n/** Gemini's native per-part content shape for a `contents` entry. */\ntype GeminiPart = { text: string } | { inlineData: { mimeType: string; data: string } };\n\n/**\n * Minimal structural type for Geminis `generateContent`, matching both the\n * legacy `@google/generative-ai` SDKs `model.generateContent(...)` and the\n * newer `@google/genai` SDKs `ai.models.generateContent({ model, ... })`\n * closely enough to adapt either — pass whichever `.generateContent` you have.\n */\nexport interface GeminiClient {\n generateContent(\n params: {\n model?: string;\n contents: Array<{ role: 'user' | 'model'; parts: GeminiPart[] }>;\n systemInstruction?: { parts: Array<{ text: string }> };\n generationConfig?: {\n temperature?: number;\n maxOutputTokens?: number;\n responseMimeType?: string;\n responseSchema?: Record<string, unknown>;\n };\n },\n options: { signal: AbortSignal },\n ): Promise<{\n candidates?: Array<{ content?: { parts?: Array<{ text?: string }> } }>;\n usageMetadata?: {\n promptTokenCount?: number;\n candidatesTokenCount?: number;\n totalTokenCount?: number;\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 } }`), Geminis 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/**\n * Wraps a Gemini client so it satisfies the `LLMClient` interface VernLLM\n * uses for OpenAI/Groq. Geminis shape differs on nearly every axis: a\n * `contents` array instead of `messages`, a separate `systemInstruction`\n * field instead of a `system` role message, `generationConfig` instead of\n * top-level `temperature`/`max_tokens`, and native JSON Schema support via\n * `responseMimeType: 'application/json'` + `responseSchema` (so `jsonSchema`\n * is provider-enforced here, unlike the Anthropic adapters prompt-embedding\n * fallback). `reasoning_effort` has no equivalent. Geminis thinking models\n * use a token budget, not an effort tier, so its dropped, same as Anthropic.\n */\nexport function fromGemini(geminiClient: GeminiClient): LLMClient {\n return {\n chat: {\n completions: {\n async create(params, options) {\n const systemMessage = params.messages.find((m) => m.role === 'system');\n // Keep both user and assistant turns, in order. Gemini calls the\n // assistant role 'model' rather than 'assistant'.\n const conversationMessages = params.messages.filter(\n (m) => m.role === 'user' || m.role === 'assistant',\n );\n\n const wantsJson = Boolean(params.response_format);\n const generationConfig: NonNullable<\n Parameters<GeminiClient['generateContent']>[0]['generationConfig']\n > = {\n temperature: params.temperature,\n maxOutputTokens: params.max_tokens,\n };\n\n if (wantsJson) {\n generationConfig.responseMimeType = 'application/json';\n }\n if (params.response_format?.type === 'json_schema') {\n generationConfig.responseSchema = params.response_format.json_schema.schema;\n }\n\n const response = await geminiClient.generateContent(\n {\n model: params.model,\n contents: conversationMessages.map((m) => ({\n role: m.role === 'assistant' ? ('model' as const) : ('user' as const),\n parts: Array.isArray(m.content) ? toGeminiParts(m.content) : [{ text: m.content }],\n })),\n systemInstruction: systemMessage\n ? // System turns are always plain strings; only user turns can carry ContentBlock[]\n { parts: [{ text: systemMessage.content as string }] }\n : undefined,\n generationConfig,\n },\n options,\n );\n\n const text =\n response.candidates?.[0]?.content?.parts?.map((p) => p.text ?? '').join('') ?? '';\n\n return {\n choices: [{ message: { content: text } }],\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 },\n };\n}\n","import { assertSupportedImageMimeType } from '../internal/imageFormat.js';\n\nimport type { ContentBlock, LLMClient } from '../types/index.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\n/**\n * Minimal structural type matching AWS Bedrocks 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 };\n }>;\n toolChoice?: { tool: { name: string } };\n };\n },\n options: { signal: AbortSignal },\n ): Promise<{\n output?: {\n message?: {\n content?: Array<{ text?: string; toolUse?: { name?: string; input?: unknown } }>;\n };\n };\n usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number };\n }>;\n}\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 * 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 Bedrocks 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` is mapped to Converses `toolConfig`: a\n * single tool is defined from the schema and `toolChoice` forces the model\n * to call it, constraining output at generation time rather than merely\n * instructing for it via prompt text. Native tool support varies by model\n * family (most current-generation ones support it via Converse; check your\n * specific `modelId` if a call fails with an unsupported-parameter error).\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.\n */\nexport function fromBedrock(bedrockClient: BedrockConverseClient): LLMClient {\n return {\n chat: {\n completions: {\n async create(params, options) {\n const systemMessage = params.messages.find((m) => m.role === 'system');\n // Keep both user and assistant turns, in order, so conversation\n // history survives instead of collapsing to consecutive user turns.\n const conversationMessages = params.messages.filter(\n (m): m is typeof m & { role: 'user' | 'assistant' } =>\n m.role === 'user' || m.role === 'assistant',\n );\n\n const toolName =\n params.response_format?.type === 'json_schema'\n ? params.response_format.json_schema.name\n : undefined;\n\n let jsonInstruction: string | undefined;\n let toolConfig:\n | NonNullable<Parameters<BedrockConverseClient['converse']>[0]['toolConfig']>\n | undefined;\n\n if (params.response_format?.type === 'json_schema' && toolName) {\n const { schema, description } = params.response_format.json_schema;\n toolConfig = {\n tools: [{ toolSpec: { name: toolName, description, inputSchema: { json: schema } } }],\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 jsonInstruction = 'Respond with valid JSON only, no prose or markdown fences.';\n }\n\n const systemParts = [systemMessage?.content, jsonInstruction].filter((s): s is string =>\n Boolean(s),\n );\n\n const response = await bedrockClient.converse(\n {\n modelId: params.model,\n messages: conversationMessages.map((m) => ({\n role: m.role,\n content: Array.isArray(m.content)\n ? toBedrockContent(m.content)\n : [{ text: m.content }],\n })),\n system: systemParts.length ? systemParts.map((text) => ({ text })) : undefined,\n inferenceConfig: {\n temperature: params.temperature,\n maxTokens: params.max_tokens,\n },\n ...(toolConfig ? { toolConfig } : {}),\n },\n options,\n );\n\n let text: string;\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 text = toolUseBlock?.toolUse ? JSON.stringify(toolUseBlock.toolUse.input) : '';\n } else {\n text = response.output?.message?.content?.map((c) => c.text ?? '').join('') ?? '';\n }\n\n return {\n choices: [{ message: { content: text } }],\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 },\n };\n}\n","import type { LLMClient } from '../types/index.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\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? }`\n * `content` is the assistants text (JSON string when JSON mode was requested)\n */\n mapResponse: (json: unknown) => {\n content: string;\n usage?: { promptTokens?: number; completionTokens?: number; totalTokens?: number };\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 */\nexport function fromFetch(config: FetchAdapterConfig): LLMClient {\n return {\n chat: {\n completions: {\n async create(params, options) {\n const url = typeof config.url === 'function' ? config.url(params) : config.url;\n const headers =\n typeof config.headers === 'function' ? await config.headers() : config.headers;\n const method = config.method ?? 'POST';\n const request = config.request ?? fetch;\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 const res = await request(url, {\n method,\n headers: supportsBody\n ? { 'Content-Type': 'application/json', ...headers }\n : { ...headers },\n ...(supportsBody ? { body: JSON.stringify(config.mapRequest(params)) } : {}),\n signal: options.signal,\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n const err = new Error(\n `Fetch adapter request failed (${res.status}): ${body.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 } = config.mapResponse(json);\n\n return {\n choices: [{ message: { content } }],\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 },\n };\n}\n","import { assertSupportedImageMimeType } from '../internal/imageFormat.js';\n\nimport type { ContentBlock, LLMClient } 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/**\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 */\nexport function fromOpenAICompatible(client: unknown): LLMClient {\n const raw = client as LLMClient;\n\n return {\n chat: {\n completions: {\n async create(params, options) {\n const messages = params.messages.map((m) =>\n m.role === 'user' && Array.isArray(m.content)\n ? { ...m, content: toOpenAIContent(m.content) }\n : m,\n );\n\n return raw.chat.completions.create(\n { ...params, messages } as Parameters<LLMClient['chat']['completions']['create']>[0],\n options,\n );\n },\n },\n },\n };\n}\n\n// LLM aliases\n\n/** Groqs SDK matches the OpenAI wire format */\nexport const fromGroq = fromOpenAICompatible;\n\n/** Mistrals `chat.completions`-shaped client (or their OpenAI-compat endpoint) */\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/** GitHub Models is OpenAI-compatible */\nexport const fromGitHubModels = 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/** kluster.ai's inference API is OpenAI-compatible */\nexport const fromKlusterAI = 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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"}