ai
Version:
AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.
3,807 lines • 135 kB
JavaScript
import { DEFAULT_MAX_DOWNLOAD_SIZE, DownloadError, asArray, asSchema, cancelResponseBody, convertAsyncIteratorToReadableStream, convertBase64ToUint8Array, convertUint8ArrayToBase64, detectMediaType, experimental_getToolCaller, fetchUntrustedUrl, filterNullable, getRuntimeEnvironmentUserAgent, isArrayBuffer, isBuffer, isExecutableTool, isFullMediaType, isProviderReference, isUint8Array, isUrlSupported, readResponseWithSizeLimit, redactTelemetryValue, retryWithExponentialBackoff, safeParseJSON, safeValidateTypes, validateTypes, withUserAgentSuffix } from "@ai-sdk/provider-utils";
import { AISDKError, APICallError, InvalidPromptError, getErrorMessage } from "@ai-sdk/provider";
import { GatewayError, gateway } from "@ai-sdk/gateway";
//#region src/util/async-iterable-stream.ts
/**
* Wraps a ReadableStream and returns an object that is both a ReadableStream and an AsyncIterable.
* This enables consumption of the stream using for-await-of, with proper resource cleanup on early exit or error.
*
* @template T The type of the stream's chunks.
* @param source The source ReadableStream to wrap.
* @returns An AsyncIterableStream that can be used as both a ReadableStream and an AsyncIterable.
*/
function createAsyncIterableStream(source) {
return asAsyncIterableStream(source.pipeThrough(new TransformStream()));
}
/**
* Attaches the async iterator protocol to an existing ReadableStream in place,
* turning it into an AsyncIterableStream without piping through an additional
* TransformStream.
*
* Use this when the stream is already known to be fresh and exclusively owned
* (e.g. the readable side of a TransformStream created for this consumer).
* Adding an extra `pipeThrough` in that situation creates a chain of two
* transforms fed by an active upstream pipe, which can surface a spurious
* unhandled `undefined` rejection when the consumer cancels early (observed on
* Node.js 26). {@link createAsyncIterableStream} wraps this after adding a
* fresh transform for callers that may pass shared or locked streams.
*
* @template T The type of the stream's chunks.
* @param stream The ReadableStream to augment. It must be fresh and unlocked.
* @returns The same stream, augmented with the async iterator protocol.
*/
function asAsyncIterableStream(stream) {
/**
* Implements the async iterator protocol for the stream.
* Ensures proper cleanup (cancelling and releasing the reader) on completion, early exit, or error.
*/
stream[Symbol.asyncIterator] = function() {
const reader = this.getReader();
let finished = false;
/**
* Cleans up the reader by cancelling and releasing the lock.
*/
async function cleanup(cancelStream) {
if (finished) return;
finished = true;
try {
if (cancelStream) await reader.cancel?.();
} finally {
try {
reader.releaseLock();
} catch {}
}
}
return {
/**
* Reads the next chunk from the stream.
* @returns A promise resolving to the next IteratorResult.
*/
async next() {
if (finished) return {
done: true,
value: void 0
};
let result;
try {
result = await reader.read();
} catch (error) {
await cleanup(false);
throw error;
}
const { done, value } = result;
if (done) {
await cleanup(true);
return {
done: true,
value: void 0
};
}
return {
done: false,
value
};
},
/**
* May be called on early exit (e.g., break from for-await) or after completion.
* Ensures the stream is cancelled and resources are released.
* @returns A promise resolving to a completed IteratorResult.
*/
async return() {
await cleanup(true);
return {
done: true,
value: void 0
};
},
/**
* Called on early exit with error.
* Ensures the stream is cancelled and resources are released, then rethrows the error.
* @param err The error to throw.
* @returns A promise that rejects with the provided error.
*/
async throw(err) {
await cleanup(true);
throw err;
}
};
};
return stream;
}
//#endregion
//#region src/version.ts
const VERSION = "7.0.173";
//#endregion
//#region src/util/download/download.ts
/**
* Download a file from a URL.
*
* @param url - The URL to download from.
* @param maxBytes - Maximum allowed download size in bytes. Defaults to 100 MiB.
* @param abortSignal - An optional abort signal to cancel the download.
* @param fetch - An optional fetch transport.
* @returns The downloaded data and media type.
*
* @throws DownloadError if the download fails or exceeds maxBytes.
*/
const download = async ({ url, maxBytes, abortSignal, fetch }) => {
const urlText = url.toString();
try {
const headers = withUserAgentSuffix({}, `ai-sdk/${VERSION}`, getRuntimeEnvironmentUserAgent());
const response = await fetchUntrustedUrl({
url: urlText,
headers,
abortSignal,
fetch
});
if (!response.ok) {
await cancelResponseBody(response);
throw new DownloadError({
url: urlText,
statusCode: response.status,
statusText: response.statusText
});
}
return {
data: await readResponseWithSizeLimit({
response,
url: urlText,
maxBytes: maxBytes ?? DEFAULT_MAX_DOWNLOAD_SIZE
}),
mediaType: response.headers.get("content-type") ?? void 0
};
} catch (error) {
if (DownloadError.isInstance(error)) throw error;
throw new DownloadError({
url: urlText,
cause: error
});
}
};
//#endregion
//#region src/util/download/download-function.ts
/**
* Default download function.
* Downloads the file if it is not supported by the model.
*/
const createDefaultDownloadFunction = (download$2 = download, abortSignal) => (requestedDownloads) => Promise.all(requestedDownloads.map(async (requestedDownload) => requestedDownload.isUrlSupportedByModel ? null : await download$2({
...requestedDownload,
abortSignal: abortSignal ?? requestedDownload.abortSignal
})));
//#endregion
//#region src/util/merge-objects.ts
/**
* Deeply merges two objects together.
* - Properties from the `overrides` object override those in the `base` object with the same key.
* - For nested objects, the merge is performed recursively (deep merge).
* - Arrays are replaced, not merged.
* - Primitive values are replaced.
* - If both `base` and `overrides` are undefined, returns undefined.
* - If one of `base` or `overrides` is undefined, returns the other.
*
* @param base The target object to merge into
* @param overrides The source object to merge from
* @returns A new object with the merged properties, or undefined if both inputs are undefined
*/
function mergeObjects(base, overrides) {
if (base === void 0 && overrides === void 0) return;
if (base === void 0) return overrides;
if (overrides === void 0) return base;
const result = { ...base };
for (const key in overrides) {
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
if (Object.prototype.hasOwnProperty.call(overrides, key)) {
const overridesValue = overrides[key];
if (overridesValue === void 0) continue;
const baseValue = key in base ? base[key] : void 0;
const isSourceObject = overridesValue !== null && typeof overridesValue === "object" && !Array.isArray(overridesValue) && !(overridesValue instanceof Date) && !(overridesValue instanceof RegExp);
const isTargetObject = baseValue !== null && baseValue !== void 0 && typeof baseValue === "object" && !Array.isArray(baseValue) && !(baseValue instanceof Date) && !(baseValue instanceof RegExp);
if (isSourceObject && isTargetObject) result[key] = mergeObjects(baseValue, overridesValue);
else result[key] = overridesValue;
}
}
return result;
}
//#endregion
//#region src/prompt/invalid-data-content-error.ts
const name$11 = "AI_InvalidDataContentError";
const marker$11 = `vercel.ai.error.${name$11}`;
const symbol$11 = Symbol.for(marker$11);
var InvalidDataContentError = class extends AISDKError {
constructor({ content, cause, message = `Invalid data content. Expected a base64 string, Uint8Array, ArrayBuffer, or Buffer, but got ${typeof content}.` }) {
super({
name: name$11,
message,
cause
});
this[symbol$11] = true;
this.content = content;
}
static isInstance(error) {
return AISDKError.hasMarker(error, marker$11);
}
};
//#endregion
//#region src/prompt/data-content.ts
/**
* Converts data content to a Uint8Array.
*
* @param content - Data content to convert.
* @returns Uint8Array.
*/
function convertDataContentToUint8Array(content) {
if (isUint8Array(content)) {
if (content instanceof Uint8Array) return content;
const { buffer, byteOffset, byteLength } = content;
return new Uint8Array(buffer, byteOffset, byteLength);
}
if (typeof content === "string") try {
return convertBase64ToUint8Array(content);
} catch (error) {
throw new InvalidDataContentError({
message: "Invalid data content. Content string is not a base64-encoded media.",
content,
cause: error
});
}
if (isArrayBuffer(content)) return new Uint8Array(content);
throw new InvalidDataContentError({ content });
}
//#endregion
//#region src/prompt/split-data-url.ts
function splitDataUrl(dataUrl) {
try {
const [header, base64Content] = dataUrl.split(",");
return {
mediaType: header.split(";")[0].split(":")[1],
base64Content
};
} catch {
return {
mediaType: void 0,
base64Content: void 0
};
}
}
//#endregion
//#region src/prompt/file-part-data.ts
function isTaggedFileData(value) {
if (typeof value !== "object" || value === null) return false;
const type = value.type;
return type === "data" || type === "url" || type === "reference" || type === "text";
}
function convertUrlToFilePartData(url, originalUrl) {
if (url.protocol === "data:") {
const { mediaType, base64Content } = splitDataUrl(url.toString());
if (mediaType == null || base64Content == null) throw new InvalidDataContentError({
content: url,
message: `Invalid data URL format in content ${url.toString()}`
});
return {
data: {
type: "data",
data: base64Content
},
mediaType
};
}
return {
data: {
type: "url",
url,
...originalUrl != null ? { originalUrl } : {}
},
mediaType: void 0
};
}
function convertUrlStringToFilePartData(content) {
const result = convertUrlToFilePartData(new URL(content));
if (result.data.type === "url" && result.data.url.toString() !== content) result.data.originalUrl = content;
return result;
}
function convertInlineDataToFilePartData(content) {
if (isUint8Array(content) || isArrayBuffer(content)) return {
data: {
type: "data",
data: convertDataContentToUint8Array(content)
},
mediaType: void 0
};
if (isBuffer(content)) return {
data: {
type: "data",
data: new Uint8Array(content)
},
mediaType: void 0
};
return {
data: {
type: "data",
data: content
},
mediaType: void 0
};
}
/**
* Converts any legacy-or-tagged top-level `FilePart.data` /
* `ReasoningFilePart.data` value into the tagged v4 provider prompt shape.
*
* Returns the tagged `data` together with the resolved mediaType (extracted
* from a `data:` URL when applicable).
*/
function convertToLanguageModelV4FilePart(content) {
if (isTaggedFileData(content)) switch (content.type) {
case "data":
if (typeof content.data === "string" && content.data.startsWith("data:")) throw new InvalidDataContentError({
content: content.data,
message: "Data URLs are not valid inline data. Pass them as { type: \"url\", url } instead."
});
return convertInlineDataToFilePartData(content.data);
case "url": return convertUrlToFilePartData(content.url, content.originalUrl);
case "reference": return {
data: {
type: "reference",
reference: content.reference
},
mediaType: void 0
};
case "text": return {
data: {
type: "text",
text: content.text
},
mediaType: void 0
};
}
if (content instanceof URL) return convertUrlToFilePartData(content);
if (typeof content === "string") try {
return convertUrlStringToFilePartData(content);
} catch {
return convertInlineDataToFilePartData(content);
}
if (isUint8Array(content) || isArrayBuffer(content)) return convertInlineDataToFilePartData(content);
if (isProviderReference(content)) return {
data: {
type: "reference",
reference: content
},
mediaType: void 0
};
return convertInlineDataToFilePartData(content);
}
//#endregion
//#region src/logger/deprecations.ts
const deprecationCodes = /* @__PURE__ */ new Map([
["generateObject", "AISDK_DEP_GENERATE_OBJECT"],
["streamObject", "AISDK_DEP_STREAM_OBJECT"],
["experimental_generateSpeech", "AISDK_DEP_EXPERIMENTAL_GENERATE_SPEECH"],
["experimental_transcribe", "AISDK_DEP_EXPERIMENTAL_TRANSCRIBE"],
["\"image\" content part", "AISDK_DEP_IMAGE_CONTENT_PART"],
["\"tool-result\" content of type \"file-data\"", "AISDK_DEP_TOOL_RESULT_FILE_DATA"],
["\"tool-result\" content of type \"file-url\"", "AISDK_DEP_TOOL_RESULT_FILE_URL"],
["\"tool-result\" content of type \"file-id\"", "AISDK_DEP_TOOL_RESULT_FILE_ID"],
["\"tool-result\" content of type \"file-reference\"", "AISDK_DEP_TOOL_RESULT_FILE_REFERENCE"],
["\"tool-result\" content of type \"image-data\"", "AISDK_DEP_TOOL_RESULT_IMAGE_DATA"],
["\"tool-result\" content of type \"image-url\"", "AISDK_DEP_TOOL_RESULT_IMAGE_URL"],
["\"tool-result\" content of type \"image-file-id\"", "AISDK_DEP_TOOL_RESULT_IMAGE_FILE_ID"],
["\"tool-result\" content of type \"image-file-reference\"", "AISDK_DEP_TOOL_RESULT_IMAGE_FILE_REFERENCE"],
["rawInput in output-error UI message parts", "AISDK_DEP_UI_MESSAGE_RAW_INPUT"]
]);
/**
* Escape punctuation and UTF-16 code units without losing case or collapsing
* distinct settings. Underscores are escaped too, so `__` separates code parts.
*/
function encodeCodePart(value) {
return value.replace(/[^a-zA-Z0-9]/g, (character) => `_${character.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0")}`);
}
function getDeprecationCode({ setting, provider }) {
if (provider == null) return deprecationCodes.get(setting) ?? `AISDK_DEP_SETTING_${encodeCodePart(setting)}`;
return `AISDK_DEP_PROVIDER_${encodeCodePart(provider)}__${encodeCodePart(setting)}`;
}
//#endregion
//#region src/logger/log-warnings.ts
/**
* Formats a warning object into a human-readable string with clear AI SDK branding.
*
* @param options - The options for formatting the warning.
* @param options.warning - The warning to format.
* @param options.provider - The provider id used for the call, if scoped to a specific provider.
* @param options.model - The model id used for the call, if scoped to a specific provider.
* @returns A formatted warning message string.
*/
function formatWarning({ warning, provider, model }) {
const prefix = `AI SDK Warning${provider != null && model != null ? ` (${provider} / ${model})` : ""}:`;
switch (warning.type) {
case "unsupported": {
let message = `${prefix} The feature "${warning.feature}" is not supported.`;
if (warning.details) message += ` ${warning.details}`;
return message;
}
case "compatibility": {
let message = `${prefix} The feature "${warning.feature}" is used in a compatibility mode.`;
if (warning.details) message += ` ${warning.details}`;
return message;
}
case "deprecated": return `${prefix} Deprecated: "${warning.setting}". ${warning.message}`;
case "other": return `${prefix} ${warning.message}`;
default: return `${prefix} ${JSON.stringify(warning, null, 2)}`;
}
}
const FIRST_WARNING_INFO_MESSAGE = "AI SDK Warning System: To turn off warning logging, set the AI_SDK_LOG_WARNINGS global to false.";
let hasLoggedBefore = false;
const emittedDeprecationCodes = /* @__PURE__ */ new Set();
function emitWarning({ message, type, code }) {
if (typeof process !== "undefined" && typeof process.emitWarning === "function") process.emitWarning(message, code == null ? { type } : {
type,
code
});
else console.warn(code == null ? message : `[${code}] ${message}`);
}
/**
* Logs warnings to the console or uses a custom logger if configured.
*
* The behavior can be customized via the `AI_SDK_LOG_WARNINGS` global variable:
* - If set to `false`, warnings are suppressed.
* - If set to a function, that function is called with the warnings.
* - Otherwise, warnings use `process.emitWarning` when available, falling back
* to `console.warn`. Deprecations have stable codes and are emitted once per
* code by the default logger. Custom loggers receive every warning.
*
* @param options - The options containing warnings and context.
* @param options.warnings - The warnings to log.
* @param options.provider - The provider id used for the call, if scoped to a specific provider.
* @param options.model - The model id used for the call, if scoped to a specific provider.
*/
const logWarnings = (options) => {
if (options.warnings.length === 0) return;
const logger = globalThis.AI_SDK_LOG_WARNINGS;
if (logger === false) return;
if (typeof logger === "function") {
logger(options);
return;
}
if (!hasLoggedBefore) {
hasLoggedBefore = true;
emitWarning({
message: FIRST_WARNING_INFO_MESSAGE,
type: "Warning"
});
}
for (const warning of options.warnings) {
const code = warning.type === "deprecated" ? getDeprecationCode({
setting: warning.setting,
provider: options.provider
}) : void 0;
if (code != null) {
if (emittedDeprecationCodes.has(code)) continue;
emittedDeprecationCodes.add(code);
}
emitWarning({
message: formatWarning({
warning,
provider: options.provider,
model: options.model
}),
type: warning.type === "deprecated" ? "DeprecationWarning" : "Warning",
code
});
}
};
//#endregion
//#region src/prompt/invalid-message-role-error.ts
const name$10 = "AI_InvalidMessageRoleError";
const marker$10 = `vercel.ai.error.${name$10}`;
const symbol$10 = Symbol.for(marker$10);
var InvalidMessageRoleError = class extends AISDKError {
constructor({ role, message = `Invalid message role: '${role}'. Must be one of: "system", "user", "assistant", "tool".` }) {
super({
name: name$10,
message
});
this[symbol$10] = true;
this.role = role;
}
static isInstance(error) {
return AISDKError.hasMarker(error, marker$10);
}
};
//#endregion
//#region src/error/missing-tool-result-error.ts
const name$9 = "AI_MissingToolResultsError";
const marker$9 = `vercel.ai.error.${name$9}`;
const symbol$9 = Symbol.for(marker$9);
var MissingToolResultsError = class extends AISDKError {
constructor({ toolCallIds }) {
super({
name: name$9,
message: `Tool result${toolCallIds.length > 1 ? "s are" : " is"} missing for tool call${toolCallIds.length > 1 ? "s" : ""} ${toolCallIds.join(", ")}.`
});
this[symbol$9] = true;
this.toolCallIds = toolCallIds;
}
static isInstance(error) {
return AISDKError.hasMarker(error, marker$9);
}
};
//#endregion
//#region src/prompt/convert-to-language-model-prompt.ts
async function convertToLanguageModelPrompt({ prompt, supportedUrls, download, abortSignal, provider }) {
const downloadedAssets = await downloadAssets(prompt.messages, download ?? createDefaultDownloadFunction(void 0, abortSignal), supportedUrls, abortSignal);
const messages = [...prompt.instructions != null ? typeof prompt.instructions === "string" ? [{
role: "system",
content: prompt.instructions
}] : asArray(prompt.instructions).map((message) => ({
role: "system",
content: message.content,
providerOptions: message.providerOptions
})) : [], ...prompt.messages.map((message) => convertToLanguageModelMessage({
message,
downloadedAssets,
provider
}))];
const combinedMessages = [];
for (const message of messages) {
if (message.role !== "tool") {
combinedMessages.push(message);
continue;
}
const lastCombinedMessage = combinedMessages.at(-1);
if (lastCombinedMessage?.role === "tool") {
const lastContentPart = lastCombinedMessage.content.at(-1);
if (lastContentPart != null && lastCombinedMessage.providerOptions != null) lastContentPart.providerOptions = mergeObjects(lastCombinedMessage.providerOptions, lastContentPart.providerOptions);
lastCombinedMessage.content.push(...message.content);
lastCombinedMessage.providerOptions = lastContentPart == null ? mergeObjects(lastCombinedMessage.providerOptions, message.providerOptions) : message.providerOptions;
} else combinedMessages.push(message);
}
const toolCallIds = /* @__PURE__ */ new Set();
for (const message of combinedMessages) switch (message.role) {
case "assistant":
for (const content of message.content) if (content.type === "tool-call" && !content.providerExecuted) toolCallIds.add(content.toolCallId);
break;
case "tool":
for (const content of message.content) if (content.type === "tool-result") toolCallIds.delete(content.toolCallId);
break;
case "user":
case "system": if (toolCallIds.size > 0) throw new MissingToolResultsError({ toolCallIds: Array.from(toolCallIds) });
}
if (toolCallIds.size > 0) throw new MissingToolResultsError({ toolCallIds: Array.from(toolCallIds) });
return combinedMessages.filter((message) => message.role !== "tool" || message.content.length > 0);
}
/**
* Convert a ModelMessage to a LanguageModelV4Message.
*
* @param message - The ModelMessage to convert.
* @param downloadedAssets - A map of URLs to their downloaded data. Only
* available if the model does not support URLs, null otherwise.
*/
function convertToLanguageModelMessage({ message, downloadedAssets, provider }) {
const warnings = [];
const role = message.role;
switch (role) {
case "system": return {
role: "system",
content: message.content,
providerOptions: message.providerOptions
};
case "user": {
if (typeof message.content === "string") return {
role: "user",
content: [{
type: "text",
text: message.content
}],
providerOptions: message.providerOptions
};
const converted = {
role: "user",
content: message.content.map((part) => {
if (part.type === "image") warnings.push({
type: "deprecated",
setting: "\"image\" content part",
message: `The "image" content part type is deprecated. Use a "file" part with mediaType: 'image' (or a more specific image/* subtype) instead.`
});
return convertImagePartToFilePart(part);
}).map((part) => convertPartToLanguageModelPart(part, downloadedAssets)).filter((part) => part.type !== "text" || part.text !== ""),
providerOptions: message.providerOptions
};
if (warnings.length > 0) logWarnings({ warnings });
return converted;
}
case "assistant": {
if (typeof message.content === "string") return {
role: "assistant",
content: [{
type: "text",
text: message.content
}],
providerOptions: message.providerOptions
};
const converted = {
role: "assistant",
content: message.content.filter((part) => part.type !== "text" || part.text !== "" || part.providerOptions != null).filter((part) => part.type !== "tool-approval-request").map((part) => {
const providerOptions = part.providerOptions;
switch (part.type) {
case "custom": return {
type: "custom",
kind: part.kind,
providerOptions
};
case "file": {
const { data, mediaType } = convertToLanguageModelV4FilePart(part.data);
return {
type: "file",
data,
filename: part.filename,
mediaType: mediaType ?? part.mediaType,
providerOptions
};
}
case "reasoning": return {
type: "reasoning",
text: part.text,
providerOptions
};
case "reasoning-file": {
const { data, mediaType } = convertToLanguageModelV4FilePart(part.data);
if (data.type !== "data" && data.type !== "url") throw new Error(`Unsupported reasoning-file data type: ${data.type}`);
return {
type: "reasoning-file",
data,
mediaType: mediaType ?? part.mediaType,
providerOptions
};
}
case "text": return {
type: "text",
text: part.text,
providerOptions
};
case "tool-call": return {
type: "tool-call",
toolCallId: part.toolCallId,
toolName: part.toolName,
input: part.input,
providerExecuted: part.providerExecuted,
providerOptions
};
case "tool-result": return {
type: "tool-result",
toolCallId: part.toolCallId,
toolName: part.toolName,
output: mapToolResultOutput({
output: part.output,
provider,
warnings,
downloadedAssets
}),
providerOptions
};
}
}),
providerOptions: message.providerOptions
};
if (warnings.length > 0) logWarnings({ warnings });
return converted;
}
case "tool": {
const converted = {
role: "tool",
content: message.content.filter((part) => part.type !== "tool-approval-response" || part.providerExecuted).map((part) => {
switch (part.type) {
case "tool-result": return {
type: "tool-result",
toolCallId: part.toolCallId,
toolName: part.toolName,
output: mapToolResultOutput({
output: part.output,
provider,
warnings,
downloadedAssets
}),
providerOptions: part.providerOptions
};
case "tool-approval-response": return {
type: "tool-approval-response",
approvalId: part.approvalId,
approved: part.approved,
reason: part.reason
};
}
}),
providerOptions: message.providerOptions
};
if (warnings.length > 0) logWarnings({ warnings });
return converted;
}
default: throw new InvalidMessageRoleError({ role });
}
}
function convertImagePartToFilePart(part) {
if (part.type !== "image") return part;
return {
type: "file",
data: part.image,
mediaType: part.mediaType ?? "image",
providerOptions: part.providerOptions
};
}
/**
* Downloads files from URLs in the user messages.
*/
async function downloadAssets(messages, download, supportedUrls, abortSignal) {
const downloadableFiles = [];
for (const message of messages) {
if (message.role === "user" && Array.isArray(message.content)) for (const part of message.content) {
const filePart = convertImagePartToFilePart(part);
if (filePart.type === "file") downloadableFiles.push(filePart);
}
if (message.role === "tool") for (const part of message.content) {
if (part.type !== "tool-result") continue;
if (part.output.type !== "content") continue;
for (const contentPart of part.output.value) {
const filePart = getDownloadableFilePart(contentPart);
if (filePart != null) downloadableFiles.push(filePart);
}
}
if (message.role === "assistant" && Array.isArray(message.content)) for (const part of message.content) {
if (part.type !== "tool-result") continue;
if (part.output.type !== "content") continue;
for (const contentPart of part.output.value) {
const filePart = getDownloadableFilePart(contentPart);
if (filePart != null) downloadableFiles.push(filePart);
}
}
}
const plannedDownloads = downloadableFiles.map((part) => {
const mediaType = part.mediaType;
const { data } = convertToLanguageModelV4FilePart(part.data);
return {
mediaType,
data
};
}).filter((part) => part.data.type === "url").map((part) => ({
url: part.data.url,
abortSignal,
isUrlSupportedByModel: part.mediaType != null && isUrlSupported({
url: part.data.url.toString(),
mediaType: part.mediaType,
supportedUrls
})
}));
const downloadedFiles = await download(plannedDownloads);
return Object.fromEntries(downloadedFiles.map((file, index) => file == null ? null : [plannedDownloads[index].url.toString(), {
data: file.data,
mediaType: file.mediaType
}]).filter((file) => file != null));
}
/**
* Resolves the tool result content parts that point at a remote file to the
* tagged file shape, including the deprecated `file-url` and `image-url`
* types, so that they are downloaded and inlined exactly like their `file`
* replacement. Returns `undefined` for content that is already inline or
* provider-referenced.
*/
function getDownloadableFilePart(contentPart) {
switch (contentPart.type) {
case "file": return contentPart;
case "file-url": return {
type: "file",
data: {
type: "url",
url: new URL(contentPart.url)
},
mediaType: contentPart.mediaType ?? getMediaTypeFromUrl(contentPart.url),
providerOptions: contentPart.providerOptions
};
case "image-url": return {
type: "file",
data: {
type: "url",
url: new URL(contentPart.url)
},
mediaType: "image",
providerOptions: contentPart.providerOptions
};
default: return;
}
}
/**
* Resolves file data and media type for a remote tool result content part: the downloaded
* bytes when the asset was fetched, otherwise the URL reference. `originalUrl`
* is preserved when the URL string does not round-trip through `URL`.
*/
function resolveDownloadedFile({ url, originalUrl, downloadedAssets, mediaType }) {
const convertedPart = convertPartToLanguageModelPart({
type: "file",
data: {
type: "url",
url,
...url.toString() !== originalUrl ? { originalUrl } : {}
},
mediaType
}, downloadedAssets);
return {
data: convertedPart.data,
mediaType: convertedPart.mediaType
};
}
/**
* Convert part of a user message to a LanguageModelV4Part.
*
* @param part - The part to convert.
* @param downloadedAssets - A map of URLs to their downloaded data. Only
* available if the model does not support URLs, null otherwise.
* @returns The converted part.
*/
function convertPartToLanguageModelPart(part, downloadedAssets) {
if (part.type === "text") return {
type: "text",
text: part.text,
providerOptions: part.providerOptions
};
const { data: normalizedData, mediaType: dataUrlMediaType } = convertToLanguageModelV4FilePart(part.data);
let mediaType = dataUrlMediaType ?? part.mediaType;
let data = normalizedData;
if (data.type === "url") {
const downloadedFile = downloadedAssets[data.url.toString()];
if (downloadedFile) {
data = {
type: "data",
data: downloadedFile.data
};
if (downloadedFile.mediaType != null && (mediaType == null || !isFullMediaType(mediaType))) mediaType = downloadedFile.mediaType;
}
}
if (data.type === "data" && (data.data instanceof Uint8Array || typeof data.data === "string")) {
const imageMediaType = detectMediaType({
data: data.data,
topLevelType: "image"
});
if (imageMediaType != null) mediaType = imageMediaType;
}
if (mediaType == null) throw new Error(`Media type is missing for file part`);
return {
type: "file",
mediaType,
filename: part.filename,
data,
providerOptions: part.providerOptions
};
}
function mapToolResultOutput({ output, provider, warnings = [], downloadedAssets }) {
if (output.type !== "content") return output;
return {
type: "content",
value: output.value.map((item) => {
switch (item.type) {
case "file": {
const convertedPart = convertPartToLanguageModelPart(item, downloadedAssets);
if (convertedPart.type !== "file") throw new Error("Expected tool result file content to convert to file.");
return convertedPart;
}
case "file-data":
warnings.push({
type: "deprecated",
setting: "\"tool-result\" content of type \"file-data\"",
message: `The "file-data" type for tool result content is deprecated. Use the "file" type with mediaType and { type: 'data', data } instead.`
});
return {
type: "file",
data: {
type: "data",
data: item.data
},
filename: item.filename,
mediaType: item.mediaType,
providerOptions: item.providerOptions
};
case "file-url": {
const mediaType = item.mediaType ?? getMediaTypeFromUrl(item.url);
const url = new URL(item.url);
let message = `The "file-url" type for tool result content is deprecated. Use the "file" type with mediaType and { type: 'url', url } instead.`;
if (!item.mediaType) {
const inferenceSuffix = mediaType === "application/octet-stream" ? `Unable to infer media type from URL. Defaulting to 'application/octet-stream'.` : `Inferred media type '${mediaType}' from URL.`;
message = `The "file-url" tool result content part with URL "${item.url}" is missing a "mediaType". ${inferenceSuffix} ${message}`;
}
warnings.push({
type: "deprecated",
setting: "\"tool-result\" content of type \"file-url\"",
message
});
return {
type: "file",
...resolveDownloadedFile({
url,
originalUrl: item.url,
downloadedAssets,
mediaType
}),
providerOptions: item.providerOptions
};
}
case "file-id":
warnings.push({
type: "deprecated",
setting: "\"tool-result\" content of type \"file-id\"",
message: `The "file-id" type for tool result content is deprecated. Use the "file" type with mediaType and { type: 'reference', reference } instead.`
});
return {
type: "file",
data: {
type: "reference",
reference: convertFileIdToProviderReference({
fileId: item.fileId,
provider
})
},
mediaType: "application",
providerOptions: item.providerOptions
};
case "file-reference":
warnings.push({
type: "deprecated",
setting: "\"tool-result\" content of type \"file-reference\"",
message: `The "file-reference" type for tool result content is deprecated. Use the "file" type with mediaType and { type: 'reference', reference } instead.`
});
return {
type: "file",
data: {
type: "reference",
reference: item.providerReference
},
mediaType: "application",
providerOptions: item.providerOptions
};
case "image-data":
warnings.push({
type: "deprecated",
setting: "\"tool-result\" content of type \"image-data\"",
message: `The "image-data" type for tool result content is deprecated. Use the "file" type with mediaType and { type: 'data', data } instead.`
});
return {
type: "file",
data: {
type: "data",
data: item.data
},
mediaType: item.mediaType,
providerOptions: item.providerOptions
};
case "image-url": {
const url = new URL(item.url);
warnings.push({
type: "deprecated",
setting: "\"tool-result\" content of type \"image-url\"",
message: `The "image-url" type for tool result content is deprecated. Use the "file" type with mediaType 'image' (or a specific image/* subtype) and { type: 'url', url } instead.`
});
return {
type: "file",
...resolveDownloadedFile({
url,
originalUrl: item.url,
downloadedAssets,
mediaType: "image"
}),
providerOptions: item.providerOptions
};
}
case "image-file-id":
warnings.push({
type: "deprecated",
setting: "\"tool-result\" content of type \"image-file-id\"",
message: `The "image-file-id" type for tool result content is deprecated. Use the "file" type with mediaType and { type: 'reference', reference } instead.`
});
return {
type: "file",
data: {
type: "reference",
reference: convertFileIdToProviderReference({
fileId: item.fileId,
provider
})
},
mediaType: "image",
providerOptions: item.providerOptions
};
case "image-file-reference":
warnings.push({
type: "deprecated",
setting: "\"tool-result\" content of type \"image-file-reference\"",
message: `The "image-file-reference" type for tool result content is deprecated. Use the "file" type with mediaType and { type: 'reference', reference } instead.`
});
return {
type: "file",
data: {
type: "reference",
reference: item.providerReference
},
mediaType: "image",
providerOptions: item.providerOptions
};
default: return item;
}
})
};
}
function convertFileIdToProviderReference({ fileId, provider }) {
if (typeof fileId === "object") return fileId;
if (provider == null) throw new Error("Cannot convert string fileId to provider reference without a provider ID. Use a Record<string, string> fileId or switch to the file-reference type.");
return { [provider]: fileId };
}
const URL_EXTENSION_TO_MEDIA_TYPE = {
jpg: "image/jpeg",
jpeg: "image/jpeg",
png: "image/png",
gif: "image/gif",
webp: "image/webp",
svg: "image/svg+xml",
avif: "image/avif",
heic: "image/heic",
bmp: "image/bmp",
tiff: "image/tiff",
tif: "image/tiff",
pdf: "application/pdf",
mp4: "video/mp4",
webm: "video/webm",
mp3: "audio/mpeg",
wav: "audio/wav",
ogg: "audio/ogg"
};
function getMediaTypeFromUrl(url, fallbackMediaType = "application/octet-stream") {
try {
const fileExtension = new URL(url).pathname.split(".").pop()?.toLowerCase();
if (fileExtension && Object.hasOwn(URL_EXTENSION_TO_MEDIA_TYPE, fileExtension)) return URL_EXTENSION_TO_MEDIA_TYPE[fileExtension];
} catch {}
return fallbackMediaType;
}
//#endregion
//#region src/prompt/create-tool-model-output.ts
async function createToolModelOutput({ toolCallId, input, output, tool, errorMode }) {
if (errorMode === "text") return {
type: "error-text",
value: getErrorMessage(output)
};
else if (errorMode === "json") return {
type: "error-json",
value: toJSONValue(output)
};
if (tool?.toModelOutput) return await tool.toModelOutput({
toolCallId,
input,
output
});
return typeof output === "string" ? {
type: "text",
value: output
} : {
type: "json",
value: toJSONValue(output)
};
}
/**
* Normalizes an in-process tool output to a plain JSON value
* (applies `toJSON`, converts `Date`, drops `undefined`, etc.)
* by round-tripping it through `JSON.stringify`.
*
* The parsed text is produced by `JSON.stringify` from a value that is
* already materialized in this process, so it is not untrusted input.
* `JSON.parse` is used deliberately instead of the secure parser from
* `@ai-sdk/provider-utils`: the secure parser rejects own `__proto__` and
* `constructor.prototype` keys, which are valid data in tool outputs
* (e.g. rows from an external API). `JSON.parse` defines `__proto__` as an
* own data property and never modifies the prototype chain, so preserving
* these keys here is safe. Do not reuse this pattern for text that comes
* from outside the process; use `parseJSON` / `safeParseJSON` instead.
*/
function toJSONValue(value) {
if (value === void 0) return null;
const serialized = JSON.stringify(value);
return serialized === void 0 ? null : JSON.parse(serialized);
}
//#endregion
//#region src/prompt/prepare-tool-choice.ts
function prepareToolChoice({ toolChoice }) {
return toolChoice == null ? { type: "auto" } : typeof toolChoice === "string" ? { type: toolChoice } : {
type: "tool",
toolName: toolChoice.toolName
};
}
//#endregion
//#region src/util/is-non-empty-object.ts
function isNonEmptyObject(object) {
return object != null && Object.keys(object).length > 0;
}
//#endregion
//#region src/prompt/prepare-tools.ts
async function prepareTools({ tools, toolOrder, toolsContext = {}, experimental_sandbox: sandbox }) {
if (!isNonEmptyObject(tools)) return;
const languageModelTools = [];
for (const [name, tool] of orderToolEntries({
tools,
toolOrder
})) {
const toolType = tool.type;
switch (toolType) {
case void 0:
case "dynamic":
case "function": {
const description = resolveToolDescription({
tool,
toolName: name,
toolsContext,
experimental_sandbox: sandbox
});
const providerOptions = tool.providerOptions;
const inputExamples = tool.inputExamples;
const strict = tool.strict;
languageModelTools.push({
type: "function",
name,
inputSchema: await asSchema(tool.inputSchema).jsonSchema,
...description != null ? { description } : {},
...inputExamples != null ? { inputExamples } : {},
...providerOptions != null ? { providerOptions } : {},
...strict != null ? { strict } : {}
});
break;
}
case "provider":
languageModelTools.push({
type: "provider",
name,
id: tool.id,
args: tool.args
});
break;
default: throw new Error(`Unsupported tool type: ${toolType}`);
}
}
return languageModelTools;
}
function orderToolEntries({ tools, toolOrder }) {
if (toolOrder == null) return Object.entries(tools);
const toolEntries = Object.entries(tools);
const orderedTools = toolEntries.filter(([name]) => toolOrder.includes(name)).sort(([nameA], [nameB]) => toolOrder.indexOf(nameA) - toolOrder.indexOf(nameB));
const unorderedTools = toolEntries.filter(([name]) => !toolOrder.includes(name)).sort(([nameA], [nameB]) => nameA < nameB ? -1 : nameA > nameB ? 1 : 0);
return [...orderedTools, ...unorderedTools];
}
function resolveToolDescription({ tool, toolName, toolsContext, experimental_sandbox: sandbox }) {
return tool.description === void 0 ? void 0 : typeof tool.description === "string" ? tool.description : tool.description({
context: toolsContext[toolName],
experimental_sandbox: sandbox
});
}
//#endregion
//#region src/prompt/standardize-prompt.ts
/**
* Converts a prompt input into a standardized prompt with model messages.
*
* @param prompt - The prompt definition to standardize.
* Set `allowSystemInMessages` to true to allow system messages in the
* `prompt` or `messages` fields. System messages in the `instructions`
* option are always allowed.
* @returns The standardized prompt.
* @throws {InvalidPromptError} When the prompt is invalid.
*/
async function standardizePrompt({ allowSystemInMessages = false, system, instructions = system, prompt, messages }) {
messages = prompt == null ? messages : typeof prompt === "string" ? [{
role: "user",
content: prompt
}] : prompt;
if (messages.length === 0) throw new InvalidPromptError({
prompt,
message: "messages must not be empty"
});
if (!allowSystemInMessages && messages.some((message) => message.role === "system")) throw new InvalidPromptError({
prompt,
message: "System messages are not allowed in the prompt or messages fields. Use the instructions option instead."
});
return {
messages,
instructions
};
}
//#endregion
//#region src/error/invalid-argument-error.ts
const name$8 = "AI_InvalidArgumentError";
const marker$8 = `vercel.ai.error.${name$8}`;
const symbol$8 = Symbol.for(marker$8);
var InvalidArgumentError = class extends AISDKError {
constructor({ parameter, value, message }) {
super({
name: name$8,
message: `Invalid argument for parameter ${parameter}: ${message}`
});
this[symbol$8] = true;
this.parameter = parameter;
this.value = value;
}
static isInstance(error) {
return AISDKError.hasMarker(error, marker$8);
}
};
//#endregion
//#region src/prompt/prepare-language-model-call-options.ts
/**
* Validates model call options and returns a new object with normalized values.
*/
function prepareLanguageModelCallOptions({ maxOutputTokens, temperature, topP, topK, presencePenalty, frequencyPenalty, seed, stopSequences, reasoning }) {
if (maxOutputTokens != null) {
if (!Number.isInteger(maxOutputTokens)) throw new InvalidArgumentError({
parameter: "maxOutputTokens",
value: maxOutputTokens,
message: "maxOutputTokens must be an integer"
});
if (maxOutputTokens < 1) throw new InvalidArgumentError({
parameter: "maxOutputTokens",
value: maxOutputTokens,
message: "maxOutputTokens must be >= 1"
});
}
if (seed != null) {
if (!Number.isInteger(seed)) throw new InvalidArgumentError({
parameter: "seed",
value: seed,
message: "seed must be an integer"
});
}
return {
maxOutputTokens,
temperature,
topP,
topK,
presencePenalty,
frequencyPenalty,
stopSequences,
seed,
reasoning
};
}
//#endregion
//#region src/util/retry-error.ts
const name$7 = "AI_RetryError";
const marker$7 = `vercel.ai.error.${name$7}`;
const symbol$7 = Symbol.for(marker$7);
var RetryError = class extends AISDKError {
constructor({ message, reason, errors }) {
super({
name: name$7,
message
});
this[symbol$7] = true;
this.reason = reason;
this.errors = errors;
this.lastError = errors[errors.length - 1];
}
static isInstance(error) {
return AISDKError.hasMarker(error, marker$7);
}
};
//#endregion
//#region src/util/retry-with-exponential-backoff.ts
function getRetryDelayInMs({ error, exponentialBackoffDelay }) {
const headers = APICallError.isInstance(error) ? error.responseHeaders : APICallError.isInstance(error.cause) ? error.cause.responseHeaders : void 0;
if (!headers) return exponentialBackoffDelay;
let ms;
const retryAfterMs = headers["retry-after-ms"];
if (retryAfterMs) {
const timeoutMs = parseFloat(retryAfterMs);
if (!Number.isNaN(timeoutMs)) ms = timeoutMs;
}
const retryAfter = headers["retry-after"];
if (retryAfter && ms === void 0) {
const timeoutSeconds = parseFloat(retryAfter);
if (!Number.isNaN(timeoutSeconds)) ms = timeoutSeconds * 1e3;
else ms = Date.parse(retryAfter) - Date.now();
}
if (ms != null && !Number.isNaN(ms) && 0 <= ms && (ms < 6e4 || ms < exponentialBackoffDelay)) return ms;
return exponentialBackoffDelay;
}
/**
* The `retryWithExponentialBackoffRespectingRetryHeaders` strategy retries a failed API call with an exponential backoff,
* while respecting rate limit headers (retry-after-ms and retry-after) if they are provided and reasonable (0-60 seconds).
* You can configure the maximum number of retries, the initial delay, and the backoff factor.
*/
const retryWithExponentialBackoffRespectingRetryHeaders = ({ maxRetries = 2, initialDelayInMs = 2e3, backoffFactor = 2, abortSignal, additionalRetryableError } = {}) => retryWithExponentialBackoff({
maxRetries,
initialDelayInMs,
backoffFactor,
abortSignal,
shouldRetry: async (error) => error instanceof Error && (APICallError.isInstance(error) && error.isRetryable === true || GatewayError.isInstance(error) && error.isRetryable === true) || additionalRetryableError != null && await additionalRetryableError(error),
getDelayInMs: ({ error, exponentialBackoffDelay }) => getRetryDelayInMs({
error,
exponentialBackoffDelay
}),
createRetryError: ({ message, reason, errors }) => new RetryError({
message,
reason,
errors
})
});
//#endregion
//#region src/util/prepare-retries.ts
/**
* Validate and prepare retries.
*/
function prepareRetries({ maxRetries, abortSignal, additionalRetryableError, parameter = "maxRetries", defaultMaxRetries = 2 }) {
if (maxRetries != null) {
if (!Number.isInteger(maxRetries)) throw new InvalidArgumentError({
parameter,
value: maxRetries,
message: `${parameter} must be an integer`
});
if (maxRetries < 0) throw new InvalidArgumentError({
parameter,
value: maxRetries,
message: `${parameter} must be >= 0`
});
}
const maxRetriesResult = maxRetries ?? defaultMaxRetries;
return {
maxRetries: maxRetriesResult,
retry: retryWithExponentialBackoffRespectingRetryHeaders({
maxRetries: maxRetriesResult,
abortSignal,
additionalRetryableError
})
};
}
//#endregion
//#region src/types/usage.ts
function asLanguageModelUsage(usage) {
return {
inputTokens: usage.inputTokens.total,
inputTokenDetails: {
noCacheTokens: usage.inputTokens.noCache,
cacheReadTokens: usage.inputTokens.cacheRead,
cacheWriteTokens: usage.inputTokens.cacheWrite
},
outputTokens: usage.outputTokens.total,
outputTokenDetails: {
textTokens: usage.outputTokens.text,
reasoningTokens: usage.outputTokens.reasoning
},
totalTokens: addTokenCounts(usage.inputTokens.total, usage.outputTokens.total),
raw: usage.raw
};
}
function createNullLanguageModelUsage() {
return {
inputTokens: void 0,
inputTokenDetails: {
noCacheTokens: void 0,
cacheReadTokens: void 0,
cacheWriteTokens: void 0
},
outputTokens: void 0,
outputTokenDetails: {
textTokens: void 0,
reasoningTokens: void 0
},
totalTokens: void 0,
raw: void 0
};
}
function addLanguageModelUsage(usage1, usage2) {
const raw = isNullLanguageModelUsage(usage1) ? usage2.raw : isNullLanguageModelUsage(usage2) ? usage1.raw : void 0;
return {
inputTokens: addTokenCounts(usage1.inputTokens, usage2.inputTokens),
inputTokenDetails: {
noCacheTokens: addTokenCounts(usage1.inputTokenDetails?.noCacheTokens, usage2.inputTokenDetails?.noCacheTokens),
cacheReadTokens: addTokenCounts(usage1.inputTokenDetails?.cacheReadTokens, usage2.inputTokenDetails?.cacheReadTokens),
cacheWriteTokens: addTokenCounts(usage1.inputTokenDetails?.cacheWriteTokens, usage2.inputTokenDetails?.cacheWriteTokens)
},
outputTokens: addTokenCounts(usage1.outputTokens, usage2.outputTokens),
outputTokenDetails: {
textTokens: addTokenCounts(usage1.outputTokenDetails?.textTokens, usage2.outputTokenDetails?.textTokens),
reasoningTokens: addTokenCounts(usage1.outputTokenDetails?.reasoningTokens, usage2.outputTokenDetails?.reasoningTokens)
},
totalTokens: addTokenCounts(usage1.totalTokens, usage2.totalTokens),
...raw == null ? {} : { raw }
};
}
function isNullLanguageModelUsage(usage) {
return usage.inputTokens == null && usage.inputTokenDetails?.noCacheTokens == null && usage.inputTokenDetails?.cacheReadTokens == null && usage.inputTokenDetails?.cacheWriteTokens == null && usage.outputTokens == null && usage.outputTokenDetails?.textTokens == null && usage.outputTokenDetails?.reasoningTokens == null && usage.totalTokens == null && usage.raw == null;
}
function addTokenCounts(tokenCount1, tokenCount2) {
return tokenCount1 == null && tokenCount2 == null ? void 0 : (tokenCount1 ?? 0) + (tokenCount2 ?? 0);
}
//#endregion
//#region src/error/invalid-tool-approval-error.ts
const name$6 = "AI_InvalidToolApprovalError";
const marker$6 = `vercel.ai.error.${name$6}`;
const symbol$6 = Symbol.for(marker$6);
var InvalidToolApprovalError = class extends AISDKError {
constructor({ approvalId }) {
super({
name: name$6,
message: `Tool approval response references unknown approvalId: "${approvalId}". No matching tool-approval-request found in message history.`
});
this[symbol$6] = true;
this.approvalId = approvalId;
}
static isInstance(error) {
return AISDKError.hasMarker(error, marker$6);
}
};
//#endregion
//#region src/error/invalid-tool-approval-signature-error.ts
const name$5 = "AI_InvalidToolApprovalSignatureError";
const marker$5 = `vercel.ai.error.${name$5}`;
const symbol$5 = Symbol.for(marker$5);
var InvalidToolApprovalSignatureError = class extends AISDKError {
constructor({ approvalId, toolCallId, reason }) {
super({
name: name$5,
message: `Tool approval signature verification failed for approval "${approvalId}" (tool call "${toolCallId}"): ${reason}`
});
this[symbol$5] = true;
this.approvalId = approvalId;
this.toolCallId = toolCallId;
}
static isInstance(error) {
return AISDKError.hasMarker(error, marker$5);
}
};
//#endregion
//#region src/error/invalid-tool-input-error.ts
const name$4 = "AI_InvalidToolInputError";
const marker$4 = `vercel.ai.error.${name$4}`;
const symbol$4 = Symbol.for(marker$4);
var InvalidToolInputError = class extends AISDKError {
constructor({ toolInput, toolName, cause, message = `Invalid input for tool ${toolName}: ${getErrorMessage(cause)}` }) {
super({
name: name$4,
message,
cause
});
this[symbol$4] = true;
this.toolInput = toolInput;
this.toolName = toolName;
}
static isInstance(error) {
return AISDKError.hasMarker(error, marker$4);
}
};
//#endregion
//#region src/error/tool-call-not-found-for-approval-error.ts
const name$3 = "AI_ToolCallNotFoundForApprovalError";
const marker$3 = `vercel.ai.error.${name$3}`;
const symbol$3 = Symbol.for(marker$3);
var ToolCallNotFoundForApprovalError = class extends AISDKError {
constructor({ toolCallId, approvalId }) {
super({
name: name$3,
message: `Tool call "${toolCallId}" not found for approval request "${approvalId}".`
});
this[symbol$3] = true;
this.toolCallId = toolCallId;
this.approvalId = approvalId;
}
static isInstance(error) {
return AISDKError.hasMarker(error, marker$3);
}
};
//#endregion
//#region src/error/no-output-generated-error.ts
const name$2 = "AI_NoOutputGeneratedError";
const marker$2 = `vercel.ai.error.${name$2}`;
const symbol$2 = Symbol.for(marker$2);
/**
* Thrown when no LLM output was generated, e.g. because of errors.
*/
var NoOutputGeneratedError = class extends AISDKError {
constructor({ message = "No output generated.", cause } = {}) {
super({
name: name$2,
message,
cause
});
this[symbol$2] = true;
}
static isInstance(error) {
return AISDKError.hasMarker(error, marker$2);
}
};
//#endregion
//#region src/error/no-such-tool-error.ts
const name$1 = "AI_NoSuchToolError";
const marker$1 = `vercel.ai.error.${name$1}`;
const symbol$1 = Symbol.for(marker$1);
var NoSuchToolError = class extends AISDKError {
constructor({ toolName, availableTools = void 0, message = `Model tried to call unavailable tool '${toolName}'. ${availableTools === void 0 ? "No tools are available." : `Available tools: ${availableTools.join(", ")}.`}` }) {
super({
name: name$1,
message
});
this[symbol$1] = true;
this.toolName = toolName;
this.availableTools = availableTools;
}
static isInstance(error) {
return AISDKError.hasMarker(error, marker$1);
}
};
//#endregion
//#region src/error/tool-call-repair-error.ts
const name = "AI_ToolCallRepairError";
const marker = `vercel.ai.error.${name}`;
const symbol = Symbol.for(marker);
var ToolCallRepairError = class extends AISDKError {
constructor({ cause, originalError, message = `Error repairing tool call: ${getErrorMessage(cause)}` }) {
super({
name,
message,
cause
});
this[symbol] = true;
this.originalError = originalError;
}
static isInstance(error) {
return AISDKError.hasMarker(error, marker);
}
};
//#endregion
//#region src/error/unsupported-model-version-error.ts
/**
* Error that is thrown when a model with an unsupported version is used.
*/
var UnsupportedModelVersionError = class extends AISDKError {
constructor(options) {
super({
name: "AI_UnsupportedModelVersionError",
message: `Unsupported model version ${options.version} for provider "${options.provider}" and model "${options.modelId}". AI SDK 5 only supports models that implement specification version "v2".`
});
this.version = options.version;
this.provider = options.provider;
this.modelId = options.modelId;
}
};
//#endregion
//#region src/util/log-v2-compatibility-warning.ts
function logV2CompatibilityWarning({ provider, modelId }) {
logWarnings({
warnings: [{
type: "compatibility",
feature: "specificationVersion",
details: `Using v2 specification compatibility mode. Some features may not be available.`
}],
provider,
model: modelId
});
}
//#endregion
//#region src/model/as-embedding-model-v3.ts
function asEmbeddingModelV3(model) {
if (model.specificationVersion === "v3") return model;
logV2CompatibilityWarning({
provider: model.provider,
modelId: model.modelId
});
return new Proxy(model, { get(target, prop) {
if (prop === "specificationVersion") return "v3";
return target[prop];
} });
}
//#endregion
//#region src/model/as-embedding-model-v4.ts
function asEmbeddingModelV4(model) {
if (model.specificationVersion === "v4") return model;
const v3Model = model.specificationVersion === "v2" ? asEmbeddingModelV3(model) : model;
return new Proxy(v3Model, { get(target, prop) {
if (prop === "specificationVersion") return "v4";
return target[prop];
} });
}
//#endregion
//#region src/model/as-image-model-v3.ts
function asImageModelV3(model) {
if (model.specificationVersion === "v3") return model;
logV2CompatibilityWarning({
provider: model.provider,
modelId: model.modelId
});
return new Proxy(model, { get(target, prop) {
if (prop === "specificationVersion") return "v3";
return target[prop];
} });
}
//#endregion
//#region src/model/as-image-model-v4.ts
function asImageModelV4(model) {
if (model.specificationVersion === "v4") return model;
const v3Model = model.specificationVersion === "v2" ? asImageModelV3(model) : model;
return new Proxy(v3Model, { get(target, prop) {
if (prop === "specificationVersion") return "v4";
return target[prop];
} });
}
//#endregion
//#region src/model/as-language-model-v3.ts
function asLanguageModelV3(model) {
if (model.specificationVersion === "v3") return model;
logV2CompatibilityWarning({
provider: model.provider,
modelId: model.modelId
});
return new Proxy(model, { get(target, prop) {
switch (prop) {
case "specificationVersion": return "v3";
case "doGenerate": return async (...args) => {
const result = await target.doGenerate(...args);
return {
...result,
finishReason: convertV2FinishReasonToV3(result.finishReason),
usage: convertV2UsageToV3(result.usage)
};
};
case "doStream": return async (...args) => {
const result = await target.doStream(...args);
return {
...result,
stream: convertV2StreamToV3(result.stream)
};
};
default: return target[prop];
}
} });
}
function convertV2StreamToV3(stream) {
return stream.pipeThrough(new TransformStream({ transform(chunk, controller) {
switch (chunk.type) {
case "finish":
controller.enqueue({
...chunk,
finishReason: convertV2FinishReasonToV3(chunk.finishReason),
usage: convertV2UsageToV3(chunk.usage)
});
break;
default: controller.enqueue(chunk);
}
} }));
}
function convertV2FinishReasonToV3(finishReason) {
return {
unified: finishReason === "unknown" ? "other" : finishReason,
raw: void 0
};
}
function convertV2UsageToV3(usage) {
return {
inputTokens: {
total: usage.inputTokens,
noCache: void 0,
cacheRead: usage.cachedInputTokens,
cacheWrite: void 0
},
outputTokens: {
total: usage.outputTokens,
text: void 0,
reasoning: usage.reasoningTokens
},
raw: usage.totalTokens == null ? void 0 : { totalTokens: usage.totalTokens }
};
}
//#endregion
//#region src/model/as-language-model-v4.ts
function asLanguageModelV4(model) {
if (model.specificationVersion === "v4") return model;
const v3Model = model.specificationVersion === "v2" ? asLanguageModelV3(model) : model;
return new Proxy(v3Model, { get(target, prop) {
switch (prop) {
case "specificationVersion": return "v4";
case "doGenerate": return async (options) => {
const result = await target.doGenerate({
...options,
prompt: convertV4PromptToV3(options.prompt)
});
return {
...result,
content: result.content.map(convertV3ContentToV4)
};
};
case "doStream": return async (options) => {
const result = await target.doStream({
...options,
prompt: convertV4PromptToV3(options.prompt)
});
return {
...result,
stream: convertV3StreamToV4(result.stream)
};
};
default: return target[prop];
}
} });
}
function convertV4PromptToV3(prompt) {
return prompt.map((message) => {
if (message.role === "system") return message;
return {
...message,
content: message.content.map((part) => {
switch (part.type) {
case "file": return {
...part,
data: convertV4FileDataToV3(part.data)
};
case "tool-result": return {
...part,
output: convertV4ToolResultOutputToV3(part.output)
};
default: return part;
}
})
};
});
}
function convertV4FileDataToV3(data) {
switch (data.type) {
case "data": return data.data;
case "url": return data.url;
case "reference":
case "text": return data;
}
}
function convertV4ToolResultOutputToV3(output) {
if (output.type !== "content") return output;
return {
...output,
value: output.value.map((part) => {
if (part.type !== "file") return part;
switch (part.data.type) {
case "data": return {
type: "file-data",
data: typeof part.data.data === "string" ? part.data.data : convertUint8ArrayToBase64(part.data.data),
mediaType: part.mediaType,
filename: part.filename,
providerOptions: part.providerOptions
};
case "url": return {
type: "file-url",
url: part.data.url.toString(),
providerOptions: part.providerOptions
};
case "reference": return {
type: "file-id",
fileId: part.data.reference,
providerOptions: part.providerOptions
};
case "text": return part;
}
})
};
}
function convertV3ContentToV4(content) {
return content.type === "file" ? {
...content,
data: {
type: "data",
data: content.data
}
} : content;
}
function convertV3StreamToV4(stream) {
return stream.pipeThrough(new TransformStream({ transform(chunk, controller) {
controller.enqueue(chunk.type === "file" ? {
...chunk,
data: {
type: "data",
data: chunk.data
}
} : chunk);
} }));
}
//#endregion
//#region src/model/as-reranking-model-v4.ts
function asRerankingModelV4(model) {
if (model.specificationVersion === "v4") return model;
return new Proxy(model, { get(target, prop) {
if (prop === "specificationVersion") return "v4";
return target[prop];
} });
}
//#endregion
//#region src/model/as-speech-model-v3.ts
function asSpeechModelV3(model) {
if (model.specificationVersion === "v3") return model;
logV2CompatibilityWarning({
provider: model.provider,
modelId: model.modelId
});
return new Proxy(model, { get(target, prop) {
if (prop === "specificationVersion") return "v3";
return target[prop];
} });
}
//#endregion
//#region src/model/as-speech-model-v4.ts
function asSpeechModelV4(model) {
if (model.specificationVersion === "v4") return model;
const v3Model = model.specificationVersion === "v2" ? asSpeechModelV3(model) : model;
return new Proxy(v3Model, { get(target, prop) {
if (prop === "specificationVersion") return "v4";
return target[prop];
} });
}
//#endregion
//#region src/model/as-transcription-model-v3.ts
function asTranscriptionModelV3(model) {
if (model.specificationVersion === "v3") return model;
logV2CompatibilityWarning({
provider: model.provider,
modelId: model.modelId
});
return new Proxy(model, { get(target, prop) {
if (prop === "specificationVersion") return "v3";
return target[prop];
} });
}
//#endregion
//#region src/model/as-transcription-model-v4.ts
function asTranscriptionModelV4(model) {
if (model.specificationVersion === "v4") return model;
const v3Model = model.specificationVersion === "v2" ? asTranscriptionModelV3(model) : model;
return new Proxy(v3Model, { get(target, prop) {
if (prop === "specificationVersion") return "v4";
if (prop === "doGenerate") return async ({ timestampGranularities, ...options }) => {
const result = await target.doGenerate.call(model, options);
if (!timestampGranularities?.length) return result;
return {
...result,
warnings: [...result.warnings, {
type: "unsupported",
feature: "timestampGranularities",
details: `Shared timestamp granularities are not supported by ${model.specificationVersion} transcription models.`
}]
};
};
return target[prop];
} });
}
//#endregion
//#region src/model/as-provider-v3.ts
function asProviderV3(provider) {
if ("specificationVersion" in provider && provider.specificationVersion === "v3") return provider;
const v2Provider = provider;
return {
specificationVersion: "v3",
languageModel: (modelId) => asLanguageModelV3(v2Provider.languageModel(modelId)),
embeddingModel: (modelId) => asEmbeddingModelV3(v2Provider.textEmbeddingModel(modelId)),
imageModel: (modelId) => asImageModelV3(v2Provider.imageModel(modelId)),
transcriptionModel: v2Provider.transcriptionModel ? (modelId) => asTranscriptionModelV3(v2Provider.transcriptionModel(modelId)) : void 0,
speechModel: v2Provider.speechModel ? (modelId) => asSpeechModelV3(v2Provider.speechModel(modelId)) : void 0,
rerankingModel: void 0
};
}
//#endregion
//#region src/model/as-provider-v4.ts
function asProviderV4(provider) {
if ("specificationVersion" in provider && provider.specificationVersion === "v4") return provider;
const v3Provider = !("specificationVersion" in provider) || provider.specificationVersion !== "v3" ? asProviderV3(provider) : provider;
return {
specificationVersion: "v4",
languageModel: (modelId) => asLanguageModelV4(v3Provider.languageModel(modelId)),
embeddingModel: (modelId) => asEmbeddingModelV4(v3Provider.embeddingModel(modelId)),
imageModel: (modelId) => asImageModelV4(v3Provider.imageModel(modelId)),
transcriptionModel: v3Provider.transcriptionModel ? (modelId) => asTranscriptionModelV4(v3Provider.transcriptionModel(modelId)) : void 0,
speechModel: v3Provider.speechModel ? (modelId) => asSpeechModelV4(v3Provider.speechModel(modelId)) : void 0,
rerankingModel: v3Provider.rerankingModel ? (modelId) => asRerankingModelV4(v3Provider.rerankingModel(modelId)) : void 0
};
}
//#endregion
//#region src/model/resolve-model.ts
function resolveLanguageModel(model) {
if (typeof model === "string") return getGlobalProvider().languageModel(model);
if (![
"v4",
"v3",
"v2"
].includes(model.specificationVersion)) {
const unsupportedModel = model;
throw new UnsupportedModelVersionError({
version: unsupportedModel.specificationVersion,
provider: unsupportedModel.provider,
modelId: unsupportedModel.modelId
});
}
return asLanguageModelV4(model);
}
function getGlobalProvider() {
return asProviderV4(globalThis.AI_SDK_DEFAULT_PROVIDER ?? gateway);
}
//#endregion
//#region src/util/merge-abort-signals.ts
/**
* Merges multiple abort sources into a single `AbortSignal`.
* The returned signal will abort when any input signal aborts or when any
* numeric timeout elapses, using the reason from the first source to abort.
*
* @param signals - Abort signals or timeout durations in milliseconds.
* `null` and `undefined` values are ignored.
* @returns An `AbortSignal` that aborts when any valid source aborts,
* or `undefined` if no valid sources are provided.
*/
function mergeAbortSignals(...signals) {
const validSignals = filterNullable(...signals).map((signal) => typeof signal === "number" ? AbortSignal.timeout(signal) : signal);
return validSignals.length === 0 ? void 0 : validSignals.length === 1 ? validSignals[0] : AbortSignal.any(validSignals);
}
//#endregion
//#region src/util/set-abort-timeout.ts
/**
* Schedules a timeout that aborts the given controller with a `TimeoutError`
* `DOMException`, matching the reason produced by `AbortSignal.timeout(ms)`.
*
* @param abortController - The controller to abort when the timeout elapses.
* If undefined, no timeout is scheduled.
* @param label - Human-readable label included in the error message
* (e.g. "Step", "Chunk").
* @param timeoutMs - Duration in milliseconds before the controller is aborted.
* If undefined, no timeout is scheduled.
* @returns The timeout id (suitable for passing to `clearTimeout`), or
* `undefined` if no timeout was scheduled.
*/
function setAbortTimeout({ abortController, label, timeoutMs }) {
if (abortController == null || timeoutMs == null) return;
return setTimeout(() => abortController.abort(new DOMException(`${label} timeout of ${timeoutMs}ms exceeded`, "TimeoutError")), timeoutMs);
}
//#endregion
//#region src/util/merge-callbacks.ts
/**
* Creates an async callback that invokes the provided callbacks in parallel.
* Undefined callbacks are skipped, and thrown or rejected callback errors are
* ignored.
*
* @param callbacks The callbacks to invoke for each event.
* @returns A callback that forwards each event to all callbacks and waits for
* them to settle.
*/
function mergeCallbacks(...callbacks) {
return async (event) => {
await Promise.allSettled(callbacks.map(async (callback) => {
await callback?.(event);
}));
};
}
//#endregion
//#region src/telemetry/filter-included-context.ts
/**
* Returns a shallow copy of the runtime context with only top-level
* properties marked for telemetry inclusion.
*/
function filterIncludedContext({ context, includeContext }) {
if (context == null) return {};
return Object.fromEntries(Object.entries(context).filter(([key]) => includeContext?.[key] === true));
}
function filterToolsContext({ toolsContext, includeToolsContext }) {
if (includeToolsContext == null) return {};
return Object.fromEntries(Object.entries(toolsContext).map(([toolName, toolContext]) => [toolName, filterToolContext({
toolName,
toolContext,
includeToolsContext
})]));
}
function filterToolContext({ toolName, toolContext, includeToolsContext }) {
const includeToolContext = includeToolsContext?.[toolName];
return filterIncludedContext({
context: toolContext,
includeContext: includeToolContext
});
}
//#endregion
//#region src/telemetry/tracing-channel.ts
const AI_SDK_TELEMETRY_TRACING_CHANNEL = "ai:telemetry";
//#endregion
//#region src/util/is-node-runtime.ts
function isNodeRuntime() {
return globalThis.process?.release?.name === "node";
}
//#endregion
//#region src/telemetry/redact-telemetry-credentials.ts
/**
* Apply provider-owned credential redaction on telemetry-only copies. Only visit SDK
* metadata containers, never arbitrary prompts, tool outputs, or user context.
*/
function redactTelemetryCredentials(value) {
return replaceProperties(value, {
args: redactTelemetryValue,
tools: redactTools,
stepTools: redactTools,
request: redactMetadata,
response: redactMetadata,
steps: (steps) => mapArray(steps, redactTelemetryCredentials),
finalStep: redactTelemetryCredentials,
error: redactTelemetryCredentials,
cause: redactTelemetryCredentials,
errors: (errors) => mapArray(errors, redactTelemetryCredentials),
lastError: redactTelemetryCredentials,
requestBodyValues: redactBody
});
}
function redactMetadata(value) {
return Array.isArray(value) ? mapArray(value, redactMetadata) : replaceProperties(value, { body: redactBody });
}
function redactBody(value) {
return replaceProperties(redactTelemetryValue(value), { tools: redactTools });
}
function redactTools(value) {
if (Array.isArray(value)) return mapArray(value, redactTelemetryCredentials);
if (!isRecord(value)) return value;
return replaceProperties(value, Object.fromEntries(Object.keys(value).map((key) => [key, redactTelemetryCredentials])));
}
function isRecord(value) {
return value != null && typeof value === "object" && !Array.isArray(value);
}
function mapArray(value, redact) {
if (!Array.isArray(value)) return value;
const copy = value.map(redact);
return copy.some((entry, index) => entry !== value[index]) ? copy : value;
}
/** Preserve result getters, streams, and property descriptors without mutation. */
function replaceProperties(value, redactors) {
if (!isRecord(value)) return value;
let descriptors;
for (const [key, redact] of Object.entries(redactors)) {
const descriptor = Object.getOwnPropertyDescriptor(value, key);
if (descriptor == null || !("value" in descriptor)) continue;
const redacted = redact(descriptor.value);
if (redacted !== descriptor.value) {
descriptors ??= Object.getOwnPropertyDescriptors(value);
descriptors[key] = {
...descriptor,
value: redacted
};
}
}
return descriptors == null ? value : Object.create(Object.getPrototypeOf(value), descriptors);
}
//#endregion
//#region src/telemetry/tracing-channel-publisher.ts
let diagnosticsChannelPromise;
/**
* Loads Node's diagnostics channel module only when the current runtime supports
* it. Unsupported runtimes and failed imports intentionally resolve to
* undefined so telemetry tracing never crashes user code.
*/
async function loadDiagnosticsChannel() {
if (!isNodeRuntime()) return;
if (diagnosticsChannelPromise == null) diagnosticsChannelPromise = Promise.resolve(loadBuiltinModule("node:diagnostics_channel"));
return diagnosticsChannelPromise;
}
function loadBuiltinModule(id) {
const processWithBuiltins = globalThis.process;
try {
return processWithBuiltins?.getBuiltinModule?.(id);
} catch {
return;
}
}
/**
* Runs an async operation inside the AI SDK telemetry tracing channel when
* tracing subscribers may exist. Without Node diagnostics-channel support,
* without tracingChannel support, or when the runtime reports no subscribers,
* this is a direct pass-through.
*
* The execution bookkeeping preserves the original model/tool result or error
* if tracing itself throws, and prevents falling back by calling `execute` a
* second time.
*/
async function runWithTracingChannelSpan(message, execute, recordResult = true) {
const tracingChannel = (await loadDiagnosticsChannel())?.tracingChannel?.(AI_SDK_TELEMETRY_TRACING_CHANNEL);
if (tracingChannel == null || tracingChannel.hasSubscribers === false) return await execute();
let executePromise;
let executionResult;
let executionError;
let hasExecutionResult = false;
let hasExecutionError = false;
const tracedExecute = () => {
try {
executePromise = Promise.resolve(execute());
} catch (error) {
executePromise = Promise.reject(error);
}
executePromise = executePromise.then((result) => {
executionResult = result;
hasExecutionResult = true;
return result;
}, (error) => {
executionError = error;
hasExecutionError = true;
throw error;
});
return recordResult ? executePromise.then(redactTelemetryCredentials, (error) => {
throw redactTelemetryCredentials(error);
}) : executePromise.then(() => void 0, () => {
throw new Error("AI SDK operation failed");
});
};
try {
await tracingChannel.tracePromise(tracedExecute, message);
return executionResult;
} catch {
if (hasExecutionError) throw executionError;
if (hasExecutionResult) return executionResult;
if (executePromise != null) return await executePromise;
return await execute();
}
}
/**
* Opens a long-lived tracing-channel span context and returns a runner that can
* re-enter that context later without changing stream setup timing.
*/
function openTelemetryChannelSpanContext({ message, completion, recordResult = true }) {
if (!isNodeRuntime()) {
Promise.resolve(completion).catch(() => {});
return;
}
const diagnosticsChannel = loadBuiltinModule("node:diagnostics_channel");
const asyncHooks = loadBuiltinModule("node:async_hooks");
const tracingChannel = diagnosticsChannel?.tracingChannel?.(AI_SDK_TELEMETRY_TRACING_CHANNEL);
if (tracingChannel == null || tracingChannel.hasSubscribers === false || asyncHooks == null) {
Promise.resolve(completion).catch(() => {});
return;
}
const context = message;
let asyncResource;
let asyncEndPublished = false;
const safePublish = (publish) => {
try {
publish();
} catch {}
};
const publishAsyncEnd = ({ result, error }) => {
if (asyncEndPublished) return;
asyncEndPublished = true;
if (error !== void 0) {
context.error = recordResult ? redactTelemetryCredentials(error) : /* @__PURE__ */ new Error("AI SDK operation failed");
safePublish(() => tracingChannel.error.publish(context));
}
if (recordResult && result !== void 0) context.result = redactTelemetryCredentials(result);
safePublish(() => tracingChannel.asyncEnd.publish(context));
};
safePublish(() => {
tracingChannel.start.runStores(context, () => {
asyncResource = new asyncHooks.AsyncResource("ai.telemetry");
});
});
safePublish(() => tracingChannel.end.publish(context));
Promise.resolve(completion).then((result) => publishAsyncEnd({ result }), (error) => publishAsyncEnd({ error }));
return { run: (execute) => asyncResource == null ? execute() : asyncResource.runInAsyncScope(execute) };
}
//#endregion
//#region src/telemetry/telemetry-registry.ts
function getGlobalTelemetryIntegrations() {
return globalThis.AI_SDK_TELEMETRY_INTEGRATIONS ?? [];
}
//#endregion
//#region src/telemetry/filter-telemetry-event.ts
/**
* Filters SDK-owned content fields on a copy. Only descend into SDK event
* containers; user context has its own inclusion policy.
*/
function filterTelemetryEvent(event, telemetry) {
const hideInputs = telemetry.recordInputs === false;
const hideOutputs = telemetry.recordOutputs === false;
if (!hideInputs && !hideOutputs || event == null || typeof event !== "object") return event;
const filtered = Object.assign(Object.create(Object.getPrototypeOf(event)), event);
const replace = (fields) => {
for (const [key, value] of Object.entries(fields)) if (Object.hasOwn(event, key)) filtered[key] = value;
};
const isSpeech = filtered.operationId === "ai.generateSpeech";
if (hideInputs) {
replace({
instructions: void 0,
system: void 0,
prompt: void 0,
messages: [],
promptMessages: [],
experimental_steeringMessages: [],
tools: void 0,
stepTools: void 0,
schema: void 0,
schemaName: void 0,
schemaDescription: void 0,
...typeof filtered.output === "object" ? { output: void 0 } : {},
providerOptions: {},
value: "",
values: [],
query: "",
documents: [],
state: Array.isArray(filtered.state) ? [] : typeof filtered.state === "string" ? "" : {},
questions: {},
...isSpeech ? { text: "" } : {}
});
if (filtered.toolCall != null) filtered.toolCall = {
...filtered.toolCall,
input: void 0
};
if (filtered.toolOutput != null) filtered.toolOutput = {
...filtered.toolOutput,
input: void 0
};
if (Array.isArray(filtered.ranking)) filtered.ranking = filtered.ranking.map((entry) => "document" in entry ? {
...entry,
document: typeof entry.document === "string" ? "" : {}
} : entry);
}
if (hideOutputs) {
replace({
content: [],
...!isSpeech ? { text: "" } : {},
reasoning: Array.isArray(filtered.reasoning) ? [] : void 0,
reasoningText: void 0,
files: [],
sources: [],
toolCalls: [],
staticToolCalls: [],
dynamicToolCalls: [],
toolResults: [],
staticToolResults: [],
dynamicToolResults: [],
responseMessages: [],
object: void 0,
objectText: "",
embedding: [],
embeddings: [],
ranking: [],
answers: {},
segments: [],
words: []
});
if (filtered.toolOutput != null) filtered.toolOutput = {
...filtered.toolOutput,
output: void 0,
error: void 0
};
}
replace({
providerMetadata: void 0,
error: filtered.error == null ? filtered.error : /* @__PURE__ */ new Error("AI SDK operation failed")
});
for (const key of ["toolCall", "toolOutput"]) {
const toolEvent = filtered[key];
if (toolEvent != null && typeof toolEvent === "object") filtered[key] = {
...toolEvent,
providerMetadata: void 0,
..."error" in toolEvent ? { error: toolEvent.error == null ? toolEvent.error : /* @__PURE__ */ new Error("AI SDK operation failed") } : {}
};
}
for (const key of ["request", "response"]) {
const filterMetadata = (metadata) => {
if (metadata == null || typeof metadata !== "object") return metadata;
const filteredMetadata = { ...metadata };
delete filteredMetadata.body;
if (key === "request" ? hideInputs : hideOutputs) {
if ("messages" in metadata) filteredMetadata.messages = [];
if ("instructions" in metadata) filteredMetadata.instructions = void 0;
}
return filteredMetadata;
};
const metadata = filtered[key];
if (metadata != null) filtered[key] = Array.isArray(metadata) ? metadata.map(filterMetadata) : filterMetadata(metadata);
}
if (Array.isArray(filtered.steps)) filtered.steps = filtered.steps.map((step) => filterTelemetryEvent(step, telemetry));
if (Object.hasOwn(event, "finalStep")) filtered.finalStep = filterTelemetryEvent(filtered.finalStep, telemetry);
return filtered;
}
//#endregion
//#region src/telemetry/create-telemetry-dispatcher.ts
function augmentEvent(event, telemetry, filterContext = false) {
const augmentedEvent = Object.assign(Object.create(Object.getPrototypeOf(event)), event, {
recordInputs: telemetry.recordInputs,
recordOutputs: telemetry.recordOutputs,
functionId: telemetry.functionId,
aiVersion: VERSION
});
if (filterContext && event != null && typeof event === "object" && "runtimeContext" in event) augmentedEvent.runtimeContext = filterIncludedContext({
context: event.runtimeContext,
includeContext: telemetry.includeRuntimeContext
});
if (filterContext && event != null && typeof event === "object") {
if ("toolsContext" in event) augmentedEvent.toolsContext = filterToolsContext({
toolsContext: event.toolsContext,
includeToolsContext: telemetry.includeToolsContext
});
else if ("toolContext" in event && event.toolContext != null && "toolCall" in event && event.toolCall != null && typeof event.toolCall === "object" && "toolName" in event.toolCall) augmentedEvent.toolContext = filterToolContext({
toolName: event.toolCall.toolName,
toolContext: event.toolContext,
includeToolsContext: telemetry.includeToolsContext
});
}
return redactTelemetryCredentials(filterTelemetryEvent(augmentedEvent, telemetry));
}
/**
* Creates a telemetry dispatcher that sends telemetry events
* to the resolved set of integrations.
*
* When per-call integrations are provided, they take precedence over the globally
* registered integrations for that call. When no per-call integrations are
* provided, the globally registered integrations are used.
*
* @param args.telemetry - Optional per-call telemetry settings and integrations.
*
* @returns A telemetry dispatcher that fans out lifecycle events to the
* resolved set of integrations.
*/
function createTelemetryDispatcher({ telemetry }) {
if (telemetry?.isEnabled === false) return {};
const localIntegrations = telemetry?.integrations;
const integrations = localIntegrations != null ? asArray(localIntegrations) : getGlobalTelemetryIntegrations();
const telemetryMetadata = {
recordInputs: telemetry?.recordInputs,
recordOutputs: telemetry?.recordOutputs,
functionId: telemetry?.functionId,
includeRuntimeContext: telemetry?.includeRuntimeContext,
includeToolsContext: telemetry?.includeToolsContext
};
const recordResult = telemetry?.recordInputs !== false && telemetry?.recordOutputs !== false;
const mergeTelemetryCallback = (key, deprecatedKey) => {
const integrationCallbacks = integrations.map((integration) => {
const callback = integration[key] ?? (deprecatedKey == null ? void 0 : integration[deprecatedKey]);
return typeof callback === "function" ? callback.bind(integration) : void 0;
}).filter(Boolean).map((callback) => ((event) => callback(augmentEvent(event, telemetryMetadata))));
if (integrationCallbacks.length === 0) return;
const mergedIntegrationCallback = mergeCallbacks(...integrationCallbacks);
return async (event) => {
await mergedIntegrationCallback(event);
};
};
const onStepEnd = mergeTelemetryCallback("onStepEnd");
const onStepFinish = mergeTelemetryCallback("onStepFinish");
const executeLanguageModelCallWrappers = integrations.map((integration) => integration.executeLanguageModelCall?.bind(integration)).filter(Boolean);
const executeToolWrappers = integrations.map((integration) => integration.executeTool?.bind(integration)).filter(Boolean);
return {
runInTracingChannelSpan: async ({ type, event, execute }) => await runWithTracingChannelSpan({
type,
event: augmentEvent(event, telemetryMetadata, true)
}, execute, recordResult),
startTracingChannelContext: ({ type, event, completion }) => openTelemetryChannelSpanContext({
message: {
type,
event: augmentEvent(event, telemetryMetadata, true)
},
completion,
recordResult
}),
onStart: mergeTelemetryCallback("onStart"),
onStepStart: mergeTelemetryCallback("onStepStart"),
onLanguageModelCallStart: mergeTelemetryCallback("onLanguageModelCallStart"),
onLanguageModelCallEnd: mergeTelemetryCallback("onLanguageModelCallEnd"),
onToolExecutionStart: mergeTelemetryCallback("onToolExecutionStart"),
onToolExecutionEnd: mergeTelemetryCallback("onToolExecutionEnd"),
onStepEnd: onStepEnd == null && onStepFinish == null ? void 0 : mergeCallbacks(onStepEnd, onStepFinish),
onStepError: mergeTelemetryCallback("onStepError"),
onObjectStepStart: mergeTelemetryCallback("onObjectStepStart"),
onObjectStepEnd: mergeTelemetryCallback("onObjectStepEnd"),
onEmbedStart: mergeTelemetryCallback("onEmbedStart"),
onEmbedEnd: mergeTelemetryCallback("onEmbedEnd"),
onRerankStart: mergeTelemetryCallback("onRerankStart"),
onRerankEnd: mergeTelemetryCallback("onRerankEnd"),
experimental_onDecideStart: mergeTelemetryCallback("experimental_onDecideStart", "experimental_onEvaluateStart"),
experimental_onDecisionModelCallStart: mergeTelemetryCallback("experimental_onDecisionModelCallStart", "experimental_onEvaluationModelCallStart"),
experimental_onDecisionModelCallEnd: mergeTelemetryCallback("experimental_onDecisionModelCallEnd", "experimental_onEvaluationModelCallEnd"),
experimental_onDecideEnd: mergeTelemetryCallback("experimental_onDecideEnd", "experimental_onEvaluateEnd"),
experimental_onStreamTranscriptionStart: mergeTelemetryCallback("experimental_onStreamTranscriptionStart"),
experimental_onStreamTranscriptionEnd: mergeTelemetryCallback("experimental_onStreamTranscriptionEnd"),
onEnd: mergeTelemetryCallback("onEnd"),
onAbort: mergeTelemetryCallback("onAbort"),
onError: mergeTelemetryCallback("onError"),
/**
* Runs provider calls inside integration-specific context so
* auto-instrumented provider requests can be associated with model work.
*/
executeLanguageModelCall: async ({ execute, ...event }) => {
const augmentedEvent = augmentEvent(event, telemetryMetadata);
let wrappedExecute = execute;
for (const executeWrapper of executeLanguageModelCallWrappers) {
const innerExecute = wrappedExecute;
wrappedExecute = async () => {
const originals = /* @__PURE__ */ new Map();
const redact = (original) => {
const redacted = redactTelemetryCredentials(original);
if (redacted !== original) originals.set(redacted, original);
return redacted;
};
const restore = (value) => originals.has(value) ? originals.get(value) : value;
try {
return restore(await executeWrapper({
...augmentedEvent,
execute: async () => {
try {
return redact(await innerExecute());
} catch (error) {
throw redact(error);
}
}
}));
} catch (error) {
throw restore(error);
}
};
}
return await runWithTracingChannelSpan({
type: "languageModelCall",
event: augmentedEvent
}, wrappedExecute, recordResult);
},
/**
* Composes all `executeTool` wrappers around the original tool execution.
* Each wrapper receives an `execute` function that calls the next wrapper in
* the chain, so integrations can establish nested telemetry context before
* delegating to the underlying tool.
*/
executeTool: async ({ execute, ...event }) => {
const augmentedEvent = augmentEvent(event, telemetryMetadata);
let wrappedExecute = execute;
for (const executeWrapper of executeToolWrappers) {
const innerExecute = wrappedExecute;
wrappedExecute = () => executeWrapper({
...augmentedEvent,
execute: innerExecute
});
}
return await wrappedExecute();
}
};
}
//#endregion
//#region src/generate-text/reasoning.ts
function asReasoningText(reasoningParts) {
const reasoningText = reasoningParts.map((part) => "text" in part ? part.text : "").join("");
return reasoningText.length > 0 ? reasoningText : void 0;
}
//#endregion
//#region src/generate-text/generated-file.ts
var DefaultGeneratedFile = class {
constructor({ data, mediaType, providerMetadata }) {
this.base64Data = typeof data === "string" ? data : void 0;
this.uint8ArrayData = typeof data === "string" ? void 0 : convertDataContentToUint8Array(data);
this.mediaType = mediaType;
this.providerMetadata = providerMetadata;
}
get base64() {
if (this.base64Data == null) this.base64Data = convertUint8ArrayToBase64(this.uint8ArrayData);
return this.base64Data;
}
get uint8Array() {
if (this.uint8ArrayData == null) this.uint8ArrayData = convertBase64ToUint8Array(this.base64Data);
return this.uint8ArrayData;
}
};
//#endregion
//#region src/generate-text/reasoning-output.ts
function unwrapReasoningFileData(data) {
if (typeof data === "object" && data !== null && "type" in data) return data.type === "data" ? data.data : data.url;
return data;
}
function convertFromReasoningOutputs(parts) {
return parts.map((part) => {
if (part.type === "reasoning") return {
type: "reasoning",
text: part.text,
...part.providerMetadata != null ? { providerOptions: part.providerMetadata } : {}
};
return {
type: "reasoning-file",
data: part.file.base64,
mediaType: part.file.mediaType,
...part.providerMetadata != null ? { providerOptions: part.providerMetadata } : {}
};
});
}
function convertToReasoningOutputs(parts) {
return parts.map((part) => {
if (part.type === "reasoning") return {
type: "reasoning",
text: part.text,
...part.providerOptions != null ? { providerMetadata: part.providerOptions } : {}
};
const rawData = unwrapReasoningFileData(part.data);
return {
type: "reasoning-file",
file: new DefaultGeneratedFile({
data: rawData instanceof ArrayBuffer ? new Uint8Array(rawData) : rawData instanceof URL ? rawData.toString() : rawData,
mediaType: part.mediaType
}),
...part.providerOptions != null ? { providerMetadata: part.providerOptions } : {}
};
});
}
//#endregion
//#region src/generate-text/step-result.ts
var DefaultStepResult = class {
constructor({ callId, stepNumber, provider, modelId, runtimeContext, toolsContext, content, finishReason, rawFinishReason, usage, performance, warnings, request, response, providerMetadata, steeringMessages }) {
this.callId = callId;
this.stepNumber = stepNumber;
this.model = {
provider,
modelId
};
this.runtimeContext = runtimeContext;
this.toolsContext = toolsContext;
this.content = content;
this.finishReason = finishReason;
this.rawFinishReason = rawFinishReason;
this.usage = usage;
this.performance = performance;
this.warnings = warnings;
this.request = request;
this.response = response;
this.providerMetadata = providerMetadata;
if (steeringMessages != null && steeringMessages.length > 0) this.experimental_steeringMessages = steeringMessages;
}
get text() {
return this.content.filter((part) => part.type === "text").map((part) => part.text).join("");
}
get reasoning() {
return convertFromReasoningOutputs(this.content.filter((part) => part.type === "reasoning" || part.type === "reasoning-file"));
}
get reasoningText() {
return asReasoningText(this.reasoning);
}
get files() {
return this.content.filter((part) => part.type === "file").map((part) => part.file);
}
get sources() {
return this.content.filter((part) => part.type === "source");
}
get toolCalls() {
return this.content.filter((part) => part.type === "tool-call");
}
get staticToolCalls() {
return this.toolCalls.filter((toolCall) => toolCall.dynamic !== true);
}
get dynamicToolCalls() {
return this.toolCalls.filter((toolCall) => toolCall.dynamic === true);
}
get toolResults() {
return this.content.filter((part) => part.type === "tool-result");
}
get staticToolResults() {
return this.toolResults.filter((toolResult) => toolResult.dynamic !== true);
}
get dynamicToolResults() {
return this.toolResults.filter((toolResult) => toolResult.dynamic === true);
}
};
//#endregion
//#region src/generate-text/restricted-telemetry-dispatcher.ts
/**
* Creates a copy of a step result whose runtime context only contains
* top-level properties marked for telemetry inclusion.
*/
function restrictStepResult({ step, includeRuntimeContext, includeToolsContext }) {
return new DefaultStepResult({
callId: step.callId,
stepNumber: step.stepNumber,
provider: step.model.provider,
modelId: step.model.modelId,
runtimeContext: filterIncludedContext({
context: step.runtimeContext,
includeContext: includeRuntimeContext
}),
toolsContext: filterToolsContext({
toolsContext: step.toolsContext,
includeToolsContext
}),
content: step.content,
finishReason: step.finishReason,
rawFinishReason: step.rawFinishReason,
usage: step.usage,
performance: step.performance,
warnings: step.warnings,
request: step.request,
response: step.response,
providerMetadata: step.providerMetadata,
steeringMessages: step.experimental_steeringMessages
});
}
/**
* Creates a telemetry dispatcher that only includes configured runtime context
* properties in text-generation lifecycle events before dispatching them.
*/
function createRestrictedTelemetryDispatcher({ telemetry, includeRuntimeContext, includeToolsContext }) {
const telemetryDispatcher = createTelemetryDispatcher({ telemetry });
const restrictStepStartEvent = (event) => ({
...event,
runtimeContext: filterIncludedContext({
context: event.runtimeContext,
includeContext: includeRuntimeContext
}),
steps: event.steps.map((step) => restrictStepResult({
step,
includeRuntimeContext,
includeToolsContext
})),
toolsContext: filterToolsContext({
toolsContext: event.toolsContext,
includeToolsContext
})
});
return {
...telemetryDispatcher,
onStart: (event) => telemetryDispatcher.onStart?.({
...event,
runtimeContext: filterIncludedContext({
context: event.runtimeContext,
includeContext: includeRuntimeContext
}),
toolsContext: filterToolsContext({
toolsContext: event.toolsContext,
includeToolsContext
})
}),
onStepStart: (event) => telemetryDispatcher.onStepStart?.(restrictStepStartEvent(event)),
onStepEnd: (event) => telemetryDispatcher.onStepEnd?.(restrictStepResult({
step: event,
includeRuntimeContext,
includeToolsContext
})),
onStepFinish: (event) => telemetryDispatcher.onStepEnd?.(restrictStepResult({
step: event,
includeRuntimeContext,
includeToolsContext
})),
onStepError: (event) => telemetryDispatcher.onStepError?.(restrictStepStartEvent(event)),
onEnd: (event) => telemetryDispatcher.onEnd?.(((restrictedSteps) => {
return {
...event,
runtimeContext: filterIncludedContext({
context: event.runtimeContext,
includeContext: includeRuntimeContext
}),
steps: restrictedSteps,
finalStep: restrictedSteps.at(-1),
toolsContext: filterToolsContext({
toolsContext: event.toolsContext,
includeToolsContext
})
};
})(event.steps.map((step) => restrictStepResult({
step,
includeRuntimeContext,
includeToolsContext
})))),
onAbort: (event) => telemetryDispatcher.onAbort?.({
...event,
steps: event.steps.map((step) => restrictStepResult({
step,
includeRuntimeContext,
includeToolsContext
}))
}),
onToolExecutionStart: (event) => telemetryDispatcher.onToolExecutionStart?.({
...event,
toolContext: filterToolContext({
toolName: event.toolCall.toolName,
toolContext: event.toolContext,
includeToolsContext
})
}),
onToolExecutionEnd: (event) => telemetryDispatcher.onToolExecutionEnd?.({
...event,
toolContext: filterToolContext({
toolName: event.toolCall.toolName,
toolContext: event.toolContext,
includeToolsContext
})
})
};
}
//#endregion
//#region src/util/get-own.ts
/**
* Reads a property by an untrusted key, ignoring inherited prototype members.
*
* Tool sets, tool contexts, and similar lookup objects are indexed by names
* that can come from model output or client-supplied message history. Plain
* bracket access (`obj[name]`) resolves names such as `constructor`,
* `toString`, or `__proto__` to values on `Object.prototype`, which would slip
* past the `== null` / `!value` guards that treat an unknown name as "not
* present". This helper returns `undefined` unless `key` is an own property.
*/
function getOwn(obj, key) {
return obj != null && Object.hasOwn(obj, key) ? obj[key] : void 0;
}
//#endregion
//#region src/generate-text/tool-call.ts
const inputSchemaInputSymbol = Symbol("ai-sdk-tool-call-input-schema-input");
function setToolCallInputSchemaInput(toolCall, inputSchemaInput) {
Object.defineProperty(toolCall, inputSchemaInputSymbol, { value: inputSchemaInput });
return toolCall;
}
function getToolCallInputSchemaInput(toolCall) {
return inputSchemaInputSymbol in toolCall ? { value: toolCall[inputSchemaInputSymbol] } : void 0;
}
//#endregion
//#region src/generate-text/parse-tool-call.ts
async function parseToolCall({ toolCall, tools, repairToolCall, refineToolInput, messages, instructions, abortSignal }) {
try {
if (tools == null) {
if (toolCall.providerExecuted && toolCall.dynamic) return await refineParsedToolCallInput({
toolCall: await parseProviderExecutedDynamicToolCall(toolCall),
refineToolInput
});
throw new NoSuchToolError({ toolName: toolCall.toolName });
}
try {
return await refineParsedToolCallInput({
toolCall: await doParseToolCall({
toolCall,
tools
}),
refineToolInput
});
} catch (error) {
if (repairToolCall == null || !(NoSuchToolError.isInstance(error) || InvalidToolInputError.isInstance(error))) throw error;
let repairedToolCall = null;
try {
abortSignal?.throwIfAborted();
repairedToolCall = await waitForPromiseWithAbortSignal({
promise: repairToolCall({
toolCall,
tools,
inputSchema: async ({ toolName }) => {
const inputSchema = getOwn(tools, toolName)?.inputSchema;
return await asSchema(inputSchema).jsonSchema;
},
instructions,
system: instructions,
messages,
error,
abortSignal
}),
abortSignal
});
} catch (repairError) {
abortSignal?.throwIfAborted();
throw new ToolCallRepairError({
cause: repairError,
originalError: error
});
}
if (repairedToolCall == null) throw error;
const parsedRepairedToolCall = await refineParsedToolCallInput({
toolCall: await doParseToolCall({
toolCall: repairedToolCall,
tools
}),
refineToolInput
});
abortSignal?.throwIfAborted();
return parsedRepairedToolCall;
}
} catch (error) {
abortSignal?.throwIfAborted();
const parsedInput = await safeParseJSON({ text: toolCall.input });
const input = parsedInput.success ? parsedInput.value : toolCall.input;
const tool = getOwn(tools, toolCall.toolName);
return {
type: "tool-call",
toolCallId: toolCall.toolCallId,
toolName: toolCall.toolName,
input,
dynamic: true,
invalid: true,
error,
title: tool?.title,
providerExecuted: toolCall.providerExecuted,
providerMetadata: toolCall.providerMetadata,
...tool?.metadata != null ? { toolMetadata: tool.metadata } : {}
};
}
}
async function waitForPromiseWithAbortSignal({ promise, abortSignal }) {
if (abortSignal == null) return await promise;
return await new Promise((resolve, reject) => {
const cleanup = () => {
abortSignal.removeEventListener("abort", onAbort);
};
const onAbort = () => {
cleanup();
reject(abortSignal.reason);
};
Promise.resolve(promise).then((value) => {
cleanup();
resolve(value);
}).catch((error) => {
cleanup();
reject(error);
});
abortSignal.addEventListener("abort", onAbort, { once: true });
if (abortSignal.aborted) onAbort();
});
}
async function refineParsedToolCallInput({ toolCall, refineToolInput }) {
const refine = getOwn(refineToolInput, toolCall.toolName);
if (refine == null) return toolCall;
const refinedToolCall = {
...toolCall,
input: await refine(toolCall.input)
};
const inputSchemaInput = getToolCallInputSchemaInput(toolCall);
return inputSchemaInput == null ? refinedToolCall : setToolCallInputSchemaInput(refinedToolCall, inputSchemaInput.value);
}
async function parseProviderExecutedDynamicToolCall(toolCall) {
const parseResult = toolCall.input.trim() === "" ? {
success: true,
value: {}
} : await safeParseJSON({ text: toolCall.input });
if (parseResult.success === false) throw new InvalidToolInputError({
toolName: toolCall.toolName,
toolInput: toolCall.input,
cause: parseResult.error
});
return {
type: "tool-call",
toolCallId: toolCall.toolCallId,
toolName: toolCall.toolName,
input: parseResult.value,
providerExecuted: true,
dynamic: true,
providerMetadata: toolCall.providerMetadata
};
}
async function doParseToolCall({ toolCall, tools }) {
const toolName = toolCall.toolName;
const tool = getOwn(tools, toolName);
if (tool == null) {
if (toolCall.providerExecuted && toolCall.dynamic) return await parseProviderExecutedDynamicToolCall(toolCall);
throw new NoSuchToolError({
toolName: toolCall.toolName,
availableTools: Object.keys(tools)
});
}
const schema = asSchema(tool.inputSchema);
const parseResult = toolCall.input.trim() === "" ? await safeValidateTypes({
value: {},
schema
}) : await safeParseJSON({
text: toolCall.input,
schema
});
if (parseResult.success === false) throw new InvalidToolInputError({
toolName,
toolInput: toolCall.input,
cause: parseResult.error
});
return setToolCallInputSchemaInput(tool.type === "dynamic" ? {
type: "tool-call",
toolCallId: toolCall.toolCallId,
toolName: toolCall.toolName,
input: parseResult.value,
providerExecuted: toolCall.providerExecuted,
providerMetadata: toolCall.providerMetadata,
...tool.metadata != null ? { toolMetadata: tool.metadata } : {},
dynamic: true,
title: tool.title
} : {
type: "tool-call",
toolCallId: toolCall.toolCallId,
toolName,
input: parseResult.value,
providerExecuted: toolCall.providerExecuted,
providerMetadata: toolCall.providerMetadata,
...tool.metadata != null ? { toolMetadata: tool.metadata } : {},
title: tool.title
}, parseResult.rawValue);
}
//#endregion
//#region src/generate-text/is-output-chunk.ts
const isOutputChunkType = {
file: true,
custom: false,
source: false,
"text-start": false,
"text-end": false,
"text-delta": true,
"reasoning-start": false,
"reasoning-end": false,
"reasoning-delta": true,
"reasoning-file": true,
"tool-input-start": false,
"tool-input-end": false,
"tool-input-delta": true,
"tool-approval-request": false,
"tool-approval-response": false,
"tool-call": true,
"tool-result": false,
"tool-error": false,
"tool-output-denied": false,
"tool-execution-end": false,
"model-call-start": false,
"model-call-response-metadata": false,
"model-call-end": false,
error: false,
ping: false,
raw: false
};
function isOutputChunk(chunk) {
if (!isOutputChunkType[chunk.type]) return false;
switch (chunk.type) {
case "text-delta":
case "reasoning-delta": return chunk.text.length > 0;
case "tool-input-delta": return chunk.delta.length > 0;
case "file":
case "reasoning-file":
case "tool-call": return true;
default: return false;
}
}
//#endregion
//#region src/generate-text/validate-tool-context.ts
/**
* Validates a tool context value against the tool's optional context schema.
*
* When no context schema is defined, the original context value is returned as-is.
* Otherwise, the context is validated and normalized through the schema before
* being passed into tool execution and approval hooks.
*
* @throws {TypeValidationError} When the provided tool context does not match
* the tool's declared `contextSchema`.
*/
async function validateToolContext({ toolName, context, contextSchema }) {
if (contextSchema == null) return context;
return await validateTypes({
value: context,
schema: contextSchema,
context: {
field: "tool context",
entityName: toolName
}
});
}
//#endregion
//#region src/tool-search/tool-search.ts
const toolSearchSymbol = Symbol.for("vercel.ai.toolSearch");
const toolSearchFunctionSymbol = Symbol.for("vercel.ai.toolSearch.search");
const toolSearchMaxResultsSymbol = Symbol.for("vercel.ai.toolSearch.maxResults");
function getToolSearchMaxResults(tool) {
return tool[toolSearchMaxResultsSymbol] ?? 5;
}
function getToolSearchFunction(tool) {
return tool[toolSearchFunctionSymbol];
}
function isToolSearch(tool) {
return tool[toolSearchSymbol] === true;
}
//#endregion
//#region src/tool-search/prepare-tool-search.ts
/** Create discovery state for one generation, never for a shared tool instance. */
function createToolSearchState({ tools, toolCallers }) {
const searchTools = Object.entries(tools ?? {}).filter(([, tool]) => tool.deferLoading || isToolSearch(tool));
if (searchTools.length === 0) return (activeTools) => activeTools;
const discovered = /* @__PURE__ */ new Set();
const getCallers = (name) => getOwn(toolCallers, name) ?? ["AI_SDK_DIRECT_TOOL_CALL"];
for (const [name, tool] of searchTools) if (getCallers(name).some((name) => {
if (name === "AI_SDK_DIRECT_TOOL_CALL") return false;
const caller = experimental_getToolCaller(tools?.[name]);
return caller?.type !== "local" || caller.prepareModelMessage == null;
}) || isToolSearch(tool) && tool.deferLoading) throw new InvalidArgumentError({
parameter: "tools",
value: name,
message: `tool "${name}" must be callable directly or through code mode with toolDiscovery: 'conversation'. The search tool itself must not defer loading.`
});
return (activeTools, { toolsContext = {}, experimental_sandbox } = {}) => {
if (activeTools == null) return;
const entries = Object.entries(activeTools);
return Object.fromEntries(entries.filter(([name, tool]) => !tool.deferLoading || discovered.has(name)).map(([searchName, tool]) => {
if (!isToolSearch(tool)) return [searchName, tool];
const callers = getCallers(searchName).filter((name) => name === "AI_SDK_DIRECT_TOOL_CALL" || Object.hasOwn(activeTools, name));
const candidates = entries.filter(([name, candidate]) => candidate.deferLoading && !isToolSearch(candidate) && callers.some((caller) => getCallers(name).includes(caller)));
return [searchName, {
...tool,
execute: async ({ query }) => {
const availableTools = candidates.map(([name, candidate]) => {
const description = resolveToolDescription({
tool: candidate,
toolName: name,
toolsContext,
experimental_sandbox
});
return {
name,
...description == null ? {} : { description }
};
});
const toolsByName = new Map(availableTools.map((tool) => [tool.name, tool]));
const customSearch = getToolSearchFunction(tool);
const terms = [...new Set(tokenize(query))];
const rankedNames = customSearch ? await customSearch({
query,
tools: availableTools.map((tool) => ({ ...tool }))
}) : availableTools.map(({ name, description }) => {
const nameTerms = tokenize(name);
const descriptionTerms = tokenize(description ?? "");
return {
name,
score: terms.reduce((score, term) => score + (nameTerms.includes(term) ? 2 : 0) + (descriptionTerms.includes(term) ? 1 : 0), 0)
};
}).filter((match) => match.score > 0).sort((a, b) => b.score - a.score).map((match) => match.name);
const matches = [...new Set(rankedNames)].flatMap((name) => {
const match = toolsByName.get(name);
return match == null ? [] : [match];
}).slice(0, getToolSearchMaxResults(tool));
for (const { name } of matches) discovered.add(name);
return { tools: matches };
}
}];
}));
};
}
function tokenize(text) {
return text.replace(/([a-z\d])([A-Z])/g, "$1 $2").toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
}
//#endregion
//#region src/agent/default-stop-condition.ts
/**
* Logs the warning for the default stopWhen condition.
*/
function logDefaultStopConditionWarning({ stepCount, provider, modelId }) {
logWarnings({
provider,
model: modelId,
warnings: [{
type: "other",
message: `The tool loop stopped because it reached the default stopWhen condition, isStepCount(${stepCount}). To allow more steps, set stopWhen to isStepCount(...) with a higher limit or provide a custom stop condition. Learn more: https://ai-sdk.dev/docs/agents/loop-control`
}]
});
}
//#endregion
//#region src/generate-text/collect-tool-approvals.ts
/**
* If the last message is a tool message, this function collects all tool approvals
* from that message.
*/
function collectToolApprovals({ messages }) {
const lastMessage = messages.at(-1);
if (lastMessage?.role != "tool") return {
approvedToolApprovals: [],
deniedToolApprovals: []
};
const toolCallsByToolCallId = Object.create(null);
const toolCallOrder = Object.create(null);
let nextToolCallIndex = 0;
for (const message of messages) if (message.role === "assistant" && typeof message.content !== "string") {
const content = message.content;
for (const part of content) if (part.type === "tool-call") {
toolCallsByToolCallId[part.toolCallId] = part;
toolCallOrder[part.toolCallId] = nextToolCallIndex++;
}
}
const toolApprovalRequestsByApprovalId = Object.create(null);
for (const message of messages) if (message.role === "assistant" && typeof message.content !== "string") {
const content = message.content;
for (const part of content) if (part.type === "tool-approval-request") toolApprovalRequestsByApprovalId[part.approvalId] = part;
}
const toolResults = Object.create(null);
for (const part of lastMessage.content) if (part.type === "tool-result") toolResults[part.toolCallId] = part;
const approvedToolApprovals = [];
const deniedToolApprovals = [];
const collectedToolCallIds = /* @__PURE__ */ new Set();
const approvalResponses = lastMessage.content.filter((part) => part.type === "tool-approval-response");
for (const approvalResponse of approvalResponses) {
const approvalRequest = toolApprovalRequestsByApprovalId[approvalResponse.approvalId];
if (approvalRequest == null) throw new InvalidToolApprovalError({ approvalId: approvalResponse.approvalId });
const existingToolResult = toolResults[approvalRequest.toolCallId];
if (existingToolResult != null && (approvalResponse.approved || existingToolResult.output.type !== "execution-denied")) continue;
const toolCall = toolCallsByToolCallId[approvalRequest.toolCallId];
if (toolCall == null) throw new ToolCallNotFoundForApprovalError({
toolCallId: approvalRequest.toolCallId,
approvalId: approvalRequest.approvalId
});
if (collectedToolCallIds.has(toolCall.toolCallId)) continue;
collectedToolCallIds.add(toolCall.toolCallId);
const approval = {
approvalRequest,
approvalResponse,
toolCall,
...existingToolResult != null ? { existingToolResult } : {}
};
if (approvalResponse.approved) approvedToolApprovals.push(approval);
else deniedToolApprovals.push(approval);
}
approvedToolApprovals.sort((a, b) => toolCallOrder[a.toolCall.toolCallId] - toolCallOrder[b.toolCall.toolCallId]);
return {
approvedToolApprovals,
deniedToolApprovals
};
}
//#endregion
//#region src/util/canonical-hash.ts
const encoder$1 = new TextEncoder();
/**
* Deterministic JSON serialization: object keys are sorted so that two
* structurally-equal values always produce the same string regardless of key
* insertion order. Used as the input to content hashing.
*/
function canonicalJSON(value) {
if (value === null || value === void 0) return JSON.stringify(value);
if (typeof value !== "object") return JSON.stringify(value);
if (Array.isArray(value)) return `[${value.map((element) => element === void 0 ? "null" : canonicalJSON(element)).join(",")}]`;
return `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${canonicalJSON(value[k])}`).join(",")}}`;
}
function toBase64url(bytes) {
return convertUint8ArrayToBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
}
/**
* Canonical SHA-256 digest (base64url) of an arbitrary JSON-serializable value.
*/
async function hashCanonical(value) {
const digest = await crypto.subtle.digest("SHA-256", encoder$1.encode(canonicalJSON(value)));
return toBase64url(new Uint8Array(digest));
}
//#endregion
//#region src/generate-text/tool-approval-signature.ts
const encoder = new TextEncoder();
function fromBase64url(str) {
return convertBase64ToUint8Array(str);
}
async function importKey(secret) {
const keyData = typeof secret === "string" ? encoder.encode(secret) : secret;
return crypto.subtle.importKey("raw", keyData, {
name: "HMAC",
hash: "SHA-256"
}, false, ["sign", "verify"]);
}
function buildPayload(approvalId, toolCallId, toolName, inputDigest) {
return encoder.encode(JSON.stringify([
"ai-sdk-tool-approval-v1",
approvalId,
toolCallId,
toolName,
inputDigest
]));
}
function buildLegacyPayload(approvalId, toolCallId, toolName, inputDigest) {
return encoder.encode(`${approvalId}\n${toolCallId}\n${toolName}\n${inputDigest}`);
}
async function signToolApproval({ secret, approvalId, toolCallId, toolName, input }) {
const key = await importKey(secret);
const payload = buildPayload(approvalId, toolCallId, toolName, await hashCanonical(input));
const sig = await crypto.subtle.sign("HMAC", key, payload);
return toBase64url(new Uint8Array(sig));
}
async function verifyToolApprovalSignature({ secret, signature, approvalId, toolCallId, toolName, input }) {
const key = await importKey(secret);
const inputDigest = await hashCanonical(input);
const sigBytes = fromBase64url(signature);
const payload = buildPayload(approvalId, toolCallId, toolName, inputDigest);
if (await crypto.subtle.verify("HMAC", key, sigBytes, payload)) return true;
if (!approvalId.includes("\n") && !toolCallId.includes("\n") && !toolName.includes("\n")) {
const legacyPayload = buildLegacyPayload(approvalId, toolCallId, toolName, inputDigest);
return crypto.subtle.verify("HMAC", key, sigBytes, legacyPayload);
}
return false;
}
//#endregion
//#region src/util/is-deep-equal-data.ts
/**
* Performs a deep-equal comparison of two parsed JSON objects.
*
* @param {any} obj1 - The first object to compare.
* @param {any} obj2 - The second object to compare.
* @returns {boolean} - Returns true if the two objects are deeply equal, false otherwise.
*/
function isDeepEqualData(obj1, obj2) {
if (obj1 === obj2) return true;
if (obj1 == null || obj2 == null) return false;
if (typeof obj1 !== "object" && typeof obj2 !== "object") return obj1 === obj2;
if (obj1.constructor !== obj2.constructor) return false;
if (obj1 instanceof Date && obj2 instanceof Date) return obj1.getTime() === obj2.getTime();
if (Array.isArray(obj1)) {
if (obj1.length !== obj2.length) return false;
for (let i = 0; i < obj1.length; i++) if (!isDeepEqualData(obj1[i], obj2[i])) return false;
return true;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) {
if (!keys2.includes(key)) return false;
if (!isDeepEqualData(obj1[key], obj2[key])) return false;
}
return true;
}
//#endregion
//#region src/generate-text/resolve-tool-approval.ts
/**
* Resolves the approval state for a tool call by checking user-supplied and tool-defined
* approval settings, and normalizes the result to the object status shape.
* User-defined approval settings take precedence over tool-defined settings.
* If no approval settings are provided, the tool call does not require approval.
*/
async function resolveToolApproval({ tools, toolCall, toolApproval, messages, toolsContext, runtimeContext }) {
if (toolApproval != null && typeof toolApproval === "function") return normalizeToolApprovalStatus(await toolApproval({
toolCall,
tools,
toolsContext,
messages,
runtimeContext
}));
const toolName = toolCall.toolName;
const tool = getOwn(tools, toolName);
const input = toolCall.input;
const userDefinedToolApprovalStatus = getOwn(toolApproval, toolName);
if (userDefinedToolApprovalStatus != null) return normalizeToolApprovalStatus(typeof userDefinedToolApprovalStatus === "function" ? await userDefinedToolApprovalStatus(input, {
toolCallId: toolCall.toolCallId,
messages,
toolContext: await validateToolContext({
toolName,
context: getOwn(toolsContext, toolName),
contextSchema: tool?.contextSchema
}),
runtimeContext
}) : userDefinedToolApprovalStatus);
if (tool?.needsApproval == null) return { type: "not-applicable" };
return (typeof tool.needsApproval === "function" ? await tool.needsApproval(input, {
toolCallId: toolCall.toolCallId,
messages,
context: await validateToolContext({
toolName,
context: getOwn(toolsContext, toolName),
contextSchema: tool?.contextSchema
})
}) : tool.needsApproval) ? { type: "user-approval" } : { type: "not-applicable" };
}
function normalizeToolApprovalStatus(status) {
return status === void 0 ? { type: "not-applicable" } : typeof status === "string" ? { type: status } : status;
}
//#endregion
//#region src/generate-text/validate-tool-approvals.ts
/**
* Re-validates approved tool approvals reconstructed from client-supplied
* message history before they are executed. Checks HMAC signature (when
* configured), input schema, and approval policy.
*/
async function validateApprovedToolApprovals({ approvedToolApprovals, tools, toolApproval, messages, toolsContext, runtimeContext, toolApprovalSecret, refineToolInput }) {
const approved = [];
const denied = [];
const invalid = [];
for (const collectedApproval of approvedToolApprovals) {
const tool = getOwn(tools, collectedApproval.toolCall.toolName);
const { toolMetadata: _toolMetadata, dynamic: _dynamic, ...toolCallFromHistory } = collectedApproval.toolCall;
const approval = {
...collectedApproval,
toolCall: {
...toolCallFromHistory,
...tool?.metadata != null ? { toolMetadata: tool.metadata } : {},
...tool?.type === "dynamic" ? { dynamic: true } : {},
title: tool?.title
}
};
const { approvalRequest, toolCall } = approval;
if (toolApprovalSecret != null) {
if (approvalRequest.signature == null) throw new InvalidToolApprovalSignatureError({
approvalId: approvalRequest.approvalId,
toolCallId: toolCall.toolCallId,
reason: "missing signature"
});
if (!await verifyToolApprovalSignature({
secret: toolApprovalSecret,
signature: approvalRequest.signature,
approvalId: approvalRequest.approvalId,
toolCallId: toolCall.toolCallId,
toolName: toolCall.toolName,
input: toolCall.input
})) throw new InvalidToolApprovalSignatureError({
approvalId: approvalRequest.approvalId,
toolCallId: toolCall.toolCallId,
reason: "invalid signature"
});
}
if (isExecutableTool(tool) && tool.inputSchema != null) {
let validationError;
try {
const approvedInput = structuredClone(toolCall.input);
const hasInputSchemaInput = Object.prototype.hasOwnProperty.call(approvalRequest, "inputSchemaInput");
const validation = await safeValidateTypes({
value: structuredClone(hasInputSchemaInput ? approvalRequest.inputSchemaInput : toolCall.input),
schema: asSchema(tool.inputSchema)
});
if (!validation.success) validationError = validation.error;
else {
const revalidatedToolCall = await refineParsedToolCallInput({
toolCall: {
...toolCall,
input: validation.value
},
refineToolInput
});
if (!isDeepEqualData(structuredClone(revalidatedToolCall.input), approvedInput)) validationError = /* @__PURE__ */ new Error("Approved tool input does not match the validated schema output.");
}
} catch (error) {
validationError = error;
}
if (validationError != null) {
invalid.push({
...approval,
error: new InvalidToolInputError({
toolName: toolCall.toolName,
toolInput: JSON.stringify(toolCall.input),
cause: validationError
})
});
continue;
}
}
const approvalStatus = await resolveToolApproval({
tools,
toolApproval,
toolCall,
messages,
toolsContext,
runtimeContext
});
if (approvalStatus.type === "denied") denied.push({
...approval,
approvalResponse: {
...approval.approvalResponse,
approved: false,
reason: approvalStatus.reason ?? approval.approvalResponse.reason
}
});
else approved.push(approval);
}
return {
approvedToolApprovals: approved,
deniedToolApprovals: denied,
invalidToolApprovals: invalid
};
}
//#endregion
//#region src/generate-text/to-response-messages.ts
/**
* Converts the result of a `generateText` or `streamText` call to a list of response messages.
*/
async function toResponseMessages({ content: inputContent, tools }) {
const responseMessages = [];
const toolCallOrder = /* @__PURE__ */ new Map();
const content = [];
for (const part of inputContent) {
if (part.type === "source") continue;
if ((part.type === "tool-result" || part.type === "tool-error") && !part.providerExecuted) continue;
if (part.type === "text" && part.text.length === 0 && part.providerMetadata == null) continue;
switch (part.type) {
case "text":
content.push({
type: "text",
text: part.text,
providerOptions: part.providerMetadata
});
break;
case "custom":
content.push({
type: "custom",
kind: part.kind,
providerOptions: part.providerMetadata
});
break;
case "reasoning":
content.push({
type: "reasoning",
text: part.text,
providerOptions: part.providerMetadata
});
break;
case "file":
content.push({
type: "file",
data: part.file.base64,
mediaType: part.file.mediaType,
providerOptions: part.providerMetadata
});
break;
case "reasoning-file":
content.push({
type: "reasoning-file",
data: part.file.base64,
mediaType: part.file.mediaType,
providerOptions: part.providerMetadata
});
break;
case "tool-call":
if (!toolCallOrder.has(part.toolCallId)) toolCallOrder.set(part.toolCallId, toolCallOrder.size);
content.push({
type: "tool-call",
toolCallId: part.toolCallId,
toolName: part.toolName,
input: part.invalid && typeof part.input !== "object" ? {} : part.input,
providerExecuted: part.providerExecuted,
providerOptions: part.providerMetadata
});
break;
case "tool-result": {
const output = await createToolModelOutput({
toolCallId: part.toolCallId,
input: part.input,
tool: getOwn(tools, part.toolName),
output: part.output,
errorMode: "none"
});
content.push({
type: "tool-result",
toolCallId: part.toolCallId,
toolName: part.toolName,
output,
providerOptions: part.providerMetadata
});
break;
}
case "tool-error": {
const output = await createToolModelOutput({
toolCallId: part.toolCallId,
input: part.input,
tool: getOwn(tools, part.toolName),
output: part.error,
errorMode: "json"
});
content.push({
type: "tool-result",
toolCallId: part.toolCallId,
toolName: part.toolName,
output,
providerOptions: part.providerMetadata
});
break;
}
case "tool-approval-request":
const inputSchemaInput = getToolCallInputSchemaInput(part.toolCall);
content.push({
type: "tool-approval-request",
approvalId: part.approvalId,
toolCallId: part.toolCall.toolCallId,
...part.reason != null ? { reason: part.reason } : {},
isAutomatic: part.isAutomatic,
...part.signature != null ? { signature: part.signature } : {},
...inputSchemaInput != null && !isDeepEqualData(inputSchemaInput.value, part.toolCall.input) ? { inputSchemaInput: inputSchemaInput.value } : {}
});
}
}
if (content.length > 0) responseMessages.push({
role: "assistant",
content
});
const toolResultContent = [];
for (const part of inputContent) {
if (part.type !== "tool-approval-response" && part.type !== "tool-result" && part.type !== "tool-error") continue;
if (part.type === "tool-approval-response") {
toolResultContent.push({
type: "tool-approval-response",
approvalId: part.approvalId,
approved: part.approved,
reason: part.reason,
providerExecuted: part.providerExecuted
});
if (part.approved === false) toolResultContent.push({
type: "tool-result",
toolCallId: part.toolCall.toolCallId,
toolName: part.toolCall.toolName,
output: {
type: "execution-denied",
reason: part.reason
}
});
continue;
}
if (part.providerExecuted) continue;
const output = await createToolModelOutput({
toolCallId: part.toolCallId,
input: part.input,
tool: getOwn(tools, part.toolName),
output: part.type === "tool-result" ? part.output : part.error,
errorMode: part.type === "tool-error" ? "text" : "none"
});
toolResultContent.push({
type: "tool-result",
toolCallId: part.toolCallId,
toolName: part.toolName,
output,
...part.providerMetadata != null ? { providerOptions: part.providerMetadata } : {}
});
}
if (toolResultContent.length > 0) responseMessages.push({
role: "tool",
content: sortToolResultContentByToolCallOrder({
toolResultContent,
toolCallOrder
})
});
return responseMessages;
}
function sortToolResultContentByToolCallOrder({ toolResultContent, toolCallOrder }) {
const sortedToolResults = toolResultContent.filter((part) => part.type === "tool-result").map((part, index) => ({
part,
index
})).sort((a, b) => {
const aOrder = toolCallOrder.get(a.part.toolCallId);
const bOrder = toolCallOrder.get(b.part.toolCallId);
if (aOrder == null && bOrder == null) return a.index - b.index;
if (aOrder == null) return 1;
if (bOrder == null) return -1;
return aOrder - bOrder || a.index - b.index;
}).map(({ part }) => part);
let toolResultIndex = 0;
return toolResultContent.map((part) => part.type === "tool-result" ? sortedToolResults[toolResultIndex++] : part);
}
//#endregion
//#region src/generate-text/default-generate-text-result.ts
var DefaultGenerateTextResult = class {
constructor(options) {
this.initialResponseMessages = options.initialResponseMessages;
this.steps = options.steps;
this._output = options.output;
this.totalUsage = options.totalUsage;
}
get finalStep() {
return this.steps.at(-1);
}
get content() {
return this.steps.flatMap((step) => step.content);
}
get text() {
return this.finalStep.text;
}
get files() {
return this.steps.flatMap((step) => step.files);
}
get reasoningText() {
return this.finalStep.reasoningText;
}
get reasoning() {
return convertToReasoningOutputs(this.finalStep.reasoning);
}
get toolCalls() {
return this.steps.flatMap((step) => step.toolCalls);
}
get staticToolCalls() {
return this.steps.flatMap((step) => step.staticToolCalls);
}
get dynamicToolCalls() {
return this.steps.flatMap((step) => step.dynamicToolCalls);
}
get toolResults() {
return this.steps.flatMap((step) => step.toolResults);
}
get staticToolResults() {
return this.steps.flatMap((step) => step.staticToolResults);
}
get dynamicToolResults() {
return this.steps.flatMap((step) => step.dynamicToolResults);
}
get sources() {
return this.steps.flatMap((step) => step.sources);
}
get finishReason() {
return this.finalStep.finishReason;
}
get rawFinishReason() {
return this.finalStep.rawFinishReason;
}
get warnings() {
return this.steps.flatMap((step) => step.warnings ?? []);
}
get providerMetadata() {
return this.finalStep.providerMetadata;
}
get response() {
return this.finalStep.response;
}
get responseMessages() {
return [...this.initialResponseMessages, ...this.steps.flatMap((step) => step.response.messages)];
}
get request() {
return this.finalStep.request;
}
get usage() {
return this.totalUsage;
}
get output() {
if (this._output == null) throw new NoOutputGeneratedError();
return this._output;
}
};
//#endregion
//#region src/generate-text/resolve-generated-file-data.ts
async function resolveGeneratedFileData({ data, download: download$1, abortSignal, cache }) {
if (data.type === "data") return data.data;
const cachedData = cache?.get(data);
if (cachedData != null) return cachedData;
const downloadedData = download$1 == null ? (await download({
url: data.url,
abortSignal
})).data : await downloadGeneratedFile({
url: data.url,
download: download$1,
abortSignal
});
cache?.set(data, downloadedData);
return downloadedData;
}
async function downloadGeneratedFile({ url, download, abortSignal }) {
const [result] = await download([{
url,
isUrlSupportedByModel: false,
abortSignal
}]);
if (result == null) throw new DownloadError({
url: url.toString(),
message: `Generated file URL ${url} must be downloaded, but the download function returned null`
});
return result.data;
}
//#endregion
//#region src/generate-text/convert-language-model-content.ts
async function convertLanguageModelContent({ content, toolCalls, toolOutputs, toolApprovalRequests, toolApprovalResponses, tools, download, abortSignal, generatedFileDataCache }) {
const contentParts = [];
const toolOutputsWithApprovalResponses = [];
const toolOutputsWithoutApprovalResponses = [];
const toolCallIdsWithApprovalResponses = new Set(toolApprovalResponses.map((toolApprovalResponse) => toolApprovalResponse.toolCall.toolCallId));
for (const part of content) switch (part.type) {
case "text":
case "reasoning":
case "custom":
case "source":
contentParts.push(part);
break;
case "file":
case "reasoning-file":
contentParts.push({
type: part.type,
file: new DefaultGeneratedFile({
data: await resolveGeneratedFileData({
data: part.data,
download,
abortSignal,
cache: generatedFileDataCache
}),
mediaType: part.mediaType
}),
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
});
break;
case "tool-call": {
const toolCall = toolCalls.find((toolCall) => toolCall.toolCallId === part.toolCallId);
if (toolCall == null) throw new Error(`Tool call ${part.toolCallId} not found.`);
contentParts.push(toolCall);
break;
}
case "tool-result": {
const toolCall = toolCalls.find((toolCall) => toolCall.toolCallId === part.toolCallId);
if (toolCall == null) {
const tool = getOwn(tools, part.toolName);
if (!(tool?.type === "provider" && tool.supportsDeferredResults)) throw new Error(`Tool call ${part.toolCallId} not found.`);
if (part.isError) contentParts.push({
type: "tool-error",
toolCallId: part.toolCallId,
toolName: part.toolName,
input: void 0,
error: part.result,
providerExecuted: true,
dynamic: part.dynamic,
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
...tool?.metadata != null ? { toolMetadata: tool.metadata } : {}
});
else contentParts.push({
type: "tool-result",
toolCallId: part.toolCallId,
toolName: part.toolName,
input: void 0,
output: part.result,
providerExecuted: true,
dynamic: part.dynamic,
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
...tool?.metadata != null ? { toolMetadata: tool.metadata } : {}
});
break;
}
if (part.isError) contentParts.push({
type: "tool-error",
toolCallId: part.toolCallId,
toolName: part.toolName,
input: toolCall.input,
error: part.result,
providerExecuted: true,
dynamic: toolCall.dynamic,
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
...toolCall.toolMetadata != null ? { toolMetadata: toolCall.toolMetadata } : {}
});
else contentParts.push({
type: "tool-result",
toolCallId: part.toolCallId,
toolName: part.toolName,
input: toolCall.input,
output: part.result,
providerExecuted: true,
dynamic: toolCall.dynamic,
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
...toolCall.toolMetadata != null ? { toolMetadata: toolCall.toolMetadata } : {}
});
break;
}
case "tool-approval-request": {
const toolCall = toolCalls.find((toolCall) => toolCall.toolCallId === part.toolCallId);
if (toolCall == null) throw new ToolCallNotFoundForApprovalError({
toolCallId: part.toolCallId,
approvalId: part.approvalId
});
contentParts.push({
type: "tool-approval-request",
approvalId: part.approvalId,
toolCall
});
break;
}
}
for (const toolOutput of toolOutputs) if (toolCallIdsWithApprovalResponses.has(toolOutput.toolCallId)) toolOutputsWithApprovalResponses.push(toolOutput);
else toolOutputsWithoutApprovalResponses.push(toolOutput);
return [
...contentParts,
...toolOutputsWithoutApprovalResponses,
...toolApprovalRequests,
...toolApprovalResponses,
...toolOutputsWithApprovalResponses
];
}
//#endregion
//#region src/generate-text/calculate-tokens-per-second.ts
/**
* Calculates a token rate in tokens per second.
*
* Returns 0 when the token count is unknown, the duration is unknown or 0, or
* the computed rate cannot be represented as a finite JSON-safe number.
*/
function calculateTokensPerSecond({ tokens, durationMs }) {
const tokenRate = 1e3 * (tokens ?? 0) / (durationMs ?? 0);
return Number.isFinite(tokenRate) ? tokenRate : 0;
}
//#endregion
//#region src/generate-text/tool-call-concurrency.ts
/**
* Validates the maximum number of tool calls that may execute concurrently.
*/
function prepareToolCallConcurrency(toolCallConcurrency) {
if (toolCallConcurrency == null) return;
if (!Number.isInteger(toolCallConcurrency)) throw new InvalidArgumentError({
parameter: "toolCallConcurrency",
value: toolCallConcurrency,
message: "toolCallConcurrency must be an integer"
});
if (toolCallConcurrency < 1) throw new InvalidArgumentError({
parameter: "toolCallConcurrency",
value: toolCallConcurrency,
message: "toolCallConcurrency must be >= 1"
});
return toolCallConcurrency;
}
/**
* Maps items with an optional concurrency limit while preserving result order.
*
* Workers claim items in input order. When the signal is aborted or an
* execution rejects, no additional queued items are started.
*/
async function mapWithConcurrency({ items, concurrency, abortSignal, execute }) {
if (concurrency == null || concurrency >= items.length) return Promise.all(items.map(execute));
const results = new Array(items.length);
let nextIndex = 0;
let stopped = false;
const worker = async () => {
while (!stopped) {
abortSignal?.throwIfAborted();
const index = nextIndex++;
if (index >= items.length) return;
try {
results[index] = await execute(items[index], index);
} catch (error) {
stopped = true;
throw error;
}
}
};
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, worker));
return results;
}
//#endregion
export { DefaultGenerateTextResult, DefaultStepResult, addLanguageModelUsage, asLanguageModelUsage, calculateTokensPerSecond, collectToolApprovals, convertAsyncIteratorToReadableStream, convertLanguageModelContent, convertToLanguageModelPrompt, createAsyncIterableStream, createDefaultDownloadFunction, createNullLanguageModelUsage, createRestrictedTelemetryDispatcher, createTelemetryDispatcher, createToolModelOutput, createToolSearchState, downloadAssets, filterIncludedContext, isOutputChunk, logDefaultStopConditionWarning, logWarnings, mapToolResultOutput, mapWithConcurrency, mergeAbortSignals, mergeCallbacks, mergeObjects, parseToolCall, prepareLanguageModelCallOptions as prepareCallSettings, prepareLanguageModelCallOptions, prepareRetries, prepareToolCallConcurrency, prepareToolChoice, prepareTools, resolveLanguageModel, resolveToolApproval, setAbortTimeout, signToolApproval, standardizePrompt, toResponseMessages, validateApprovedToolApprovals, validateToolContext, verifyToolApprovalSignature };
//# sourceMappingURL=index.js.map