ai 7.0.87 → 7.0.88
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 +7 -0
- package/dist/index.d.ts +116 -44
- package/dist/index.js +513 -451
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +1 -1
- package/dist/internal/index.js.map +1 -1
- package/docs/03-agents/07-workflow-agent.mdx +9 -4
- package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +2 -1
- package/docs/07-reference/04-ai-sdk-workflow/01-workflow-agent.mdx +34 -6
- package/docs/07-reference/05-ai-sdk-errors/ai-tool-choice-violation-error.mdx +38 -0
- package/docs/07-reference/05-ai-sdk-errors/index.mdx +1 -0
- package/package.json +5 -5
- package/src/batch/batch-types.ts +47 -7
- package/src/batch/batch.ts +43 -20
- package/src/error/index.ts +1 -0
- package/src/error/tool-choice-violation-error.ts +80 -0
- package/src/generate-text/generate-text.ts +25 -1
|
@@ -587,11 +587,11 @@ Agents provide lifecycle callbacks for logging, observability, and custom teleme
|
|
|
587
587
|
const agent = new WorkflowAgent({
|
|
588
588
|
model: 'anthropic/claude-sonnet-4-6',
|
|
589
589
|
|
|
590
|
-
|
|
591
|
-
console.log(
|
|
590
|
+
onStart({ messages }) {
|
|
591
|
+
console.log(`Agent started with ${messages.length} messages`);
|
|
592
592
|
},
|
|
593
593
|
|
|
594
|
-
|
|
594
|
+
onStepStart({ stepNumber }) {
|
|
595
595
|
console.log(`Step ${stepNumber} starting`);
|
|
596
596
|
},
|
|
597
597
|
|
|
@@ -613,6 +613,11 @@ const agent = new WorkflowAgent({
|
|
|
613
613
|
});
|
|
614
614
|
```
|
|
615
615
|
|
|
616
|
+
The deprecated `experimental_onStart` and `experimental_onStepStart` names
|
|
617
|
+
remain available for backwards compatibility. When both the stable and
|
|
618
|
+
experimental name are provided in the same constructor or `stream()` call, the
|
|
619
|
+
stable callback is used.
|
|
620
|
+
|
|
616
621
|
## Type Inference
|
|
617
622
|
|
|
618
623
|
Infer the UI message type for type-safe client components:
|
|
@@ -764,7 +769,7 @@ For persistence, store `UIMessage[]` as your source of truth and call [`convertT
|
|
|
764
769
|
|
|
765
770
|
### Everything else
|
|
766
771
|
|
|
767
|
-
Other options carry over with the same names: `prepareStep`, `onStepEnd`, `onEnd`, `onError`, `toolChoice`, `activeTools`, `timeout`, `repairToolCall`, `experimental_sandbox`, and the usual generation settings (`temperature`, `maxOutputTokens`, `topP`, …). `WorkflowAgent` additionally adds `prepareCall` (runs once before the loop) and the `
|
|
772
|
+
Other options carry over with the same names: `prepareStep`, `onStart`, `onStepStart`, `onStepEnd`, `onEnd`, `onError`, `toolChoice`, `activeTools`, `timeout`, `repairToolCall`, `experimental_sandbox`, and the usual generation settings (`temperature`, `maxOutputTokens`, `topP`, …). `WorkflowAgent` additionally adds `prepareCall` (runs once before the loop) and the `onToolExecutionStart` / `onToolExecutionEnd` lifecycle callbacks documented above. The deprecated `experimental_onStart` and `experimental_onStepStart` aliases remain available for backwards compatibility.
|
|
768
773
|
|
|
769
774
|
## Next Steps
|
|
770
775
|
|
|
@@ -1133,11 +1133,12 @@ async function generateSomething(prompt: string): Promise<{
|
|
|
1133
1133
|
|
|
1134
1134
|
## Handling Errors
|
|
1135
1135
|
|
|
1136
|
-
The AI SDK has
|
|
1136
|
+
The AI SDK has four tool-call related errors:
|
|
1137
1137
|
|
|
1138
1138
|
- [`NoSuchToolError`](/docs/reference/ai-sdk-errors/ai-no-such-tool-error): the model tries to call a tool that is not defined in the tools object
|
|
1139
1139
|
- [`InvalidToolInputError`](/docs/reference/ai-sdk-errors/ai-invalid-tool-input-error): the model calls a tool with inputs that do not match the tool's input schema
|
|
1140
1140
|
- [`ToolCallRepairError`](/docs/reference/ai-sdk-errors/ai-tool-call-repair-error): an error that occurred during tool call repair
|
|
1141
|
+
- [`ToolChoiceViolationError`](/docs/reference/ai-sdk-errors/ai-tool-choice-violation-error): the `generateText` response does not satisfy a required or specifically selected tool choice
|
|
1141
1142
|
|
|
1142
1143
|
When tool execution fails (errors thrown by your tool's `execute` function), the AI SDK adds them as `tool-error` content parts to enable automated LLM roundtrips in multi-step scenarios.
|
|
1143
1144
|
|
|
@@ -179,11 +179,11 @@ To see `WorkflowAgent` in action, check out [these examples](#examples).
|
|
|
179
179
|
'Telemetry configuration with options for enabling/disabling telemetry, setting a function ID, and recording inputs/outputs.',
|
|
180
180
|
},
|
|
181
181
|
{
|
|
182
|
-
name: '
|
|
182
|
+
name: 'onStart',
|
|
183
183
|
type: 'WorkflowAgentOnStartCallback',
|
|
184
184
|
isOptional: true,
|
|
185
185
|
description:
|
|
186
|
-
'Callback called when the agent starts streaming, before any LLM calls. Receives the model, messages, runtime context, and tools context. If also specified in `stream()`, both callbacks fire (constructor first).
|
|
186
|
+
'Callback called when the agent starts streaming, before any LLM calls. Receives the model, messages, runtime context, and tools context. If also specified in `stream()`, both callbacks fire (constructor first). Takes precedence over `experimental_onStart` when both are provided in the constructor.',
|
|
187
187
|
properties: [
|
|
188
188
|
{
|
|
189
189
|
type: 'GenerateTextStartEvent',
|
|
@@ -213,11 +213,18 @@ To see `WorkflowAgent` in action, check out [these examples](#examples).
|
|
|
213
213
|
],
|
|
214
214
|
},
|
|
215
215
|
{
|
|
216
|
-
name: '
|
|
216
|
+
name: 'experimental_onStart',
|
|
217
|
+
type: 'WorkflowAgentOnStartCallback',
|
|
218
|
+
isOptional: true,
|
|
219
|
+
description:
|
|
220
|
+
'Deprecated alias for `onStart`. Used only when `onStart` is not provided in the constructor.',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: 'onStepStart',
|
|
217
224
|
type: 'WorkflowAgentOnStepStartCallback',
|
|
218
225
|
isOptional: true,
|
|
219
226
|
description:
|
|
220
|
-
'Callback called before each step (LLM call) begins. Receives step number, model, messages, previous steps, runtime context, and tools context. If also specified in `stream()`, both callbacks fire (constructor first).
|
|
227
|
+
'Callback called before each step (LLM call) begins. Receives step number, model, messages, previous steps, runtime context, and tools context. If also specified in `stream()`, both callbacks fire (constructor first). Takes precedence over `experimental_onStepStart` when both are provided in the constructor.',
|
|
221
228
|
properties: [
|
|
222
229
|
{
|
|
223
230
|
type: 'GenerateTextStepStartEvent',
|
|
@@ -252,6 +259,13 @@ To see `WorkflowAgent` in action, check out [these examples](#examples).
|
|
|
252
259
|
},
|
|
253
260
|
],
|
|
254
261
|
},
|
|
262
|
+
{
|
|
263
|
+
name: 'experimental_onStepStart',
|
|
264
|
+
type: 'WorkflowAgentOnStepStartCallback',
|
|
265
|
+
isOptional: true,
|
|
266
|
+
description:
|
|
267
|
+
'Deprecated alias for `onStepStart`. Used only when `onStepStart` is not provided in the constructor.',
|
|
268
|
+
},
|
|
255
269
|
{
|
|
256
270
|
name: 'onToolExecutionStart',
|
|
257
271
|
type: 'WorkflowAgentonToolExecutionStartCallback',
|
|
@@ -600,19 +614,33 @@ const result = await agent.stream({
|
|
|
600
614
|
description:
|
|
601
615
|
'Per-call prepareStep override. Receives the initial instructions and messages alongside the current step state.',
|
|
602
616
|
},
|
|
617
|
+
{
|
|
618
|
+
name: 'onStart',
|
|
619
|
+
type: 'WorkflowAgentOnStartCallback',
|
|
620
|
+
isOptional: true,
|
|
621
|
+
description:
|
|
622
|
+
'Per-call onStart callback. If also specified in the constructor, both fire (constructor first). Takes precedence over `experimental_onStart` when both are provided in this call.',
|
|
623
|
+
},
|
|
603
624
|
{
|
|
604
625
|
name: 'experimental_onStart',
|
|
605
626
|
type: 'WorkflowAgentOnStartCallback',
|
|
606
627
|
isOptional: true,
|
|
607
628
|
description:
|
|
608
|
-
'
|
|
629
|
+
'Deprecated alias for the per-call `onStart` callback. Used only when `onStart` is not provided in this call.',
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
name: 'onStepStart',
|
|
633
|
+
type: 'WorkflowAgentOnStepStartCallback',
|
|
634
|
+
isOptional: true,
|
|
635
|
+
description:
|
|
636
|
+
'Per-call onStepStart callback. If also specified in the constructor, both fire (constructor first). Takes precedence over `experimental_onStepStart` when both are provided in this call.',
|
|
609
637
|
},
|
|
610
638
|
{
|
|
611
639
|
name: 'experimental_onStepStart',
|
|
612
640
|
type: 'WorkflowAgentOnStepStartCallback',
|
|
613
641
|
isOptional: true,
|
|
614
642
|
description:
|
|
615
|
-
'
|
|
643
|
+
'Deprecated alias for the per-call `onStepStart` callback. Used only when `onStepStart` is not provided in this call.',
|
|
616
644
|
},
|
|
617
645
|
{
|
|
618
646
|
name: 'onToolExecutionStart',
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: ToolChoiceViolationError
|
|
3
|
+
description: Learn how to fix AI SDK ToolChoiceViolationError
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ToolChoiceViolationError
|
|
7
|
+
|
|
8
|
+
This error occurs when a `generateText` response does not satisfy an enforced
|
|
9
|
+
tool choice. It is thrown when `toolChoice` is set to `'required'` but the
|
|
10
|
+
response contains no structured tool call, or when a specifically selected tool
|
|
11
|
+
was not called.
|
|
12
|
+
|
|
13
|
+
The error does not automatically interpret text or reasoning as an executable
|
|
14
|
+
tool call. You can inspect `content` to implement opt-in recovery, including
|
|
15
|
+
schema validation before executing any recovered call.
|
|
16
|
+
|
|
17
|
+
## Properties
|
|
18
|
+
|
|
19
|
+
- `toolChoice`: The effective tool choice that the response did not satisfy
|
|
20
|
+
- `finishReason`: The reason why the model finished generating the response
|
|
21
|
+
- `provider`: The provider that returned the response
|
|
22
|
+
- `modelId`: The model that returned the response
|
|
23
|
+
- `content`: The normalized content returned by the model
|
|
24
|
+
- `message`: The error message
|
|
25
|
+
|
|
26
|
+
## Checking for this Error
|
|
27
|
+
|
|
28
|
+
You can check if an error is an instance of `ToolChoiceViolationError` using:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { ToolChoiceViolationError } from 'ai';
|
|
32
|
+
|
|
33
|
+
if (ToolChoiceViolationError.isInstance(error)) {
|
|
34
|
+
const serializedCall = error.content.find(part => part.type === 'text')?.text;
|
|
35
|
+
|
|
36
|
+
// Parse and validate serializedCall before treating it as a tool call.
|
|
37
|
+
}
|
|
38
|
+
```
|
|
@@ -36,6 +36,7 @@ collapsed: true
|
|
|
36
36
|
- [AI_StreamProviderError](/docs/reference/ai-sdk-errors/ai-stream-provider-error)
|
|
37
37
|
- [AI_ToolCallNotFoundForApprovalError](/docs/reference/ai-sdk-errors/ai-tool-call-not-found-for-approval-error)
|
|
38
38
|
- [AI_ToolCallRepairError](/docs/reference/ai-sdk-errors/ai-tool-call-repair-error)
|
|
39
|
+
- [AI_ToolChoiceViolationError](/docs/reference/ai-sdk-errors/ai-tool-choice-violation-error)
|
|
39
40
|
- [AI_TooManyEmbeddingValuesForCallError](/docs/reference/ai-sdk-errors/ai-too-many-embedding-values-for-call-error)
|
|
40
41
|
- [AI_TypeValidationError](/docs/reference/ai-sdk-errors/ai-type-validation-error)
|
|
41
42
|
- [AI_UIMessageStreamError](/docs/reference/ai-sdk-errors/ai-ui-message-stream-error)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.88",
|
|
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",
|
|
@@ -47,15 +47,15 @@
|
|
|
47
47
|
"@ai-sdk/provider-utils": "5.0.34"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@ai-sdk/amazon-bedrock": "5.0.
|
|
50
|
+
"@ai-sdk/amazon-bedrock": "5.0.70",
|
|
51
51
|
"@ai-sdk/deepseek": "3.0.37",
|
|
52
|
-
"@ai-sdk/google": "4.0.
|
|
52
|
+
"@ai-sdk/google": "4.0.60",
|
|
53
53
|
"@ai-sdk/groq": "4.0.35",
|
|
54
54
|
"@ai-sdk/huggingface": "2.0.41",
|
|
55
55
|
"@ai-sdk/moonshotai": "3.0.43",
|
|
56
|
-
"@ai-sdk/openai": "4.0.
|
|
56
|
+
"@ai-sdk/openai": "4.0.54",
|
|
57
57
|
"@ai-sdk/test-server": "2.0.1",
|
|
58
|
-
"@ai-sdk/xai": "4.0.
|
|
58
|
+
"@ai-sdk/xai": "4.0.51",
|
|
59
59
|
"@edge-runtime/vm": "^5.0.0",
|
|
60
60
|
"@smithy/eventstream-codec": "^4.3.3",
|
|
61
61
|
"@smithy/util-utf8": "^4.3.3",
|
package/src/batch/batch-types.ts
CHANGED
|
@@ -4,13 +4,19 @@ import type {
|
|
|
4
4
|
Experimental_BatchV4Status as BatchV4Status,
|
|
5
5
|
Experimental_BatchLanguageModelV4 as BatchLanguageModelV4,
|
|
6
6
|
} from '@ai-sdk/provider';
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
InferToolSetContext,
|
|
9
|
+
ProviderOptions,
|
|
10
|
+
ToolSet,
|
|
11
|
+
} from '@ai-sdk/provider-utils';
|
|
8
12
|
import type { ContentPart } from '../generate-text/content-part';
|
|
13
|
+
import type { ToolOrder } from '../generate-text/tool-order';
|
|
9
14
|
import type { LanguageModelCallOptions } from '../prompt/language-model-call-options';
|
|
10
15
|
import type { Prompt } from '../prompt/prompt';
|
|
11
16
|
import type {
|
|
12
17
|
FinishReason,
|
|
13
18
|
GlobalProviderModelId,
|
|
19
|
+
ToolChoice,
|
|
14
20
|
} from '../types/language-model';
|
|
15
21
|
import type { ProviderMetadata } from '../types/provider-metadata';
|
|
16
22
|
import type { LanguageModelUsage } from '../types/usage';
|
|
@@ -74,9 +80,34 @@ type BatchRequestOptions = {
|
|
|
74
80
|
/**
|
|
75
81
|
* Options for starting a text batch.
|
|
76
82
|
*/
|
|
77
|
-
export type StartTextBatchOptions = {
|
|
83
|
+
export type StartTextBatchOptions<TOOLS extends ToolSet = ToolSet> = {
|
|
78
84
|
model: BatchLanguageModel;
|
|
79
85
|
requests: ReadonlyArray<TextBatchRequest>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Tools that the model can call for every request in the batch.
|
|
89
|
+
*
|
|
90
|
+
* Tool definitions are sent to the provider, but their `execute` functions
|
|
91
|
+
* are never invoked by batch processing.
|
|
92
|
+
*/
|
|
93
|
+
tools?: TOOLS;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The tool choice strategy. Default: 'auto'.
|
|
97
|
+
*/
|
|
98
|
+
toolChoice?: ToolChoice<NoInfer<TOOLS>>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Controls the order in which tools are sent to the provider. Tools not
|
|
102
|
+
* listed are appended alphabetically.
|
|
103
|
+
*/
|
|
104
|
+
toolOrder?: ToolOrder<TOOLS>;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Context used when resolving dynamic tool descriptions.
|
|
108
|
+
*/
|
|
109
|
+
toolsContext?: InferToolSetContext<TOOLS>;
|
|
110
|
+
|
|
80
111
|
providerOptions?: ProviderOptions;
|
|
81
112
|
|
|
82
113
|
/**
|
|
@@ -97,9 +128,18 @@ export type StartTextBatchResult = TextBatch & {
|
|
|
97
128
|
/**
|
|
98
129
|
* Options shared by batch status and result retrieval operations.
|
|
99
130
|
*/
|
|
100
|
-
export type BatchOperationOptions = {
|
|
131
|
+
export type BatchOperationOptions<TOOLS extends ToolSet = ToolSet> = {
|
|
101
132
|
model: BatchLanguageModel;
|
|
102
133
|
batch: BatchReference;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Definitions for client tools that were provided to `startTextBatch`.
|
|
137
|
+
*
|
|
138
|
+
* The definitions are used only to validate and normalize returned tool
|
|
139
|
+
* calls. Their `execute` functions are never invoked.
|
|
140
|
+
*/
|
|
141
|
+
tools?: TOOLS;
|
|
142
|
+
|
|
103
143
|
providerOptions?: ProviderOptions;
|
|
104
144
|
maxRetries?: number;
|
|
105
145
|
} & BatchRequestOptions;
|
|
@@ -107,9 +147,9 @@ export type BatchOperationOptions = {
|
|
|
107
147
|
/**
|
|
108
148
|
* A normalized result for a successful text batch item.
|
|
109
149
|
*/
|
|
110
|
-
export type TextBatchGenerationResult = {
|
|
150
|
+
export type TextBatchGenerationResult<TOOLS extends ToolSet = ToolSet> = {
|
|
111
151
|
/** Ordered normalized content, including citations, sources, and tool data. */
|
|
112
|
-
readonly content: Array<ContentPart<
|
|
152
|
+
readonly content: Array<ContentPart<TOOLS>>;
|
|
113
153
|
readonly text: string;
|
|
114
154
|
readonly finishReason: FinishReason;
|
|
115
155
|
readonly rawFinishReason?: string;
|
|
@@ -125,8 +165,8 @@ export type TextBatchGenerationResult = {
|
|
|
125
165
|
/**
|
|
126
166
|
* A complete terminal result for one request in a text batch.
|
|
127
167
|
*/
|
|
128
|
-
export type TextBatchItemResult =
|
|
129
|
-
| (TextBatchGenerationResult & {
|
|
168
|
+
export type TextBatchItemResult<TOOLS extends ToolSet = ToolSet> =
|
|
169
|
+
| (TextBatchGenerationResult<TOOLS> & {
|
|
130
170
|
readonly id: string;
|
|
131
171
|
readonly status: 'succeeded';
|
|
132
172
|
})
|
package/src/batch/batch.ts
CHANGED
|
@@ -10,6 +10,8 @@ import { type ToolSet, withUserAgentSuffix } from '@ai-sdk/provider-utils';
|
|
|
10
10
|
import { InvalidArgumentError } from '../error/invalid-argument-error';
|
|
11
11
|
import { convertLanguageModelContent } from '../generate-text/convert-language-model-content';
|
|
12
12
|
import { parseToolCall } from '../generate-text/parse-tool-call';
|
|
13
|
+
import { prepareToolChoice } from '../prompt/prepare-tool-choice';
|
|
14
|
+
import { prepareTools } from '../prompt/prepare-tools';
|
|
13
15
|
import { logWarnings } from '../logger/log-warnings';
|
|
14
16
|
import { resolveLanguageModel } from '../model/resolve-model';
|
|
15
17
|
import { convertToLanguageModelPrompt } from '../prompt/convert-to-language-model-prompt';
|
|
@@ -36,15 +38,19 @@ import type {
|
|
|
36
38
|
/**
|
|
37
39
|
* Starts a durable text-generation batch.
|
|
38
40
|
*/
|
|
39
|
-
export async function startTextBatch({
|
|
41
|
+
export async function startTextBatch<TOOLS extends ToolSet>({
|
|
40
42
|
model: modelArg,
|
|
41
43
|
requests,
|
|
44
|
+
tools,
|
|
45
|
+
toolChoice,
|
|
46
|
+
toolOrder,
|
|
47
|
+
toolsContext,
|
|
42
48
|
providerOptions,
|
|
43
49
|
webhookUrl,
|
|
44
50
|
abortSignal,
|
|
45
51
|
headers,
|
|
46
52
|
timeout,
|
|
47
|
-
}: StartTextBatchOptions): Promise<StartTextBatchResult> {
|
|
53
|
+
}: StartTextBatchOptions<TOOLS>): Promise<StartTextBatchResult> {
|
|
48
54
|
validateRequests(requests);
|
|
49
55
|
|
|
50
56
|
const model = resolveBatchLanguageModel(modelArg);
|
|
@@ -53,6 +59,12 @@ export async function startTextBatch({
|
|
|
53
59
|
getTotalTimeoutMs(timeout),
|
|
54
60
|
);
|
|
55
61
|
const supportedUrls = await model.supportedUrls;
|
|
62
|
+
const preparedTools = await prepareTools({
|
|
63
|
+
tools,
|
|
64
|
+
toolOrder,
|
|
65
|
+
toolsContext,
|
|
66
|
+
});
|
|
67
|
+
const preparedToolChoice = prepareToolChoice({ toolChoice });
|
|
56
68
|
operationAbortSignal?.throwIfAborted();
|
|
57
69
|
const normalizedRequests = [];
|
|
58
70
|
|
|
@@ -69,6 +81,8 @@ export async function startTextBatch({
|
|
|
69
81
|
download: undefined,
|
|
70
82
|
provider: model.provider.split('.')[0],
|
|
71
83
|
}),
|
|
84
|
+
tools: preparedTools,
|
|
85
|
+
toolChoice: preparedToolChoice,
|
|
72
86
|
providerOptions: request.providerOptions,
|
|
73
87
|
},
|
|
74
88
|
});
|
|
@@ -120,7 +134,7 @@ export async function getBatchStatus({
|
|
|
120
134
|
abortSignal,
|
|
121
135
|
headers,
|
|
122
136
|
timeout,
|
|
123
|
-
}: BatchOperationOptions): Promise<BatchStatus> {
|
|
137
|
+
}: Omit<BatchOperationOptions, 'tools'>): Promise<BatchStatus> {
|
|
124
138
|
const model = resolveBatchLanguageModel(modelArg);
|
|
125
139
|
validateBatchReference({ model, batch });
|
|
126
140
|
|
|
@@ -152,15 +166,16 @@ export async function getBatchStatus({
|
|
|
152
166
|
/**
|
|
153
167
|
* Streams complete terminal results for the requests in a durable batch.
|
|
154
168
|
*/
|
|
155
|
-
export function getBatchResults({
|
|
169
|
+
export function getBatchResults<TOOLS extends ToolSet>({
|
|
156
170
|
model: modelArg,
|
|
157
171
|
batch,
|
|
172
|
+
tools,
|
|
158
173
|
providerOptions,
|
|
159
174
|
maxRetries,
|
|
160
175
|
abortSignal,
|
|
161
176
|
headers,
|
|
162
177
|
timeout,
|
|
163
|
-
}: BatchOperationOptions) {
|
|
178
|
+
}: BatchOperationOptions<TOOLS>) {
|
|
164
179
|
const model = resolveBatchLanguageModel(modelArg);
|
|
165
180
|
validateBatchReference({ model, batch });
|
|
166
181
|
|
|
@@ -176,10 +191,10 @@ export function getBatchResults({
|
|
|
176
191
|
});
|
|
177
192
|
const transformer: Transformer<
|
|
178
193
|
BatchV4ItemResult<LanguageModelV4GenerateResult>,
|
|
179
|
-
TextBatchItemResult
|
|
194
|
+
TextBatchItemResult<TOOLS>
|
|
180
195
|
> & { cancel?: (reason?: unknown) => void } = {
|
|
181
196
|
async transform(item, controller) {
|
|
182
|
-
controller.enqueue(await convertBatchItemResult(item));
|
|
197
|
+
controller.enqueue(await convertBatchItemResult({ item, tools }));
|
|
183
198
|
},
|
|
184
199
|
|
|
185
200
|
cancel(reason) {
|
|
@@ -190,7 +205,7 @@ export function getBatchResults({
|
|
|
190
205
|
};
|
|
191
206
|
const transform = new TransformStream<
|
|
192
207
|
BatchV4ItemResult<LanguageModelV4GenerateResult>,
|
|
193
|
-
TextBatchItemResult
|
|
208
|
+
TextBatchItemResult<TOOLS>
|
|
194
209
|
>(transformer);
|
|
195
210
|
|
|
196
211
|
void (async () => {
|
|
@@ -299,9 +314,13 @@ function validateBatchReference({
|
|
|
299
314
|
}
|
|
300
315
|
}
|
|
301
316
|
|
|
302
|
-
async function convertBatchItemResult(
|
|
303
|
-
item
|
|
304
|
-
|
|
317
|
+
async function convertBatchItemResult<TOOLS extends ToolSet>({
|
|
318
|
+
item,
|
|
319
|
+
tools,
|
|
320
|
+
}: {
|
|
321
|
+
item: BatchV4ItemResult<LanguageModelV4GenerateResult>;
|
|
322
|
+
tools: TOOLS | undefined;
|
|
323
|
+
}): Promise<TextBatchItemResult<TOOLS>> {
|
|
305
324
|
if (item.status !== 'succeeded') {
|
|
306
325
|
return item;
|
|
307
326
|
}
|
|
@@ -309,22 +328,26 @@ async function convertBatchItemResult(
|
|
|
309
328
|
return {
|
|
310
329
|
id: item.id,
|
|
311
330
|
status: 'succeeded',
|
|
312
|
-
...(await convertGenerateResult(item.result)),
|
|
331
|
+
...(await convertGenerateResult({ result: item.result, tools })),
|
|
313
332
|
};
|
|
314
333
|
}
|
|
315
334
|
|
|
316
|
-
async function convertGenerateResult(
|
|
317
|
-
result
|
|
318
|
-
|
|
335
|
+
async function convertGenerateResult<TOOLS extends ToolSet>({
|
|
336
|
+
result,
|
|
337
|
+
tools,
|
|
338
|
+
}: {
|
|
339
|
+
result: LanguageModelV4GenerateResult;
|
|
340
|
+
tools: TOOLS | undefined;
|
|
341
|
+
}): Promise<TextBatchGenerationResult<TOOLS>> {
|
|
319
342
|
const toolCalls = await Promise.all(
|
|
320
343
|
result.content
|
|
321
344
|
.filter(
|
|
322
345
|
(part): part is LanguageModelV4ToolCall => part.type === 'tool-call',
|
|
323
346
|
)
|
|
324
347
|
.map(toolCall =>
|
|
325
|
-
parseToolCall<
|
|
348
|
+
parseToolCall<TOOLS>({
|
|
326
349
|
toolCall,
|
|
327
|
-
tools
|
|
350
|
+
tools,
|
|
328
351
|
repairToolCall: undefined,
|
|
329
352
|
refineToolInput: undefined,
|
|
330
353
|
instructions: undefined,
|
|
@@ -332,18 +355,18 @@ async function convertGenerateResult(
|
|
|
332
355
|
}),
|
|
333
356
|
),
|
|
334
357
|
);
|
|
335
|
-
const content = convertLanguageModelContent<
|
|
358
|
+
const content = convertLanguageModelContent<TOOLS>({
|
|
336
359
|
content: result.content,
|
|
337
360
|
toolCalls,
|
|
338
361
|
toolOutputs: [],
|
|
339
362
|
toolApprovalRequests: [],
|
|
340
363
|
toolApprovalResponses: [],
|
|
341
|
-
tools
|
|
364
|
+
tools,
|
|
342
365
|
});
|
|
343
366
|
|
|
344
367
|
return {
|
|
345
368
|
content,
|
|
346
|
-
text: content
|
|
369
|
+
text: result.content
|
|
347
370
|
.filter(
|
|
348
371
|
(part): part is Extract<typeof part, { type: 'text' }> =>
|
|
349
372
|
part.type === 'text',
|
package/src/error/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ export { NoVideoGeneratedError } from './no-video-generated-error';
|
|
|
32
32
|
export { NoSuchToolError } from './no-such-tool-error';
|
|
33
33
|
export { StreamProviderError } from './stream-provider-error';
|
|
34
34
|
export { ToolCallRepairError } from './tool-call-repair-error';
|
|
35
|
+
export { ToolChoiceViolationError } from './tool-choice-violation-error';
|
|
35
36
|
export { UnsupportedModelVersionError } from './unsupported-model-version-error';
|
|
36
37
|
export { UIMessageStreamError } from './ui-message-stream-error';
|
|
37
38
|
export { InvalidDataContentError } from '../prompt/invalid-data-content-error';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AISDKError,
|
|
3
|
+
type LanguageModelV4Content,
|
|
4
|
+
type LanguageModelV4ToolChoice,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
6
|
+
import type { FinishReason } from '../types/language-model';
|
|
7
|
+
|
|
8
|
+
const name = 'AI_ToolChoiceViolationError';
|
|
9
|
+
const marker = `vercel.ai.error.${name}`;
|
|
10
|
+
const symbol = Symbol.for(marker);
|
|
11
|
+
|
|
12
|
+
type EnforcedToolChoice = Extract<
|
|
13
|
+
LanguageModelV4ToolChoice,
|
|
14
|
+
{ type: 'required' } | { type: 'tool' }
|
|
15
|
+
>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Thrown when a model response does not satisfy an enforced tool choice.
|
|
19
|
+
*/
|
|
20
|
+
export class ToolChoiceViolationError extends AISDKError {
|
|
21
|
+
private readonly [symbol] = true; // used in isInstance
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The tool choice that the model response did not satisfy.
|
|
25
|
+
*/
|
|
26
|
+
readonly toolChoice: EnforcedToolChoice;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Reason why the model finished generating the response.
|
|
30
|
+
*/
|
|
31
|
+
readonly finishReason: FinishReason;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The provider that returned the response.
|
|
35
|
+
*/
|
|
36
|
+
readonly provider: string;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The model that returned the response.
|
|
40
|
+
*/
|
|
41
|
+
readonly modelId: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The normalized content returned by the model.
|
|
45
|
+
*
|
|
46
|
+
* This can be inspected to recover a tool call that the provider returned as
|
|
47
|
+
* text or reasoning instead of a structured tool call.
|
|
48
|
+
*/
|
|
49
|
+
readonly content: Array<LanguageModelV4Content>;
|
|
50
|
+
|
|
51
|
+
constructor({
|
|
52
|
+
toolChoice,
|
|
53
|
+
finishReason,
|
|
54
|
+
provider,
|
|
55
|
+
modelId,
|
|
56
|
+
content,
|
|
57
|
+
message = toolChoice.type === 'required'
|
|
58
|
+
? 'Model response did not contain a tool call even though tool choice was required.'
|
|
59
|
+
: `Model response did not contain a call to the required tool '${toolChoice.toolName}'.`,
|
|
60
|
+
}: {
|
|
61
|
+
toolChoice: EnforcedToolChoice;
|
|
62
|
+
finishReason: FinishReason;
|
|
63
|
+
provider: string;
|
|
64
|
+
modelId: string;
|
|
65
|
+
content: Array<LanguageModelV4Content>;
|
|
66
|
+
message?: string;
|
|
67
|
+
}) {
|
|
68
|
+
super({ name, message });
|
|
69
|
+
|
|
70
|
+
this.toolChoice = toolChoice;
|
|
71
|
+
this.finishReason = finishReason;
|
|
72
|
+
this.provider = provider;
|
|
73
|
+
this.modelId = modelId;
|
|
74
|
+
this.content = content;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static isInstance(error: unknown): error is ToolChoiceViolationError {
|
|
78
|
+
return AISDKError.hasMarker(error, marker);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
type ProviderOptions,
|
|
16
16
|
type ToolSet,
|
|
17
17
|
} from '@ai-sdk/provider-utils';
|
|
18
|
-
import { NoOutputGeneratedError } from '../error';
|
|
18
|
+
import { NoOutputGeneratedError, ToolChoiceViolationError } from '../error';
|
|
19
19
|
import { logWarnings } from '../logger/log-warnings';
|
|
20
20
|
import { resolveLanguageModel } from '../model/resolve-model';
|
|
21
21
|
import type { ModelMessage } from '../prompt';
|
|
@@ -1073,6 +1073,7 @@ export async function generateText<
|
|
|
1073
1073
|
}),
|
|
1074
1074
|
),
|
|
1075
1075
|
);
|
|
1076
|
+
|
|
1076
1077
|
const toolApprovalRequests: Record<
|
|
1077
1078
|
string,
|
|
1078
1079
|
ToolApprovalRequestOutput<TOOLS>
|
|
@@ -1132,6 +1133,29 @@ export async function generateText<
|
|
|
1132
1133
|
],
|
|
1133
1134
|
});
|
|
1134
1135
|
|
|
1136
|
+
const enforcedToolChoice =
|
|
1137
|
+
stepToolChoice.type === 'required' ||
|
|
1138
|
+
stepToolChoice.type === 'tool'
|
|
1139
|
+
? stepToolChoice
|
|
1140
|
+
: undefined;
|
|
1141
|
+
|
|
1142
|
+
if (
|
|
1143
|
+
enforcedToolChoice != null &&
|
|
1144
|
+
!stepToolCalls.some(
|
|
1145
|
+
toolCall =>
|
|
1146
|
+
enforcedToolChoice.type === 'required' ||
|
|
1147
|
+
toolCall.toolName === enforcedToolChoice.toolName,
|
|
1148
|
+
)
|
|
1149
|
+
) {
|
|
1150
|
+
throw new ToolChoiceViolationError({
|
|
1151
|
+
toolChoice: enforcedToolChoice,
|
|
1152
|
+
finishReason: currentModelResponse.finishReason.unified,
|
|
1153
|
+
provider: stepModel.provider,
|
|
1154
|
+
modelId: stepModel.modelId,
|
|
1155
|
+
content: currentModelResponse.content,
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1135
1159
|
// notify the tools that the tool calls are available:
|
|
1136
1160
|
for (const toolCall of stepToolCalls) {
|
|
1137
1161
|
if (toolCall.invalid) {
|