ai 7.0.148 → 7.0.149
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 +21 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +47 -32
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +2 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +4 -1
- package/dist/internal/index.js.map +1 -1
- package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +7 -0
- package/docs/03-ai-sdk-core/18-code-mode.mdx +3 -1
- package/docs/03-ai-sdk-core/36-transcription.mdx +40 -39
- package/docs/03-ai-sdk-core/37-speech.mdx +1 -0
- package/docs/03-ai-sdk-core/60-telemetry.mdx +25 -2
- package/docs/03-ai-sdk-harnesses/05-harness-adapters.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +4 -4
- package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +7 -7
- package/docs/07-reference/02-ai-sdk-ui/01-use-chat.mdx +1 -1
- package/docs/07-reference/02-ai-sdk-ui/40-create-ui-message-stream.mdx +1 -1
- package/docs/07-reference/03-ai-sdk-rsc/01-stream-ui.mdx +1 -1
- package/docs/07-reference/05-ai-sdk-errors/ai-tool-choice-violation-error.mdx +12 -7
- package/package.json +12 -12
- package/src/error/tool-choice-violation-error.ts +6 -4
- package/src/generate-text/collect-tool-approvals.ts +10 -0
- package/src/generate-text/generate-text.ts +24 -12
- package/src/generate-text/stream-language-model-call.ts +37 -12
- package/src/generate-text/stream-text.ts +4 -1
- package/src/types/language-model.ts +2 -0
- package/src/ui/chat.ts +40 -12
- package/src/ui-message-stream/ui-message-chunks.ts +1 -0
|
@@ -819,6 +819,13 @@ It supports the following settings:
|
|
|
819
819
|
- `none`: the model must not call tools
|
|
820
820
|
- `{ type: 'tool', toolName: string (typed) }`: the model must call the specified tool
|
|
821
821
|
|
|
822
|
+
When `toolChoice` is `'none'`, the SDK prevents local tool execution even if the
|
|
823
|
+
model returns a tool call. Such responses produce a
|
|
824
|
+
[`ToolChoiceViolationError`](/docs/reference/ai-sdk-errors/ai-tool-choice-violation-error).
|
|
825
|
+
With `streamText`, tool parts are withheld from the stream, so client-side tools
|
|
826
|
+
are not invoked either. Tool call repair and input refinement are skipped for
|
|
827
|
+
rejected tool calls.
|
|
828
|
+
|
|
822
829
|
```ts highlight="19"
|
|
823
830
|
import { z } from 'zod';
|
|
824
831
|
import { generateText, tool } from 'ai';
|
|
@@ -251,6 +251,7 @@ payloads, console output, and tool calls. Override them with
|
|
|
251
251
|
const codeMode = codeModeTool({
|
|
252
252
|
executionPolicy: {
|
|
253
253
|
timeoutMs: 30_000,
|
|
254
|
+
cpuTimeoutMs: 10_000,
|
|
254
255
|
memoryLimitBytes: 64 * 1024 * 1024,
|
|
255
256
|
maxResultBytes: 1024 * 1024,
|
|
256
257
|
maxBridgeRequests: 100,
|
|
@@ -261,7 +262,8 @@ const codeMode = codeModeTool({
|
|
|
261
262
|
|
|
262
263
|
The available limits are:
|
|
263
264
|
|
|
264
|
-
- `timeoutMs`: total execution time
|
|
265
|
+
- `timeoutMs`: total execution time, including time spent waiting on tools
|
|
266
|
+
- `cpuTimeoutMs`: CPU time spent running sandboxed code, excluding tool waits
|
|
265
267
|
- `memoryLimitBytes`: QuickJS memory
|
|
266
268
|
- `maxStackSizeBytes`: QuickJS stack
|
|
267
269
|
- `maxSourceBytes`: generated source code
|
|
@@ -331,44 +331,45 @@ try {
|
|
|
331
331
|
|
|
332
332
|
## Transcription Models
|
|
333
333
|
|
|
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-
|
|
347
|
-
| [
|
|
348
|
-
| [Azure
|
|
349
|
-
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-transcribe) | `mai-transcribe-
|
|
350
|
-
| [Azure
|
|
351
|
-
| [Azure
|
|
352
|
-
| [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `gpt-4o-
|
|
353
|
-
| [
|
|
354
|
-
| [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `
|
|
355
|
-
| [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `
|
|
356
|
-
| [
|
|
357
|
-
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `
|
|
358
|
-
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `
|
|
359
|
-
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova
|
|
360
|
-
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova-
|
|
361
|
-
| [
|
|
362
|
-
| [
|
|
363
|
-
| [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `universal-3-pro` |
|
|
364
|
-
| [
|
|
365
|
-
| [Fal](/providers/ai-sdk-providers/fal#transcription-models) | `
|
|
366
|
-
| [
|
|
367
|
-
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `
|
|
368
|
-
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `
|
|
369
|
-
| [
|
|
370
|
-
| [
|
|
371
|
-
| [Cartesia](/providers/ai-sdk-providers/cartesia#
|
|
372
|
-
| [
|
|
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-transcribe-3-0` |
|
|
347
|
+
| [Mistral](/providers/ai-sdk-providers/mistral#transcription-models) | `voxtral-small-transcribe-3-0` |
|
|
348
|
+
| [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `whisper-1` |
|
|
349
|
+
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-transcribe) | `mai-transcribe-2` |
|
|
350
|
+
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-transcribe) | `mai-transcribe-1.5` |
|
|
351
|
+
| [Azure MAI](/providers/ai-sdk-providers/azure#mai-transcribe-2-streaming) | `mai-transcribe-2-streaming` |
|
|
352
|
+
| [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `gpt-4o-transcribe` |
|
|
353
|
+
| [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `gpt-4o-mini-transcribe` |
|
|
354
|
+
| [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `machine` |
|
|
355
|
+
| [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `low_cost` |
|
|
356
|
+
| [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `fusion` |
|
|
357
|
+
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `base` (+ variants) |
|
|
358
|
+
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `enhanced` (+ variants) |
|
|
359
|
+
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova` (+ variants) |
|
|
360
|
+
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova-2` (+ variants) |
|
|
361
|
+
| [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova-3` (+ variants) |
|
|
362
|
+
| [Gladia](/providers/ai-sdk-providers/gladia#transcription-models) | `default` |
|
|
363
|
+
| [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `universal-3-5-pro` |
|
|
364
|
+
| [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `universal-3-pro` |
|
|
365
|
+
| [Fal](/providers/ai-sdk-providers/fal#transcription-models) | `whisper` |
|
|
366
|
+
| [Fal](/providers/ai-sdk-providers/fal#transcription-models) | `wizper` |
|
|
367
|
+
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `chirp_2` |
|
|
368
|
+
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `chirp_3` |
|
|
369
|
+
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `telephony` |
|
|
370
|
+
| [xAI](/providers/ai-sdk-providers/xai#transcription-models) | `default` |
|
|
371
|
+
| [Cartesia](/providers/ai-sdk-providers/cartesia#transcription-models) | `ink-whisper` |
|
|
372
|
+
| [Cartesia](/providers/ai-sdk-providers/cartesia#streaming-transcription-models) | `ink-2` |
|
|
373
|
+
| [Fish Audio](/providers/ai-sdk-providers/fish-audio#transcription-models) | `transcribe-1` |
|
|
373
374
|
|
|
374
375
|
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,7 @@ 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
|
+
| [Mistral](/providers/ai-sdk-providers/mistral#speech-models) | `voxtral-mini-tts-3-0` |
|
|
161
162
|
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-voice) | `mai-voice-2.1-flash` |
|
|
162
163
|
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-voice) | `mai-voice-2.1` |
|
|
163
164
|
| [Azure Speech](/providers/ai-sdk-providers/azure#mai-voice) | `mai-voice-2-flash` |
|
|
@@ -214,6 +214,29 @@ Properties set to `false` or omitted are excluded. If `telemetry.includeToolsCon
|
|
|
214
214
|
between tool execution context and telemetry event context.
|
|
215
215
|
</Note>
|
|
216
216
|
|
|
217
|
+
## Agent span names
|
|
218
|
+
|
|
219
|
+
`agentSpanName` controls root text/object generation span names. The strings
|
|
220
|
+
`'modelId'` (default) and `'functionId'` select a naming strategy, producing
|
|
221
|
+
`invoke_agent {modelId}` or `invoke_agent {telemetry.functionId}`:
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
registerTelemetry(new OpenTelemetry({ agentSpanName: 'functionId' }));
|
|
225
|
+
// With telemetry: { functionId: 'travel-planner' }, the span is:
|
|
226
|
+
// invoke_agent travel-planner
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
For a literal custom name, use a callback returning the complete span name:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
registerTelemetry(new OpenTelemetry({ agentSpanName: () => 'my-custom-span' }));
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The callback receives `{ modelId, functionId }` for dynamic names. If it returns
|
|
236
|
+
`undefined` or throws, the default name is used. The `'functionId'` strategy also
|
|
237
|
+
falls back to `modelId` when `functionId` is absent. Child span names and
|
|
238
|
+
attributes are unchanged.
|
|
239
|
+
|
|
217
240
|
## Custom Tracer
|
|
218
241
|
|
|
219
242
|
If you want your traces to use a `TracerProvider` other than the one provided by the `@opentelemetry/api` singleton, pass a custom `Tracer` to the `OpenTelemetry` constructor:
|
|
@@ -481,7 +504,7 @@ By default, attributes use the `gen_ai.*` prefix. Provider names are mapped to w
|
|
|
481
504
|
|
|
482
505
|
For `generateText` and `streamText`, the integration records 3 types of spans:
|
|
483
506
|
|
|
484
|
-
- **`invoke_agent {modelId}`** (root span, `INTERNAL`): covers the full operation including all steps and tool calls.
|
|
507
|
+
- **`invoke_agent {modelId}`** (default root span name, `INTERNAL`): covers the full operation including all steps and tool calls. Use [`agentSpanName`](#agent-span-names) to customize the name.
|
|
485
508
|
|
|
486
509
|
Initial attributes:
|
|
487
510
|
- `gen_ai.operation.name`: `"invoke_agent"`
|
|
@@ -739,7 +762,7 @@ registerTelemetry(
|
|
|
739
762
|
);
|
|
740
763
|
```
|
|
741
764
|
|
|
742
|
-
This does not create additional AI SDK-specific spans such as `ai.generateText`, `ai.generateText.doGenerate`, or `ai.toolCall`. Instead, it adds only the selected supplemental attributes to the spans emitted by `OpenTelemetry`, such as `invoke_agent {modelId}`, `step {n}`, `chat {modelId}`, and `execute_tool {toolName}`.
|
|
765
|
+
This does not create additional AI SDK-specific spans such as `ai.generateText`, `ai.generateText.doGenerate`, or `ai.toolCall`. Instead, it adds only the selected supplemental attributes to the spans emitted by `OpenTelemetry`, such as the default root span `invoke_agent {modelId}`, `step {n}`, `chat {modelId}`, and `execute_tool {toolName}`.
|
|
743
766
|
|
|
744
767
|
All supplemental attributes are disabled by default. Enable only the data you want to collect:
|
|
745
768
|
|
|
@@ -40,7 +40,7 @@ The AI SDK includes the following harness adapters:
|
|
|
40
40
|
| [Codex](/providers/ai-sdk-harnesses/codex) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> | <Cross /> |
|
|
41
41
|
| [Cursor](/providers/ai-sdk-harnesses/cursor) | Sandbox via ACP | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Cross /> |
|
|
42
42
|
| [Deep Agents](/providers/ai-sdk-harnesses/deepagents) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> via auto-rejection | <Check /> | <Cross /> |
|
|
43
|
-
| [fx](/providers/ai-sdk-harnesses/fx) | Host process | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <
|
|
43
|
+
| [fx](/providers/ai-sdk-harnesses/fx) | Host process | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
|
|
44
44
|
| [GitHub Copilot](/providers/ai-sdk-harnesses/github-copilot) | Sandbox via ACP | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Cross /> |
|
|
45
45
|
| [Grok Build](/providers/ai-sdk-harnesses/grok-build) | Sandbox via ACP | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> | <Cross /> |
|
|
46
46
|
| [OpenCode](/providers/ai-sdk-harnesses/opencode) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
|
|
@@ -1735,7 +1735,7 @@ To see `generateText` in action, check out [these examples](#examples).
|
|
|
1735
1735
|
},
|
|
1736
1736
|
{
|
|
1737
1737
|
name: 'finishReason',
|
|
1738
|
-
type: '"stop" | "length" | "content-filter" | "tool-calls" | "error" | "other"',
|
|
1738
|
+
type: '"stop" | "length" | "content-filter" | "tool-calls" | "error" | "interrupted" | "other"',
|
|
1739
1739
|
description: 'The unified reason why the generation finished.',
|
|
1740
1740
|
},
|
|
1741
1741
|
{
|
|
@@ -2029,7 +2029,7 @@ To see `generateText` in action, check out [these examples](#examples).
|
|
|
2029
2029
|
},
|
|
2030
2030
|
{
|
|
2031
2031
|
name: 'finishReason',
|
|
2032
|
-
type: '"stop" | "length" | "content-filter" | "tool-calls" | "error" | "other"',
|
|
2032
|
+
type: '"stop" | "length" | "content-filter" | "tool-calls" | "error" | "interrupted" | "other"',
|
|
2033
2033
|
description: 'The unified reason why the generation finished.',
|
|
2034
2034
|
},
|
|
2035
2035
|
{
|
|
@@ -2634,7 +2634,7 @@ To see `generateText` in action, check out [these examples](#examples).
|
|
|
2634
2634
|
},
|
|
2635
2635
|
{
|
|
2636
2636
|
name: 'finishReason',
|
|
2637
|
-
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'",
|
|
2637
|
+
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'",
|
|
2638
2638
|
description: 'The reason the model finished generating the text.',
|
|
2639
2639
|
},
|
|
2640
2640
|
{
|
|
@@ -3039,7 +3039,7 @@ To see `generateText` in action, check out [these examples](#examples).
|
|
|
3039
3039
|
},
|
|
3040
3040
|
{
|
|
3041
3041
|
name: 'finishReason',
|
|
3042
|
-
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'",
|
|
3042
|
+
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'",
|
|
3043
3043
|
description: 'The reason why the generation finished.',
|
|
3044
3044
|
},
|
|
3045
3045
|
{
|
|
@@ -1411,7 +1411,7 @@ retain the content their contracts require.
|
|
|
1411
1411
|
},
|
|
1412
1412
|
{
|
|
1413
1413
|
name: 'finishReason',
|
|
1414
|
-
type: '"stop" | "length" | "content-filter" | "tool-calls" | "error" | "other"',
|
|
1414
|
+
type: '"stop" | "length" | "content-filter" | "tool-calls" | "error" | "interrupted" | "other"',
|
|
1415
1415
|
description:
|
|
1416
1416
|
'The unified finish reason why the generation finished.',
|
|
1417
1417
|
},
|
|
@@ -1746,7 +1746,7 @@ retain the content their contracts require.
|
|
|
1746
1746
|
parameters: [
|
|
1747
1747
|
{
|
|
1748
1748
|
name: 'finishReason',
|
|
1749
|
-
type: '"stop" | "length" | "content-filter" | "tool-calls" | "error" | "other"',
|
|
1749
|
+
type: '"stop" | "length" | "content-filter" | "tool-calls" | "error" | "interrupted" | "other"',
|
|
1750
1750
|
description:
|
|
1751
1751
|
'The unified finish reason why the generation finished.',
|
|
1752
1752
|
},
|
|
@@ -2674,9 +2674,9 @@ retain the content their contracts require.
|
|
|
2674
2674
|
},
|
|
2675
2675
|
{
|
|
2676
2676
|
name: 'finishReason',
|
|
2677
|
-
type: "PromiseLike<'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'>",
|
|
2677
|
+
type: "PromiseLike<'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'>",
|
|
2678
2678
|
description:
|
|
2679
|
-
'The reason why the generation finished. Automatically consumes the stream.',
|
|
2679
|
+
'The reason why the generation finished. A stream that ends cleanly without its provider completion signal can report "interrupted". Partial output is preserved; interruption alone does not emit an error. Streams that produce no output and no finish chunk still reject with NoOutputGeneratedError. OpenAI-compatible retains its existing error behavior. Automatically consumes the stream.',
|
|
2680
2680
|
},
|
|
2681
2681
|
{
|
|
2682
2682
|
name: 'rawFinishReason',
|
|
@@ -3165,7 +3165,7 @@ retain the content their contracts require.
|
|
|
3165
3165
|
},
|
|
3166
3166
|
{
|
|
3167
3167
|
name: 'finishReason',
|
|
3168
|
-
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'",
|
|
3168
|
+
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'",
|
|
3169
3169
|
description: 'The reason the model finished generating the text.',
|
|
3170
3170
|
},
|
|
3171
3171
|
{
|
|
@@ -3873,7 +3873,7 @@ retain the content their contracts require.
|
|
|
3873
3873
|
},
|
|
3874
3874
|
{
|
|
3875
3875
|
name: 'finishReason',
|
|
3876
|
-
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'",
|
|
3876
|
+
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'",
|
|
3877
3877
|
description: 'The reason the model finished generating the text.',
|
|
3878
3878
|
},
|
|
3879
3879
|
{
|
|
@@ -3911,7 +3911,7 @@ retain the content their contracts require.
|
|
|
3911
3911
|
},
|
|
3912
3912
|
{
|
|
3913
3913
|
name: 'finishReason',
|
|
3914
|
-
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'",
|
|
3914
|
+
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'",
|
|
3915
3915
|
description: 'The reason the model finished generating the text.',
|
|
3916
3916
|
},
|
|
3917
3917
|
{
|
|
@@ -317,7 +317,7 @@ Allows you to easily create a conversational user interface for your chatbot app
|
|
|
317
317
|
},
|
|
318
318
|
{
|
|
319
319
|
name: 'finishReason',
|
|
320
|
-
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'",
|
|
320
|
+
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'",
|
|
321
321
|
isOptional: true,
|
|
322
322
|
description:
|
|
323
323
|
'The reason why the model finished generating the response. Undefined if the finish reason was not provided by the model.',
|
|
@@ -179,7 +179,7 @@ outcomes and call `setOutcome` once.
|
|
|
179
179
|
name: 'finishReason',
|
|
180
180
|
type: 'FinishReason | undefined',
|
|
181
181
|
description:
|
|
182
|
-
"The reason why the generation finished. One of: 'stop', 'length', 'content-filter', 'tool-calls', 'error', or 'other'.",
|
|
182
|
+
"The reason why the generation finished. One of: 'stop', 'length', 'content-filter', 'tool-calls', 'error', 'interrupted', or 'other'.",
|
|
183
183
|
},
|
|
184
184
|
],
|
|
185
185
|
},
|
|
@@ -649,7 +649,7 @@ To see `streamUI` in action, check out [these examples](#examples).
|
|
|
649
649
|
},
|
|
650
650
|
{
|
|
651
651
|
name: 'finishReason',
|
|
652
|
-
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'",
|
|
652
|
+
type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'",
|
|
653
653
|
description: 'The reason the model finished generating the text.',
|
|
654
654
|
},
|
|
655
655
|
{
|
|
@@ -5,14 +5,16 @@ description: Learn how to fix AI SDK ToolChoiceViolationError
|
|
|
5
5
|
|
|
6
6
|
# ToolChoiceViolationError
|
|
7
7
|
|
|
8
|
-
This error occurs when a `generateText` response does not satisfy
|
|
9
|
-
tool choice. It is thrown when `toolChoice` is set to `'
|
|
10
|
-
response contains
|
|
11
|
-
was not called.
|
|
8
|
+
This error occurs when a `generateText` or `streamText` response does not satisfy
|
|
9
|
+
an enforced tool choice. It is thrown when `toolChoice` is set to `'none'` but
|
|
10
|
+
the response contains a tool call, when `'required'` produces no structured tool
|
|
11
|
+
call, or when a specifically selected tool was not called. With `streamText`,
|
|
12
|
+
the error is emitted in the stream. Tool calls returned while `'none'` is selected
|
|
13
|
+
are not executed.
|
|
12
14
|
|
|
13
15
|
The error does not automatically interpret text or reasoning as an executable
|
|
14
|
-
tool call.
|
|
15
|
-
schema validation before executing any recovered call.
|
|
16
|
+
tool call. When tool calls are allowed, you can inspect `content` to implement
|
|
17
|
+
opt-in recovery, including schema validation before executing any recovered call.
|
|
16
18
|
|
|
17
19
|
## Properties
|
|
18
20
|
|
|
@@ -30,7 +32,10 @@ You can check if an error is an instance of `ToolChoiceViolationError` using:
|
|
|
30
32
|
```typescript
|
|
31
33
|
import { ToolChoiceViolationError } from 'ai';
|
|
32
34
|
|
|
33
|
-
if (
|
|
35
|
+
if (
|
|
36
|
+
ToolChoiceViolationError.isInstance(error) &&
|
|
37
|
+
error.toolChoice.type !== 'none'
|
|
38
|
+
) {
|
|
34
39
|
const serializedCall = error.content.find(part => part.type === 'text')?.text;
|
|
35
40
|
|
|
36
41
|
// Parse and validate serializedCall before treating it as a tool call.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.149",
|
|
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,20 +42,20 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ai-sdk/gateway": "4.0.
|
|
46
|
-
"@ai-sdk/provider": "4.0.
|
|
47
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
45
|
+
"@ai-sdk/gateway": "4.0.122",
|
|
46
|
+
"@ai-sdk/provider": "4.0.32",
|
|
47
|
+
"@ai-sdk/provider-utils": "5.0.66"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@ai-sdk/amazon-bedrock": "5.0.
|
|
51
|
-
"@ai-sdk/deepseek": "3.0.
|
|
52
|
-
"@ai-sdk/google": "4.0.
|
|
53
|
-
"@ai-sdk/groq": "4.0.
|
|
54
|
-
"@ai-sdk/huggingface": "2.0.
|
|
55
|
-
"@ai-sdk/moonshotai": "3.0.
|
|
56
|
-
"@ai-sdk/openai": "4.0.
|
|
50
|
+
"@ai-sdk/amazon-bedrock": "5.0.122",
|
|
51
|
+
"@ai-sdk/deepseek": "3.0.71",
|
|
52
|
+
"@ai-sdk/google": "4.0.103",
|
|
53
|
+
"@ai-sdk/groq": "4.0.68",
|
|
54
|
+
"@ai-sdk/huggingface": "2.0.76",
|
|
55
|
+
"@ai-sdk/moonshotai": "3.0.75",
|
|
56
|
+
"@ai-sdk/openai": "4.0.100",
|
|
57
57
|
"@ai-sdk/test-server": "2.0.4",
|
|
58
|
-
"@ai-sdk/xai": "5.0.
|
|
58
|
+
"@ai-sdk/xai": "5.0.28",
|
|
59
59
|
"@edge-runtime/vm": "^5.0.0",
|
|
60
60
|
"@smithy/eventstream-codec": "^4.3.3",
|
|
61
61
|
"@smithy/util-utf8": "^4.3.3",
|
|
@@ -11,7 +11,7 @@ const symbol = Symbol.for(marker);
|
|
|
11
11
|
|
|
12
12
|
type EnforcedToolChoice = Extract<
|
|
13
13
|
LanguageModelV4ToolChoice,
|
|
14
|
-
{ type: 'required' } | { type: 'tool' }
|
|
14
|
+
{ type: 'none' } | { type: 'required' } | { type: 'tool' }
|
|
15
15
|
>;
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -54,9 +54,11 @@ export class ToolChoiceViolationError extends AISDKError {
|
|
|
54
54
|
provider,
|
|
55
55
|
modelId,
|
|
56
56
|
content,
|
|
57
|
-
message = toolChoice.type === '
|
|
58
|
-
? 'Model response
|
|
59
|
-
:
|
|
57
|
+
message = toolChoice.type === 'none'
|
|
58
|
+
? 'Model response contained a tool call even though tool choice was none.'
|
|
59
|
+
: toolChoice.type === 'required'
|
|
60
|
+
? 'Model response did not contain a tool call even though tool choice was required.'
|
|
61
|
+
: `Model response did not contain a call to the required tool '${toolChoice.toolName}'.`,
|
|
60
62
|
}: {
|
|
61
63
|
toolChoice: EnforcedToolChoice;
|
|
62
64
|
finishReason: FinishReason;
|
|
@@ -88,6 +88,7 @@ export function collectToolApprovals<TOOLS extends ToolSet>({
|
|
|
88
88
|
|
|
89
89
|
const approvedToolApprovals: Array<CollectedToolApprovals<TOOLS>> = [];
|
|
90
90
|
const deniedToolApprovals: Array<CollectedToolApprovals<TOOLS>> = [];
|
|
91
|
+
const collectedToolCallIds = new Set<string>();
|
|
91
92
|
|
|
92
93
|
const approvalResponses = lastMessage.content.filter(
|
|
93
94
|
part => part.type === 'tool-approval-response',
|
|
@@ -119,6 +120,15 @@ export function collectToolApprovals<TOOLS extends ToolSet>({
|
|
|
119
120
|
});
|
|
120
121
|
}
|
|
121
122
|
|
|
123
|
+
// Only the first response for a tool call is collected. Later responses
|
|
124
|
+
// (replays, conflicting decisions, or responses to another approval of the
|
|
125
|
+
// same call) would otherwise execute the tool twice or both execute and
|
|
126
|
+
// deny it.
|
|
127
|
+
if (collectedToolCallIds.has(toolCall.toolCallId)) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
collectedToolCallIds.add(toolCall.toolCallId);
|
|
131
|
+
|
|
122
132
|
const approval: CollectedToolApprovals<TOOLS> = {
|
|
123
133
|
approvalRequest,
|
|
124
134
|
approvalResponse,
|
|
@@ -939,7 +939,7 @@ export async function generateText<
|
|
|
939
939
|
activeTools: prepareStepResult?.activeTools ?? activeTools,
|
|
940
940
|
});
|
|
941
941
|
const {
|
|
942
|
-
executionTools
|
|
942
|
+
executionTools,
|
|
943
943
|
modelTools: stepModelTools,
|
|
944
944
|
toolCallerMessages,
|
|
945
945
|
} = prepareToolsForToolCallers({
|
|
@@ -969,6 +969,8 @@ export async function generateText<
|
|
|
969
969
|
const stepToolChoice = prepareToolChoice({
|
|
970
970
|
toolChoice: prepareStepResult?.toolChoice ?? toolChoice,
|
|
971
971
|
});
|
|
972
|
+
const stepExecutionTools =
|
|
973
|
+
stepToolChoice.type === 'none' ? undefined : executionTools;
|
|
972
974
|
|
|
973
975
|
const stepMessages = appendToolCallerMessages({
|
|
974
976
|
messages: prepareStepResult?.messages ?? stepInputMessages,
|
|
@@ -1097,8 +1099,16 @@ export async function generateText<
|
|
|
1097
1099
|
parseToolCall({
|
|
1098
1100
|
toolCall,
|
|
1099
1101
|
tools: stepModelTools as TOOLS,
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
+
// tool calls are rejected with tool choice none,
|
|
1103
|
+
// so user callbacks must not process them:
|
|
1104
|
+
repairToolCall:
|
|
1105
|
+
stepToolChoice.type === 'none'
|
|
1106
|
+
? undefined
|
|
1107
|
+
: repairToolCall,
|
|
1108
|
+
refineToolInput:
|
|
1109
|
+
stepToolChoice.type === 'none'
|
|
1110
|
+
? undefined
|
|
1111
|
+
: refineToolInput,
|
|
1102
1112
|
instructions: stepInstructions,
|
|
1103
1113
|
messages: stepMessages,
|
|
1104
1114
|
abortSignal: mergedAbortSignal,
|
|
@@ -1171,18 +1181,19 @@ export async function generateText<
|
|
|
1171
1181
|
});
|
|
1172
1182
|
|
|
1173
1183
|
const enforcedToolChoice =
|
|
1174
|
-
stepToolChoice.type === '
|
|
1175
|
-
stepToolChoice.type === 'tool'
|
|
1176
|
-
? stepToolChoice
|
|
1177
|
-
: undefined;
|
|
1184
|
+
stepToolChoice.type === 'auto' ? undefined : stepToolChoice;
|
|
1178
1185
|
|
|
1179
1186
|
if (
|
|
1180
1187
|
enforcedToolChoice != null &&
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1188
|
+
(enforcedToolChoice.type === 'none'
|
|
1189
|
+
? stepToolCalls.length > 0
|
|
1190
|
+
: currentModelResponse.finishReason.unified !==
|
|
1191
|
+
'interrupted' &&
|
|
1192
|
+
!stepToolCalls.some(
|
|
1193
|
+
toolCall =>
|
|
1194
|
+
enforcedToolChoice.type === 'required' ||
|
|
1195
|
+
toolCall.toolName === enforcedToolChoice.toolName,
|
|
1196
|
+
))
|
|
1186
1197
|
) {
|
|
1187
1198
|
throw new ToolChoiceViolationError({
|
|
1188
1199
|
toolChoice: enforcedToolChoice,
|
|
@@ -1531,6 +1542,7 @@ export async function generateText<
|
|
|
1531
1542
|
}
|
|
1532
1543
|
}
|
|
1533
1544
|
} while (
|
|
1545
|
+
steps[steps.length - 1].finishReason !== 'interrupted' &&
|
|
1534
1546
|
// Continue only after all client tool calls have been executed or denied,
|
|
1535
1547
|
// and if there are client results or pending deferred provider results.
|
|
1536
1548
|
clientToolOutputs.length + deniedToolApprovalResponses.length ===
|
|
@@ -448,6 +448,12 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
448
448
|
let previousOutputChunkTimestampMs: number | undefined;
|
|
449
449
|
const timeBetweenOutputChunksMs: number[] = [];
|
|
450
450
|
|
|
451
|
+
// Tool choice none rejects tool calls when the model call finishes.
|
|
452
|
+
// Withhold tool parts so that consumers (e.g. client-side tools) cannot
|
|
453
|
+
// act on them before the rejection, and skip user callbacks that would
|
|
454
|
+
// repair or refine them.
|
|
455
|
+
const withholdToolParts = toolChoice.type === 'none';
|
|
456
|
+
|
|
451
457
|
return new TransformStream<
|
|
452
458
|
LanguageModelV4StreamPart,
|
|
453
459
|
LanguageModelStreamPart<TOOLS>
|
|
@@ -467,6 +473,15 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
467
473
|
previousOutputChunkTimestampMs = outputChunkTimestampMs;
|
|
468
474
|
}
|
|
469
475
|
|
|
476
|
+
if (
|
|
477
|
+
withholdToolParts &&
|
|
478
|
+
(chunk.type === 'tool-input-start' ||
|
|
479
|
+
chunk.type === 'tool-input-delta' ||
|
|
480
|
+
chunk.type === 'tool-input-end')
|
|
481
|
+
) {
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
|
|
470
485
|
switch (chunk.type) {
|
|
471
486
|
case 'error':
|
|
472
487
|
controller.enqueue({
|
|
@@ -655,17 +670,18 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
655
670
|
});
|
|
656
671
|
|
|
657
672
|
const enforcedToolChoice =
|
|
658
|
-
toolChoice.type === '
|
|
659
|
-
? toolChoice
|
|
660
|
-
: undefined;
|
|
673
|
+
toolChoice.type === 'auto' ? undefined : toolChoice;
|
|
661
674
|
|
|
662
675
|
const toolChoiceViolationError =
|
|
663
676
|
enforcedToolChoice != null &&
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
677
|
+
(enforcedToolChoice.type === 'none'
|
|
678
|
+
? toolCallsByToolCallId.size > 0
|
|
679
|
+
: chunk.finishReason.unified !== 'interrupted' &&
|
|
680
|
+
![...toolCallsByToolCallId.values()].some(
|
|
681
|
+
toolCall =>
|
|
682
|
+
enforcedToolChoice.type === 'required' ||
|
|
683
|
+
toolCall.toolName === enforcedToolChoice.toolName,
|
|
684
|
+
))
|
|
669
685
|
? new ToolChoiceViolationError({
|
|
670
686
|
toolChoice: enforcedToolChoice,
|
|
671
687
|
finishReason: chunk.finishReason.unified,
|
|
@@ -708,17 +724,22 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
708
724
|
const toolCall = await parseToolCall({
|
|
709
725
|
toolCall: chunk,
|
|
710
726
|
tools,
|
|
711
|
-
repairToolCall,
|
|
712
|
-
refineToolInput,
|
|
727
|
+
repairToolCall: withholdToolParts ? undefined : repairToolCall,
|
|
728
|
+
refineToolInput: withholdToolParts ? undefined : refineToolInput,
|
|
713
729
|
instructions,
|
|
714
730
|
messages,
|
|
715
731
|
abortSignal,
|
|
716
732
|
});
|
|
717
733
|
|
|
718
734
|
toolCallsByToolCallId.set(toolCall.toolCallId, toolCall);
|
|
719
|
-
controller.enqueue(toolCall);
|
|
720
735
|
if (collectContent) modelCallContent.push(toolCall);
|
|
721
736
|
|
|
737
|
+
if (withholdToolParts) {
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
controller.enqueue(toolCall);
|
|
742
|
+
|
|
722
743
|
if (toolCall.invalid) {
|
|
723
744
|
if (!toolCall.providerExecuted) {
|
|
724
745
|
controller.enqueue({
|
|
@@ -746,6 +767,10 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
746
767
|
case 'tool-approval-request': {
|
|
747
768
|
if (collectContent) rawModelCallContent.push(chunk);
|
|
748
769
|
|
|
770
|
+
if (withholdToolParts) {
|
|
771
|
+
break;
|
|
772
|
+
}
|
|
773
|
+
|
|
749
774
|
const toolCall = toolCallsByToolCallId.get(chunk.toolCallId);
|
|
750
775
|
|
|
751
776
|
if (toolCall == null) {
|
|
@@ -808,7 +833,7 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
808
833
|
: {}),
|
|
809
834
|
} as TypedToolResult<TOOLS>);
|
|
810
835
|
|
|
811
|
-
controller.enqueue(toolResultPart);
|
|
836
|
+
if (!withholdToolParts) controller.enqueue(toolResultPart);
|
|
812
837
|
if (collectContent) modelCallContent.push(toolResultPart);
|
|
813
838
|
|
|
814
839
|
break;
|