ai 7.0.124 → 7.0.127

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/index.d.ts +62 -24
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +101 -31
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts.map +1 -1
  7. package/dist/internal/index.js +32 -12
  8. package/dist/internal/index.js.map +1 -1
  9. package/docs/03-ai-sdk-core/16-mcp-tools.mdx +25 -0
  10. package/docs/03-ai-sdk-core/19-tool-search.mdx +40 -4
  11. package/docs/03-ai-sdk-core/35-image-generation.mdx +5 -0
  12. package/docs/03-ai-sdk-core/36-transcription.mdx +42 -38
  13. package/docs/03-ai-sdk-core/37-speech.mdx +4 -0
  14. package/docs/04-ai-sdk-ui/02-chatbot.mdx +2 -1
  15. package/docs/07-reference/01-ai-sdk-core/17-create-agent-ui-stream.mdx +7 -0
  16. package/docs/07-reference/01-ai-sdk-core/18-create-agent-ui-stream-response.mdx +7 -0
  17. package/docs/07-reference/01-ai-sdk-core/18-pipe-agent-ui-stream-to-response.mdx +7 -0
  18. package/docs/07-reference/01-ai-sdk-core/23-tool-search.mdx +62 -6
  19. package/docs/07-reference/02-ai-sdk-ui/01-use-chat.mdx +13 -0
  20. package/docs/07-reference/04-ai-sdk-workflow/01-workflow-agent.mdx +60 -3
  21. package/package.json +4 -4
  22. package/src/agent/create-agent-ui-stream-response.ts +16 -4
  23. package/src/agent/create-agent-ui-stream.ts +16 -2
  24. package/src/agent/pipe-agent-ui-stream-to-response.ts +16 -4
  25. package/src/generate-text/validate-tool-approvals.ts +5 -1
  26. package/src/tool-search/prepare-tool-search.ts +49 -29
  27. package/src/tool-search/tool-search.ts +58 -4
  28. package/src/ui/chat.ts +36 -5
  29. package/src/ui-message-stream/create-ui-message-stream.ts +29 -4
package/dist/index.js CHANGED
@@ -1102,7 +1102,7 @@ function cloneValue(value) {
1102
1102
  }
1103
1103
  //#endregion
1104
1104
  //#region src/version.ts
1105
- const VERSION = "7.0.124";
1105
+ const VERSION = "7.0.127";
1106
1106
  //#endregion
1107
1107
  //#region src/util/download/download.ts
1108
1108
  /**
@@ -2949,14 +2949,21 @@ function appendToolCallerMessages({ messages, toolCallerMessages }) {
2949
2949
  //#endregion
2950
2950
  //#region src/tool-search/tool-search.ts
2951
2951
  const toolSearchSymbol = Symbol.for("vercel.ai.toolSearch");
2952
+ const toolSearchFunctionSymbol = Symbol.for("vercel.ai.toolSearch.search");
2953
+ const toolSearchMaxResultsSymbol = Symbol.for("vercel.ai.toolSearch.maxResults");
2952
2954
  /**
2953
2955
  * Search deferred tools by name and description. The surrounding generation
2954
2956
  * binds the search registry and makes matches available on the next step.
2955
2957
  * Use directly or through code mode with `toolDiscovery: 'conversation'`.
2956
2958
  */
