ai 7.0.79 → 7.0.82
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 +36 -0
- package/dist/index.d.ts +95 -35
- package/dist/index.js +601 -418
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +9 -1
- package/dist/internal/index.js +4 -2
- package/dist/internal/index.js.map +1 -1
- package/dist/test/index.d.ts +4 -1
- package/dist/test/index.js +4 -0
- package/dist/test/index.js.map +1 -1
- package/docs/03-agents/06-policy-tool-approvals.mdx +1 -1
- package/docs/03-agents/06-tool-approvals.mdx +6 -0
- package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +6 -4
- package/docs/03-ai-sdk-core/50-error-handling.mdx +17 -2
- package/docs/04-ai-sdk-ui/03-chatbot-tool-usage.mdx +6 -0
- package/docs/04-ai-sdk-ui/50-stream-protocol.mdx +2 -2
- package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +5 -4
- package/docs/07-reference/01-ai-sdk-core/06-embed-many.mdx +6 -2
- package/docs/07-reference/01-ai-sdk-core/16-tool-loop-agent.mdx +1 -1
- package/docs/07-reference/02-ai-sdk-ui/31-convert-to-model-messages.mdx +2 -2
- package/docs/07-reference/04-ai-sdk-workflow/03-generate-video.mdx +157 -0
- package/docs/07-reference/04-ai-sdk-workflow/index.mdx +6 -0
- package/docs/07-reference/05-ai-sdk-errors/ai-stream-provider-error.mdx +52 -0
- package/docs/07-reference/05-ai-sdk-errors/index.mdx +1 -0
- package/package.json +14 -3
- package/src/embed/embed-many.ts +78 -8
- package/src/error/index.ts +1 -0
- package/src/error/stream-provider-error.ts +78 -0
- package/src/generate-text/execute-tools-from-stream.ts +3 -0
- package/src/generate-text/generate-text-result.ts +2 -1
- package/src/generate-text/generate-text.ts +6 -3
- package/src/generate-text/stream-language-model-call.ts +8 -0
- package/src/generate-text/to-response-messages.ts +1 -0
- package/src/generate-text/tool-approval-configuration.ts +5 -1
- package/src/generate-text/tool-approval-request-output.ts +5 -0
- package/src/middleware/wrap-embedding-model.ts +11 -2
- package/src/model/get-embedding-model-max-input-bytes-per-call.ts +15 -0
- package/src/prompt/content-part.ts +1 -0
- package/src/prompt/normalize-stream-provider-error.ts +131 -0
- package/src/test/mock-embedding-model-v4.ts +9 -0
- package/src/ui/convert-to-model-messages.ts +3 -0
- package/src/ui/process-ui-message-stream.ts +6 -0
- package/src/ui/ui-messages.ts +10 -0
- package/src/ui/validate-ui-messages.ts +10 -0
- package/src/ui-message-stream/to-ui-message-chunk.ts +1 -0
- package/src/ui-message-stream/ui-message-chunks.ts +2 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: generateVideo
|
|
3
|
+
description: API Reference for durable video generation in workflows.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `generateVideo()`
|
|
7
|
+
|
|
8
|
+
Generates videos durably inside a workflow. The helper starts an asynchronous
|
|
9
|
+
video generation job with a Workflow webhook URL, suspends the workflow until
|
|
10
|
+
the provider sends a terminal notification, and then retrieves the completed
|
|
11
|
+
result with one status request.
|
|
12
|
+
|
|
13
|
+
Unlike [`experimental_generateVideo`](/docs/reference/ai-sdk-core/generate-video)
|
|
14
|
+
from `ai`, this helper does not download provider-hosted videos. URL results
|
|
15
|
+
remain URLs so your workflow can decide whether to persist, copy, or process
|
|
16
|
+
them in another step without serializing the video bytes through workflow step
|
|
17
|
+
boundaries.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { experimental_generateVideo as generateVideo } from '@ai-sdk/workflow/video';
|
|
21
|
+
|
|
22
|
+
export async function videoWorkflow(prompt: string) {
|
|
23
|
+
'use workflow';
|
|
24
|
+
|
|
25
|
+
// The workflow suspends while the video renders, consuming no compute.
|
|
26
|
+
const result = await generateVideo({
|
|
27
|
+
model: 'klingai/kling-v3.0-t2v',
|
|
28
|
+
prompt,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return result.videos;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The selected model must support asynchronous start and status operations and
|
|
36
|
+
provider webhooks. The helper must be called from a Workflow SDK workflow.
|
|
37
|
+
|
|
38
|
+
## Import
|
|
39
|
+
|
|
40
|
+
<Snippet
|
|
41
|
+
text={`import { experimental_generateVideo as generateVideo } from "@ai-sdk/workflow/video"`}
|
|
42
|
+
prompt={false}
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
## Parameters
|
|
46
|
+
|
|
47
|
+
The helper accepts the same parameters as `experimental_startVideo` from `ai`,
|
|
48
|
+
except `webhookUrl` and `abortSignal`. It creates and manages the webhook URL.
|
|
49
|
+
|
|
50
|
+
<PropertiesTable
|
|
51
|
+
content={[
|
|
52
|
+
{
|
|
53
|
+
name: 'model',
|
|
54
|
+
type: 'VideoModel',
|
|
55
|
+
isRequired: true,
|
|
56
|
+
description:
|
|
57
|
+
'The asynchronous video model to use. A string compatible with Vercel AI Gateway or a serializable provider model.',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'prompt',
|
|
61
|
+
type: 'string | GenerateVideoPrompt',
|
|
62
|
+
isRequired: true,
|
|
63
|
+
description: 'The prompt or image prompt used to generate the video.',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'n',
|
|
67
|
+
type: 'number',
|
|
68
|
+
isOptional: true,
|
|
69
|
+
description: 'Number of videos to generate. Default: 1.',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'aspectRatio',
|
|
73
|
+
type: '`${number}:${number}` | "adaptive"',
|
|
74
|
+
isOptional: true,
|
|
75
|
+
description: 'Aspect ratio of the generated videos.',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'resolution',
|
|
79
|
+
type: '`${number}x${number}`',
|
|
80
|
+
isOptional: true,
|
|
81
|
+
description: 'Resolution of the generated videos.',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'duration',
|
|
85
|
+
type: 'number',
|
|
86
|
+
isOptional: true,
|
|
87
|
+
description: 'Duration of each generated video in seconds.',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'maxVideosPerCall',
|
|
91
|
+
type: 'number',
|
|
92
|
+
isOptional: true,
|
|
93
|
+
description: 'Maximum number of videos that may be started in one call.',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'fps',
|
|
97
|
+
type: 'number',
|
|
98
|
+
isOptional: true,
|
|
99
|
+
description: 'Frames per second for the generated videos.',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'seed',
|
|
103
|
+
type: 'number',
|
|
104
|
+
isOptional: true,
|
|
105
|
+
description: 'Seed used for deterministic generation when supported.',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'frameImages',
|
|
109
|
+
type: 'Array<{ image: DataContent; frameType: VideoModelV4FrameType }>',
|
|
110
|
+
isOptional: true,
|
|
111
|
+
description: 'Role-tagged first and last frame images.',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'inputReferences',
|
|
115
|
+
type: 'Array<DataContent | { data: DataContent; mediaType?: string }>',
|
|
116
|
+
isOptional: true,
|
|
117
|
+
description: 'Reference images or videos for the generation.',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'generateAudio',
|
|
121
|
+
type: 'boolean',
|
|
122
|
+
isOptional: true,
|
|
123
|
+
description: 'Whether to generate audio with the video.',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'providerOptions',
|
|
127
|
+
type: 'ProviderOptions',
|
|
128
|
+
isOptional: true,
|
|
129
|
+
description: 'Additional provider-specific options.',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'headers',
|
|
133
|
+
type: 'Record<string, string>',
|
|
134
|
+
isOptional: true,
|
|
135
|
+
description: 'Additional HTTP headers for start and status requests.',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'maxRetries',
|
|
139
|
+
type: 'number',
|
|
140
|
+
isOptional: true,
|
|
141
|
+
description:
|
|
142
|
+
'Maximum number of AI SDK retries for each start and status request. Default: 2.',
|
|
143
|
+
},
|
|
144
|
+
]}
|
|
145
|
+
/>
|
|
146
|
+
|
|
147
|
+
## Returns
|
|
148
|
+
|
|
149
|
+
Returns the completed status result from the provider. `videos` contains raw
|
|
150
|
+
provider video data discriminated by `type`:
|
|
151
|
+
|
|
152
|
+
- `url`: A provider-hosted URL and media type
|
|
153
|
+
- `base64`: Base64-encoded video data and media type
|
|
154
|
+
- `binary`: A `Uint8Array` and media type
|
|
155
|
+
|
|
156
|
+
Hosted URLs can expire. Handle any video you need to retain in a separate
|
|
157
|
+
workflow step.
|
|
@@ -22,5 +22,11 @@ collapsed: true
|
|
|
22
22
|
'Chat transport with automatic stream reconnection for workflow-based apps.',
|
|
23
23
|
href: '/docs/reference/ai-sdk-workflow/workflow-chat-transport',
|
|
24
24
|
},
|
|
25
|
+
{
|
|
26
|
+
title: 'generateVideo',
|
|
27
|
+
description:
|
|
28
|
+
'Generate videos durably with provider webhooks and no implicit downloads.',
|
|
29
|
+
href: '/docs/reference/ai-sdk-workflow/generate-video',
|
|
30
|
+
},
|
|
25
31
|
]}
|
|
26
32
|
/>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: AI_StreamProviderError
|
|
3
|
+
description: Learn how to handle AI_StreamProviderError
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AI_StreamProviderError
|
|
7
|
+
|
|
8
|
+
This error represents a well-formed error event reported by a provider after a
|
|
9
|
+
model response stream has started. The AI SDK exposes it in streaming error
|
|
10
|
+
parts and callbacks such as the `streamText` `onError` callback.
|
|
11
|
+
|
|
12
|
+
## Properties
|
|
13
|
+
|
|
14
|
+
- `message`: The provider error message
|
|
15
|
+
- `type`: The provider-defined error type (optional)
|
|
16
|
+
- `code`: The provider-defined error code as a string or number (optional)
|
|
17
|
+
- `statusCode`: The HTTP-equivalent status code when supplied by or inferable from provider metadata (optional)
|
|
18
|
+
- `isRetryable`: Whether retrying the model call may succeed
|
|
19
|
+
- `data`: The original provider error payload (optional)
|
|
20
|
+
- `cause`: The underlying error that caused the failure (optional)
|
|
21
|
+
|
|
22
|
+
`isRetryable` is a retry classification, not an automatic retry. A stream may
|
|
23
|
+
already contain partial output, so applications should decide whether to
|
|
24
|
+
discard, replace, or preserve that output before starting another model call.
|
|
25
|
+
|
|
26
|
+
## Checking for this Error
|
|
27
|
+
|
|
28
|
+
Use `StreamProviderError.isInstance` so identification also works when multiple
|
|
29
|
+
AI SDK versions are present:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { StreamProviderError, streamText } from 'ai';
|
|
33
|
+
|
|
34
|
+
const result = streamText({
|
|
35
|
+
model: __MODEL__,
|
|
36
|
+
prompt: 'Write a vegetarian lasagna recipe for 4 people.',
|
|
37
|
+
onError: ({ error }) => {
|
|
38
|
+
if (StreamProviderError.isInstance(error) && error.isRetryable) {
|
|
39
|
+
// Schedule an application-managed retry.
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Providers may not include enough metadata to determine a status code. In that
|
|
46
|
+
case, `statusCode` is `undefined`, and retryability is classified
|
|
47
|
+
conservatively. Provider-specific status and retry mappings are supplied by the
|
|
48
|
+
provider adapter rather than inferred from arbitrary provider error type or code
|
|
49
|
+
substrings. Provider `type` and `code` values are preserved independently, even
|
|
50
|
+
when a numeric code is also used to determine `statusCode`. Errors that are
|
|
51
|
+
already `Error` instances and malformed or unknown provider values are
|
|
52
|
+
preserved unchanged.
|
|
@@ -33,6 +33,7 @@ collapsed: true
|
|
|
33
33
|
- [AI_NoSuchProviderReferenceError](/docs/reference/ai-sdk-errors/ai-no-such-provider-reference-error)
|
|
34
34
|
- [AI_NoSuchToolError](/docs/reference/ai-sdk-errors/ai-no-such-tool-error)
|
|
35
35
|
- [AI_RetryError](/docs/reference/ai-sdk-errors/ai-retry-error)
|
|
36
|
+
- [AI_StreamProviderError](/docs/reference/ai-sdk-errors/ai-stream-provider-error)
|
|
36
37
|
- [AI_ToolCallNotFoundForApprovalError](/docs/reference/ai-sdk-errors/ai-tool-call-not-found-for-approval-error)
|
|
37
38
|
- [AI_ToolCallRepairError](/docs/reference/ai-sdk-errors/ai-tool-call-repair-error)
|
|
38
39
|
- [AI_TooManyEmbeddingValuesForCallError](/docs/reference/ai-sdk-errors/ai-too-many-embedding-values-for-call-error)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.82",
|
|
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,13 +42,24 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ai-sdk/gateway": "4.0.
|
|
45
|
+
"@ai-sdk/gateway": "4.0.67",
|
|
46
46
|
"@ai-sdk/provider": "4.0.8",
|
|
47
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
47
|
+
"@ai-sdk/provider-utils": "5.0.32"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
+
"@ai-sdk/amazon-bedrock": "5.0.65",
|
|
51
|
+
"@ai-sdk/deepseek": "3.0.34",
|
|
52
|
+
"@ai-sdk/google": "4.0.53",
|
|
53
|
+
"@ai-sdk/groq": "4.0.33",
|
|
54
|
+
"@ai-sdk/huggingface": "2.0.39",
|
|
55
|
+
"@ai-sdk/moonshotai": "3.0.41",
|
|
56
|
+
"@ai-sdk/open-responses": "2.0.34",
|
|
57
|
+
"@ai-sdk/openai": "4.0.49",
|
|
50
58
|
"@ai-sdk/test-server": "2.0.1",
|
|
59
|
+
"@ai-sdk/xai": "4.0.47",
|
|
51
60
|
"@edge-runtime/vm": "^5.0.0",
|
|
61
|
+
"@smithy/eventstream-codec": "^4.3.3",
|
|
62
|
+
"@smithy/util-utf8": "^4.3.3",
|
|
52
63
|
"@types/json-schema": "7.0.15",
|
|
53
64
|
"@types/node": "22.19.19",
|
|
54
65
|
"@vercel/ai-tsconfig": "0.0.0",
|
package/src/embed/embed-many.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type ProviderOptions,
|
|
5
5
|
} from '@ai-sdk/provider-utils';
|
|
6
6
|
import { logWarnings } from '../logger/log-warnings';
|
|
7
|
+
import { getEmbeddingModelMaxInputBytesPerCall } from '../model/get-embedding-model-max-input-bytes-per-call';
|
|
7
8
|
import { resolveEmbeddingModel } from '../model/resolve-model';
|
|
8
9
|
import { createTelemetryDispatcher } from '../telemetry/create-telemetry-dispatcher';
|
|
9
10
|
import type { TelemetryOptions } from '../telemetry/telemetry-options';
|
|
@@ -26,8 +27,9 @@ const originalGenerateCallId = createIdGenerator({
|
|
|
26
27
|
* Embed several values using an embedding model. The type of the value is defined
|
|
27
28
|
* by the embedding model.
|
|
28
29
|
*
|
|
29
|
-
* `embedMany` automatically splits large requests into smaller chunks
|
|
30
|
-
* has a limit on
|
|
30
|
+
* `embedMany` automatically splits large requests into smaller chunks when the
|
|
31
|
+
* model has a limit on either the number of embeddings or the UTF-8 input bytes
|
|
32
|
+
* that can be processed in a single call.
|
|
31
33
|
*
|
|
32
34
|
* @param model - The embedding model to use.
|
|
33
35
|
* @param values - The values that should be embedded.
|
|
@@ -197,11 +199,22 @@ export async function embedMany({
|
|
|
197
199
|
});
|
|
198
200
|
|
|
199
201
|
try {
|
|
200
|
-
const [
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
const [
|
|
203
|
+
maxEmbeddingsPerCall,
|
|
204
|
+
maxInputBytesPerCall,
|
|
205
|
+
supportsParallelCalls,
|
|
206
|
+
] = await Promise.all([
|
|
207
|
+
model.maxEmbeddingsPerCall,
|
|
208
|
+
getEmbeddingModelMaxInputBytesPerCall(model),
|
|
209
|
+
model.supportsParallelCalls,
|
|
210
|
+
]);
|
|
211
|
+
|
|
212
|
+
const hasEmbeddingLimit =
|
|
213
|
+
maxEmbeddingsPerCall != null && maxEmbeddingsPerCall !== Infinity;
|
|
214
|
+
const hasInputByteLimit =
|
|
215
|
+
maxInputBytesPerCall != null && maxInputBytesPerCall !== Infinity;
|
|
216
|
+
|
|
217
|
+
if (!hasEmbeddingLimit && !hasInputByteLimit) {
|
|
205
218
|
const { embeddings, usage, warnings, response, providerMetadata } =
|
|
206
219
|
await retry(async () => {
|
|
207
220
|
const embedCallId = generateCallId();
|
|
@@ -283,7 +296,15 @@ export async function embedMany({
|
|
|
283
296
|
});
|
|
284
297
|
}
|
|
285
298
|
|
|
286
|
-
const valueChunks =
|
|
299
|
+
const valueChunks = splitByEmbeddingLimits({
|
|
300
|
+
values,
|
|
301
|
+
maxEmbeddingsPerCall: hasEmbeddingLimit
|
|
302
|
+
? maxEmbeddingsPerCall
|
|
303
|
+
: Infinity,
|
|
304
|
+
maxInputBytesPerCall: hasInputByteLimit
|
|
305
|
+
? maxInputBytesPerCall
|
|
306
|
+
: Infinity,
|
|
307
|
+
});
|
|
287
308
|
|
|
288
309
|
const embeddings: Array<Embedding> = [];
|
|
289
310
|
const warnings: Array<Warning> = [];
|
|
@@ -415,6 +436,55 @@ export async function embedMany({
|
|
|
415
436
|
});
|
|
416
437
|
}
|
|
417
438
|
|
|
439
|
+
const textEncoder = new TextEncoder();
|
|
440
|
+
|
|
441
|
+
function splitByEmbeddingLimits({
|
|
442
|
+
values,
|
|
443
|
+
maxEmbeddingsPerCall,
|
|
444
|
+
maxInputBytesPerCall,
|
|
445
|
+
}: {
|
|
446
|
+
values: Array<string>;
|
|
447
|
+
maxEmbeddingsPerCall: number;
|
|
448
|
+
maxInputBytesPerCall: number;
|
|
449
|
+
}): Array<Array<string>> {
|
|
450
|
+
if (maxEmbeddingsPerCall <= 0) {
|
|
451
|
+
throw new Error('maxEmbeddingsPerCall must be greater than 0');
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (maxInputBytesPerCall <= 0) {
|
|
455
|
+
throw new Error('maxInputBytesPerCall must be greater than 0');
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (values.length === 0) {
|
|
459
|
+
return [];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const chunks: Array<Array<string>> = [];
|
|
463
|
+
let currentChunk: Array<string> = [];
|
|
464
|
+
let currentInputBytes = 0;
|
|
465
|
+
|
|
466
|
+
for (const value of values) {
|
|
467
|
+
const inputBytes = textEncoder.encode(value).length;
|
|
468
|
+
|
|
469
|
+
if (
|
|
470
|
+
currentChunk.length > 0 &&
|
|
471
|
+
(currentChunk.length >= maxEmbeddingsPerCall ||
|
|
472
|
+
currentInputBytes + inputBytes > maxInputBytesPerCall)
|
|
473
|
+
) {
|
|
474
|
+
chunks.push(currentChunk);
|
|
475
|
+
currentChunk = [];
|
|
476
|
+
currentInputBytes = 0;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
currentChunk.push(value);
|
|
480
|
+
currentInputBytes += inputBytes;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
chunks.push(currentChunk);
|
|
484
|
+
|
|
485
|
+
return chunks;
|
|
486
|
+
}
|
|
487
|
+
|
|
418
488
|
class DefaultEmbedManyResult implements EmbedManyResult {
|
|
419
489
|
readonly values: EmbedManyResult['values'];
|
|
420
490
|
readonly embeddings: EmbedManyResult['embeddings'];
|
package/src/error/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ export { NoTranscriptGeneratedError } from './no-transcript-generated-error';
|
|
|
30
30
|
export { NoTranslationGeneratedError } from './no-translation-generated-error';
|
|
31
31
|
export { NoVideoGeneratedError } from './no-video-generated-error';
|
|
32
32
|
export { NoSuchToolError } from './no-such-tool-error';
|
|
33
|
+
export { StreamProviderError } from './stream-provider-error';
|
|
33
34
|
export { ToolCallRepairError } from './tool-call-repair-error';
|
|
34
35
|
export { UnsupportedModelVersionError } from './unsupported-model-version-error';
|
|
35
36
|
export { UIMessageStreamError } from './ui-message-stream-error';
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { AISDKError } from '@ai-sdk/provider';
|
|
2
|
+
|
|
3
|
+
const name = 'AI_StreamProviderError';
|
|
4
|
+
const marker = `vercel.ai.error.${name}`;
|
|
5
|
+
const symbol = Symbol.for(marker);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Error reported by a provider after a model response stream has started.
|
|
9
|
+
*/
|
|
10
|
+
export class StreamProviderError extends AISDKError {
|
|
11
|
+
private readonly [symbol] = true; // used in isInstance
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Provider-defined error type, when supplied by the provider.
|
|
15
|
+
*/
|
|
16
|
+
readonly type?: string;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Provider-defined error code, when supplied by the provider.
|
|
20
|
+
*/
|
|
21
|
+
readonly code?: string | number;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* HTTP-equivalent status code, when supplied by or inferable from the
|
|
25
|
+
* provider error metadata.
|
|
26
|
+
*/
|
|
27
|
+
readonly statusCode?: number;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Whether retrying the model call may succeed.
|
|
31
|
+
*/
|
|
32
|
+
readonly isRetryable: boolean;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Original provider error payload.
|
|
36
|
+
*/
|
|
37
|
+
readonly data?: unknown;
|
|
38
|
+
|
|
39
|
+
constructor({
|
|
40
|
+
message,
|
|
41
|
+
type,
|
|
42
|
+
code,
|
|
43
|
+
statusCode,
|
|
44
|
+
isRetryable = isRetryableStatusCode(statusCode),
|
|
45
|
+
data,
|
|
46
|
+
cause,
|
|
47
|
+
}: {
|
|
48
|
+
message: string;
|
|
49
|
+
type?: string;
|
|
50
|
+
code?: string | number;
|
|
51
|
+
statusCode?: number;
|
|
52
|
+
isRetryable?: boolean;
|
|
53
|
+
data?: unknown;
|
|
54
|
+
cause?: unknown;
|
|
55
|
+
}) {
|
|
56
|
+
super({ name, message, cause });
|
|
57
|
+
|
|
58
|
+
this.type = type;
|
|
59
|
+
this.code = code;
|
|
60
|
+
this.statusCode = statusCode;
|
|
61
|
+
this.isRetryable = isRetryable;
|
|
62
|
+
this.data = data;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static isInstance(error: unknown): error is StreamProviderError {
|
|
66
|
+
return AISDKError.hasMarker(error, marker);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isRetryableStatusCode(statusCode: number | undefined): boolean {
|
|
71
|
+
return (
|
|
72
|
+
statusCode != null &&
|
|
73
|
+
(statusCode === 408 ||
|
|
74
|
+
statusCode === 409 ||
|
|
75
|
+
statusCode === 429 ||
|
|
76
|
+
statusCode >= 500)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -141,6 +141,9 @@ export function executeToolsFromStream<
|
|
|
141
141
|
type: 'tool-approval-request',
|
|
142
142
|
approvalId,
|
|
143
143
|
toolCall: chunk,
|
|
144
|
+
...(toolApprovalStatus.reason != null
|
|
145
|
+
? { reason: toolApprovalStatus.reason }
|
|
146
|
+
: {}),
|
|
144
147
|
...(signature != null ? { signature } : {}),
|
|
145
148
|
});
|
|
146
149
|
|
|
@@ -175,7 +175,8 @@ export interface GenerateTextResult<
|
|
|
175
175
|
* The generated output according to the `output` specification.
|
|
176
176
|
*
|
|
177
177
|
* @throws {NoOutputGeneratedError} When no output is available, for example
|
|
178
|
-
* when the final step
|
|
178
|
+
* when the final step finishes with a `tool-calls` reason, or when it
|
|
179
|
+
* contains no text and does not finish with a `stop` reason.
|
|
179
180
|
*/
|
|
180
181
|
readonly output: InferCompleteOutput<OUTPUT>;
|
|
181
182
|
}
|
|
@@ -1201,6 +1201,9 @@ export async function generateText<
|
|
|
1201
1201
|
type: 'tool-approval-request',
|
|
1202
1202
|
approvalId,
|
|
1203
1203
|
toolCall,
|
|
1204
|
+
...(toolApprovalStatus.reason != null
|
|
1205
|
+
? { reason: toolApprovalStatus.reason }
|
|
1206
|
+
: {}),
|
|
1204
1207
|
...(signature != null ? { signature } : {}),
|
|
1205
1208
|
};
|
|
1206
1209
|
blockedToolCallIds.add(toolCall.toolCallId);
|
|
@@ -1538,12 +1541,12 @@ export async function generateText<
|
|
|
1538
1541
|
callbacks: [onEnd, telemetryDispatcher.onEnd],
|
|
1539
1542
|
});
|
|
1540
1543
|
|
|
1541
|
-
// parse output
|
|
1542
|
-
//
|
|
1544
|
+
// parse output for stop responses and non-empty responses that are not
|
|
1545
|
+
// tool calls:
|
|
1543
1546
|
let resolvedOutput;
|
|
1544
1547
|
if (
|
|
1545
1548
|
lastStep.finishReason === 'stop' ||
|
|
1546
|
-
(lastStep.finishReason
|
|
1549
|
+
(lastStep.finishReason !== 'tool-calls' && lastStep.text.length > 0)
|
|
1547
1550
|
) {
|
|
1548
1551
|
const outputSpecification = output ?? text();
|
|
1549
1552
|
resolvedOutput = await outputSpecification.parseCompleteOutput(
|
|
@@ -20,6 +20,7 @@ import { getOwn } from '../util/get-own';
|
|
|
20
20
|
import type { Instructions, Prompt } from '../prompt';
|
|
21
21
|
import { convertToLanguageModelPrompt } from '../prompt/convert-to-language-model-prompt';
|
|
22
22
|
import type { LanguageModelCallOptions } from '../prompt/language-model-call-options';
|
|
23
|
+
import { normalizeStreamProviderError } from '../prompt/normalize-stream-provider-error';
|
|
23
24
|
import { prepareToolChoice } from '../prompt/prepare-tool-choice';
|
|
24
25
|
import { prepareTools } from '../prompt/prepare-tools';
|
|
25
26
|
import { standardizePrompt } from '../prompt/standardize-prompt';
|
|
@@ -447,6 +448,13 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
447
448
|
}
|
|
448
449
|
|
|
449
450
|
switch (chunk.type) {
|
|
451
|
+
case 'error':
|
|
452
|
+
controller.enqueue({
|
|
453
|
+
type: 'error',
|
|
454
|
+
error: normalizeStreamProviderError(chunk.error),
|
|
455
|
+
});
|
|
456
|
+
break;
|
|
457
|
+
|
|
450
458
|
case 'text-start':
|
|
451
459
|
upsertTextContentPart({
|
|
452
460
|
content: modelCallContent,
|
|
@@ -133,6 +133,7 @@ export async function toResponseMessages<TOOLS extends ToolSet>({
|
|
|
133
133
|
type: 'tool-approval-request',
|
|
134
134
|
approvalId: part.approvalId,
|
|
135
135
|
toolCallId: part.toolCall.toolCallId,
|
|
136
|
+
...(part.reason != null ? { reason: part.reason } : {}),
|
|
136
137
|
isAutomatic: part.isAutomatic,
|
|
137
138
|
...(part.signature != null ? { signature: part.signature } : {}),
|
|
138
139
|
});
|
|
@@ -19,6 +19,8 @@ import type { TypedToolCall } from './tool-call';
|
|
|
19
19
|
* - 'user-approval': The tool requires user approval.
|
|
20
20
|
*
|
|
21
21
|
* In addition to the string statuses, you can also use object statuses with a reason property.
|
|
22
|
+
* For approved and denied statuses, the reason is emitted on the approval response.
|
|
23
|
+
* For user-approval statuses, the reason is emitted on the approval request so it can be shown to the approver.
|
|
22
24
|
*
|
|
23
25
|
* `undefined` is treated as the `not-applicable` status.
|
|
24
26
|
*/
|
|
@@ -31,7 +33,7 @@ export type ToolApprovalStatus =
|
|
|
31
33
|
| { type: 'not-applicable'; reason?: never }
|
|
32
34
|
| { type: 'approved'; reason?: string }
|
|
33
35
|
| { type: 'denied'; reason?: string }
|
|
34
|
-
| { type: 'user-approval'; reason?:
|
|
36
|
+
| { type: 'user-approval'; reason?: string };
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
39
|
* Function that is called to determine if the tool needs approval before it can be executed.
|
|
@@ -107,6 +109,8 @@ export type GenericToolApprovalFunction<
|
|
|
107
109
|
* - 'user-approval': The tool requires user approval.
|
|
108
110
|
*
|
|
109
111
|
* In addition to the string statuses, you can also use object statuses with a reason property.
|
|
112
|
+
* For approved and denied statuses, the reason is emitted on the approval response.
|
|
113
|
+
* For user-approval statuses, the reason is emitted on the approval request so it can be shown to the approver.
|
|
110
114
|
*/
|
|
111
115
|
export type ToolApprovalConfiguration<
|
|
112
116
|
TOOLS extends ToolSet,
|
|
@@ -19,6 +19,11 @@ export type ToolApprovalRequestOutput<TOOLS extends ToolSet> = {
|
|
|
19
19
|
*/
|
|
20
20
|
toolCall: TypedToolCall<TOOLS>;
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Reason why the tool call requires approval.
|
|
24
|
+
*/
|
|
25
|
+
reason?: string;
|
|
26
|
+
|
|
22
27
|
/**
|
|
23
28
|
* Flag indicating whether the tool was automatically approved or denied.
|
|
24
29
|
*
|
|
@@ -4,8 +4,15 @@ import type {
|
|
|
4
4
|
EmbeddingModelV4CallOptions,
|
|
5
5
|
EmbeddingModelV4Result,
|
|
6
6
|
} from '@ai-sdk/provider';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
asArray,
|
|
9
|
+
EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL,
|
|
10
|
+
} from '@ai-sdk/provider-utils';
|
|
8
11
|
import { asEmbeddingModelV4 } from '../model/as-embedding-model-v4';
|
|
12
|
+
import {
|
|
13
|
+
getEmbeddingModelMaxInputBytesPerCall,
|
|
14
|
+
type EmbeddingModelWithMaxInputBytesPerCall,
|
|
15
|
+
} from '../model/get-embedding-model-max-input-bytes-per-call';
|
|
9
16
|
import type { EmbeddingModelMiddleware } from '../types';
|
|
10
17
|
|
|
11
18
|
/**
|
|
@@ -56,7 +63,7 @@ const doWrap = ({
|
|
|
56
63
|
middleware: EmbeddingModelMiddleware;
|
|
57
64
|
modelId?: string;
|
|
58
65
|
providerId?: string;
|
|
59
|
-
}):
|
|
66
|
+
}): EmbeddingModelWithMaxInputBytesPerCall => {
|
|
60
67
|
async function doTransform({
|
|
61
68
|
params,
|
|
62
69
|
}: {
|
|
@@ -71,6 +78,8 @@ const doWrap = ({
|
|
|
71
78
|
modelId: modelId ?? overrideModelId?.({ model }) ?? model.modelId,
|
|
72
79
|
maxEmbeddingsPerCall:
|
|
73
80
|
overrideMaxEmbeddingsPerCall?.({ model }) ?? model.maxEmbeddingsPerCall,
|
|
81
|
+
[EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL]:
|
|
82
|
+
getEmbeddingModelMaxInputBytesPerCall(model),
|
|
74
83
|
supportsParallelCalls:
|
|
75
84
|
overrideSupportsParallelCalls?.({ model }) ?? model.supportsParallelCalls,
|
|
76
85
|
async doEmbed(
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { EmbeddingModelV4 } from '@ai-sdk/provider';
|
|
2
|
+
import { EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL } from '@ai-sdk/provider-utils';
|
|
3
|
+
|
|
4
|
+
export type EmbeddingModelWithMaxInputBytesPerCall = EmbeddingModelV4 & {
|
|
5
|
+
readonly [EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL]?:
|
|
6
|
+
| PromiseLike<number | undefined>
|
|
7
|
+
| number
|
|
8
|
+
| undefined;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function getEmbeddingModelMaxInputBytesPerCall(model: EmbeddingModelV4) {
|
|
12
|
+
return (model as EmbeddingModelWithMaxInputBytesPerCall)[
|
|
13
|
+
EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL
|
|
14
|
+
];
|
|
15
|
+
}
|