UNPKG

@ai-sdk/provider-utils

Version:
1 lines 181 kB
{"version":3,"sources":["../src/combine-headers.ts","../src/convert-async-iterator-to-readable-stream.ts","../src/create-tool-name-mapping.ts","../src/delay.ts","../src/delayed-promise.ts","../src/extract-response-headers.ts","../src/uint8-utils.ts","../src/convert-image-model-file-to-data-uri.ts","../src/convert-to-form-data.ts","../src/download-error.ts","../src/download-blob.ts","../src/generate-id.ts","../src/get-error-message.ts","../src/get-from-api.ts","../src/handle-fetch-error.ts","../src/is-abort-error.ts","../src/get-runtime-environment-user-agent.ts","../src/normalize-headers.ts","../src/with-user-agent-suffix.ts","../src/version.ts","../src/inject-json-instruction.ts","../src/is-non-nullable.ts","../src/is-url-supported.ts","../src/load-api-key.ts","../src/load-optional-setting.ts","../src/load-setting.ts","../src/media-type-to-extension.ts","../src/parse-json.ts","../src/secure-json-parse.ts","../src/validate-types.ts","../src/schema.ts","../src/add-additional-properties-to-json-schema.ts","../src/to-json-schema/zod3-to-json-schema/options.ts","../src/to-json-schema/zod3-to-json-schema/select-parser.ts","../src/to-json-schema/zod3-to-json-schema/parsers/any.ts","../src/to-json-schema/zod3-to-json-schema/parsers/array.ts","../src/to-json-schema/zod3-to-json-schema/parsers/bigint.ts","../src/to-json-schema/zod3-to-json-schema/parsers/boolean.ts","../src/to-json-schema/zod3-to-json-schema/parsers/branded.ts","../src/to-json-schema/zod3-to-json-schema/parsers/catch.ts","../src/to-json-schema/zod3-to-json-schema/parsers/date.ts","../src/to-json-schema/zod3-to-json-schema/parsers/default.ts","../src/to-json-schema/zod3-to-json-schema/parsers/effects.ts","../src/to-json-schema/zod3-to-json-schema/parsers/enum.ts","../src/to-json-schema/zod3-to-json-schema/parsers/intersection.ts","../src/to-json-schema/zod3-to-json-schema/parsers/literal.ts","../src/to-json-schema/zod3-to-json-schema/parsers/record.ts","../src/to-json-schema/zod3-to-json-schema/parsers/string.ts","../src/to-json-schema/zod3-to-json-schema/parsers/map.ts","../src/to-json-schema/zod3-to-json-schema/parsers/native-enum.ts","../src/to-json-schema/zod3-to-json-schema/parsers/never.ts","../src/to-json-schema/zod3-to-json-schema/parsers/null.ts","../src/to-json-schema/zod3-to-json-schema/parsers/union.ts","../src/to-json-schema/zod3-to-json-schema/parsers/nullable.ts","../src/to-json-schema/zod3-to-json-schema/parsers/number.ts","../src/to-json-schema/zod3-to-json-schema/parsers/object.ts","../src/to-json-schema/zod3-to-json-schema/parsers/optional.ts","../src/to-json-schema/zod3-to-json-schema/parsers/pipeline.ts","../src/to-json-schema/zod3-to-json-schema/parsers/promise.ts","../src/to-json-schema/zod3-to-json-schema/parsers/set.ts","../src/to-json-schema/zod3-to-json-schema/parsers/tuple.ts","../src/to-json-schema/zod3-to-json-schema/parsers/undefined.ts","../src/to-json-schema/zod3-to-json-schema/parsers/unknown.ts","../src/to-json-schema/zod3-to-json-schema/parsers/readonly.ts","../src/to-json-schema/zod3-to-json-schema/get-relative-path.ts","../src/to-json-schema/zod3-to-json-schema/parse-def.ts","../src/to-json-schema/zod3-to-json-schema/refs.ts","../src/to-json-schema/zod3-to-json-schema/zod3-to-json-schema.ts","../src/parse-json-event-stream.ts","../src/parse-provider-options.ts","../src/post-to-api.ts","../src/types/tool.ts","../src/provider-tool-factory.ts","../src/remove-undefined-entries.ts","../src/resolve.ts","../src/response-handler.ts","../src/without-trailing-slash.ts","../src/is-async-iterable.ts","../src/types/execute-tool.ts","../src/index.ts"],"sourcesContent":["export function combineHeaders(\n ...headers: Array<Record<string, string | undefined> | undefined>\n): Record<string, string | undefined> {\n return headers.reduce(\n (combinedHeaders, currentHeaders) => ({\n ...combinedHeaders,\n ...(currentHeaders ?? {}),\n }),\n {},\n ) as Record<string, string | undefined>;\n}\n","/**\n * Converts an AsyncIterator to a ReadableStream.\n *\n * @template T - The type of elements produced by the AsyncIterator.\n * @param { <T>} iterator - The AsyncIterator to convert.\n * @returns {ReadableStream<T>} - A ReadableStream that provides the same data as the AsyncIterator.\n */\nexport function convertAsyncIteratorToReadableStream<T>(\n iterator: AsyncIterator<T>,\n): ReadableStream<T> {\n let cancelled = false;\n\n return new ReadableStream<T>({\n /**\n * Called when the consumer wants to pull more data from the stream.\n *\n * @param {ReadableStreamDefaultController<T>} controller - The controller to enqueue data into the stream.\n * @returns {Promise<void>}\n */\n async pull(controller) {\n if (cancelled) return;\n try {\n const { value, done } = await iterator.next();\n if (done) {\n controller.close();\n } else {\n controller.enqueue(value);\n }\n } catch (error) {\n controller.error(error);\n }\n },\n /**\n * Called when the consumer cancels the stream.\n */\n async cancel(reason?: unknown) {\n cancelled = true;\n if (iterator.return) {\n try {\n await iterator.return(reason);\n } catch {\n // intentionally ignore errors during cancellation\n }\n }\n },\n });\n}\n","import {\n LanguageModelV3FunctionTool,\n LanguageModelV3ProviderTool,\n} from '@ai-sdk/provider';\n\n/**\n * Interface for mapping between custom tool names and provider tool names.\n */\nexport interface ToolNameMapping {\n /**\n * Maps a custom tool name (used by the client) to the provider's tool name.\n * If the custom tool name does not have a mapping, returns the input name.\n *\n * @param customToolName - The custom name of the tool defined by the client.\n * @returns The corresponding provider tool name, or the input name if not mapped.\n */\n toProviderToolName: (customToolName: string) => string;\n\n /**\n * Maps a provider tool name to the custom tool name used by the client.\n * If the provider tool name does not have a mapping, returns the input name.\n *\n * @param providerToolName - The name of the tool as understood by the provider.\n * @returns The corresponding custom tool name, or the input name if not mapped.\n */\n toCustomToolName: (providerToolName: string) => string;\n}\n\n/**\n * @param tools - Tools that were passed to the language model.\n * @param providerToolNames - Maps the provider tool ids to the provider tool names.\n */\nexport function createToolNameMapping({\n tools = [],\n providerToolNames,\n}: {\n /**\n * Tools that were passed to the language model.\n */\n tools:\n | Array<LanguageModelV3FunctionTool | LanguageModelV3ProviderTool>\n | undefined;\n\n /**\n * Maps the provider tool ids to the provider tool names.\n */\n providerToolNames: Record<`${string}.${string}`, string>;\n}): ToolNameMapping {\n const customToolNameToProviderToolName: Record<string, string> = {};\n const providerToolNameToCustomToolName: Record<string, string> = {};\n\n for (const tool of tools) {\n if (tool.type === 'provider' && tool.id in providerToolNames) {\n const providerToolName = providerToolNames[tool.id];\n customToolNameToProviderToolName[tool.name] = providerToolName;\n providerToolNameToCustomToolName[providerToolName] = tool.name;\n }\n }\n\n return {\n toProviderToolName: (customToolName: string) =>\n customToolNameToProviderToolName[customToolName] ?? customToolName,\n toCustomToolName: (providerToolName: string) =>\n providerToolNameToCustomToolName[providerToolName] ?? providerToolName,\n };\n}\n","/**\n * Creates a Promise that resolves after a specified delay\n * @param delayInMs - The delay duration in milliseconds. If null or undefined, resolves immediately.\n * @param signal - Optional AbortSignal to cancel the delay\n * @returns A Promise that resolves after the specified delay\n * @throws {DOMException} When the signal is aborted\n */\nexport async function delay(\n delayInMs?: number | null,\n options?: {\n abortSignal?: AbortSignal;\n },\n): Promise<void> {\n if (delayInMs == null) {\n return Promise.resolve();\n }\n\n const signal = options?.abortSignal;\n\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(createAbortError());\n return;\n }\n\n const timeoutId = setTimeout(() => {\n cleanup();\n resolve();\n }, delayInMs);\n\n const cleanup = () => {\n clearTimeout(timeoutId);\n signal?.removeEventListener('abort', onAbort);\n };\n\n const onAbort = () => {\n cleanup();\n reject(createAbortError());\n };\n\n signal?.addEventListener('abort', onAbort);\n });\n}\n\nfunction createAbortError(): DOMException {\n return new DOMException('Delay was aborted', 'AbortError');\n}\n","/**\n * Delayed promise. It is only constructed once the value is accessed.\n * This is useful to avoid unhandled promise rejections when the promise is created\n * but not accessed.\n */\nexport class DelayedPromise<T> {\n private status:\n | { type: 'pending' }\n | { type: 'resolved'; value: T }\n | { type: 'rejected'; error: unknown } = { type: 'pending' };\n private _promise: Promise<T> | undefined;\n private _resolve: undefined | ((value: T) => void) = undefined;\n private _reject: undefined | ((error: unknown) => void) = undefined;\n\n get promise(): Promise<T> {\n if (this._promise) {\n return this._promise;\n }\n\n this._promise = new Promise<T>((resolve, reject) => {\n if (this.status.type === 'resolved') {\n resolve(this.status.value);\n } else if (this.status.type === 'rejected') {\n reject(this.status.error);\n }\n\n this._resolve = resolve;\n this._reject = reject;\n });\n\n return this._promise;\n }\n\n resolve(value: T): void {\n this.status = { type: 'resolved', value };\n\n if (this._promise) {\n this._resolve?.(value);\n }\n }\n\n reject(error: unknown): void {\n this.status = { type: 'rejected', error };\n\n if (this._promise) {\n this._reject?.(error);\n }\n }\n\n isResolved(): boolean {\n return this.status.type === 'resolved';\n }\n\n isRejected(): boolean {\n return this.status.type === 'rejected';\n }\n\n isPending(): boolean {\n return this.status.type === 'pending';\n }\n}\n","/**\nExtracts the headers from a response object and returns them as a key-value object.\n\n@param response - The response object to extract headers from.\n@returns The headers as a key-value object.\n*/\nexport function extractResponseHeaders(response: Response) {\n return Object.fromEntries<string>([...response.headers]);\n}\n","// btoa and atob need to be invoked as a function call, not as a method call.\n// Otherwise CloudFlare will throw a\n// \"TypeError: Illegal invocation: function called with incorrect this reference\"\nconst { btoa, atob } = globalThis;\n\nexport function convertBase64ToUint8Array(base64String: string) {\n const base64Url = base64String.replace(/-/g, '+').replace(/_/g, '/');\n const latin1string = atob(base64Url);\n return Uint8Array.from(latin1string, byte => byte.codePointAt(0)!);\n}\n\nexport function convertUint8ArrayToBase64(array: Uint8Array): string {\n let latin1string = '';\n\n // Note: regular for loop to support older JavaScript versions that\n // do not support for..of on Uint8Array\n for (let i = 0; i < array.length; i++) {\n latin1string += String.fromCodePoint(array[i]);\n }\n\n return btoa(latin1string);\n}\n\nexport function convertToBase64(value: string | Uint8Array): string {\n return value instanceof Uint8Array ? convertUint8ArrayToBase64(value) : value;\n}\n","import { ImageModelV3File } from '@ai-sdk/provider';\nimport { convertUint8ArrayToBase64 } from './uint8-utils';\n\n/**\n * Convert an ImageModelV3File to a URL or data URI string.\n *\n * If the file is a URL, it returns the URL as-is.\n * If the file is base64 data, it returns a data URI with the base64 data.\n * If the file is a Uint8Array, it converts it to base64 and returns a data URI.\n */\nexport function convertImageModelFileToDataUri(file: ImageModelV3File): string {\n if (file.type === 'url') return file.url;\n\n return `data:${file.mediaType};base64,${\n typeof file.data === 'string'\n ? file.data\n : convertUint8ArrayToBase64(file.data)\n }`;\n}\n","/**\n * Converts an input object to FormData for multipart/form-data requests.\n *\n * Handles the following cases:\n * - `null` or `undefined` values are skipped\n * - Arrays with a single element are appended as a single value\n * - Arrays with multiple elements are appended with `[]` suffix (e.g., `image[]`)\n * unless `useArrayBrackets` is set to `false`\n * - All other values are appended directly\n *\n * @param input - The input object to convert. Use a generic type for type validation.\n * @param options - Optional configuration object.\n * @param options.useArrayBrackets - Whether to add `[]` suffix for multi-element arrays.\n * Defaults to `true`. Set to `false` for APIs that expect repeated keys without brackets.\n * @returns A FormData object containing the input values.\n *\n * @example\n * ```ts\n * type MyInput = {\n * model: string;\n * prompt: string;\n * images: Blob[];\n * };\n *\n * const formData = convertToFormData<MyInput>({\n * model: 'gpt-image-1',\n * prompt: 'A cat',\n * images: [blob1, blob2],\n * });\n * ```\n */\nexport function convertToFormData<T extends Record<string, unknown>>(\n input: T,\n options: { useArrayBrackets?: boolean } = {},\n): FormData {\n const { useArrayBrackets = true } = options;\n const formData = new FormData();\n\n for (const [key, value] of Object.entries(input)) {\n if (value == null) {\n continue;\n }\n\n if (Array.isArray(value)) {\n if (value.length === 1) {\n formData.append(key, value[0] as string | Blob);\n continue;\n }\n\n const arrayKey = useArrayBrackets ? `${key}[]` : key;\n for (const item of value) {\n formData.append(arrayKey, item as string | Blob);\n }\n continue;\n }\n\n formData.append(key, value as string | Blob);\n }\n\n return formData;\n}\n","import { AISDKError } from '@ai-sdk/provider';\n\nconst name = 'AI_DownloadError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\nexport class DownloadError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n readonly url: string;\n readonly statusCode?: number;\n readonly statusText?: string;\n\n constructor({\n url,\n statusCode,\n statusText,\n cause,\n message = cause == null\n ? `Failed to download ${url}: ${statusCode} ${statusText}`\n : `Failed to download ${url}: ${cause}`,\n }: {\n url: string;\n statusCode?: number;\n statusText?: string;\n message?: string;\n cause?: unknown;\n }) {\n super({ name, message, cause });\n\n this.url = url;\n this.statusCode = statusCode;\n this.statusText = statusText;\n }\n\n static isInstance(error: unknown): error is DownloadError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { DownloadError } from './download-error';\n\n/**\n * Download a file from a URL and return it as a Blob.\n *\n * @param url - The URL to download from.\n * @returns A Promise that resolves to the downloaded Blob.\n *\n * @throws DownloadError if the download fails.\n */\nexport async function downloadBlob(url: string): Promise<Blob> {\n try {\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new DownloadError({\n url,\n statusCode: response.status,\n statusText: response.statusText,\n });\n }\n\n return await response.blob();\n } catch (error) {\n if (DownloadError.isInstance(error)) {\n throw error;\n }\n\n throw new DownloadError({ url, cause: error });\n }\n}\n","import { InvalidArgumentError } from '@ai-sdk/provider';\n\n/**\nCreates an ID generator.\nThe total length of the ID is the sum of the prefix, separator, and random part length.\nNot cryptographically secure.\n\n@param alphabet - The alphabet to use for the ID. Default: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.\n@param prefix - The prefix of the ID to generate. Optional.\n@param separator - The separator between the prefix and the random part of the ID. Default: '-'.\n@param size - The size of the random part of the ID to generate. Default: 16.\n */\nexport const createIdGenerator = ({\n prefix,\n size = 16,\n alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',\n separator = '-',\n}: {\n prefix?: string;\n separator?: string;\n size?: number;\n alphabet?: string;\n} = {}): IdGenerator => {\n const generator = () => {\n const alphabetLength = alphabet.length;\n const chars = new Array(size);\n for (let i = 0; i < size; i++) {\n chars[i] = alphabet[(Math.random() * alphabetLength) | 0];\n }\n return chars.join('');\n };\n\n if (prefix == null) {\n return generator;\n }\n\n // check that the prefix is not part of the alphabet (otherwise prefix checking can fail randomly)\n if (alphabet.includes(separator)) {\n throw new InvalidArgumentError({\n argument: 'separator',\n message: `The separator \"${separator}\" must not be part of the alphabet \"${alphabet}\".`,\n });\n }\n\n return () => `${prefix}${separator}${generator()}`;\n};\n\n/**\nA function that generates an ID.\n */\nexport type IdGenerator = () => string;\n\n/**\nGenerates a 16-character random string to use for IDs.\nNot cryptographically secure.\n */\nexport const generateId = createIdGenerator();\n","export function getErrorMessage(error: unknown | undefined) {\n if (error == null) {\n return 'unknown error';\n }\n\n if (typeof error === 'string') {\n return error;\n }\n\n if (error instanceof Error) {\n return error.message;\n }\n\n return JSON.stringify(error);\n}\n","import { APICallError } from '@ai-sdk/provider';\nimport { extractResponseHeaders } from './extract-response-headers';\nimport { FetchFunction } from './fetch-function';\nimport { handleFetchError } from './handle-fetch-error';\nimport { isAbortError } from './is-abort-error';\nimport { ResponseHandler } from './response-handler';\nimport { getRuntimeEnvironmentUserAgent } from './get-runtime-environment-user-agent';\nimport { withUserAgentSuffix } from './with-user-agent-suffix';\nimport { VERSION } from './version';\n\n// use function to allow for mocking in tests:\nconst getOriginalFetch = () => globalThis.fetch;\n\nexport const getFromApi = async <T>({\n url,\n headers = {},\n successfulResponseHandler,\n failedResponseHandler,\n abortSignal,\n fetch = getOriginalFetch(),\n}: {\n url: string;\n headers?: Record<string, string | undefined>;\n failedResponseHandler: ResponseHandler<Error>;\n successfulResponseHandler: ResponseHandler<T>;\n abortSignal?: AbortSignal;\n fetch?: FetchFunction;\n}) => {\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: withUserAgentSuffix(\n headers,\n `ai-sdk/provider-utils/${VERSION}`,\n getRuntimeEnvironmentUserAgent(),\n ),\n signal: abortSignal,\n });\n\n const responseHeaders = extractResponseHeaders(response);\n\n if (!response.ok) {\n let errorInformation: {\n value: Error;\n responseHeaders?: Record<string, string> | undefined;\n };\n\n try {\n errorInformation = await failedResponseHandler({\n response,\n url,\n requestBodyValues: {},\n });\n } catch (error) {\n if (isAbortError(error) || APICallError.isInstance(error)) {\n throw error;\n }\n\n throw new APICallError({\n message: 'Failed to process error response',\n cause: error,\n statusCode: response.status,\n url,\n responseHeaders,\n requestBodyValues: {},\n });\n }\n\n throw errorInformation.value;\n }\n\n try {\n return await successfulResponseHandler({\n response,\n url,\n requestBodyValues: {},\n });\n } catch (error) {\n if (error instanceof Error) {\n if (isAbortError(error) || APICallError.isInstance(error)) {\n throw error;\n }\n }\n\n throw new APICallError({\n message: 'Failed to process successful response',\n cause: error,\n statusCode: response.status,\n url,\n responseHeaders,\n requestBodyValues: {},\n });\n }\n } catch (error) {\n throw handleFetchError({ error, url, requestBodyValues: {} });\n }\n};\n","import { APICallError } from '@ai-sdk/provider';\nimport { isAbortError } from './is-abort-error';\n\nconst FETCH_FAILED_ERROR_MESSAGES = ['fetch failed', 'failed to fetch'];\n\nexport function handleFetchError({\n error,\n url,\n requestBodyValues,\n}: {\n error: unknown;\n url: string;\n requestBodyValues: unknown;\n}) {\n if (isAbortError(error)) {\n return error;\n }\n\n // unwrap original error when fetch failed (for easier debugging):\n if (\n error instanceof TypeError &&\n FETCH_FAILED_ERROR_MESSAGES.includes(error.message.toLowerCase())\n ) {\n const cause = (error as any).cause;\n\n if (cause != null) {\n // Failed to connect to server:\n return new APICallError({\n message: `Cannot connect to API: ${cause.message}`,\n cause,\n url,\n requestBodyValues,\n isRetryable: true, // retry when network error\n });\n }\n }\n\n return error;\n}\n","export function isAbortError(error: unknown): error is Error {\n return (\n (error instanceof Error || error instanceof DOMException) &&\n (error.name === 'AbortError' ||\n error.name === 'ResponseAborted' || // Next.js\n error.name === 'TimeoutError')\n );\n}\n","export function getRuntimeEnvironmentUserAgent(\n globalThisAny: any = globalThis as any,\n): string {\n // Browsers\n if (globalThisAny.window) {\n return `runtime/browser`;\n }\n\n // Cloudflare Workers / Deno / Bun / Node.js >= 21.1\n if (globalThisAny.navigator?.userAgent) {\n return `runtime/${globalThisAny.navigator.userAgent.toLowerCase()}`;\n }\n\n // Nodes.js < 21.1\n if (globalThisAny.process?.versions?.node) {\n return `runtime/node.js/${globalThisAny.process.version.substring(0)}`;\n }\n\n if (globalThisAny.EdgeRuntime) {\n return `runtime/vercel-edge`;\n }\n\n return 'runtime/unknown';\n}\n","/**\n * Normalizes different header inputs into a plain record with lower-case keys.\n * Entries with `undefined` or `null` values are removed.\n *\n * @param headers - Input headers (`Headers`, tuples array, plain record) to normalize.\n * @returns A record containing the normalized header entries.\n */\nexport function normalizeHeaders(\n headers:\n | HeadersInit\n | Record<string, string | undefined>\n | Array<[string, string | undefined]>\n | undefined,\n): Record<string, string> {\n if (headers == null) {\n return {};\n }\n\n const normalized: Record<string, string> = {};\n\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n normalized[key.toLowerCase()] = value;\n });\n } else {\n if (!Array.isArray(headers)) {\n headers = Object.entries(headers);\n }\n\n for (const [key, value] of headers) {\n if (value != null) {\n normalized[key.toLowerCase()] = value;\n }\n }\n }\n\n return normalized;\n}\n","import { normalizeHeaders } from './normalize-headers';\n\n/**\n * Appends suffix parts to the `user-agent` header.\n * If a `user-agent` header already exists, the suffix parts are appended to it.\n * If no `user-agent` header exists, a new one is created with the suffix parts.\n * Automatically removes undefined entries from the headers.\n *\n * @param headers - The original headers.\n * @param userAgentSuffixParts - The parts to append to the `user-agent` header.\n * @returns The new headers with the `user-agent` header set or updated.\n */\nexport function withUserAgentSuffix(\n headers: HeadersInit | Record<string, string | undefined> | undefined,\n ...userAgentSuffixParts: string[]\n): Record<string, string> {\n const normalizedHeaders = new Headers(normalizeHeaders(headers));\n\n const currentUserAgentHeader = normalizedHeaders.get('user-agent') || '';\n\n normalizedHeaders.set(\n 'user-agent',\n [currentUserAgentHeader, ...userAgentSuffixParts].filter(Boolean).join(' '),\n );\n\n return Object.fromEntries(normalizedHeaders.entries());\n}\n","// Version string of this package injected at build time.\ndeclare const __PACKAGE_VERSION__: string | undefined;\nexport const VERSION: string =\n typeof __PACKAGE_VERSION__ !== 'undefined'\n ? __PACKAGE_VERSION__\n : '0.0.0-test';\n","import {\n JSONSchema7,\n LanguageModelV3Message,\n LanguageModelV3Prompt,\n} from '@ai-sdk/provider';\n\nconst DEFAULT_SCHEMA_PREFIX = 'JSON schema:';\nconst DEFAULT_SCHEMA_SUFFIX =\n 'You MUST answer with a JSON object that matches the JSON schema above.';\nconst DEFAULT_GENERIC_SUFFIX = 'You MUST answer with JSON.';\n\nexport function injectJsonInstruction({\n prompt,\n schema,\n schemaPrefix = schema != null ? DEFAULT_SCHEMA_PREFIX : undefined,\n schemaSuffix = schema != null\n ? DEFAULT_SCHEMA_SUFFIX\n : DEFAULT_GENERIC_SUFFIX,\n}: {\n prompt?: string;\n schema?: JSONSchema7;\n schemaPrefix?: string;\n schemaSuffix?: string;\n}): string {\n return [\n prompt != null && prompt.length > 0 ? prompt : undefined,\n prompt != null && prompt.length > 0 ? '' : undefined, // add a newline if prompt is not null\n schemaPrefix,\n schema != null ? JSON.stringify(schema) : undefined,\n schemaSuffix,\n ]\n .filter(line => line != null)\n .join('\\n');\n}\n\nexport function injectJsonInstructionIntoMessages({\n messages,\n schema,\n schemaPrefix,\n schemaSuffix,\n}: {\n messages: LanguageModelV3Prompt;\n schema?: JSONSchema7;\n schemaPrefix?: string;\n schemaSuffix?: string;\n}): LanguageModelV3Prompt {\n const systemMessage: LanguageModelV3Message =\n messages[0]?.role === 'system'\n ? { ...messages[0] }\n : { role: 'system', content: '' };\n\n systemMessage.content = injectJsonInstruction({\n prompt: systemMessage.content,\n schema,\n schemaPrefix,\n schemaSuffix,\n });\n\n return [\n systemMessage,\n ...(messages[0]?.role === 'system' ? messages.slice(1) : messages),\n ];\n}\n","/**\n * Type guard that checks whether a value is not `null` or `undefined`.\n *\n * @template T - The type of the value to check.\n * @param value - The value to check.\n * @returns `true` if the value is neither `null` nor `undefined`, otherwise `false`.\n */\nexport function isNonNullable<T>(\n value: T | undefined | null,\n): value is NonNullable<T> {\n return value != null;\n}\n","/**\n * Checks if the given URL is supported natively by the model.\n *\n * @param mediaType - The media type of the URL. Case-sensitive.\n * @param url - The URL to check.\n * @param supportedUrls - A record where keys are case-sensitive media types (or '*')\n * and values are arrays of RegExp patterns for URLs.\n *\n * @returns `true` if the URL matches a pattern under the specific media type\n * or the wildcard '*', `false` otherwise.\n */\nexport function isUrlSupported({\n mediaType,\n url,\n supportedUrls,\n}: {\n mediaType: string;\n url: string;\n supportedUrls: Record<string, RegExp[]>;\n}): boolean {\n // standardize media type and url to lower case\n url = url.toLowerCase();\n mediaType = mediaType.toLowerCase();\n\n return (\n Object.entries(supportedUrls)\n // standardize supported url map into lowercase prefixes:\n .map(([key, value]) => {\n const mediaType = key.toLowerCase();\n return mediaType === '*' || mediaType === '*/*'\n ? { mediaTypePrefix: '', regexes: value }\n : { mediaTypePrefix: mediaType.replace(/\\*/, ''), regexes: value };\n })\n // gather all regexp pattern from matched media type prefixes:\n .filter(({ mediaTypePrefix }) => mediaType.startsWith(mediaTypePrefix))\n .flatMap(({ regexes }) => regexes)\n // check if any pattern matches the url:\n .some(pattern => pattern.test(url))\n );\n}\n","import { LoadAPIKeyError } from '@ai-sdk/provider';\n\nexport function loadApiKey({\n apiKey,\n environmentVariableName,\n apiKeyParameterName = 'apiKey',\n description,\n}: {\n apiKey: string | undefined;\n environmentVariableName: string;\n apiKeyParameterName?: string;\n description: string;\n}): string {\n if (typeof apiKey === 'string') {\n return apiKey;\n }\n\n if (apiKey != null) {\n throw new LoadAPIKeyError({\n message: `${description} API key must be a string.`,\n });\n }\n\n if (typeof process === 'undefined') {\n throw new LoadAPIKeyError({\n message: `${description} API key is missing. Pass it using the '${apiKeyParameterName}' parameter. Environment variables is not supported in this environment.`,\n });\n }\n\n apiKey = process.env[environmentVariableName];\n\n if (apiKey == null) {\n throw new LoadAPIKeyError({\n message: `${description} API key is missing. Pass it using the '${apiKeyParameterName}' parameter or the ${environmentVariableName} environment variable.`,\n });\n }\n\n if (typeof apiKey !== 'string') {\n throw new LoadAPIKeyError({\n message: `${description} API key must be a string. The value of the ${environmentVariableName} environment variable is not a string.`,\n });\n }\n\n return apiKey;\n}\n","/**\n * Loads an optional `string` setting from the environment or a parameter.\n *\n * @param settingValue - The setting value.\n * @param environmentVariableName - The environment variable name.\n * @returns The setting value.\n */\nexport function loadOptionalSetting({\n settingValue,\n environmentVariableName,\n}: {\n settingValue: string | undefined;\n environmentVariableName: string;\n}): string | undefined {\n if (typeof settingValue === 'string') {\n return settingValue;\n }\n\n if (settingValue != null || typeof process === 'undefined') {\n return undefined;\n }\n\n settingValue = process.env[environmentVariableName];\n\n if (settingValue == null || typeof settingValue !== 'string') {\n return undefined;\n }\n\n return settingValue;\n}\n","import { LoadSettingError } from '@ai-sdk/provider';\n\n/**\n * Loads a `string` setting from the environment or a parameter.\n *\n * @param settingValue - The setting value.\n * @param environmentVariableName - The environment variable name.\n * @param settingName - The setting name.\n * @param description - The description of the setting.\n * @returns The setting value.\n */\nexport function loadSetting({\n settingValue,\n environmentVariableName,\n settingName,\n description,\n}: {\n settingValue: string | undefined;\n environmentVariableName: string;\n settingName: string;\n description: string;\n}): string {\n if (typeof settingValue === 'string') {\n return settingValue;\n }\n\n if (settingValue != null) {\n throw new LoadSettingError({\n message: `${description} setting must be a string.`,\n });\n }\n\n if (typeof process === 'undefined') {\n throw new LoadSettingError({\n message:\n `${description} setting is missing. ` +\n `Pass it using the '${settingName}' parameter. ` +\n `Environment variables is not supported in this environment.`,\n });\n }\n\n settingValue = process.env[environmentVariableName];\n\n if (settingValue == null) {\n throw new LoadSettingError({\n message:\n `${description} setting is missing. ` +\n `Pass it using the '${settingName}' parameter ` +\n `or the ${environmentVariableName} environment variable.`,\n });\n }\n\n if (typeof settingValue !== 'string') {\n throw new LoadSettingError({\n message:\n `${description} setting must be a string. ` +\n `The value of the ${environmentVariableName} environment variable is not a string.`,\n });\n }\n\n return settingValue;\n}\n","/**\n * Maps a media type to its corresponding file extension.\n * It was originally introduced to set a filename for audio file uploads\n * in https://github.com/vercel/ai/pull/8159.\n *\n * @param mediaType The media type to map.\n * @returns The corresponding file extension\n * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types/Common_types\n */\nexport function mediaTypeToExtension(mediaType: string) {\n const [_type, subtype = ''] = mediaType.toLowerCase().split('/');\n\n return (\n {\n mpeg: 'mp3',\n 'x-wav': 'wav',\n opus: 'ogg',\n mp4: 'm4a',\n 'x-m4a': 'm4a',\n }[subtype] ?? subtype\n );\n}\n","import {\n JSONParseError,\n JSONValue,\n TypeValidationError,\n} from '@ai-sdk/provider';\nimport { secureJsonParse } from './secure-json-parse';\nimport { safeValidateTypes, validateTypes } from './validate-types';\nimport { FlexibleSchema } from './schema';\n\n/**\n * Parses a JSON string into an unknown object.\n *\n * @param text - The JSON string to parse.\n * @returns {JSONValue} - The parsed JSON object.\n */\nexport async function parseJSON(options: {\n text: string;\n schema?: undefined;\n}): Promise<JSONValue>;\n/**\n * Parses a JSON string into a strongly-typed object using the provided schema.\n *\n * @template T - The type of the object to parse the JSON into.\n * @param {string} text - The JSON string to parse.\n * @param {Validator<T>} schema - The schema to use for parsing the JSON.\n * @returns {Promise<T>} - The parsed object.\n */\nexport async function parseJSON<T>(options: {\n text: string;\n schema: FlexibleSchema<T>;\n}): Promise<T>;\nexport async function parseJSON<T>({\n text,\n schema,\n}: {\n text: string;\n schema?: FlexibleSchema<T>;\n}): Promise<T> {\n try {\n const value = secureJsonParse(text);\n\n if (schema == null) {\n return value;\n }\n\n return validateTypes<T>({ value, schema });\n } catch (error) {\n if (\n JSONParseError.isInstance(error) ||\n TypeValidationError.isInstance(error)\n ) {\n throw error;\n }\n\n throw new JSONParseError({ text, cause: error });\n }\n}\n\nexport type ParseResult<T> =\n | { success: true; value: T; rawValue: unknown }\n | {\n success: false;\n error: JSONParseError | TypeValidationError;\n rawValue: unknown;\n };\n\n/**\n * Safely parses a JSON string and returns the result as an object of type `unknown`.\n *\n * @param text - The JSON string to parse.\n * @returns {Promise<object>} Either an object with `success: true` and the parsed data, or an object with `success: false` and the error that occurred.\n */\nexport async function safeParseJSON(options: {\n text: string;\n schema?: undefined;\n}): Promise<ParseResult<JSONValue>>;\n/**\n * Safely parses a JSON string into a strongly-typed object, using a provided schema to validate the object.\n *\n * @template T - The type of the object to parse the JSON into.\n * @param {string} text - The JSON string to parse.\n * @param {Validator<T>} schema - The schema to use for parsing the JSON.\n * @returns An object with either a `success` flag and the parsed and typed data, or a `success` flag and an error object.\n */\nexport async function safeParseJSON<T>(options: {\n text: string;\n schema: FlexibleSchema<T>;\n}): Promise<ParseResult<T>>;\nexport async function safeParseJSON<T>({\n text,\n schema,\n}: {\n text: string;\n schema?: FlexibleSchema<T>;\n}): Promise<ParseResult<T>> {\n try {\n const value = secureJsonParse(text);\n\n if (schema == null) {\n return { success: true, value: value as T, rawValue: value };\n }\n\n return await safeValidateTypes<T>({ value, schema });\n } catch (error) {\n return {\n success: false,\n error: JSONParseError.isInstance(error)\n ? error\n : new JSONParseError({ text, cause: error }),\n rawValue: undefined,\n };\n }\n}\n\nexport function isParsableJson(input: string): boolean {\n try {\n secureJsonParse(input);\n return true;\n } catch {\n return false;\n }\n}\n","// Licensed under BSD-3-Clause (this file only)\n// Code adapted from https://github.com/fastify/secure-json-parse/blob/783fcb1b5434709466759847cec974381939673a/index.js\n//\n// Copyright (c) Vercel, Inc. (https://vercel.com)\n// Copyright (c) 2019 The Fastify Team\n// Copyright (c) 2019, Sideway Inc, and project contributors\n// All rights reserved.\n//\n// The complete list of contributors can be found at:\n// - https://github.com/hapijs/bourne/graphs/contributors\n// - https://github.com/fastify/secure-json-parse/graphs/contributors\n// - https://github.com/vercel/ai/commits/main/packages/provider-utils/src/secure-parse-json.ts\n//\n// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n//\n// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n//\n// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n//\n// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nconst suspectProtoRx = /\"__proto__\"\\s*:/;\nconst suspectConstructorRx = /\"constructor\"\\s*:/;\n\nfunction _parse(text: string) {\n // Parse normally\n const obj = JSON.parse(text);\n\n // Ignore null and non-objects\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n\n if (\n suspectProtoRx.test(text) === false &&\n suspectConstructorRx.test(text) === false\n ) {\n return obj;\n }\n\n // Scan result for proto keys\n return filter(obj);\n}\n\nfunction filter(obj: any) {\n let next = [obj];\n\n while (next.length) {\n const nodes = next;\n next = [];\n\n for (const node of nodes) {\n if (Object.prototype.hasOwnProperty.call(node, '__proto__')) {\n throw new SyntaxError('Object contains forbidden prototype property');\n }\n\n if (\n Object.prototype.hasOwnProperty.call(node, 'constructor') &&\n Object.prototype.hasOwnProperty.call(node.constructor, 'prototype')\n ) {\n throw new SyntaxError('Object contains forbidden prototype property');\n }\n\n for (const key in node) {\n const value = node[key];\n if (value && typeof value === 'object') {\n next.push(value);\n }\n }\n }\n }\n return obj;\n}\n\nexport function secureJsonParse(text: string) {\n const { stackTraceLimit } = Error;\n try {\n // Performance optimization, see https://github.com/fastify/secure-json-parse/pull/90\n Error.stackTraceLimit = 0;\n } catch (e) {\n // Fallback in case Error is immutable (v8 readonly)\n return _parse(text);\n }\n\n try {\n return _parse(text);\n } finally {\n Error.stackTraceLimit = stackTraceLimit;\n }\n}\n","import { TypeValidationError } from '@ai-sdk/provider';\nimport { FlexibleSchema, asSchema } from './schema';\n\n/**\n * Validates the types of an unknown object using a schema and\n * return a strongly-typed object.\n *\n * @template T - The type of the object to validate.\n * @param {string} options.value - The object to validate.\n * @param {Validator<T>} options.schema - The schema to use for validating the JSON.\n * @returns {Promise<T>} - The typed object.\n */\nexport async function validateTypes<OBJECT>({\n value,\n schema,\n}: {\n value: unknown;\n schema: FlexibleSchema<OBJECT>;\n}): Promise<OBJECT> {\n const result = await safeValidateTypes({ value, schema });\n\n if (!result.success) {\n throw TypeValidationError.wrap({ value, cause: result.error });\n }\n\n return result.value;\n}\n\n/**\n * Safely validates the types of an unknown object using a schema and\n * return a strongly-typed object.\n *\n * @template T - The type of the object to validate.\n * @param {string} options.value - The JSON object to validate.\n * @param {Validator<T>} options.schema - The schema to use for validating the JSON.\n * @returns An object with either a `success` flag and the parsed and typed data, or a `success` flag and an error object.\n */\nexport async function safeValidateTypes<OBJECT>({\n value,\n schema,\n}: {\n value: unknown;\n schema: FlexibleSchema<OBJECT>;\n}): Promise<\n | {\n success: true;\n value: OBJECT;\n rawValue: unknown;\n }\n | {\n success: false;\n error: TypeValidationError;\n rawValue: unknown;\n }\n> {\n const actualSchema = asSchema(schema);\n\n try {\n if (actualSchema.validate == null) {\n return { success: true, value: value as OBJECT, rawValue: value };\n }\n\n const result = await actualSchema.validate(value);\n\n if (result.success) {\n return { success: true, value: result.value, rawValue: value };\n }\n\n return {\n success: false,\n error: TypeValidationError.wrap({ value, cause: result.error }),\n rawValue: value,\n };\n } catch (error) {\n return {\n success: false,\n error: TypeValidationError.wrap({ value, cause: error }),\n rawValue: value,\n };\n }\n}\n","import { JSONSchema7, TypeValidationError } from '@ai-sdk/provider';\nimport { StandardSchemaV1, StandardJSONSchemaV1 } from '@standard-schema/spec';\nimport * as z3 from 'zod/v3';\nimport * as z4 from 'zod/v4';\nimport { addAdditionalPropertiesToJsonSchema } from './add-additional-properties-to-json-schema';\nimport { zod3ToJsonSchema } from './to-json-schema/zod3-to-json-schema';\n\n/**\n * Used to mark schemas so we can support both Zod and custom schemas.\n */\nconst schemaSymbol = Symbol.for('vercel.ai.schema');\n\nexport type ValidationResult<OBJECT> =\n | { success: true; value: OBJECT }\n | { success: false; error: Error };\n\nexport type Schema<OBJECT = unknown> = {\n /**\n * Used to mark schemas so we can support both Zod and custom schemas.\n */\n [schemaSymbol]: true;\n\n /**\n * Schema type for inference.\n */\n _type: OBJECT;\n\n /**\n * Optional. Validates that the structure of a value matches this schema,\n * and returns a typed version of the value if it does.\n */\n readonly validate?: (\n value: unknown,\n ) => ValidationResult<OBJECT> | PromiseLike<ValidationResult<OBJECT>>;\n\n /**\n * The JSON Schema for the schema. It is passed to the providers.\n */\n readonly jsonSchema: JSONSchema7 | PromiseLike<JSONSchema7>;\n};\n\n/**\n * Creates a schema with deferred creation.\n * This is important to reduce the startup time of the library\n * and to avoid initializing unused validators.\n *\n * @param createValidator A function that creates a schema.\n * @returns A function that returns a schema.\n */\nexport function lazySchema<SCHEMA>(\n createSchema: () => Schema<SCHEMA>,\n): LazySchema<SCHEMA> {\n // cache the validator to avoid initializing it multiple times\n let schema: Schema<SCHEMA> | undefined;\n return () => {\n if (schema == null) {\n schema = createSchema();\n }\n return schema;\n };\n}\n\nexport type LazySchema<SCHEMA> = () => Schema<SCHEMA>;\n\nexport type ZodSchema<SCHEMA = any> =\n | z3.Schema<SCHEMA, z3.ZodTypeDef, any>\n | z4.core.$ZodType<SCHEMA, any>;\n\nexport type StandardSchema<SCHEMA = any> = StandardSchemaV1<unknown, SCHEMA> &\n StandardJSONSchemaV1<unknown, SCHEMA>;\n\nexport type FlexibleSchema<SCHEMA = any> =\n | Schema<SCHEMA>\n | LazySchema<SCHEMA>\n | ZodSchema<SCHEMA>\n | StandardSchema<SCHEMA>;\n\nexport type InferSchema<SCHEMA> =\n SCHEMA extends ZodSchema<infer T>\n ? T\n : SCHEMA extends StandardSchema<infer T>\n ? T\n : SCHEMA extends LazySchema<infer T>\n ? T\n : SCHEMA extends Schema<infer T>\n ? T\n : never;\n\n/**\n * Create a schema using a JSON Schema.\n *\n * @param jsonSchema The JSON Schema for the schema.\n * @param options.validate Optional. A validation function for the schema.\n */\nexport function jsonSchema<OBJECT = unknown>(\n jsonSchema:\n | JSONSchema7\n | PromiseLike<JSONSchema7>\n | (() => JSONSchema7 | PromiseLike<JSONSchema7>),\n {\n validate,\n }: {\n validate?: (\n value: unknown,\n ) => ValidationResult<OBJECT> | PromiseLike<ValidationResult<OBJECT>>;\n } = {},\n): Schema<OBJECT> {\n return {\n [schemaSymbol]: true,\n _type: undefined as OBJECT, // should never be used directly\n get jsonSchema() {\n if (typeof jsonSchema === 'function') {\n jsonSchema = jsonSchema(); // cache the function results\n }\n return jsonSchema;\n },\n validate,\n };\n}\n\nfunction isSchema(value: unknown): value is Schema {\n return (\n typeof value === 'object' &&\n value !== null &&\n schemaSymbol in value &&\n value[schemaSymbol] === true &&\n 'jsonSchema' in value &&\n 'validate' in value\n );\n}\n\nexport function asSchema<OBJECT>(\n schema: FlexibleSchema<OBJECT> | undefined,\n): Schema<OBJECT> {\n return schema == null\n ? jsonSchema({ properties: {}, additionalProperties: false })\n : isSchema(schema)\n ? schema\n : '~standard' in schema\n ? schema['~standard'].vendor === 'zod'\n ? zodSchema(schema as ZodSchema<OBJECT>)\n : standardSchema(schema as StandardSchema<OBJECT>)\n : schema();\n}\n\nfunction standardSchema<OBJECT>(\n standardSchema: StandardSchema<OBJECT>,\n): Schema<OBJECT> {\n return jsonSchema(\n () =>\n standardSchema['~standard'].jsonSchema.input({\n target: 'draft-07',\n }),\n {\n validate: async value => {\n const result = await standardSchema['~standard'].validate(value);\n return 'value' in result\n ? { success: true, value: result.value }\n : {\n success: false,\n error: new TypeValidationError({\n value,\n cause: result.issues,\n }),\n };\n },\n },\n );\n}\n\nexport function zod3Schema<OBJECT>(\n zodSchema: z3.Schema<OBJECT, z3.ZodTypeDef, any>,\n options?: {\n /**\n * Enables support for references in the schema.\n * This is required for recursive schemas, e.g. with `z.lazy`.\n * However, not all language models and providers support such references.\n * Defaults to `false`.\n */\n useReferences?: boolean;\n },\n): Schema<OBJECT> {\n // default to no references (to support openapi conversion for google)\n const useReferences = options?.useReferences ?? false;\n\n return jsonSchema(\n // defer json schema creation to avoid unnecessary computation when only validation is needed\n () =>\n zod3ToJsonSchema(zodSchema, {\n $refStrategy: useReferences ? 'root' : 'none',\n }) as JSONSchema7,\n {\n validate: async value => {\n const result = await zodSchema.safeParseAsync(value);\n return result.success\n ? { success: true, value: result.data }\n : { success: false, error: result.error };\n },\n },\n );\n}\n\nexport function zod4Schema<OBJECT>(\n zodSchema: z4.core.$ZodType<OBJECT, any>,\n options?: {\n /**\n * Enables support for references in the schema.\n * This is required for recursive schemas, e.g. with `z.lazy`.\n * However, not all language models and providers support such references.\n * Defaults to `false`.\n */\n useReferences?: boolean;\n },\n): Schema<OBJECT> {\n // default to no references (to support openapi conversion for google)\n const useReferences = options?.useReferences ?? false;\n\n return jsonSchema(\n // defer json schema creation to avoid unnecessary computation when only validation is needed\n () =>\n addAdditionalPropertiesToJsonSchema(\n z4.toJSONSchema(zodSchema, {\n target: 'draft-7',\n io: 'input',\n reused: useReferences ? 'ref' : 'inline',\n }) as JSONSchema7,\n ),\n {\n validate: async value => {\n const result = await z4.safeParseAsync(zodSchema, value);\n return result.success\n ? { success: true, value: result.data }\n : { success: false, error: result.error };\n },\n },\n );\n}\n\nexport function isZod4Schema(\n zodSchema: z4.core.$ZodType<any, any> | z3.Schema<any, z3.ZodTypeDef, any>,\n): zodSchema is z4.core.$ZodType<any, any> {\n // https://zod.dev/library-authors?id=how-to-support-zod-3-and-zod-4-simultaneously\n return '_zod' in zodSchema;\n}\n\nexport func