ai 7.0.126 → 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.
- package/CHANGELOG.md +11 -0
- package/dist/index.d.ts +21 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +69 -22
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +32 -12
- package/dist/internal/index.js.map +1 -1
- package/docs/03-ai-sdk-core/19-tool-search.mdx +40 -4
- package/docs/03-ai-sdk-core/35-image-generation.mdx +5 -0
- package/docs/03-ai-sdk-core/36-transcription.mdx +42 -39
- package/docs/03-ai-sdk-core/37-speech.mdx +2 -0
- package/docs/04-ai-sdk-ui/02-chatbot.mdx +2 -1
- package/docs/07-reference/01-ai-sdk-core/23-tool-search.mdx +62 -6
- package/docs/07-reference/02-ai-sdk-ui/01-use-chat.mdx +13 -0
- package/package.json +3 -3
- package/src/generate-text/validate-tool-approvals.ts +5 -1
- package/src/tool-search/prepare-tool-search.ts +49 -29
- package/src/tool-search/tool-search.ts +58 -4
- package/src/ui-message-stream/create-ui-message-stream.ts +29 -4
|
@@ -11,8 +11,9 @@ search matches their names and descriptions and makes them available on the
|
|
|
11
11
|
**next model step**.
|
|
12
12
|
|
|
13
13
|
Use it with `generateText`, `streamText`, `ToolLoopAgent`, or `WorkflowAgent` from
|
|
14
|
-
`@ai-sdk/workflow`. The
|
|
15
|
-
|
|
14
|
+
`@ai-sdk/workflow`. The model supplies a search query; the factory optionally
|
|
15
|
+
accepts an application-defined `search` callback and a `maxResults` limit.
|
|
16
|
+
`WorkflowAgent` supports direct tool calling; the other APIs also support
|
|
16
17
|
cache-preserving code mode.
|
|
17
18
|
|
|
18
19
|
## Direct Tool Calling
|
|
@@ -73,10 +74,45 @@ The model first runs `tools.search({ query: 'weather forecast' })`. On the next
|
|
|
73
74
|
step, it receives the updated capability catalog and can call
|
|
74
75
|
`tools.weather({ city: 'Bangalore' })`.
|
|
75
76
|
|
|
76
|
-
In both modes, search loads up to five matches, respects `activeTools`
|
|
77
|
-
routing, and keeps discovered tools available for the rest of the
|
|
77
|
+
In both modes, search loads up to five matches by default, respects `activeTools`
|
|
78
|
+
and caller routing, and keeps discovered tools available for the rest of the
|
|
79
|
+
generation.
|
|
78
80
|
MCP client tools work too: add `deferLoading: true` to the tools returned by
|
|
79
81
|
`client.tools()`.
|
|
80
82
|
|
|
83
|
+
Set `maxResults` to change the number of matching tools loaded per search:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
const search = toolSearch({ maxResults: 10 });
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`maxResults` must be a positive safe integer. A larger limit can load related
|
|
90
|
+
tools in one search, but also adds more tool definitions to the model context.
|
|
91
|
+
It does not change keyword matching or ranking.
|
|
92
|
+
|
|
93
|
+
## Custom Search
|
|
94
|
+
|
|
95
|
+
Provide a `search` callback when your application needs different matching or
|
|
96
|
+
ranking. It receives the query and all eligible deferred tool names and resolved
|
|
97
|
+
descriptions, and returns ranked names. It can be synchronous or asynchronous.
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
const search = toolSearch({
|
|
101
|
+
search: ({ query, tools }) => {
|
|
102
|
+
// Discover related Gmail actions together, in the application's preferred order.
|
|
103
|
+
const preferred = /\bgmail\b/i.test(query)
|
|
104
|
+
? ['gmail_list_messages', 'gmail_get_message']
|
|
105
|
+
: [];
|
|
106
|
+
return preferred.filter(name => tools.some(tool => tool.name === name));
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The SDK filters unknown names and duplicates before keeping the first `maxResults`
|
|
112
|
+
valid results (five by default). Tools excluded by `activeTools` or caller routing
|
|
113
|
+
remain unavailable.
|
|
114
|
+
Discovery still takes effect on the next model step. Without a callback, search
|
|
115
|
+
keeps its default keyword matching and ranking.
|
|
116
|
+
|
|
81
117
|
See the [`toolSearch()` reference](/docs/reference/ai-sdk-core/tool-search) for
|
|
82
118
|
input, output, and matching details.
|
|
@@ -400,6 +400,11 @@ for (const file of result.files) {
|
|
|
400
400
|
| [Together.ai](/providers/ai-sdk-providers/togetherai#image-models) | `black-forest-labs/FLUX.1.1-pro` | 512x512, 768x768, 1024x1024 |
|
|
401
401
|
| [Together.ai](/providers/ai-sdk-providers/togetherai#image-models) | `black-forest-labs/FLUX.1-pro` | 512x512, 768x768, 1024x1024 |
|
|
402
402
|
| [Together.ai](/providers/ai-sdk-providers/togetherai#image-models) | `black-forest-labs/FLUX.1-schnell-Free` | 512x512, 768x768, 1024x1024 |
|
|
403
|
+
| [Black Forest Labs](/providers/ai-sdk-providers/black-forest-labs#image-models) | `flux-2-pro` | From 3:7 (portrait) to 7:3 (landscape) |
|
|
404
|
+
| [Black Forest Labs](/providers/ai-sdk-providers/black-forest-labs#image-models) | `flux-2-max` | From 3:7 (portrait) to 7:3 (landscape) |
|
|
405
|
+
| [Black Forest Labs](/providers/ai-sdk-providers/black-forest-labs#image-models) | `flux-2-flex` | From 3:7 (portrait) to 7:3 (landscape) |
|
|
406
|
+
| [Black Forest Labs](/providers/ai-sdk-providers/black-forest-labs#image-models) | `flux-2-klein-9b` | From 3:7 (portrait) to 7:3 (landscape) |
|
|
407
|
+
| [Black Forest Labs](/providers/ai-sdk-providers/black-forest-labs#image-models) | `flux-2-klein-4b` | From 3:7 (portrait) to 7:3 (landscape) |
|
|
403
408
|
| [Black Forest Labs](/providers/ai-sdk-providers/black-forest-labs#image-models) | `flux-kontext-pro` | From 3:7 (portrait) to 7:3 (landscape) |
|
|
404
409
|
| [Black Forest Labs](/providers/ai-sdk-providers/black-forest-labs#image-models) | `flux-kontext-max` | From 3:7 (portrait) to 7:3 (landscape) |
|
|
405
410
|
| [Black Forest Labs](/providers/ai-sdk-providers/black-forest-labs#image-models) | `flux-pro-1.1-ultra` | From 3:7 (portrait) to 7:3 (landscape) |
|
|
@@ -138,7 +138,9 @@ The `audio` stream must contain raw audio chunks. `Uint8Array` chunks are raw by
|
|
|
138
138
|
OpenAI streaming transcription uses `openai.transcription('gpt-realtime-whisper')`.
|
|
139
139
|
Cartesia uses `cartesia.transcription('ink-2')` for streaming-only Ink 2
|
|
140
140
|
transcription. ElevenLabs uses
|
|
141
|
-
`elevenLabs.transcription('scribe_v2_realtime')` for Scribe v2 Realtime.
|
|
141
|
+
`elevenLabs.transcription('scribe_v2_realtime')` for Scribe v2 Realtime. Azure
|
|
142
|
+
uses `azure.transcription('mai-transcribe-2-streaming')` for streaming-only
|
|
143
|
+
MAI-Transcribe-2-Streaming. xAI
|
|
142
144
|
uses the same `xai.transcription()` model for request/response and streaming
|
|
143
145
|
transcription; `experimental_streamTranscribe` selects the provider's
|
|
144
146
|
WebSocket STT transport.
|
|
@@ -329,43 +331,44 @@ try {
|
|
|
329
331
|
|
|
330
332
|
## Transcription Models
|
|
331
333
|
|
|
332
|
-
| Provider | Model
|
|
333
|
-
| ----------------------------------------------------------------------------------- |
|
|
334
|
-
| [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `whisper-1`
|
|
335
|
-
| [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `gpt-4o-transcribe`
|
|
336
|
-
| [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `gpt-4o-mini-transcribe`
|
|
337
|
-
| [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `gpt-4o-transcribe-diarize`
|
|
338
|
-
| [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#transcription-models) | `scribe_v1`
|
|
339
|
-
| [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#transcription-models) | `scribe_v1_experimental`
|
|
340
|
-
| [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#transcription-models) | `scribe_v2`
|
|
341
|
-
| [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#streaming-transcription-models) | `scribe_v2_realtime`
|
|
342
|
-
| [Groq](/providers/ai-sdk-providers/groq#transcription-models) | `whisper-large-v3-turbo`
|
|
343
|
-
| [Groq](/providers/ai-sdk-providers/groq#transcription-models) | `whisper-large-v3`
|
|
344
|
-
| [Mistral](/providers/ai-sdk-providers/mistral#transcription-models) | `voxtral-mini-latest`
|
|
345
|
-
| [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `whisper-1`
|
|
346
|
-
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-transcribe) | `mai-transcribe-2`
|
|
347
|
-
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-transcribe) | `mai-transcribe-1.5`
|
|
348
|
-
| [Azure
|
|
349
|
-
| [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `gpt-4o-
|
|
350
|
-
| [
|
|
351
|
-
| [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `
|
|
352
|
-
| [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `
|
|
353
|
-
| [
|
|
354
|
-
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `
|
|
355
|
-
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `
|
|
356
|
-
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova
|
|
357
|
-
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova-
|
|
358
|
-
| [
|
|
359
|
-
| [
|
|
360
|
-
| [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `universal-3-pro`
|
|
361
|
-
| [
|
|
362
|
-
| [Fal](/providers/ai-sdk-providers/fal#transcription-models) | `
|
|
363
|
-
| [
|
|
364
|
-
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `
|
|
365
|
-
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `
|
|
366
|
-
| [
|
|
367
|
-
| [
|
|
368
|
-
| [Cartesia](/providers/ai-sdk-providers/cartesia#
|
|
369
|
-
| [
|
|
334
|
+
| Provider | Model |
|
|
335
|
+
| ----------------------------------------------------------------------------------- | ---------------------------- |
|
|
336
|
+
| [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `whisper-1` |
|
|
337
|
+
| [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `gpt-4o-transcribe` |
|
|
338
|
+
| [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `gpt-4o-mini-transcribe` |
|
|
339
|
+
| [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `gpt-4o-transcribe-diarize` |
|
|
340
|
+
| [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#transcription-models) | `scribe_v1` |
|
|
341
|
+
| [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#transcription-models) | `scribe_v1_experimental` |
|
|
342
|
+
| [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#transcription-models) | `scribe_v2` |
|
|
343
|
+
| [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#streaming-transcription-models) | `scribe_v2_realtime` |
|
|
344
|
+
| [Groq](/providers/ai-sdk-providers/groq#transcription-models) | `whisper-large-v3-turbo` |
|
|
345
|
+
| [Groq](/providers/ai-sdk-providers/groq#transcription-models) | `whisper-large-v3` |
|
|
346
|
+
| [Mistral](/providers/ai-sdk-providers/mistral#transcription-models) | `voxtral-mini-latest` |
|
|
347
|
+
| [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `whisper-1` |
|
|
348
|
+
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-transcribe) | `mai-transcribe-2` |
|
|
349
|
+
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-transcribe) | `mai-transcribe-1.5` |
|
|
350
|
+
| [Azure MAI](/providers/ai-sdk-providers/azure#mai-transcribe-2-streaming) | `mai-transcribe-2-streaming` |
|
|
351
|
+
| [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `gpt-4o-transcribe` |
|
|
352
|
+
| [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `gpt-4o-mini-transcribe` |
|
|
353
|
+
| [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `machine` |
|
|
354
|
+
| [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `low_cost` |
|
|
355
|
+
| [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `fusion` |
|
|
356
|
+
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `base` (+ variants) |
|
|
357
|
+
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `enhanced` (+ variants) |
|
|
358
|
+
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova` (+ variants) |
|
|
359
|
+
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova-2` (+ variants) |
|
|
360
|
+
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova-3` (+ variants) |
|
|
361
|
+
| [Gladia](/providers/ai-sdk-providers/gladia#transcription-models) | `default` |
|
|
362
|
+
| [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `universal-3-5-pro` |
|
|
363
|
+
| [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `universal-3-pro` |
|
|
364
|
+
| [Fal](/providers/ai-sdk-providers/fal#transcription-models) | `whisper` |
|
|
365
|
+
| [Fal](/providers/ai-sdk-providers/fal#transcription-models) | `wizper` |
|
|
366
|
+
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `chirp_2` |
|
|
367
|
+
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `chirp_3` |
|
|
368
|
+
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `telephony` |
|
|
369
|
+
| [xAI](/providers/ai-sdk-providers/xai#transcription-models) | `default` |
|
|
370
|
+
| [Cartesia](/providers/ai-sdk-providers/cartesia#transcription-models) | `ink-whisper` |
|
|
371
|
+
| [Cartesia](/providers/ai-sdk-providers/cartesia#streaming-transcription-models) | `ink-2` |
|
|
372
|
+
| [Fish Audio](/providers/ai-sdk-providers/fish-audio#transcription-models) | `transcribe-1` |
|
|
370
373
|
|
|
371
374
|
Above are a small subset of the transcription models supported by the AI SDK providers. For more, see the respective provider documentation.
|
|
@@ -158,6 +158,8 @@ try {
|
|
|
158
158
|
| [OpenAI](/providers/ai-sdk-providers/openai#speech-models) | `tts-1-hd` |
|
|
159
159
|
| [OpenAI](/providers/ai-sdk-providers/openai#speech-models) | `gpt-4o-mini-tts` |
|
|
160
160
|
| [Mistral](/providers/ai-sdk-providers/mistral#speech-models) | `voxtral-mini-tts-2603` |
|
|
161
|
+
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-voice) | `mai-voice-2.1-flash` |
|
|
162
|
+
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-voice) | `mai-voice-2.1` |
|
|
161
163
|
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-voice) | `mai-voice-2-flash` |
|
|
162
164
|
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-voice) | `mai-voice-2` |
|
|
163
165
|
| [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#speech-models) | `eleven_v3` |
|
|
@@ -323,7 +323,8 @@ When the user clicks the "Regenerate" button, the AI provider will regenerate th
|
|
|
323
323
|
|
|
324
324
|
### Throttling UI Updates
|
|
325
325
|
|
|
326
|
-
By default,
|
|
326
|
+
By default, `useChat` publishes message updates as each chunk is processed.
|
|
327
|
+
React may batch multiple updates into a single render.
|
|
327
328
|
React and Vue applications can throttle reactive message updates with the `throttle` option.
|
|
328
329
|
Stream processing and event callbacks remain immediate, and the latest messages are published before the chat enters a terminal `ready` or `error` status.
|
|
329
330
|
|
|
@@ -5,8 +5,8 @@ description: Search deferred tools and load their definitions on demand for dire
|
|
|
5
5
|
|
|
6
6
|
# `toolSearch()`
|
|
7
7
|
|
|
8
|
-
Creates a tool that searches the surrounding generation's deferred tools
|
|
9
|
-
|
|
8
|
+
Creates a tool that searches the surrounding generation's deferred tools, using
|
|
9
|
+
keyword matching by default or a custom search callback. Use it with `generateText`,
|
|
10
10
|
`streamText`, `ToolLoopAgent`, or `WorkflowAgent` from `@ai-sdk/workflow`.
|
|
11
11
|
`WorkflowAgent` supports direct tool calling. The other APIs also support code
|
|
12
12
|
mode configured with `toolDiscovery: 'conversation'`.
|
|
@@ -20,6 +20,60 @@ const search = toolSearch();
|
|
|
20
20
|
See the [Tool Search guide](/docs/ai-sdk-core/tool-search) for direct-calling and
|
|
21
21
|
code mode examples, including how code mode preserves the tool-definition cache.
|
|
22
22
|
|
|
23
|
+
## Options
|
|
24
|
+
|
|
25
|
+
Pass an optional configuration object to control the result limit:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
const search = toolSearch({ maxResults: 10 });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`maxResults` is the maximum number of matching tools returned and discovered per
|
|
32
|
+
search. It defaults to `5` and must be a positive safe integer. Invalid values
|
|
33
|
+
throw `InvalidArgumentError` when creating the search tool. The model-facing
|
|
34
|
+
description reflects the configured limit.
|
|
35
|
+
|
|
36
|
+
The limit applies to both default keyword search and custom search callbacks,
|
|
37
|
+
without changing their matching or ranking. Discovered tools accumulate across
|
|
38
|
+
searches, so this is not a limit on the total number of tools loaded during a
|
|
39
|
+
generation.
|
|
40
|
+
|
|
41
|
+
Pass a `search` callback to select and rank tools using your application's search
|
|
42
|
+
logic. The callback can return synchronously or asynchronously:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const search = toolSearch({
|
|
46
|
+
search: ({ query, tools }) => {
|
|
47
|
+
const preferred = /\bgmail\b/i.test(query)
|
|
48
|
+
? ['gmail_list_messages', 'gmail_get_message']
|
|
49
|
+
: [];
|
|
50
|
+
return preferred.filter(name => tools.some(tool => tool.name === name));
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The callback has this signature:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
search?: (options: {
|
|
59
|
+
query: string;
|
|
60
|
+
tools: Array<{ name: string; description?: string }>;
|
|
61
|
+
}) => string[] | PromiseLike<string[]>;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`tools` contains all eligible deferred tools, before keyword matching or ranking.
|
|
65
|
+
Eligibility respects `activeTools` and caller routing. Descriptions are resolved
|
|
66
|
+
using the current tool context and sandbox; schemas are not included.
|
|
67
|
+
|
|
68
|
+
Return tool names in ranked order. The SDK ignores unknown or ineligible names,
|
|
69
|
+
removes duplicates while preserving order, and keeps the first `maxResults` valid
|
|
70
|
+
names (five by default). Returned descriptions come from the SDK's candidate
|
|
71
|
+
catalog. An empty array
|
|
72
|
+
discovers no tools. Callback errors follow normal tool-execution error handling;
|
|
73
|
+
the SDK does not fall back to keyword search.
|
|
74
|
+
|
|
75
|
+
Without a callback, `toolSearch()` uses the default keyword search described below.
|
|
76
|
+
|
|
23
77
|
## Model Input
|
|
24
78
|
|
|
25
79
|
The model supplies the following input to the search tool:
|
|
@@ -28,11 +82,12 @@ The model supplies the following input to the search tool:
|
|
|
28
82
|
{ "query": "weather forecast" }
|
|
29
83
|
```
|
|
30
84
|
|
|
31
|
-
`query` is a required, nonempty string
|
|
85
|
+
`query` is a required, nonempty string. By default, search is local and
|
|
32
86
|
case-insensitive, matching words in tool names and descriptions. Camel-case names
|
|
33
87
|
are split into words. Name matches rank above description matches; equal scores
|
|
34
88
|
preserve registration order. Function descriptions are resolved with the current
|
|
35
|
-
tool context and sandbox.
|
|
89
|
+
tool context and sandbox. The default search uses no embedding service or
|
|
90
|
+
additional model call. A custom callback can use either.
|
|
36
91
|
|
|
37
92
|
## Output
|
|
38
93
|
|
|
@@ -44,8 +99,9 @@ tool context and sandbox. No embedding service or additional model call is used.
|
|
|
44
99
|
}
|
|
45
100
|
```
|
|
46
101
|
|
|
47
|
-
Results contain at most
|
|
48
|
-
descriptions. They do not include schemas. No matches returns
|
|
102
|
+
Results contain at most `maxResults` matching tools (five by default), with their
|
|
103
|
+
names and optional descriptions. They do not include schemas. No matches returns
|
|
104
|
+
`{ tools: [] }`.
|
|
49
105
|
Every returned match is queued for discovery. Newly discovered tools can only be
|
|
50
106
|
called on the next model step, after their definitions have been provided. A
|
|
51
107
|
parallel call in the same response as the search cannot use a newly discovered
|
|
@@ -486,6 +486,19 @@ Allows you to easily create a conversational user interface for your chatbot app
|
|
|
486
486
|
]}
|
|
487
487
|
/>
|
|
488
488
|
|
|
489
|
+
## React rendering and shared Chat instances
|
|
490
|
+
|
|
491
|
+
In React, `messages`, `status`, and `error` describe the snapshot for the current render.
|
|
492
|
+
The underlying `Chat` updates as stream chunks are processed. React may batch updates,
|
|
493
|
+
and `throttle` can delay message rendering without delaying stream processing or callbacks.
|
|
494
|
+
Status and error updates include the latest messages, so a completed or failed response
|
|
495
|
+
is rendered with its final message snapshot.
|
|
496
|
+
|
|
497
|
+
Each `useChat({ chat })` call subscribes independently. Consumers sharing a `Chat`
|
|
498
|
+
can display different message snapshots when they use different throttle intervals.
|
|
499
|
+
A functional `setMessages` updater receives the Chat's latest messages, which may be
|
|
500
|
+
newer than the messages in the current render.
|
|
501
|
+
|
|
489
502
|
## Learn more
|
|
490
503
|
|
|
491
504
|
- [Chatbot](/docs/ai-sdk-ui/chatbot)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.127",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "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.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ai-sdk/gateway": "4.0.
|
|
45
|
+
"@ai-sdk/gateway": "4.0.103",
|
|
46
46
|
"@ai-sdk/provider": "4.0.21",
|
|
47
47
|
"@ai-sdk/provider-utils": "5.0.53"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@ai-sdk/amazon-bedrock": "5.0.
|
|
50
|
+
"@ai-sdk/amazon-bedrock": "5.0.105",
|
|
51
51
|
"@ai-sdk/deepseek": "3.0.58",
|
|
52
52
|
"@ai-sdk/google": "4.0.87",
|
|
53
53
|
"@ai-sdk/groq": "4.0.54",
|
|
@@ -117,9 +117,13 @@ export async function validateApprovedToolApprovals<
|
|
|
117
117
|
refineToolInput,
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
+
// Clone both inputs into the current realm before comparing them.
|
|
121
|
+
const revalidatedInput = structuredClone(revalidatedToolCall.input);
|
|
122
|
+
const approvedInput = structuredClone(toolCall.input);
|
|
123
|
+
|
|
120
124
|
// Revalidation must never change the operation that was approved,
|
|
121
125
|
// including when older or projected history omits the schema input.
|
|
122
|
-
if (!isDeepEqualData(
|
|
126
|
+
if (!isDeepEqualData(revalidatedInput, approvedInput)) {
|
|
123
127
|
validationError = new Error(
|
|
124
128
|
'Approved tool input does not match the validated schema output.',
|
|
125
129
|
);
|
|
@@ -11,7 +11,11 @@ import {
|
|
|
11
11
|
} from '../generate-text/tool-caller-configuration';
|
|
12
12
|
import { resolveToolDescription } from '../prompt/prepare-tools';
|
|
13
13
|
import { getOwn } from '../util/get-own';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
getToolSearchFunction,
|
|
16
|
+
getToolSearchMaxResults,
|
|
17
|
+
isToolSearch,
|
|
18
|
+
} from './tool-search';
|
|
15
19
|
|
|
16
20
|
/** Create discovery state for one generation, never for a shared tool instance. */
|
|
17
21
|
export function createToolSearchState({
|
|
@@ -90,41 +94,57 @@ export function createToolSearchState({
|
|
|
90
94
|
searchName,
|
|
91
95
|
{
|
|
92
96
|
...tool,
|
|
93
|
-
execute: ({ query }: { query: string }) => {
|
|
97
|
+
execute: async ({ query }: { query: string }) => {
|
|
98
|
+
const availableTools = candidates.map(([name, candidate]) => {
|
|
99
|
+
const description = resolveToolDescription({
|
|
100
|
+
tool: candidate,
|
|
101
|
+
toolName: name,
|
|
102
|
+
toolsContext,
|
|
103
|
+
experimental_sandbox,
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
name,
|
|
107
|
+
...(description == null ? {} : { description }),
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
const toolsByName = new Map(
|
|
111
|
+
availableTools.map(tool => [tool.name, tool]),
|
|
112
|
+
);
|
|
113
|
+
const customSearch = getToolSearchFunction(tool);
|
|
94
114
|
const terms = [...new Set(tokenize(query))];
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
const rankedNames = customSearch
|
|
116
|
+
? await customSearch({
|
|
117
|
+
query,
|
|
118
|
+
tools: availableTools.map(tool => ({ ...tool })),
|
|
119
|
+
})
|
|
120
|
+
: availableTools
|
|
121
|
+
.map(({ name, description }) => {
|
|
122
|
+
const nameTerms = tokenize(name);
|
|
123
|
+
const descriptionTerms = tokenize(description ?? '');
|
|
124
|
+
const score = terms.reduce(
|
|
125
|
+
(score, term) =>
|
|
126
|
+
score +
|
|
127
|
+
(nameTerms.includes(term) ? 2 : 0) +
|
|
128
|
+
(descriptionTerms.includes(term) ? 1 : 0),
|
|
129
|
+
0,
|
|
130
|
+
);
|
|
131
|
+
return { name, score };
|
|
132
|
+
})
|
|
133
|
+
.filter(match => match.score > 0)
|
|
134
|
+
.sort((a, b) => b.score - a.score)
|
|
135
|
+
.map(match => match.name);
|
|
136
|
+
const matches = [...new Set(rankedNames)]
|
|
137
|
+
.flatMap(name => {
|
|
138
|
+
const match = toolsByName.get(name);
|
|
139
|
+
return match == null ? [] : [match];
|
|
113
140
|
})
|
|
114
|
-
.
|
|
115
|
-
.sort((a, b) => b.score - a.score)
|
|
116
|
-
.slice(0, 5);
|
|
141
|
+
.slice(0, getToolSearchMaxResults(tool));
|
|
117
142
|
|
|
118
143
|
for (const { name } of matches) {
|
|
119
144
|
discovered.add(name);
|
|
120
145
|
}
|
|
121
146
|
|
|
122
|
-
return {
|
|
123
|
-
tools: matches.map(({ name, description }) => ({
|
|
124
|
-
name,
|
|
125
|
-
...(description == null ? {} : { description }),
|
|
126
|
-
})),
|
|
127
|
-
};
|
|
147
|
+
return { tools: matches };
|
|
128
148
|
},
|
|
129
149
|
},
|
|
130
150
|
];
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { jsonSchema, tool, type Tool } from '@ai-sdk/provider-utils';
|
|
2
|
+
import { InvalidArgumentError } from '../error/invalid-argument-error';
|
|
2
3
|
|
|
3
4
|
const toolSearchSymbol = Symbol.for('vercel.ai.toolSearch');
|
|
5
|
+
const toolSearchFunctionSymbol = Symbol.for('vercel.ai.toolSearch.search');
|
|
6
|
+
const toolSearchMaxResultsSymbol = Symbol.for(
|
|
7
|
+
'vercel.ai.toolSearch.maxResults',
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
type ToolSearchFunction = (options: {
|
|
11
|
+
query: string;
|
|
12
|
+
tools: Array<{ name: string; description?: string }>;
|
|
13
|
+
}) => string[] | PromiseLike<string[]>;
|
|
4
14
|
|
|
5
15
|
type ToolSearchInput = { query: string };
|
|
6
16
|
type ToolSearchOutput = {
|
|
@@ -12,13 +22,36 @@ type ToolSearchOutput = {
|
|
|
12
22
|
* binds the search registry and makes matches available on the next step.
|
|
13
23
|
* Use directly or through code mode with `toolDiscovery: 'conversation'`.
|
|
14
24
|
*/
|
|
15
|
-
export function toolSearch(
|
|
25
|
+
export function toolSearch({
|
|
26
|
+
search,
|
|
27
|
+
maxResults = 5,
|
|
28
|
+
}: {
|
|
29
|
+
/**
|
|
30
|
+
* Search eligible deferred tools and return their names in ranked order.
|
|
31
|
+
* Supports synchronous and asynchronous callbacks. Unknown names and
|
|
32
|
+
* duplicates are ignored before applying the result limit.
|
|
33
|
+
*/
|
|
34
|
+
search?: ToolSearchFunction;
|
|
35
|
+
/**
|
|
36
|
+
* Maximum number of matching tools returned per search.
|
|
37
|
+
*
|
|
38
|
+
* @default 5
|
|
39
|
+
*/
|
|
40
|
+
maxResults?: number;
|
|
41
|
+
} = {}): Tool<ToolSearchInput, ToolSearchOutput> & {
|
|
16
42
|
type: 'function';
|
|
17
43
|
} {
|
|
44
|
+
if (!Number.isSafeInteger(maxResults) || maxResults < 1) {
|
|
45
|
+
throw new InvalidArgumentError({
|
|
46
|
+
parameter: 'maxResults',
|
|
47
|
+
value: maxResults,
|
|
48
|
+
message: 'maxResults must be a positive safe integer.',
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
18
52
|
return Object.assign(
|
|
19
53
|
tool({
|
|
20
|
-
description:
|
|
21
|
-
'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.',
|
|
54
|
+
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.`,
|
|
22
55
|
inputSchema: jsonSchema<ToolSearchInput>({
|
|
23
56
|
type: 'object',
|
|
24
57
|
properties: { query: { type: 'string', minLength: 1 } },
|
|
@@ -48,10 +81,31 @@ export function toolSearch(): Tool<ToolSearchInput, ToolSearchOutput> & {
|
|
|
48
81
|
throw new Error('toolSearch must be bound by an AI SDK generation.');
|
|
49
82
|
},
|
|
50
83
|
}),
|
|
51
|
-
{
|
|
84
|
+
{
|
|
85
|
+
type: 'function' as const,
|
|
86
|
+
[toolSearchSymbol]: true,
|
|
87
|
+
[toolSearchFunctionSymbol]: search,
|
|
88
|
+
[toolSearchMaxResultsSymbol]: maxResults,
|
|
89
|
+
},
|
|
52
90
|
);
|
|
53
91
|
}
|
|
54
92
|
|
|
93
|
+
export function getToolSearchMaxResults(tool: Tool): number {
|
|
94
|
+
return (
|
|
95
|
+
(tool as Tool & { [toolSearchMaxResultsSymbol]?: number })[
|
|
96
|
+
toolSearchMaxResultsSymbol
|
|
97
|
+
] ?? 5
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function getToolSearchFunction(
|
|
102
|
+
tool: Tool,
|
|
103
|
+
): ToolSearchFunction | undefined {
|
|
104
|
+
return (tool as Tool & { [toolSearchFunctionSymbol]?: ToolSearchFunction })[
|
|
105
|
+
toolSearchFunctionSymbol
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
|
|
55
109
|
export function isToolSearch(tool: Tool): boolean {
|
|
56
110
|
return (
|
|
57
111
|
(tool as Tool & { [toolSearchSymbol]?: boolean })[toolSearchSymbol] === true
|
|
@@ -73,12 +73,25 @@ export function createUIMessageStream<UI_MESSAGE extends UIMessage>({
|
|
|
73
73
|
>;
|
|
74
74
|
|
|
75
75
|
const ongoingStreamPromises: Promise<void>[] = [];
|
|
76
|
+
const activeReaders = new Set<
|
|
77
|
+
ReadableStreamDefaultReader<InferUIMessageChunk<UI_MESSAGE>>
|
|
78
|
+
>();
|
|
79
|
+
let isCancelled = false;
|
|
80
|
+
let cancelReason: unknown;
|
|
76
81
|
let outcome: UIMessageStreamOutcome = { status: 'unknown' };
|
|
77
82
|
|
|
78
83
|
const stream = new ReadableStream({
|
|
79
84
|
start(controllerArg) {
|
|
80
85
|
controller = controllerArg;
|
|
81
86
|
},
|
|
87
|
+
async cancel(reason) {
|
|
88
|
+
isCancelled = true;
|
|
89
|
+
cancelReason = reason;
|
|
90
|
+
|
|
91
|
+
await Promise.all(
|
|
92
|
+
Array.from(activeReaders, reader => reader.cancel(reason)),
|
|
93
|
+
);
|
|
94
|
+
},
|
|
82
95
|
});
|
|
83
96
|
|
|
84
97
|
function safeEnqueue(data: InferUIMessageChunk<UI_MESSAGE>) {
|
|
@@ -132,13 +145,25 @@ export function createUIMessageStream<UI_MESSAGE extends UIMessage>({
|
|
|
132
145
|
safeEnqueue(part);
|
|
133
146
|
},
|
|
134
147
|
merge(streamArg) {
|
|
148
|
+
if (isCancelled) {
|
|
149
|
+
void streamArg.cancel(cancelReason).catch(() => {});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
135
153
|
ongoingStreamPromises.push(
|
|
136
154
|
(async () => {
|
|
137
155
|
const reader = streamArg.getReader();
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
156
|
+
activeReaders.add(reader);
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
while (true) {
|
|
160
|
+
const { done, value } = await reader.read();
|
|
161
|
+
if (done) break;
|
|
162
|
+
safeEnqueue(value);
|
|
163
|
+
}
|
|
164
|
+
} finally {
|
|
165
|
+
activeReaders.delete(reader);
|
|
166
|
+
reader.releaseLock();
|
|
142
167
|
}
|
|
143
168
|
})().catch(error => {
|
|
144
169
|
handleError(error);
|