2957
- function toolSearch() {
2959
+ function toolSearch({ search, maxResults = 5 } = {}) {
2960
+ if (!Number.isSafeInteger(maxResults) || maxResults < 1) throw new InvalidArgumentError({
2961
+ parameter: "maxResults",
2962
+ value: maxResults,
2963
+ message: "maxResults must be a positive safe integer."
2964
+ });
2958
2965
  return Object.assign(tool$1({
2959
- description: "Search for tools by keywords in their names and descriptions. Returns up to five matching tools. Matches become available on the next model step, after this execution finishes. Wait for their tool definitions before calling the discovered tools. If no tools match, try different keywords.",
2966
+ description: `${search == null ? "Search for tools by keywords in their names and descriptions." : "Search for tools matching a query."} Returns up to ${maxResults === 5 ? "five" : maxResults} matching tools. Matches become available on the next model step, after this execution finishes. Wait for their tool definitions before calling the discovered tools. If no tools match, try different keywords.`,
2960
2967
  inputSchema: jsonSchema$1({
2961
2968
  type: "object",
2962
2969
  properties: { query: {
@@ -2988,9 +2995,17 @@ function toolSearch() {
2988
2995
  }
2989
2996
  }), {
2990
2997
  type: "function",
2991
- [toolSearchSymbol]: true
2998
+ [toolSearchSymbol]: true,
2999
+ [toolSearchFunctionSymbol]: search,
3000
+ [toolSearchMaxResultsSymbol]: maxResults
2992
3001
  });
2993
3002
  }
3003
+ function getToolSearchMaxResults(tool) {
3004
+ return tool[toolSearchMaxResultsSymbol] ?? 5;
3005
+ }
3006
+ function getToolSearchFunction(tool) {
3007
+ return tool[toolSearchFunctionSymbol];
3008
+ }
2994
3009
  function isToolSearch(tool) {
2995
3010
  return tool[toolSearchSymbol] === true;
2996
3011
  }
@@ -3020,28 +3035,39 @@ function createToolSearchState({ tools, toolCallers }) {
3020
3035
  const candidates = entries.filter(([name, candidate]) => candidate.deferLoading && !isToolSearch(candidate) && callers.some((caller) => getCallers(name).includes(caller)));
3021
3036
  return [searchName, {
3022
3037
  ...tool,
3023
- execute: ({ query }) => {
3024
- const terms = [...new Set(tokenize(query))];
3025
- const matches = candidates.map(([name, candidate]) => {
3038
+ execute: async ({ query }) => {
3039
+ const availableTools = candidates.map(([name, candidate]) => {
3026
3040
  const description = resolveToolDescription({
3027
3041
  tool: candidate,
3028
3042
  toolName: name,
3029
3043
  toolsContext,
3030
3044
  experimental_sandbox
3031
3045
  });
3046
+ return {
3047
+ name,
3048
+ ...description == null ? {} : { description }
3049
+ };
3050
+ });
3051
+ const toolsByName = new Map(availableTools.map((tool) => [tool.name, tool]));
3052
+ const customSearch = getToolSearchFunction(tool);
3053
+ const terms = [...new Set(tokenize(query))];
3054
+ const rankedNames = customSearch ? await customSearch({
3055
+ query,
3056
+ tools: availableTools.map((tool) => ({ ...tool }))
3057
+ }) : availableTools.map(({ name, description }) => {
3032
3058
  const nameTerms = tokenize(name);
3033
3059
  const descriptionTerms = tokenize(description ?? "");
3034
3060
  return {
3035
3061
  name,
3036
- description,
3037
3062
  score: terms.reduce((score, term) => score + (nameTerms.includes(term) ? 2 : 0) + (descriptionTerms.includes(term) ? 1 : 0), 0)
3038
3063
  };
3039
- }).filter((match) => match.score > 0).sort((a, b) => b.score - a.score).slice(0, 5);
3064
+ }).filter((match) => match.score > 0).sort((a, b) => b.score - a.score).map((match) => match.name);
3065
+ const matches = [...new Set(rankedNames)].flatMap((name) => {
3066
+ const match = toolsByName.get(name);
3067
+ return match == null ? [] : [match];
3068
+ }).slice(0, getToolSearchMaxResults(tool));
3040
3069
  for (const { name } of matches) discovered.add(name);
3041
- return { tools: matches.map(({ name, description }) => ({
3042
- name,
3043
- ...description == null ? {} : { description }
3044
- })) };
3070
+ return { tools: matches };
3045
3071
  }
3046
3072
  }];
3047
3073
  }));
@@ -4995,13 +5021,14 @@ async function validateApprovedToolApprovals({ approvedToolApprovals, tools, too
4995
5021
  let validationError;
4996
5022
  if (!validation.success) validationError = validation.error;
4997
5023
  else try {
4998
- if (!isDeepEqualData((await refineParsedToolCallInput({
5024
+ const revalidatedToolCall = await refineParsedToolCallInput({
4999
5025
  toolCall: {
5000
5026
  ...toolCall,
5001
5027
  input: validation.value
5002
5028
  },
5003
5029
  refineToolInput
5004
- })).input, toolCall.input)) validationError = /* @__PURE__ */ new Error("Approved tool input does not match the validated schema output.");
5030
+ });
5031
+ if (!isDeepEqualData(structuredClone(revalidatedToolCall.input), structuredClone(toolCall.input))) validationError = /* @__PURE__ */ new Error("Approved tool input does not match the validated schema output.");
5005
5032
  } catch (error) {
5006
5033
  validationError = error;
5007
5034
  }
@@ -10331,10 +10358,20 @@ var ToolLoopAgent = class {
10331
10358
  function createUIMessageStream({ execute, onError = () => "An error occurred.", originalMessages, onStepEnd, onStepFinish, onEnd, onFinish, generateId = generateId$1 }) {
10332
10359
  let controller;
10333
10360
  const ongoingStreamPromises = [];
10361
+ const activeReaders = /* @__PURE__ */ new Set();
10362
+ let isCancelled = false;
10363
+ let cancelReason;
10334
10364
  let outcome = { status: "unknown" };
10335
- const stream = new ReadableStream({ start(controllerArg) {
10336
- controller = controllerArg;
10337
- } });
10365
+ const stream = new ReadableStream({
10366
+ start(controllerArg) {
10367
+ controller = controllerArg;
10368
+ },
10369
+ async cancel(reason) {
10370
+ isCancelled = true;
10371
+ cancelReason = reason;
10372
+ await Promise.all(Array.from(activeReaders, (reader) => reader.cancel(reason)));
10373
+ }
10374
+ });
10338
10375
  function safeEnqueue(data) {
10339
10376
  try {
10340
10377
  controller.enqueue(data);
@@ -10375,12 +10412,22 @@ function createUIMessageStream({ execute, onError = () => "An error occurred.",
10375
10412
  safeEnqueue(part);
10376
10413
  },
10377
10414
  merge(streamArg) {
10415
+ if (isCancelled) {
10416
+ streamArg.cancel(cancelReason).catch(() => {});
10417
+ return;
10418
+ }
10378
10419
  ongoingStreamPromises.push((async () => {
10379
10420
  const reader = streamArg.getReader();
10380
- while (true) {
10381
- const { done, value } = await reader.read();
10382
- if (done) break;
10383
- safeEnqueue(value);
10421
+ activeReaders.add(reader);
10422
+ try {
10423
+ while (true) {
10424
+ const { done, value } = await reader.read();
10425
+ if (done) break;
10426
+ safeEnqueue(value);
10427
+ }
10428
+ } finally {
10429
+ activeReaders.delete(reader);
10430
+ reader.releaseLock();
10384
10431
  }
10385
10432
  })().catch((error) => {
10386
10433
  handleError(error);
@@ -11204,6 +11251,7 @@ async function validateUIMessagesForAgent(options) {
11204
11251
  *
11205
11252
  * @param agent - The agent to run.
11206
11253
  * @param uiMessages - The input UI messages.
11254
+ * @param convertDataPart - Optional function to convert custom data parts to text or file model message parts.
11207
11255
  * @param abortSignal - The abort signal. Optional.
11208
11256
  * @param timeout - Timeout in milliseconds. Optional.
11209
11257
  * @param experimental_sandbox - The sandbox environment that is passed through to tool execution. Optional.
@@ -11214,12 +11262,15 @@ async function validateUIMessagesForAgent(options) {
11214
11262
  *
11215
11263
  * @returns The UI message stream.
11216
11264
  */
11217
- async function createAgentUIStream({ agent, uiMessages, options, abortSignal, timeout, experimental_sandbox: sandbox, experimental_transform, onStepEnd, onStepFinish, ...uiMessageStreamOptions }) {
11265
+ async function createAgentUIStream({ agent, uiMessages, convertDataPart, options, abortSignal, timeout, experimental_sandbox: sandbox, experimental_transform, onStepEnd, onStepFinish, ...uiMessageStreamOptions }) {
11218
11266
  const validatedMessages = await validateUIMessagesForAgent({
11219
11267
  messages: uiMessages,
11220
11268
  tools: agent.tools
11221
11269
  });
11222
- const modelMessages = await convertToModelMessages(validatedMessages, { tools: agent.tools });
11270
+ const modelMessages = await convertToModelMessages(validatedMessages, {
11271
+ tools: agent.tools,
11272
+ convertDataPart
11273
+ });
11223
11274
  const result = await agent.stream({
11224
11275
  prompt: modelMessages,
11225
11276
  options,
@@ -11244,6 +11295,7 @@ async function createAgentUIStream({ agent, uiMessages, options, abortSignal, ti
11244
11295
  *
11245
11296
  * @param agent - The agent to run.
11246
11297
  * @param uiMessages - The input UI messages.
11298
+ * @param convertDataPart - Optional function to convert custom data parts to text or file model message parts.
11247
11299
  * @param abortSignal - Abort signal. Optional.
11248
11300
  * @param timeout - Timeout in milliseconds. Optional.
11249
11301
  * @param experimental_sandbox - The sandbox environment that is passed through to tool execution. Optional.
@@ -11277,6 +11329,7 @@ async function createAgentUIStreamResponse({ headers, status, statusText, keepAl
11277
11329
  * @param response - The Node.js ServerResponse object to pipe to.
11278
11330
  * @param agent - The agent to run.
11279
11331
  * @param uiMessages - The input UI messages.
11332
+ * @param convertDataPart - Optional function to convert custom data parts to text or file model message parts.
11280
11333
  * @param abortSignal - Abort signal. Optional.
11281
11334
  * @param timeout - Timeout in milliseconds. Optional.
11282
11335
  * @param experimental_sandbox - The sandbox environment that is passed through to tool execution. Optional.
@@ -19514,15 +19567,32 @@ var AbstractChat = class {
19514
19567
  }
19515
19568
  });
19516
19569
  });
19517
- this.addToolOutput = async ({ state = "output-available", toolCallId, output, errorText, options }) => this.jobExecutor.run(async () => {
19570
+ this.addToolOutput = async ({ state, toolCallId, output, errorText, options }) => this.jobExecutor.run(async () => {
19518
19571
  const messages = this.state.messages;
19519
19572
  const lastMessage = messages[messages.length - 1];
19520
- const updatePart = (part) => isToolUIPart(part) && part.toolCallId === toolCallId ? {
19521
- ...part,
19522
- state,
19523
- output,
19524
- errorText
19525
- } : part;
19573
+ const updatePart = (part) => {
19574
+ if (!isToolUIPart(part) || part.toolCallId !== toolCallId) return part;
19575
+ const { approval: existingApproval, ...toolPart } = part;
19576
+ const approval = existingApproval?.approved === true ? { approval: {
19577
+ ...existingApproval,
19578
+ approved: existingApproval.approved
19579
+ } } : {};
19580
+ return state === "output-error" ? {
19581
+ ...toolPart,
19582
+ ...approval,
19583
+ state,
19584
+ input: part.input,
19585
+ output: void 0,
19586
+ errorText
19587
+ } : {
19588
+ ...toolPart,
19589
+ ...approval,
19590
+ state: "output-available",
19591
+ input: part.input,
19592
+ output,
19593
+ errorText: void 0
19594
+ };
19595
+ };
19526
19596
  this.state.replaceMessage(messages.length - 1, {
19527
19597
  ...lastMessage,
19528
19598
  parts: lastMessage.parts.map(updatePart)