vern-llm 2.9.0 → 3.0.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.
@@ -0,0 +1,2 @@
1
+ const e=new Set([`unknown_tool`,`duplicate_tool_call_id`,`tool_choice_none_violated`,`unexpected_tool_calls`]),t=new Set([`rate_limit_queue_full`,`rate_limit_queue_timeout`,`rate_limit_capacity_exceeded`,`retry_budget_exhausted`]),n=new Set([`middleware_timeout`]),r=new Set([`parse`,`validation`,`invalid_params`,`aborted`]);function i(i,a){return a===`response_truncated`||!(r.has(i)||a&&e.has(a)||a&&t.has(a)||a&&n.has(a)||a===`payload_too_large`)}const a=new Set([`quota_exceeded`]),o=new Set([408,425,429]);function s(e){return e!==void 0&&e>=400&&e<500&&!o.has(e)}function c(e,t,n){return!(!i(e,t)||a.has(e)||t===`response_truncated`||s(n))}function l(e){if(e!==void 0)try{return JSON.stringify(e),e}catch{return`[Unserializable: issues contained a circular reference]`}}function u(e){if(e!==void 0)try{return JSON.parse(JSON.stringify(e))}catch{return`[Unserializable: request body contained a circular reference]`}}const d=new Set([`authorization`,`x-api-key`,`x-goog-api-key`,`api-key`]);function f(e){if(e===void 0)return;let t={};for(let[n,r]of Object.entries(e))d.has(n.toLowerCase())||(t[n]=r);return t}function p(e,t=0){if(e!==void 0)return t>=20?[]:e.map(e=>({...e,error:{...e.error,issues:l(e.error.issues),attempts:p(e.error.attempts,t+1)},request:e.request&&{...e.request,body:u(e.request.body),headers:f(e.request.headers)}}))}function m(e,t,n,r,i=Date.now()){return{provider:e,model:t,body:u(n),headers:f(r),startedAt:i}}var h=class extends Error{type;status;issues;cause;retryAfterMs;code;attempts;constructor(e,t,n={}){super(e),this.type=t,this.name=`LLMError`,this.status=n.status,this.issues=n.issues,this.cause=n.cause,this.retryAfterMs=n.retryAfterMs,this.code=n.code,this.attempts=n.attempts}get retryable(){return i(this.type,this.code)}get countsTowardBreaker(){return c(this.type,this.code,this.status)}toSnapshot(){return{message:this.message,type:this.type,status:this.status,issues:l(this.issues),retryAfterMs:this.retryAfterMs,code:this.code,retryable:this.retryable,attempts:p(this.attempts)}}toJSON(){return{name:this.name,message:this.message,type:this.type,status:this.status,issues:l(this.issues),retryAfterMs:this.retryAfterMs,code:this.code,retryable:this.retryable,attempts:p(this.attempts)}}};function g(e){return e instanceof h}function _(e,t){return e.code===t&&e.issues!==void 0}function v(e){let t=/^(?:(\d+(?:\.\d+)?)h)?(?:(\d+(?:\.\d+)?)m)?(?:(\d+(?:\.\d+)?)s)?(?:(\d+)ms)?$/.exec(e.trim());if(!t||t[0]===``)return;let[,n,r,i,a]=t,o=(n?parseFloat(n)*36e5:0)+(r?parseFloat(r)*6e4:0)+(i?parseFloat(i)*1e3:0)+(a?parseInt(a,10):0);return Number.isFinite(o)?o:void 0}function y(e,t){let n=e.get(t);if(n===null||n.trim()===``)return;let r=Number(n);return Number.isFinite(r)?r:void 0}function b(e){let t=e.get(`x-ratelimit-reset-requests`);return{limitRequests:y(e,`x-ratelimit-limit-requests`),remainingRequests:y(e,`x-ratelimit-remaining-requests`),resetAfterMs:t===null?void 0:v(t)}}function x(e){let t=e.get(`anthropic-ratelimit-requests-reset`),n=t===null?NaN:Date.parse(t);return{limitRequests:y(e,`anthropic-ratelimit-requests-limit`),remainingRequests:y(e,`anthropic-ratelimit-requests-remaining`),resetAfterMs:Number.isFinite(n)?Math.max(0,n-Date.now()):void 0}}const S=Symbol(`vernLLMProviderRateLimitHint`);function C(e,t){t&&Object.defineProperty(e,S,{value:t,enumerable:!1,configurable:!0})}function w(e){if(e&&typeof e==`object`)return e[S]}const T=new WeakMap;function E(e,t){let n=t.slice(0,500),r=Error(`${e}: ${n}`);return T.set(r,{prefix:e,body:n}),r}function D(e,t){if(!(e instanceof Error))return;let n=T.get(e);if(!n)return;T.delete(e);let r;try{r=t(n.body)}catch{r=`[response body withheld: redact threw]`}let i=e.stack,a=`${e.name}: ${e.message}`;if(e.message=`${n.prefix}: ${r}`,typeof i==`string`){let t=i.startsWith(a)?i.slice(a.length):``;e.stack=`${e.name}: ${e.message}${t}`}}export{b as a,_ as c,x as i,g as l,D as n,w as o,C as r,h as s,E as t,m as u};
2
+ //# sourceMappingURL=responseBody.utils-B2tQiGUi.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responseBody.utils-B2tQiGUi.mjs","names":[],"sources":["../src/types/errors.ts","../src/internal/utils/rate-limit/rateLimitHint.utils.ts","../src/internal/utils/errors/responseBody.utils.ts"],"sourcesContent":["export type LLMErrorType =\n | 'timeout'\n | 'api'\n | 'network'\n | 'parse'\n | 'validation'\n | 'invalid_params'\n | 'rate_limited'\n | 'quota_exceeded'\n | 'circuit_open'\n | 'fallback_exhausted'\n | 'aborted'\n | 'unknown';\n\n/**\n * Machine readable detail within a `type`, for when `type` alone is too coarse to act on. Optional:\n * errors from before a code existed omit it.\n */\nexport type LLMErrorCode =\n // Tool contract (validation)\n | 'unknown_tool'\n | 'duplicate_tool_call_id'\n | 'tool_choice_none_violated'\n | 'unexpected_tool_calls'\n // Caller input (invalid_params)\n | 'unsupported_capability'\n | 'duplicate_tool_names'\n | 'unknown_tool_choice'\n | 'duplicate_tool_result_ids'\n | 'unknown_tool_result_ids'\n | 'missing_tool_results'\n | 'invalid_context'\n | 'unknown_target'\n | 'no_eligible_targets'\n // Middleware (invalid_params)\n | 'middleware_threw'\n // Rate limiting (rate_limited)\n | 'rate_limit_queue_full'\n | 'rate_limit_queue_timeout'\n | 'rate_limit_capacity_exceeded'\n | 'provider_rate_limited'\n // Retry budget (rate_limited)\n | 'retry_budget_exhausted'\n // Timeouts (timeout)\n | 'request_timeout'\n | 'idle_timeout'\n | 'reader_stall_timeout'\n | 'middleware_timeout'\n // Deadline (aborted)\n | 'deadline_exceeded'\n // HTTP status (api)\n | 'authentication'\n | 'authorization'\n | 'not_found'\n | 'payload_too_large'\n | 'server_error'\n | 'empty_response'\n // Connectivity (network)\n | 'connection_failed'\n // Circuit breaker (circuit_open)\n | 'circuit_cooling_down'\n | 'circuit_trial_in_flight'\n // Fallback (fallback_exhausted)\n | 'fallback_exhausted'\n // Parsing (parse)\n | 'tool_arguments_parse_failed'\n | 'stream_frame_invalid'\n | 'response_truncated'\n // Soft failure (api, default; a custom code can also override the type)\n | 'soft_failure_detected';\n\n/**\n * Model or provider response defects. Deterministic for the same request, so never retried and\n * never counted toward the breaker.\n */\nexport const NON_RETRYABLE_TOOL_CONTRACT_CODES: ReadonlySet<LLMErrorCode> = new Set([\n 'unknown_tool',\n 'duplicate_tool_call_id',\n 'tool_choice_none_violated',\n 'unexpected_tool_calls',\n]);\n\n/**\n * Local limiter rejections. The provider was never reached, and retrying either requeues behind the\n * same limit or can never succeed.\n */\nexport const LOCAL_RATE_LIMIT_CODES: ReadonlySet<LLMErrorCode> = new Set([\n 'rate_limit_queue_full',\n 'rate_limit_queue_timeout',\n 'rate_limit_capacity_exceeded',\n 'retry_budget_exhausted',\n]);\n\n/**\n * A middleware `transform` or `enabled` timed out. Retrying reruns the same slow code, so unlike a\n * provider timeout it is not retryable.\n */\nexport const NON_RETRYABLE_MIDDLEWARE_TIMEOUT_CODES: ReadonlySet<LLMErrorCode> = new Set([\n 'middleware_timeout',\n]);\n\n/**\n * Types that are never worth retrying on their own: deterministic\n * caller-input, model-response, or cancellation failures rather than a\n * transient provider fault.\n */\nconst NON_RETRYABLE_TYPES: ReadonlySet<LLMErrorType> = new Set([\n 'parse',\n 'validation',\n 'invalid_params',\n 'aborted',\n]);\n\n/**\n * The retry rule behind both `LLMError.retryable` and `LLMErrorSnapshot.retryable`, kept in one\n * place so they can't drift.\n */\nfunction computeRetryable(type: LLMErrorType, code: LLMErrorCode | undefined): boolean {\n // Output cut off at max_tokens can come back complete on a resend, since\n // generation varies, unlike a reply that was whole and still malformed.\n if (code === 'response_truncated') return true;\n if (NON_RETRYABLE_TYPES.has(type)) return false;\n if (code && NON_RETRYABLE_TOOL_CONTRACT_CODES.has(code)) return false;\n if (code && LOCAL_RATE_LIMIT_CODES.has(code)) return false;\n if (code && NON_RETRYABLE_MIDDLEWARE_TIMEOUT_CODES.has(code)) return false;\n // Resending the same oversized body can only be rejected again.\n if (code === 'payload_too_large') return false;\n return true;\n}\n\n/**\n * Retryable types that still say nothing about provider health, so they never count toward the\n * breaker.\n */\nconst NON_BREAKER_TYPES: ReadonlySet<LLMErrorType> = new Set(['quota_exceeded']);\n\n/**\n * 4xx statuses that describe the provider rather than one caller's request. Any other 4xx counting\n * would let one bad caller open the circuit for everyone.\n */\nconst PROVIDER_SIDE_CLIENT_STATUSES: ReadonlySet<number> = new Set([408, 425, 429]);\n\nfunction isCallerSideStatus(status: number | undefined): boolean {\n return (\n status !== undefined &&\n status >= 400 &&\n status < 500 &&\n !PROVIDER_SIDE_CLIENT_STATUSES.has(status)\n );\n}\n\n/**\n * The breaker rule behind `LLMError.countsTowardBreaker`. Never counts what `computeRetryable`\n * excludes.\n */\nfunction computeCountsTowardBreaker(\n type: LLMErrorType,\n code: LLMErrorCode | undefined,\n status: number | undefined,\n): boolean {\n if (!computeRetryable(type, code)) return false;\n if (NON_BREAKER_TYPES.has(type)) return false;\n // The provider answered fine, the caller's max_tokens was too small.\n if (code === 'response_truncated') return false;\n if (isCallerSideStatus(status)) return false;\n return true;\n}\n\n/**\n * `issues` if it survives `JSON.stringify`, otherwise a marker string. Only a caller supplied\n * schema validator's error can be circular.\n */\nfunction safeIssues(issues: unknown): unknown {\n if (issues === undefined) return undefined;\n try {\n JSON.stringify(issues);\n return issues;\n } catch {\n return '[Unserializable: issues contained a circular reference]';\n }\n}\n\n/**\n * A JSON safe copy of `body`, or a marker string if it can't be serialized. Cloned, not shared,\n * since adapter code can still mutate the request after dispatch (`fromGemini` sets\n * `request.config` in place).\n */\nfunction safeBody(body: unknown): unknown {\n if (body === undefined) return undefined;\n try {\n return JSON.parse(JSON.stringify(body)) as unknown;\n } catch {\n return '[Unserializable: request body contained a circular reference]';\n }\n}\n\nconst AUTH_HEADER_NAMES = new Set(['authorization', 'x-api-key', 'x-goog-api-key', 'api-key']);\n\n/** Removes auth headers before a request snapshot is built. Case insensitive on header names. */\nfunction stripAuthHeaders(\n headers: Record<string, string> | undefined,\n): Record<string, string> | undefined {\n if (headers === undefined) return undefined;\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (!AUTH_HEADER_NAMES.has(key.toLowerCase())) out[key] = value;\n }\n return out;\n}\n\n/**\n * Depth cap for `safeAttempts`, since a caller can hand build a self referential `attempts` array.\n */\nconst MAX_ATTEMPTS_DEPTH = 20;\n\n/**\n * Copies `attempts` with every nested `issues`, request `body` and request headers rechecked,\n * recursively. A caller can pass hand built attempts straight to the constructor, and a shared\n * `issues` object can turn circular after capture. Extra fields such as `FallbackAttempt.provider`\n * are kept.\n */\nfunction safeAttempts(attempts: RetryAttempt[] | undefined, depth = 0): RetryAttempt[] | undefined {\n if (attempts === undefined) return undefined;\n if (depth >= MAX_ATTEMPTS_DEPTH) return [];\n\n return attempts.map((attempt) => ({\n ...attempt,\n error: {\n ...attempt.error,\n issues: safeIssues(attempt.error.issues),\n attempts: safeAttempts(attempt.error.attempts, depth + 1),\n },\n request: attempt.request && {\n ...attempt.request,\n body: safeBody(attempt.request.body),\n headers: stripAuthHeaders(attempt.request.headers),\n },\n }));\n}\n\n/** One tool call's contract failure, used to report every bad call in a response at once. */\nexport interface ToolIssue {\n name: string;\n toolCallId: string;\n code: LLMErrorCode;\n detail?: unknown;\n}\n\n/**\n * The specific values behind a `duplicate_tool_names` failure: the\n * offending call's `tools` array had more than one entry sharing a name.\n */\nexport interface DuplicateToolNamesIssue {\n names: string[];\n}\n\n/**\n * The specific values behind an `unknown_tool_choice` failure: `toolChoice`\n * named a tool that wasn't in the call's own `tools` array.\n */\nexport interface UnknownToolChoiceIssue {\n requested: string;\n available: string[];\n}\n\n/**\n * The specific values behind a `duplicate_tool_result_ids` /\n * `unknown_tool_result_ids` / `missing_tool_results` failure: which\n * `history` turn was affected, and which `toolCallId`s were the problem.\n */\nexport interface HistoryToolResultIssue {\n historyIndex: number;\n ids: string[];\n}\n\n/**\n * The specific values behind an `unsupported_capability` failure: which\n * capability the current adapter/client/model doesn't support.\n */\nexport interface UnsupportedCapabilityIssue {\n capability: string;\n}\n\n/**\n * The exact `issues` shape for each code that carries one. Codes whose message already says\n * everything have no entry. Schema validation `issues` stay untyped, since they come from the\n * caller's own validator.\n */\nexport interface LLMErrorIssuesByCode {\n unknown_tool: ToolIssue[];\n duplicate_tool_call_id: ToolIssue[];\n duplicate_tool_names: DuplicateToolNamesIssue;\n unknown_tool_choice: UnknownToolChoiceIssue;\n duplicate_tool_result_ids: HistoryToolResultIssue;\n unknown_tool_result_ids: HistoryToolResultIssue;\n missing_tool_results: HistoryToolResultIssue;\n unsupported_capability: UnsupportedCapabilityIssue;\n}\n\n/**\n * Plain data copy of an `LLMError`, as held by `RetryAttempt.error`. Never thrown again, so no\n * `cause` and no live getters. Nested `attempts` form a tree, not a cycle.\n */\nexport interface LLMErrorSnapshot {\n message: string;\n type: LLMErrorType;\n status?: number;\n issues?: unknown;\n retryAfterMs?: number;\n code?: LLMErrorCode;\n /** Computed once, at snapshot time, since a snapshot has no live getter. */\n retryable: boolean;\n /** This attempt's own prior attempts, if it was itself the terminal failure of a retry loop. */\n attempts?: RetryAttempt[];\n}\n\n/**\n * Plain data copy of the request an attempt sent, as held by `RetryAttempt.request`. Safe to\n * serialize and store.\n */\nexport interface LLMRequestSnapshot {\n /** Provider id this attempt targeted, e.g. \"openai\". */\n provider: string;\n /** Model id this attempt targeted. */\n model: string;\n /** The payload as actually sent for this attempt, after any transform/repair. Passed through `safeBody`. */\n body: unknown;\n /** Non sensitive request headers. Auth headers are stripped before the snapshot is built, never included. */\n headers?: Record<string, string>;\n /** Wall clock time the attempt started, ms since epoch. */\n startedAt: number;\n}\n\n/**\n * A plain data copy of one attempt's request. Pass `startedAt` when known: this often runs after\n * the attempt failed, so `Date.now()` would record the wrong time.\n */\nexport function toRequestSnapshot(\n provider: string,\n model: string,\n body: unknown,\n headers?: Record<string, string>,\n startedAt: number = Date.now(),\n): LLMRequestSnapshot {\n return {\n provider,\n model,\n body: safeBody(body),\n headers: stripAuthHeaders(headers),\n startedAt,\n };\n}\n\n/** One failed attempt: its index and a snapshot of its error. `FallbackAttempt` extends it. */\nexport interface RetryAttempt {\n index: number;\n error: LLMErrorSnapshot;\n /** What was sent for this attempt. Optional: absent for attempts predating this field. */\n request?: LLMRequestSnapshot;\n}\n\n/** Optional fields for constructing an {@link LLMError}. `message` and `type` stay positional since every throw site sets both. */\nexport interface LLMErrorOptions {\n status?: number;\n issues?: unknown;\n cause?: unknown;\n retryAfterMs?: number;\n /** Stable discriminator within `type`. Absent on errors predating it. */\n code?: LLMErrorCode;\n /** Every attempt made before this error was thrown, in order. Absent when nothing was retried. */\n attempts?: RetryAttempt[];\n}\n\nexport class LLMError extends Error {\n public status?: number;\n public issues?: unknown;\n public cause?: unknown;\n public retryAfterMs?: number;\n /** Stable discriminator within `type`. Absent on errors predating it. */\n public code?: LLMErrorCode;\n /** Every attempt made before this error was thrown, in order. Absent when nothing was retried. */\n public attempts?: RetryAttempt[];\n\n constructor(\n message: string,\n public type: LLMErrorType,\n options: LLMErrorOptions = {},\n ) {\n super(message);\n this.name = 'LLMError';\n this.status = options.status;\n this.issues = options.issues;\n this.cause = options.cause;\n this.retryAfterMs = options.retryAfterMs;\n this.code = options.code;\n this.attempts = options.attempts;\n }\n\n /**\n * Whether retrying could help, from `type` and `code` alone. See Error Handling for the full\n * list. `response_truncated` is retryable despite its `parse` type.\n */\n get retryable(): boolean {\n return computeRetryable(this.type, this.code);\n }\n\n /**\n * Whether this failure counts toward the breaker. Stricter than `retryable`: `quota_exceeded` and\n * any 4xx other than 408, 425 and 429 are excluded.\n */\n get countsTowardBreaker(): boolean {\n return computeCountsTowardBreaker(this.type, this.code, this.status);\n }\n\n /**\n * Copies the fields into an `LLMErrorSnapshot`. `cause` is left out, and `issues` are made\n * serialization safe.\n */\n toSnapshot(): LLMErrorSnapshot {\n return {\n message: this.message,\n type: this.type,\n status: this.status,\n issues: safeIssues(this.issues),\n retryAfterMs: this.retryAfterMs,\n code: this.code,\n retryable: this.retryable,\n attempts: safeAttempts(this.attempts),\n };\n }\n\n /**\n * Serializes `message` and `retryable` too, which a plain property walk misses. `cause` is left\n * out since SDK errors can be circular; read `err.cause` directly.\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n type: this.type,\n status: this.status,\n issues: safeIssues(this.issues),\n retryAfterMs: this.retryAfterMs,\n code: this.code,\n retryable: this.retryable,\n attempts: safeAttempts(this.attempts),\n };\n }\n}\n\nexport function isLLMError(err: unknown): err is LLMError {\n return err instanceof LLMError;\n}\n\n/**\n * Narrows `err.issues` to the shape `LLMErrorIssuesByCode` maps `code` to, so no cast is needed.\n */\nexport function hasIssues<C extends keyof LLMErrorIssuesByCode>(\n err: LLMError,\n code: C,\n): err is LLMError & { code: C; issues: LLMErrorIssuesByCode[C] } {\n return err.code === code && err.issues !== undefined;\n}\n","/** A normalized read of a provider's rate limit headers. */\nexport interface ProviderRateLimitHint {\n remainingRequests?: number;\n limitRequests?: number;\n resetAfterMs?: number;\n}\n\n/** The minimal shape needed to read a header by name, satisfied by `Headers` and `ResponseLike['headers']`. */\nexport interface HeaderReader {\n get(name: string): string | null;\n}\n\n/** Parses a Go-style duration (`\"1s\"`, `\"6m0s\"`) into ms, OpenAI's `x-ratelimit-reset-*` format. Undefined if unrecognized. */\nfunction parseGoDuration(value: string): number | undefined {\n const match =\n /^(?:(\\d+(?:\\.\\d+)?)h)?(?:(\\d+(?:\\.\\d+)?)m)?(?:(\\d+(?:\\.\\d+)?)s)?(?:(\\d+)ms)?$/.exec(\n value.trim(),\n );\n\n if (!match || match[0] === '') return undefined;\n\n const [, hours, minutes, seconds, millis] = match;\n const ms =\n (hours ? parseFloat(hours) * 3_600_000 : 0) +\n (minutes ? parseFloat(minutes) * 60_000 : 0) +\n (seconds ? parseFloat(seconds) * 1_000 : 0) +\n (millis ? parseInt(millis, 10) : 0);\n\n return Number.isFinite(ms) ? ms : undefined;\n}\n\nfunction parseIntHeader(headers: HeaderReader, name: string): number | undefined {\n const raw = headers.get(name);\n\n // `Number('')` is 0, which would read a blank header as \"no requests left\".\n if (raw === null || raw.trim() === '') return undefined;\n\n const value = Number(raw);\n return Number.isFinite(value) ? value : undefined;\n}\n\n/** OpenAI's (and every OpenAI-compatible provider's) header set: `x-ratelimit-{limit,remaining,reset}-requests`. */\nexport function parseOpenAIRateLimitHeaders(headers: HeaderReader): ProviderRateLimitHint {\n const resetRaw = headers.get('x-ratelimit-reset-requests');\n\n return {\n limitRequests: parseIntHeader(headers, 'x-ratelimit-limit-requests'),\n remainingRequests: parseIntHeader(headers, 'x-ratelimit-remaining-requests'),\n resetAfterMs: resetRaw !== null ? parseGoDuration(resetRaw) : undefined,\n };\n}\n\n/** Anthropic's header set: `anthropic-ratelimit-requests-{limit,remaining,reset}`. `reset` is RFC 3339, not a duration. */\nexport function parseAnthropicRateLimitHeaders(headers: HeaderReader): ProviderRateLimitHint {\n const resetRaw = headers.get('anthropic-ratelimit-requests-reset');\n const resetAt = resetRaw !== null ? Date.parse(resetRaw) : NaN;\n\n return {\n limitRequests: parseIntHeader(headers, 'anthropic-ratelimit-requests-limit'),\n remainingRequests: parseIntHeader(headers, 'anthropic-ratelimit-requests-remaining'),\n resetAfterMs: Number.isFinite(resetAt) ? Math.max(0, resetAt - Date.now()) : undefined,\n };\n}\n\n/** Tries both known header shapes, returns whichever produced anything. Used for the reactive path, where the provider behind a thrown error isn't known. */\nexport function parseAnyRateLimitHeaders(headers: HeaderReader): ProviderRateLimitHint | undefined {\n const openai = parseOpenAIRateLimitHeaders(headers);\n if (openai.remainingRequests !== undefined || openai.limitRequests !== undefined) return openai;\n\n const anthropic = parseAnthropicRateLimitHeaders(headers);\n if (anthropic.remainingRequests !== undefined || anthropic.limitRequests !== undefined) {\n return anthropic;\n }\n\n return undefined;\n}\n\n/** Not exported: lets a hint travel attached to a response without appearing on its declared shape. Mirrors the fetch adapter's `err.headers`, for the success path. */\nconst RATE_LIMIT_HINT = Symbol('vernLLMProviderRateLimitHint');\n\n/** Attaches a parsed hint onto a successful response value. No-op if `hint` is undefined. */\nexport function attachRateLimitHint(value: object, hint: ProviderRateLimitHint | undefined): void {\n if (!hint) return;\n\n Object.defineProperty(value, RATE_LIMIT_HINT, {\n value: hint,\n enumerable: false,\n configurable: true,\n });\n}\n\n/** Reads a hint attached by `attachRateLimitHint`, if any. */\nexport function readRateLimitHint(value: unknown): ProviderRateLimitHint | undefined {\n if (!value || typeof value !== 'object') return undefined;\n return (value as { [RATE_LIMIT_HINT]?: ProviderRateLimitHint })[RATE_LIMIT_HINT];\n}\n","/**\n * Lets an adapter build an error whose message embeds a raw provider\n * response body, while keeping that body redactable. The adapter has no\n * access to `VernLLMOptions.redact`, so it records where the body sits\n * and `redactResponseBody` rewrites the message once core has caught it,\n * before it becomes `LLMError.message` or reaches any log.\n */\n\n/** How much of a response body an error message keeps. */\nconst MAX_BODY_CHARS = 500;\n\nconst pendingBodies = new WeakMap<Error, { prefix: string; body: string }>();\n\n/** An `Error` reading `${prefix}: ${body}`, with the body registered for redaction. */\nexport function errorWithResponseBody(prefix: string, body: string): Error {\n const truncated = body.slice(0, MAX_BODY_CHARS);\n const error = new Error(`${prefix}: ${truncated}`);\n pendingBodies.set(error, { prefix, body: truncated });\n return error;\n}\n\n/**\n * Passes the body of an error built by `errorWithResponseBody` through\n * `redact`, rewriting its message in place. Runs at most once per error.\n * A throwing `redact` drops the body instead of leaking it unredacted.\n */\nexport function redactResponseBody(error: unknown, redact: (text: string) => string): void {\n if (!(error instanceof Error)) return;\n\n const pending = pendingBodies.get(error);\n if (!pending) return;\n\n pendingBodies.delete(error);\n\n let redacted: string;\n\n try {\n redacted = redact(pending.body);\n } catch {\n redacted = '[response body withheld: redact threw]';\n }\n\n // Read before the message changes: V8 formats `stack` lazily on first\n // access, from whatever the message is at that moment.\n const rawStack = error.stack;\n const rawHeader = `${error.name}: ${error.message}`;\n\n error.message = `${pending.prefix}: ${redacted}`;\n\n // The stack starts with the message as it was when the error was built,\n // and a body with newlines spans several lines of it. Replace exactly\n // that header so no part of the raw body survives, keeping the frames.\n // A stack that doesn't start with it (rewritten elsewhere) is dropped to\n // its header rather than risk keeping body text.\n if (typeof rawStack === 'string') {\n const frames = rawStack.startsWith(rawHeader) ? rawStack.slice(rawHeader.length) : '';\n error.stack = `${error.name}: ${error.message}${frames}`;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=new Set([`unknown_tool`,`duplicate_tool_call_id`,`tool_choice_none_violated`,`unexpected_tool_calls`]),t=new Set([`rate_limit_queue_full`,`rate_limit_queue_timeout`,`rate_limit_capacity_exceeded`,`retry_budget_exhausted`]),n=new Set([`middleware_timeout`]),r=new Set([`parse`,`validation`,`invalid_params`,`aborted`]);function i(i,a){return a===`response_truncated`||!(r.has(i)||a&&e.has(a)||a&&t.has(a)||a&&n.has(a)||a===`payload_too_large`)}const a=new Set([`quota_exceeded`]),o=new Set([408,425,429]);function s(e){return e!==void 0&&e>=400&&e<500&&!o.has(e)}function c(e,t,n){return!(!i(e,t)||a.has(e)||t===`response_truncated`||s(n))}function l(e){if(e!==void 0)try{return JSON.stringify(e),e}catch{return`[Unserializable: issues contained a circular reference]`}}function u(e){if(e!==void 0)try{return JSON.parse(JSON.stringify(e))}catch{return`[Unserializable: request body contained a circular reference]`}}const d=new Set([`authorization`,`x-api-key`,`x-goog-api-key`,`api-key`]);function f(e){if(e===void 0)return;let t={};for(let[n,r]of Object.entries(e))d.has(n.toLowerCase())||(t[n]=r);return t}function p(e,t=0){if(e!==void 0)return t>=20?[]:e.map(e=>({...e,error:{...e.error,issues:l(e.error.issues),attempts:p(e.error.attempts,t+1)},request:e.request&&{...e.request,body:u(e.request.body),headers:f(e.request.headers)}}))}function m(e,t,n,r,i=Date.now()){return{provider:e,model:t,body:u(n),headers:f(r),startedAt:i}}var h=class extends Error{type;status;issues;cause;retryAfterMs;code;attempts;constructor(e,t,n={}){super(e),this.type=t,this.name=`LLMError`,this.status=n.status,this.issues=n.issues,this.cause=n.cause,this.retryAfterMs=n.retryAfterMs,this.code=n.code,this.attempts=n.attempts}get retryable(){return i(this.type,this.code)}get countsTowardBreaker(){return c(this.type,this.code,this.status)}toSnapshot(){return{message:this.message,type:this.type,status:this.status,issues:l(this.issues),retryAfterMs:this.retryAfterMs,code:this.code,retryable:this.retryable,attempts:p(this.attempts)}}toJSON(){return{name:this.name,message:this.message,type:this.type,status:this.status,issues:l(this.issues),retryAfterMs:this.retryAfterMs,code:this.code,retryable:this.retryable,attempts:p(this.attempts)}}};function g(e){return e instanceof h}function _(e,t){return e.code===t&&e.issues!==void 0}function v(e){let t=/^(?:(\d+(?:\.\d+)?)h)?(?:(\d+(?:\.\d+)?)m)?(?:(\d+(?:\.\d+)?)s)?(?:(\d+)ms)?$/.exec(e.trim());if(!t||t[0]===``)return;let[,n,r,i,a]=t,o=(n?parseFloat(n)*36e5:0)+(r?parseFloat(r)*6e4:0)+(i?parseFloat(i)*1e3:0)+(a?parseInt(a,10):0);return Number.isFinite(o)?o:void 0}function y(e,t){let n=e.get(t);if(n===null||n.trim()===``)return;let r=Number(n);return Number.isFinite(r)?r:void 0}function b(e){let t=e.get(`x-ratelimit-reset-requests`);return{limitRequests:y(e,`x-ratelimit-limit-requests`),remainingRequests:y(e,`x-ratelimit-remaining-requests`),resetAfterMs:t===null?void 0:v(t)}}function x(e){let t=e.get(`anthropic-ratelimit-requests-reset`),n=t===null?NaN:Date.parse(t);return{limitRequests:y(e,`anthropic-ratelimit-requests-limit`),remainingRequests:y(e,`anthropic-ratelimit-requests-remaining`),resetAfterMs:Number.isFinite(n)?Math.max(0,n-Date.now()):void 0}}const S=Symbol(`vernLLMProviderRateLimitHint`);function C(e,t){t&&Object.defineProperty(e,S,{value:t,enumerable:!1,configurable:!0})}function w(e){if(e&&typeof e==`object`)return e[S]}const T=new WeakMap;function E(e,t){let n=t.slice(0,500),r=Error(`${e}: ${n}`);return T.set(r,{prefix:e,body:n}),r}function D(e,t){if(!(e instanceof Error))return;let n=T.get(e);if(!n)return;T.delete(e);let r;try{r=t(n.body)}catch{r=`[response body withheld: redact threw]`}let i=e.stack,a=`${e.name}: ${e.message}`;if(e.message=`${n.prefix}: ${r}`,typeof i==`string`){let t=i.startsWith(a)?i.slice(a.length):``;e.stack=`${e.name}: ${e.message}${t}`}}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return m}});
2
+ //# sourceMappingURL=responseBody.utils-CknznUJA.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responseBody.utils-CknznUJA.cjs","names":[],"sources":["../src/types/errors.ts","../src/internal/utils/rate-limit/rateLimitHint.utils.ts","../src/internal/utils/errors/responseBody.utils.ts"],"sourcesContent":["export type LLMErrorType =\n | 'timeout'\n | 'api'\n | 'network'\n | 'parse'\n | 'validation'\n | 'invalid_params'\n | 'rate_limited'\n | 'quota_exceeded'\n | 'circuit_open'\n | 'fallback_exhausted'\n | 'aborted'\n | 'unknown';\n\n/**\n * Machine readable detail within a `type`, for when `type` alone is too coarse to act on. Optional:\n * errors from before a code existed omit it.\n */\nexport type LLMErrorCode =\n // Tool contract (validation)\n | 'unknown_tool'\n | 'duplicate_tool_call_id'\n | 'tool_choice_none_violated'\n | 'unexpected_tool_calls'\n // Caller input (invalid_params)\n | 'unsupported_capability'\n | 'duplicate_tool_names'\n | 'unknown_tool_choice'\n | 'duplicate_tool_result_ids'\n | 'unknown_tool_result_ids'\n | 'missing_tool_results'\n | 'invalid_context'\n | 'unknown_target'\n | 'no_eligible_targets'\n // Middleware (invalid_params)\n | 'middleware_threw'\n // Rate limiting (rate_limited)\n | 'rate_limit_queue_full'\n | 'rate_limit_queue_timeout'\n | 'rate_limit_capacity_exceeded'\n | 'provider_rate_limited'\n // Retry budget (rate_limited)\n | 'retry_budget_exhausted'\n // Timeouts (timeout)\n | 'request_timeout'\n | 'idle_timeout'\n | 'reader_stall_timeout'\n | 'middleware_timeout'\n // Deadline (aborted)\n | 'deadline_exceeded'\n // HTTP status (api)\n | 'authentication'\n | 'authorization'\n | 'not_found'\n | 'payload_too_large'\n | 'server_error'\n | 'empty_response'\n // Connectivity (network)\n | 'connection_failed'\n // Circuit breaker (circuit_open)\n | 'circuit_cooling_down'\n | 'circuit_trial_in_flight'\n // Fallback (fallback_exhausted)\n | 'fallback_exhausted'\n // Parsing (parse)\n | 'tool_arguments_parse_failed'\n | 'stream_frame_invalid'\n | 'response_truncated'\n // Soft failure (api, default; a custom code can also override the type)\n | 'soft_failure_detected';\n\n/**\n * Model or provider response defects. Deterministic for the same request, so never retried and\n * never counted toward the breaker.\n */\nexport const NON_RETRYABLE_TOOL_CONTRACT_CODES: ReadonlySet<LLMErrorCode> = new Set([\n 'unknown_tool',\n 'duplicate_tool_call_id',\n 'tool_choice_none_violated',\n 'unexpected_tool_calls',\n]);\n\n/**\n * Local limiter rejections. The provider was never reached, and retrying either requeues behind the\n * same limit or can never succeed.\n */\nexport const LOCAL_RATE_LIMIT_CODES: ReadonlySet<LLMErrorCode> = new Set([\n 'rate_limit_queue_full',\n 'rate_limit_queue_timeout',\n 'rate_limit_capacity_exceeded',\n 'retry_budget_exhausted',\n]);\n\n/**\n * A middleware `transform` or `enabled` timed out. Retrying reruns the same slow code, so unlike a\n * provider timeout it is not retryable.\n */\nexport const NON_RETRYABLE_MIDDLEWARE_TIMEOUT_CODES: ReadonlySet<LLMErrorCode> = new Set([\n 'middleware_timeout',\n]);\n\n/**\n * Types that are never worth retrying on their own: deterministic\n * caller-input, model-response, or cancellation failures rather than a\n * transient provider fault.\n */\nconst NON_RETRYABLE_TYPES: ReadonlySet<LLMErrorType> = new Set([\n 'parse',\n 'validation',\n 'invalid_params',\n 'aborted',\n]);\n\n/**\n * The retry rule behind both `LLMError.retryable` and `LLMErrorSnapshot.retryable`, kept in one\n * place so they can't drift.\n */\nfunction computeRetryable(type: LLMErrorType, code: LLMErrorCode | undefined): boolean {\n // Output cut off at max_tokens can come back complete on a resend, since\n // generation varies, unlike a reply that was whole and still malformed.\n if (code === 'response_truncated') return true;\n if (NON_RETRYABLE_TYPES.has(type)) return false;\n if (code && NON_RETRYABLE_TOOL_CONTRACT_CODES.has(code)) return false;\n if (code && LOCAL_RATE_LIMIT_CODES.has(code)) return false;\n if (code && NON_RETRYABLE_MIDDLEWARE_TIMEOUT_CODES.has(code)) return false;\n // Resending the same oversized body can only be rejected again.\n if (code === 'payload_too_large') return false;\n return true;\n}\n\n/**\n * Retryable types that still say nothing about provider health, so they never count toward the\n * breaker.\n */\nconst NON_BREAKER_TYPES: ReadonlySet<LLMErrorType> = new Set(['quota_exceeded']);\n\n/**\n * 4xx statuses that describe the provider rather than one caller's request. Any other 4xx counting\n * would let one bad caller open the circuit for everyone.\n */\nconst PROVIDER_SIDE_CLIENT_STATUSES: ReadonlySet<number> = new Set([408, 425, 429]);\n\nfunction isCallerSideStatus(status: number | undefined): boolean {\n return (\n status !== undefined &&\n status >= 400 &&\n status < 500 &&\n !PROVIDER_SIDE_CLIENT_STATUSES.has(status)\n );\n}\n\n/**\n * The breaker rule behind `LLMError.countsTowardBreaker`. Never counts what `computeRetryable`\n * excludes.\n */\nfunction computeCountsTowardBreaker(\n type: LLMErrorType,\n code: LLMErrorCode | undefined,\n status: number | undefined,\n): boolean {\n if (!computeRetryable(type, code)) return false;\n if (NON_BREAKER_TYPES.has(type)) return false;\n // The provider answered fine, the caller's max_tokens was too small.\n if (code === 'response_truncated') return false;\n if (isCallerSideStatus(status)) return false;\n return true;\n}\n\n/**\n * `issues` if it survives `JSON.stringify`, otherwise a marker string. Only a caller supplied\n * schema validator's error can be circular.\n */\nfunction safeIssues(issues: unknown): unknown {\n if (issues === undefined) return undefined;\n try {\n JSON.stringify(issues);\n return issues;\n } catch {\n return '[Unserializable: issues contained a circular reference]';\n }\n}\n\n/**\n * A JSON safe copy of `body`, or a marker string if it can't be serialized. Cloned, not shared,\n * since adapter code can still mutate the request after dispatch (`fromGemini` sets\n * `request.config` in place).\n */\nfunction safeBody(body: unknown): unknown {\n if (body === undefined) return undefined;\n try {\n return JSON.parse(JSON.stringify(body)) as unknown;\n } catch {\n return '[Unserializable: request body contained a circular reference]';\n }\n}\n\nconst AUTH_HEADER_NAMES = new Set(['authorization', 'x-api-key', 'x-goog-api-key', 'api-key']);\n\n/** Removes auth headers before a request snapshot is built. Case insensitive on header names. */\nfunction stripAuthHeaders(\n headers: Record<string, string> | undefined,\n): Record<string, string> | undefined {\n if (headers === undefined) return undefined;\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (!AUTH_HEADER_NAMES.has(key.toLowerCase())) out[key] = value;\n }\n return out;\n}\n\n/**\n * Depth cap for `safeAttempts`, since a caller can hand build a self referential `attempts` array.\n */\nconst MAX_ATTEMPTS_DEPTH = 20;\n\n/**\n * Copies `attempts` with every nested `issues`, request `body` and request headers rechecked,\n * recursively. A caller can pass hand built attempts straight to the constructor, and a shared\n * `issues` object can turn circular after capture. Extra fields such as `FallbackAttempt.provider`\n * are kept.\n */\nfunction safeAttempts(attempts: RetryAttempt[] | undefined, depth = 0): RetryAttempt[] | undefined {\n if (attempts === undefined) return undefined;\n if (depth >= MAX_ATTEMPTS_DEPTH) return [];\n\n return attempts.map((attempt) => ({\n ...attempt,\n error: {\n ...attempt.error,\n issues: safeIssues(attempt.error.issues),\n attempts: safeAttempts(attempt.error.attempts, depth + 1),\n },\n request: attempt.request && {\n ...attempt.request,\n body: safeBody(attempt.request.body),\n headers: stripAuthHeaders(attempt.request.headers),\n },\n }));\n}\n\n/** One tool call's contract failure, used to report every bad call in a response at once. */\nexport interface ToolIssue {\n name: string;\n toolCallId: string;\n code: LLMErrorCode;\n detail?: unknown;\n}\n\n/**\n * The specific values behind a `duplicate_tool_names` failure: the\n * offending call's `tools` array had more than one entry sharing a name.\n */\nexport interface DuplicateToolNamesIssue {\n names: string[];\n}\n\n/**\n * The specific values behind an `unknown_tool_choice` failure: `toolChoice`\n * named a tool that wasn't in the call's own `tools` array.\n */\nexport interface UnknownToolChoiceIssue {\n requested: string;\n available: string[];\n}\n\n/**\n * The specific values behind a `duplicate_tool_result_ids` /\n * `unknown_tool_result_ids` / `missing_tool_results` failure: which\n * `history` turn was affected, and which `toolCallId`s were the problem.\n */\nexport interface HistoryToolResultIssue {\n historyIndex: number;\n ids: string[];\n}\n\n/**\n * The specific values behind an `unsupported_capability` failure: which\n * capability the current adapter/client/model doesn't support.\n */\nexport interface UnsupportedCapabilityIssue {\n capability: string;\n}\n\n/**\n * The exact `issues` shape for each code that carries one. Codes whose message already says\n * everything have no entry. Schema validation `issues` stay untyped, since they come from the\n * caller's own validator.\n */\nexport interface LLMErrorIssuesByCode {\n unknown_tool: ToolIssue[];\n duplicate_tool_call_id: ToolIssue[];\n duplicate_tool_names: DuplicateToolNamesIssue;\n unknown_tool_choice: UnknownToolChoiceIssue;\n duplicate_tool_result_ids: HistoryToolResultIssue;\n unknown_tool_result_ids: HistoryToolResultIssue;\n missing_tool_results: HistoryToolResultIssue;\n unsupported_capability: UnsupportedCapabilityIssue;\n}\n\n/**\n * Plain data copy of an `LLMError`, as held by `RetryAttempt.error`. Never thrown again, so no\n * `cause` and no live getters. Nested `attempts` form a tree, not a cycle.\n */\nexport interface LLMErrorSnapshot {\n message: string;\n type: LLMErrorType;\n status?: number;\n issues?: unknown;\n retryAfterMs?: number;\n code?: LLMErrorCode;\n /** Computed once, at snapshot time, since a snapshot has no live getter. */\n retryable: boolean;\n /** This attempt's own prior attempts, if it was itself the terminal failure of a retry loop. */\n attempts?: RetryAttempt[];\n}\n\n/**\n * Plain data copy of the request an attempt sent, as held by `RetryAttempt.request`. Safe to\n * serialize and store.\n */\nexport interface LLMRequestSnapshot {\n /** Provider id this attempt targeted, e.g. \"openai\". */\n provider: string;\n /** Model id this attempt targeted. */\n model: string;\n /** The payload as actually sent for this attempt, after any transform/repair. Passed through `safeBody`. */\n body: unknown;\n /** Non sensitive request headers. Auth headers are stripped before the snapshot is built, never included. */\n headers?: Record<string, string>;\n /** Wall clock time the attempt started, ms since epoch. */\n startedAt: number;\n}\n\n/**\n * A plain data copy of one attempt's request. Pass `startedAt` when known: this often runs after\n * the attempt failed, so `Date.now()` would record the wrong time.\n */\nexport function toRequestSnapshot(\n provider: string,\n model: string,\n body: unknown,\n headers?: Record<string, string>,\n startedAt: number = Date.now(),\n): LLMRequestSnapshot {\n return {\n provider,\n model,\n body: safeBody(body),\n headers: stripAuthHeaders(headers),\n startedAt,\n };\n}\n\n/** One failed attempt: its index and a snapshot of its error. `FallbackAttempt` extends it. */\nexport interface RetryAttempt {\n index: number;\n error: LLMErrorSnapshot;\n /** What was sent for this attempt. Optional: absent for attempts predating this field. */\n request?: LLMRequestSnapshot;\n}\n\n/** Optional fields for constructing an {@link LLMError}. `message` and `type` stay positional since every throw site sets both. */\nexport interface LLMErrorOptions {\n status?: number;\n issues?: unknown;\n cause?: unknown;\n retryAfterMs?: number;\n /** Stable discriminator within `type`. Absent on errors predating it. */\n code?: LLMErrorCode;\n /** Every attempt made before this error was thrown, in order. Absent when nothing was retried. */\n attempts?: RetryAttempt[];\n}\n\nexport class LLMError extends Error {\n public status?: number;\n public issues?: unknown;\n public cause?: unknown;\n public retryAfterMs?: number;\n /** Stable discriminator within `type`. Absent on errors predating it. */\n public code?: LLMErrorCode;\n /** Every attempt made before this error was thrown, in order. Absent when nothing was retried. */\n public attempts?: RetryAttempt[];\n\n constructor(\n message: string,\n public type: LLMErrorType,\n options: LLMErrorOptions = {},\n ) {\n super(message);\n this.name = 'LLMError';\n this.status = options.status;\n this.issues = options.issues;\n this.cause = options.cause;\n this.retryAfterMs = options.retryAfterMs;\n this.code = options.code;\n this.attempts = options.attempts;\n }\n\n /**\n * Whether retrying could help, from `type` and `code` alone. See Error Handling for the full\n * list. `response_truncated` is retryable despite its `parse` type.\n */\n get retryable(): boolean {\n return computeRetryable(this.type, this.code);\n }\n\n /**\n * Whether this failure counts toward the breaker. Stricter than `retryable`: `quota_exceeded` and\n * any 4xx other than 408, 425 and 429 are excluded.\n */\n get countsTowardBreaker(): boolean {\n return computeCountsTowardBreaker(this.type, this.code, this.status);\n }\n\n /**\n * Copies the fields into an `LLMErrorSnapshot`. `cause` is left out, and `issues` are made\n * serialization safe.\n */\n toSnapshot(): LLMErrorSnapshot {\n return {\n message: this.message,\n type: this.type,\n status: this.status,\n issues: safeIssues(this.issues),\n retryAfterMs: this.retryAfterMs,\n code: this.code,\n retryable: this.retryable,\n attempts: safeAttempts(this.attempts),\n };\n }\n\n /**\n * Serializes `message` and `retryable` too, which a plain property walk misses. `cause` is left\n * out since SDK errors can be circular; read `err.cause` directly.\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n type: this.type,\n status: this.status,\n issues: safeIssues(this.issues),\n retryAfterMs: this.retryAfterMs,\n code: this.code,\n retryable: this.retryable,\n attempts: safeAttempts(this.attempts),\n };\n }\n}\n\nexport function isLLMError(err: unknown): err is LLMError {\n return err instanceof LLMError;\n}\n\n/**\n * Narrows `err.issues` to the shape `LLMErrorIssuesByCode` maps `code` to, so no cast is needed.\n */\nexport function hasIssues<C extends keyof LLMErrorIssuesByCode>(\n err: LLMError,\n code: C,\n): err is LLMError & { code: C; issues: LLMErrorIssuesByCode[C] } {\n return err.code === code && err.issues !== undefined;\n}\n","/** A normalized read of a provider's rate limit headers. */\nexport interface ProviderRateLimitHint {\n remainingRequests?: number;\n limitRequests?: number;\n resetAfterMs?: number;\n}\n\n/** The minimal shape needed to read a header by name, satisfied by `Headers` and `ResponseLike['headers']`. */\nexport interface HeaderReader {\n get(name: string): string | null;\n}\n\n/** Parses a Go-style duration (`\"1s\"`, `\"6m0s\"`) into ms, OpenAI's `x-ratelimit-reset-*` format. Undefined if unrecognized. */\nfunction parseGoDuration(value: string): number | undefined {\n const match =\n /^(?:(\\d+(?:\\.\\d+)?)h)?(?:(\\d+(?:\\.\\d+)?)m)?(?:(\\d+(?:\\.\\d+)?)s)?(?:(\\d+)ms)?$/.exec(\n value.trim(),\n );\n\n if (!match || match[0] === '') return undefined;\n\n const [, hours, minutes, seconds, millis] = match;\n const ms =\n (hours ? parseFloat(hours) * 3_600_000 : 0) +\n (minutes ? parseFloat(minutes) * 60_000 : 0) +\n (seconds ? parseFloat(seconds) * 1_000 : 0) +\n (millis ? parseInt(millis, 10) : 0);\n\n return Number.isFinite(ms) ? ms : undefined;\n}\n\nfunction parseIntHeader(headers: HeaderReader, name: string): number | undefined {\n const raw = headers.get(name);\n\n // `Number('')` is 0, which would read a blank header as \"no requests left\".\n if (raw === null || raw.trim() === '') return undefined;\n\n const value = Number(raw);\n return Number.isFinite(value) ? value : undefined;\n}\n\n/** OpenAI's (and every OpenAI-compatible provider's) header set: `x-ratelimit-{limit,remaining,reset}-requests`. */\nexport function parseOpenAIRateLimitHeaders(headers: HeaderReader): ProviderRateLimitHint {\n const resetRaw = headers.get('x-ratelimit-reset-requests');\n\n return {\n limitRequests: parseIntHeader(headers, 'x-ratelimit-limit-requests'),\n remainingRequests: parseIntHeader(headers, 'x-ratelimit-remaining-requests'),\n resetAfterMs: resetRaw !== null ? parseGoDuration(resetRaw) : undefined,\n };\n}\n\n/** Anthropic's header set: `anthropic-ratelimit-requests-{limit,remaining,reset}`. `reset` is RFC 3339, not a duration. */\nexport function parseAnthropicRateLimitHeaders(headers: HeaderReader): ProviderRateLimitHint {\n const resetRaw = headers.get('anthropic-ratelimit-requests-reset');\n const resetAt = resetRaw !== null ? Date.parse(resetRaw) : NaN;\n\n return {\n limitRequests: parseIntHeader(headers, 'anthropic-ratelimit-requests-limit'),\n remainingRequests: parseIntHeader(headers, 'anthropic-ratelimit-requests-remaining'),\n resetAfterMs: Number.isFinite(resetAt) ? Math.max(0, resetAt - Date.now()) : undefined,\n };\n}\n\n/** Tries both known header shapes, returns whichever produced anything. Used for the reactive path, where the provider behind a thrown error isn't known. */\nexport function parseAnyRateLimitHeaders(headers: HeaderReader): ProviderRateLimitHint | undefined {\n const openai = parseOpenAIRateLimitHeaders(headers);\n if (openai.remainingRequests !== undefined || openai.limitRequests !== undefined) return openai;\n\n const anthropic = parseAnthropicRateLimitHeaders(headers);\n if (anthropic.remainingRequests !== undefined || anthropic.limitRequests !== undefined) {\n return anthropic;\n }\n\n return undefined;\n}\n\n/** Not exported: lets a hint travel attached to a response without appearing on its declared shape. Mirrors the fetch adapter's `err.headers`, for the success path. */\nconst RATE_LIMIT_HINT = Symbol('vernLLMProviderRateLimitHint');\n\n/** Attaches a parsed hint onto a successful response value. No-op if `hint` is undefined. */\nexport function attachRateLimitHint(value: object, hint: ProviderRateLimitHint | undefined): void {\n if (!hint) return;\n\n Object.defineProperty(value, RATE_LIMIT_HINT, {\n value: hint,\n enumerable: false,\n configurable: true,\n });\n}\n\n/** Reads a hint attached by `attachRateLimitHint`, if any. */\nexport function readRateLimitHint(value: unknown): ProviderRateLimitHint | undefined {\n if (!value || typeof value !== 'object') return undefined;\n return (value as { [RATE_LIMIT_HINT]?: ProviderRateLimitHint })[RATE_LIMIT_HINT];\n}\n","/**\n * Lets an adapter build an error whose message embeds a raw provider\n * response body, while keeping that body redactable. The adapter has no\n * access to `VernLLMOptions.redact`, so it records where the body sits\n * and `redactResponseBody` rewrites the message once core has caught it,\n * before it becomes `LLMError.message` or reaches any log.\n */\n\n/** How much of a response body an error message keeps. */\nconst MAX_BODY_CHARS = 500;\n\nconst pendingBodies = new WeakMap<Error, { prefix: string; body: string }>();\n\n/** An `Error` reading `${prefix}: ${body}`, with the body registered for redaction. */\nexport function errorWithResponseBody(prefix: string, body: string): Error {\n const truncated = body.slice(0, MAX_BODY_CHARS);\n const error = new Error(`${prefix}: ${truncated}`);\n pendingBodies.set(error, { prefix, body: truncated });\n return error;\n}\n\n/**\n * Passes the body of an error built by `errorWithResponseBody` through\n * `redact`, rewriting its message in place. Runs at most once per error.\n * A throwing `redact` drops the body instead of leaking it unredacted.\n */\nexport function redactResponseBody(error: unknown, redact: (text: string) => string): void {\n if (!(error instanceof Error)) return;\n\n const pending = pendingBodies.get(error);\n if (!pending) return;\n\n pendingBodies.delete(error);\n\n let redacted: string;\n\n try {\n redacted = redact(pending.body);\n } catch {\n redacted = '[response body withheld: redact threw]';\n }\n\n // Read before the message changes: V8 formats `stack` lazily on first\n // access, from whatever the message is at that moment.\n const rawStack = error.stack;\n const rawHeader = `${error.name}: ${error.message}`;\n\n error.message = `${pending.prefix}: ${redacted}`;\n\n // The stack starts with the message as it was when the error was built,\n // and a body with newlines spans several lines of it. Replace exactly\n // that header so no part of the raw body survives, keeping the frames.\n // A stack that doesn't start with it (rewritten elsewhere) is dropped to\n // its header rather than risk keeping body text.\n if (typeof rawStack === 'string') {\n const frames = rawStack.startsWith(rawHeader) ? rawStack.slice(rawHeader.length) : '';\n error.stack = `${error.name}: ${error.message}${frames}`;\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vern-llm",
3
- "version": "2.9.0",
3
+ "version": "3.0.0",
4
4
  "description": "The LLM call framework. Resilience, observability, and control for every call. Retry budgets, timeouts, provider fallback, rate limiting, circuit breaking and more, dependency-light and typed from the start.",
5
5
  "keywords": [
6
6
  "llm",
@@ -8,7 +8,6 @@
8
8
  "openai",
9
9
  "anthropic",
10
10
  "gemini",
11
- "bedrock",
12
11
  "ai-sdk",
13
12
  "llm-client",
14
13
  "retry",
@@ -32,6 +31,16 @@
32
31
  "types": "./dist/index.d.cts",
33
32
  "default": "./dist/index.cjs"
34
33
  }
34
+ },
35
+ "./adapters": {
36
+ "import": {
37
+ "types": "./dist/adapters/index.d.mts",
38
+ "default": "./dist/adapters/index.mjs"
39
+ },
40
+ "require": {
41
+ "types": "./dist/adapters/index.d.cts",
42
+ "default": "./dist/adapters/index.cjs"
43
+ }
35
44
  }
36
45
  },
37
46
  "files": [
@@ -42,13 +51,9 @@
42
51
  },
43
52
  "devDependencies": {
44
53
  "@anthropic-ai/sdk": "^0.127.0",
45
- "@aws-sdk/client-bedrock-runtime": "^3.1136.0",
46
54
  "@changesets/cli": "^3.0.3",
47
55
  "@google/genai": "^2.23.0",
48
56
  "@size-limit/file": "^13.1.1",
49
- "@smithy/eventstream-serde-node": "^4.5.2",
50
- "@smithy/node-http-handler": "^4.12.1",
51
- "@smithy/util-utf8": "^4.5.2",
52
57
  "@types/node": "^26.4.1",
53
58
  "@vitest/coverage-v8": "^4.1.11",
54
59
  "groq-sdk": "^1.6.0",
@@ -79,6 +84,14 @@
79
84
  {
80
85
  "path": "dist/index.cjs",
81
86
  "limit": "120 KB"
87
+ },
88
+ {
89
+ "path": "dist/adapters/index.mjs",
90
+ "limit": "12 KB"
91
+ },
92
+ {
93
+ "path": "dist/adapters/index.cjs",
94
+ "limit": "12 KB"
82
95
  }
83
96
  ],
84
97
  "scripts": {