ai 7.0.111 → 7.0.113
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 +41 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +345 -222
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +2 -1
- package/dist/internal/index.js +125 -31
- package/dist/internal/index.js.map +1 -1
- package/docs/03-agents/06-tool-approvals.mdx +16 -0
- package/docs/03-ai-sdk-core/37-speech.mdx +2 -0
- package/docs/03-ai-sdk-core/40-middleware.mdx +92 -7
- package/docs/06-advanced/02-stopping-streams.mdx +8 -0
- package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +3 -3
- package/docs/07-reference/01-ai-sdk-core/12-generate-speech.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/32-validate-ui-messages.mdx +18 -0
- package/docs/07-reference/02-ai-sdk-ui/40-create-ui-message-stream.mdx +9 -3
- package/package.json +12 -12
- package/src/embed/embed-many.ts +18 -2
- package/src/generate-speech/generate-speech.ts +15 -4
- package/src/generate-speech/generated-audio-file.ts +0 -8
- package/src/generate-text/execute-tools-from-stream.ts +0 -2
- package/src/generate-text/generate-text.ts +1 -0
- package/src/generate-text/generated-file.ts +0 -8
- package/src/generate-text/invoke-tool-callbacks-from-stream.ts +9 -8
- package/src/generate-text/output.ts +0 -2
- package/src/generate-text/parse-tool-call.ts +38 -25
- package/src/generate-text/prune-messages.ts +3 -1
- package/src/generate-text/stream-text.ts +1 -0
- package/src/generate-text/to-response-messages.ts +7 -0
- package/src/generate-text/tool-call.ts +26 -0
- package/src/generate-text/validate-tool-approvals.ts +39 -3
- package/src/generate-video/generate-video.ts +0 -2
- package/src/middleware/extract-reasoning-middleware.ts +1 -1
- package/src/middleware/wrap-embedding-model.ts +9 -1
- package/src/model/get-embedding-model-provider-options-transformer.ts +17 -0
- package/src/prompt/content-part.ts +3 -0
- package/src/prompt/convert-to-language-model-prompt.ts +12 -2
- package/src/prompt/file-part-data.ts +11 -1
- package/src/registry/custom-provider.ts +12 -5
- package/src/ui/chat.ts +108 -17
- package/src/ui/convert-to-model-messages.ts +8 -0
- package/src/ui/direct-chat-transport.ts +2 -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 +100 -134
- package/src/ui-message-stream/handle-ui-message-stream-finish.ts +5 -3
- package/src/ui-message-stream/to-ui-message-chunk.ts +7 -0
- package/src/ui-message-stream/ui-message-chunks.ts +2 -0
- package/src/ui-message-stream/ui-message-stream-on-end-callback.ts +8 -0
- package/src/ui-message-stream/ui-message-stream-outcome.ts +4 -0
- package/src/util/write-to-server-response.ts +0 -2
|
@@ -272,6 +272,22 @@ When a manual approval status includes a reason, it is available as
|
|
|
272
272
|
`part.approval.requestReason`. A reason supplied with
|
|
273
273
|
`addToolApprovalResponse` is stored separately as `part.approval.reason`.
|
|
274
274
|
|
|
275
|
+
### Schema transforms and persisted approvals
|
|
276
|
+
|
|
277
|
+
Approval requests preserve the original schema input in `inputSchemaInput` when
|
|
278
|
+
it differs from the input presented for approval. Keep this field when persisting
|
|
279
|
+
`responseMessages` or UI messages; the UI message conversion functions preserve it
|
|
280
|
+
automatically.
|
|
281
|
+
|
|
282
|
+
On continuation, the SDK reconstructs the transformed input and checks that it
|
|
283
|
+
matches the approved input. It never replaces the approved input with a different
|
|
284
|
+
value. Older or projected histories that omit the original input are rejected as
|
|
285
|
+
invalid tool input if revalidation fails or would change the approved value.
|
|
286
|
+
|
|
287
|
+
The original input is included in persisted messages and UI approval streams,
|
|
288
|
+
including fields removed by schema transforms. A transform that removes a field
|
|
289
|
+
does not redact it from approval metadata.
|
|
290
|
+
|
|
275
291
|
## Security Considerations
|
|
276
292
|
|
|
277
293
|
### Trust model
|
|
@@ -147,6 +147,8 @@ try {
|
|
|
147
147
|
| [Google](/providers/ai-sdk-providers/google#speech-models) | `gemini-2.5-flash-preview-tts` |
|
|
148
148
|
| [Google](/providers/ai-sdk-providers/google#speech-models) | `gemini-2.5-pro-preview-tts` |
|
|
149
149
|
| [Google](/providers/ai-sdk-providers/google#speech-models) | `gemini-3.1-flash-tts-preview` |
|
|
150
|
+
| [Google](/providers/ai-sdk-providers/google#speech-models) | `gemini-3.8-flash-tts` |
|
|
151
|
+
| [Google](/providers/ai-sdk-providers/google#speech-models) | `gemini-3.8-flash-lite-tts` |
|
|
150
152
|
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#speech-models) | `gemini-2.5-flash-tts` |
|
|
151
153
|
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#speech-models) | `gemini-2.5-pro-tts` |
|
|
152
154
|
| [Google Vertex](/providers/ai-sdk-providers/google-vertex#speech-models) | `gemini-2.5-flash-lite-preview-tts` |
|
|
@@ -310,6 +310,9 @@ You can implement any of the following three function to modify the behavior of
|
|
|
310
310
|
3. `wrapStream`: Wraps the `doStream` method of the [language model](https://github.com/vercel/ai/blob/main/packages/provider/src/language-model/v4/language-model-v4.ts).
|
|
311
311
|
You can modify the parameters, call the language model, and modify the result.
|
|
312
312
|
|
|
313
|
+
Every `LanguageModelV4Middleware` object must set
|
|
314
|
+
`specificationVersion: 'v4'`.
|
|
315
|
+
|
|
313
316
|
Here are some examples of how to implement language model middleware:
|
|
314
317
|
|
|
315
318
|
## Examples
|
|
@@ -330,6 +333,8 @@ import type {
|
|
|
330
333
|
} from '@ai-sdk/provider';
|
|
331
334
|
|
|
332
335
|
export const yourLogMiddleware: LanguageModelV4Middleware = {
|
|
336
|
+
specificationVersion: 'v4',
|
|
337
|
+
|
|
333
338
|
wrapGenerate: async ({ doGenerate, params }) => {
|
|
334
339
|
console.log('doGenerate called');
|
|
335
340
|
console.log(`params: ${JSON.stringify(params, null, 2)}`);
|
|
@@ -407,6 +412,8 @@ import type { LanguageModelV4Middleware } from '@ai-sdk/provider';
|
|
|
407
412
|
const cache = new Map<string, any>();
|
|
408
413
|
|
|
409
414
|
export const yourCacheMiddleware: LanguageModelV4Middleware = {
|
|
415
|
+
specificationVersion: 'v4',
|
|
416
|
+
|
|
410
417
|
wrapGenerate: async ({ doGenerate, params }) => {
|
|
411
418
|
const cacheKey = JSON.stringify(params);
|
|
412
419
|
|
|
@@ -439,6 +446,8 @@ This example shows how to use RAG as middleware.
|
|
|
439
446
|
import type { LanguageModelV4Middleware } from '@ai-sdk/provider';
|
|
440
447
|
|
|
441
448
|
export const yourRagMiddleware: LanguageModelV4Middleware = {
|
|
449
|
+
specificationVersion: 'v4',
|
|
450
|
+
|
|
442
451
|
transformParams: async ({ params }) => {
|
|
443
452
|
const lastUserMessageText = getLastUserMessageText({
|
|
444
453
|
prompt: params.prompt,
|
|
@@ -465,28 +474,102 @@ Guard rails are a way to ensure that the generated text of a language model call
|
|
|
465
474
|
is safe and appropriate. This example shows how to use guardrails as middleware.
|
|
466
475
|
|
|
467
476
|
```ts
|
|
468
|
-
import type {
|
|
477
|
+
import type {
|
|
478
|
+
LanguageModelV4Middleware,
|
|
479
|
+
LanguageModelV4StreamPart,
|
|
480
|
+
} from '@ai-sdk/provider';
|
|
481
|
+
|
|
482
|
+
const redactText = (text: string) => text.replace(/badword/g, '<REDACTED>');
|
|
469
483
|
|
|
470
484
|
export const yourGuardrailMiddleware: LanguageModelV4Middleware = {
|
|
485
|
+
specificationVersion: 'v4',
|
|
486
|
+
|
|
471
487
|
wrapGenerate: async ({ doGenerate }) => {
|
|
472
488
|
const result = await doGenerate();
|
|
473
489
|
|
|
474
490
|
// filtering approach, e.g. for PII or other sensitive information:
|
|
475
491
|
const content = result.content.map(part =>
|
|
476
|
-
part.type === 'text'
|
|
477
|
-
? { ...part, text: part.text.replace(/badword/g, '<REDACTED>') }
|
|
478
|
-
: part,
|
|
492
|
+
part.type === 'text' ? { ...part, text: redactText(part.text) } : part,
|
|
479
493
|
);
|
|
480
494
|
|
|
481
495
|
return { ...result, content };
|
|
482
496
|
},
|
|
483
497
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
498
|
+
wrapStream: async ({ doStream }) => {
|
|
499
|
+
const { stream, ...rest } = await doStream();
|
|
500
|
+
|
|
501
|
+
// Keep a separate buffer for each text block in the stream.
|
|
502
|
+
const buffers = new Map<string, string>();
|
|
503
|
+
|
|
504
|
+
const transformStream = new TransformStream<
|
|
505
|
+
LanguageModelV4StreamPart,
|
|
506
|
+
LanguageModelV4StreamPart
|
|
507
|
+
>({
|
|
508
|
+
transform(chunk, controller) {
|
|
509
|
+
if (chunk.type === 'text-start') {
|
|
510
|
+
buffers.set(chunk.id, '');
|
|
511
|
+
controller.enqueue(chunk);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (chunk.type === 'text-delta') {
|
|
516
|
+
buffers.set(chunk.id, (buffers.get(chunk.id) ?? '') + chunk.delta);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (chunk.type === 'text-end') {
|
|
521
|
+
const bufferedText = buffers.get(chunk.id);
|
|
522
|
+
|
|
523
|
+
if (bufferedText != null) {
|
|
524
|
+
const redactedText = redactText(bufferedText);
|
|
525
|
+
|
|
526
|
+
if (redactedText) {
|
|
527
|
+
controller.enqueue({
|
|
528
|
+
type: 'text-delta',
|
|
529
|
+
id: chunk.id,
|
|
530
|
+
delta: redactedText,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
buffers.delete(chunk.id);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
controller.enqueue(chunk);
|
|
539
|
+
},
|
|
540
|
+
|
|
541
|
+
flush(controller) {
|
|
542
|
+
for (const [id, bufferedText] of buffers) {
|
|
543
|
+
const redactedText = redactText(bufferedText);
|
|
544
|
+
|
|
545
|
+
if (redactedText) {
|
|
546
|
+
controller.enqueue({
|
|
547
|
+
type: 'text-delta',
|
|
548
|
+
id,
|
|
549
|
+
delta: redactedText,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
return {
|
|
557
|
+
stream: stream.pipeThrough(transformStream),
|
|
558
|
+
...rest,
|
|
559
|
+
};
|
|
560
|
+
},
|
|
487
561
|
};
|
|
488
562
|
```
|
|
489
563
|
|
|
564
|
+
<Note>
|
|
565
|
+
The streaming example buffers each text block until `text-end` so matches
|
|
566
|
+
split across `text-delta` chunks cannot leak through. This delays output and
|
|
567
|
+
uses memory proportional to the text block size. Do not redact each delta
|
|
568
|
+
independently. An incremental implementation must retain every possible
|
|
569
|
+
incomplete match, and a fixed-size buffer alone is not safe for unbounded
|
|
570
|
+
variable-length patterns.
|
|
571
|
+
</Note>
|
|
572
|
+
|
|
490
573
|
## Configuring Per Request Custom Metadata
|
|
491
574
|
|
|
492
575
|
To send and access custom metadata in Middleware, you can use `providerOptions`. This is useful when building logging middleware where you want to pass additional context like user IDs, timestamps, or other contextual data that can help with tracking and debugging.
|
|
@@ -497,6 +580,8 @@ __PROVIDER_IMPORT__;
|
|
|
497
580
|
import type { LanguageModelV4Middleware } from '@ai-sdk/provider';
|
|
498
581
|
|
|
499
582
|
export const yourLogMiddleware: LanguageModelV4Middleware = {
|
|
583
|
+
specificationVersion: 'v4',
|
|
584
|
+
|
|
500
585
|
wrapGenerate: async ({ doGenerate, params }) => {
|
|
501
586
|
console.log('METADATA', params?.providerMetadata?.yourLogMiddleware);
|
|
502
587
|
const result = await doGenerate();
|
|
@@ -205,6 +205,14 @@ export async function POST(req: Request) {
|
|
|
205
205
|
|
|
206
206
|
The `consumeStream` function is necessary for proper abort handling in UI message streams. It ensures that the stream is properly consumed even when aborted, preventing potential memory leaks or hanging connections.
|
|
207
207
|
|
|
208
|
+
The `onEnd` callback distinguishes consumer cancellation from an observed
|
|
209
|
+
abort. When the consumer cancels the UI message stream before an outcome is
|
|
210
|
+
declared, such as during a client disconnect, `isCancelled` is `true`,
|
|
211
|
+
`outcome.status` remains `'unknown'`, and `isAborted` remains `false`. When the
|
|
212
|
+
stream observes an `abort` part first, `outcome.status` is `'aborted'`,
|
|
213
|
+
`isAborted` is `true`, and `isCancelled` is absent. Check both flags when the
|
|
214
|
+
same cleanup should run for either case.
|
|
215
|
+
|
|
208
216
|
## AI SDK RSC
|
|
209
217
|
|
|
210
218
|
<Note type="warning">
|
|
@@ -4082,13 +4082,13 @@ To see `streamText` in action, check out [these examples](#examples).
|
|
|
4082
4082
|
},
|
|
4083
4083
|
{
|
|
4084
4084
|
name: 'onEnd',
|
|
4085
|
-
type: '(options: { messages: UIMessage[]; isContinuation: boolean; responseMessage: UIMessage; isAborted: boolean; outcome: UIMessageStreamOutcome; finishReason?: FinishReason; }) => PromiseLike<void> | void',
|
|
4085
|
+
type: '(options: { messages: UIMessage[]; isContinuation: boolean; responseMessage: UIMessage; isAborted: boolean; isCancelled?: true; outcome: UIMessageStreamOutcome; finishReason?: FinishReason; }) => PromiseLike<void> | void',
|
|
4086
4086
|
isOptional: true,
|
|
4087
|
-
description: 'Callback function called when the stream ends. Provides the updated messages, continuation and
|
|
4087
|
+
description: 'Callback function called when the stream ends. Provides the updated messages, continuation, abort and consumer-cancellation state, model finish reason, and operation-level outcome.',
|
|
4088
4088
|
},
|
|
4089
4089
|
{
|
|
4090
4090
|
name: 'onFinish',
|
|
4091
|
-
type: '(options: { messages: UIMessage[]; isContinuation: boolean; responseMessage: UIMessage; isAborted: boolean; outcome: UIMessageStreamOutcome; finishReason?: FinishReason; }) => PromiseLike<void> | void',
|
|
4091
|
+
type: '(options: { messages: UIMessage[]; isContinuation: boolean; responseMessage: UIMessage; isAborted: boolean; isCancelled?: true; outcome: UIMessageStreamOutcome; finishReason?: FinishReason; }) => PromiseLike<void> | void',
|
|
4092
4092
|
isOptional: true,
|
|
4093
4093
|
description: 'Deprecated alias for `onEnd`.',
|
|
4094
4094
|
},
|
|
@@ -79,7 +79,7 @@ const { audio } = await generateSpeech({
|
|
|
79
79
|
type: 'string',
|
|
80
80
|
isOptional: true,
|
|
81
81
|
description:
|
|
82
|
-
'The output format to use for the speech
|
|
82
|
+
'The output format to use for the speech, such as "mp3", "wav", or headerless "audio/l16", "audio/mulaw", and "audio/alaw". Supported formats and defaults vary by provider and model.',
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
name: 'instructions',
|
|
@@ -100,6 +100,24 @@ const validatedMessages = await validateUIMessages({
|
|
|
100
100
|
});
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
When validating approval messages produced with
|
|
104
|
+
`experimental_refineToolInput`, pass the same refinement functions so
|
|
105
|
+
`validateUIMessages` can reconstruct and verify the approved input:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const experimental_refineToolInput = {
|
|
109
|
+
weather: (input: { location: string }) => ({
|
|
110
|
+
location: input.location.trim(),
|
|
111
|
+
}),
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const validatedMessages = await validateUIMessages({
|
|
115
|
+
messages,
|
|
116
|
+
tools,
|
|
117
|
+
experimental_refineToolInput,
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
103
121
|
## Deprecated `rawInput` field
|
|
104
122
|
|
|
105
123
|
For backward compatibility, validation still accepts `rawInput` on tool parts
|
|
@@ -134,7 +134,7 @@ outcomes and call `setOutcome` once.
|
|
|
134
134
|
},
|
|
135
135
|
{
|
|
136
136
|
name: 'onEnd',
|
|
137
|
-
type: '(options: { messages: UIMessage[]; isContinuation: boolean; isAborted: boolean; outcome: UIMessageStreamOutcome; responseMessage: UIMessage; finishReason?: FinishReason }) => PromiseLike<void> | void',
|
|
137
|
+
type: '(options: { messages: UIMessage[]; isContinuation: boolean; isAborted: boolean; isCancelled?: true; outcome: UIMessageStreamOutcome; responseMessage: UIMessage; finishReason?: FinishReason }) => PromiseLike<void> | void',
|
|
138
138
|
description: 'A callback function that is called when the stream ends.',
|
|
139
139
|
properties: [
|
|
140
140
|
{
|
|
@@ -156,11 +156,17 @@ outcomes and call `setOutcome` once.
|
|
|
156
156
|
type: 'boolean',
|
|
157
157
|
description: 'Indicates whether the stream was aborted.',
|
|
158
158
|
},
|
|
159
|
+
{
|
|
160
|
+
name: 'isCancelled',
|
|
161
|
+
type: 'true | undefined',
|
|
162
|
+
description:
|
|
163
|
+
'Present and true when the consumer cancelled the stream before an outcome was declared, for example because the client disconnected.',
|
|
164
|
+
},
|
|
159
165
|
{
|
|
160
166
|
name: 'outcome',
|
|
161
167
|
type: "UIMessageStreamOutcome = { status: 'completed' } | { status: 'failed'; error?: unknown } | { status: 'aborted' } | { status: 'unknown' }",
|
|
162
168
|
description:
|
|
163
|
-
|
|
169
|
+
"The operation-level outcome of the stream. It reflects the stream owner declaration unless a fatal stream-processing failure occurs, and is separate from model finish reasons and individual error chunks. It remains 'unknown' when the consumer cancels before an outcome is declared; check isCancelled to distinguish that case from normal closure without a declared outcome.",
|
|
164
170
|
},
|
|
165
171
|
{
|
|
166
172
|
name: 'responseMessage',
|
|
@@ -180,7 +186,7 @@ outcomes and call `setOutcome` once.
|
|
|
180
186
|
},
|
|
181
187
|
{
|
|
182
188
|
name: 'onFinish',
|
|
183
|
-
type: '(options: { messages: UIMessage[]; isContinuation: boolean; isAborted: boolean; outcome: UIMessageStreamOutcome; responseMessage: UIMessage; finishReason?: FinishReason }) => PromiseLike<void> | void',
|
|
189
|
+
type: '(options: { messages: UIMessage[]; isContinuation: boolean; isAborted: boolean; isCancelled?: true; outcome: UIMessageStreamOutcome; responseMessage: UIMessage; finishReason?: FinishReason }) => PromiseLike<void> | void',
|
|
184
190
|
description: 'Deprecated alias for `onEnd`.',
|
|
185
191
|
},
|
|
186
192
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.113",
|
|
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.91",
|
|
46
|
+
"@ai-sdk/provider": "4.0.18",
|
|
47
|
+
"@ai-sdk/provider-utils": "5.0.47"
|
|
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.93",
|
|
51
|
+
"@ai-sdk/deepseek": "3.0.52",
|
|
52
|
+
"@ai-sdk/google": "4.0.79",
|
|
53
|
+
"@ai-sdk/groq": "4.0.48",
|
|
54
|
+
"@ai-sdk/huggingface": "2.0.55",
|
|
55
|
+
"@ai-sdk/moonshotai": "3.0.56",
|
|
56
|
+
"@ai-sdk/openai": "4.0.74",
|
|
57
57
|
"@ai-sdk/test-server": "2.0.1",
|
|
58
|
-
"@ai-sdk/xai": "5.0.
|
|
58
|
+
"@ai-sdk/xai": "5.0.7",
|
|
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/embed/embed-many.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '@ai-sdk/provider-utils';
|
|
8
8
|
import { logWarnings } from '../logger/log-warnings';
|
|
9
9
|
import { getEmbeddingModelMaxInputBytesPerCall } from '../model/get-embedding-model-max-input-bytes-per-call';
|
|
10
|
+
import { getEmbeddingModelProviderOptionsTransformer } from '../model/get-embedding-model-provider-options-transformer';
|
|
10
11
|
import { resolveEmbeddingModel } from '../model/resolve-model';
|
|
11
12
|
import { createRestrictedTelemetryDispatcher } from './restricted-telemetry-dispatcher';
|
|
12
13
|
import type { TelemetryOptions } from '../telemetry/telemetry-options';
|
|
@@ -321,6 +322,8 @@ export async function embedMany<RUNTIME_CONTEXT extends Context = Context>({
|
|
|
321
322
|
? maxInputBytesPerCall
|
|
322
323
|
: Infinity,
|
|
323
324
|
});
|
|
325
|
+
const providerOptionsTransformer =
|
|
326
|
+
getEmbeddingModelProviderOptionsTransformer(model);
|
|
324
327
|
|
|
325
328
|
const embeddings: Array<Embedding> = [];
|
|
326
329
|
const warnings: Array<Warning> = [];
|
|
@@ -339,9 +342,22 @@ export async function embedMany<RUNTIME_CONTEXT extends Context = Context>({
|
|
|
339
342
|
supportsParallelCalls ? maxParallelCalls : 1,
|
|
340
343
|
);
|
|
341
344
|
|
|
345
|
+
let nextChunkStartIndex = 0;
|
|
342
346
|
for (const parallelChunk of parallelChunks) {
|
|
343
347
|
const results = await Promise.all(
|
|
344
348
|
parallelChunk.map(async chunk => {
|
|
349
|
+
// Capture the range before awaiting transformations or retrying.
|
|
350
|
+
const startIndex = nextChunkStartIndex;
|
|
351
|
+
nextChunkStartIndex += chunk.length;
|
|
352
|
+
const chunkProviderOptions = providerOptionsTransformer
|
|
353
|
+
? await providerOptionsTransformer({
|
|
354
|
+
providerOptions,
|
|
355
|
+
values,
|
|
356
|
+
startIndex,
|
|
357
|
+
endIndex: startIndex + chunk.length,
|
|
358
|
+
})
|
|
359
|
+
: providerOptions;
|
|
360
|
+
|
|
345
361
|
const result = await retry(async () => {
|
|
346
362
|
const embedCallId = generateCallId();
|
|
347
363
|
|
|
@@ -361,7 +377,7 @@ export async function embedMany<RUNTIME_CONTEXT extends Context = Context>({
|
|
|
361
377
|
values: chunk,
|
|
362
378
|
abortSignal,
|
|
363
379
|
headers: headersWithUserAgent,
|
|
364
|
-
providerOptions,
|
|
380
|
+
providerOptions: chunkProviderOptions,
|
|
365
381
|
});
|
|
366
382
|
|
|
367
383
|
const chunkEmbeddings = modelResponse.embeddings;
|
|
@@ -412,7 +428,7 @@ export async function embedMany<RUNTIME_CONTEXT extends Context = Context>({
|
|
|
412
428
|
result.providerMetadata,
|
|
413
429
|
)) {
|
|
414
430
|
providerMetadata[providerName] = {
|
|
415
|
-
...
|
|
431
|
+
...providerMetadata[providerName],
|
|
416
432
|
...metadata,
|
|
417
433
|
};
|
|
418
434
|
}
|
|
@@ -204,10 +204,21 @@ function getOutputFormatMediaType(outputFormat: string | undefined) {
|
|
|
204
204
|
|
|
205
205
|
const normalizedOutputFormat = outputFormat.trim().toLowerCase();
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
207
|
+
switch (normalizedOutputFormat) {
|
|
208
|
+
case 'pcm':
|
|
209
|
+
case 'audio/pcm':
|
|
210
|
+
return 'audio/pcm';
|
|
211
|
+
case 'audio/l16':
|
|
212
|
+
return 'audio/l16';
|
|
213
|
+
case 'mulaw':
|
|
214
|
+
case 'audio/mulaw':
|
|
215
|
+
return 'audio/mulaw';
|
|
216
|
+
case 'alaw':
|
|
217
|
+
case 'audio/alaw':
|
|
218
|
+
return 'audio/alaw';
|
|
219
|
+
default:
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
211
222
|
}
|
|
212
223
|
|
|
213
224
|
class DefaultSpeechResult implements SpeechResult {
|
|
@@ -53,12 +53,4 @@ export class DefaultGeneratedAudioFile
|
|
|
53
53
|
|
|
54
54
|
export class DefaultGeneratedAudioFileWithType extends DefaultGeneratedAudioFile {
|
|
55
55
|
readonly type = 'audio';
|
|
56
|
-
|
|
57
|
-
constructor(options: {
|
|
58
|
-
data: string | Uint8Array;
|
|
59
|
-
mediaType: string;
|
|
60
|
-
format: string;
|
|
61
|
-
}) {
|
|
62
|
-
super(options);
|
|
63
|
-
}
|
|
64
56
|
}
|
|
@@ -78,12 +78,4 @@ export class DefaultGeneratedFile implements GeneratedFile {
|
|
|
78
78
|
|
|
79
79
|
export class DefaultGeneratedFileWithType extends DefaultGeneratedFile {
|
|
80
80
|
readonly type = 'file';
|
|
81
|
-
|
|
82
|
-
constructor(options: {
|
|
83
|
-
data: string | Uint8Array;
|
|
84
|
-
mediaType: string;
|
|
85
|
-
providerMetadata?: Record<string, JSONObject>;
|
|
86
|
-
}) {
|
|
87
|
-
super(options);
|
|
88
|
-
}
|
|
89
81
|
}
|
|
@@ -35,7 +35,7 @@ export function invokeToolCallbacksFromStream<TOOLS extends ToolSet>({
|
|
|
35
35
|
string,
|
|
36
36
|
{
|
|
37
37
|
toolName: string;
|
|
38
|
-
|
|
38
|
+
validatedContexts: Record<string, Promise<unknown> | undefined>;
|
|
39
39
|
}
|
|
40
40
|
> = createIdMap();
|
|
41
41
|
|
|
@@ -48,22 +48,23 @@ export function invokeToolCallbacksFromStream<TOOLS extends ToolSet>({
|
|
|
48
48
|
}): Promise<unknown> => {
|
|
49
49
|
const ongoingToolCall = ongoingToolCalls[toolCallId];
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
const validatedContext = ongoingToolCall?.validatedContexts[toolName];
|
|
52
|
+
if (validatedContext != null) {
|
|
53
|
+
return validatedContext;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
const tool = getOwn(tools, toolName);
|
|
56
|
-
const
|
|
57
|
+
const newValidatedContext = validateToolContext({
|
|
57
58
|
toolName,
|
|
58
59
|
context: getOwn(toolsContext, toolName),
|
|
59
60
|
contextSchema: tool?.contextSchema,
|
|
60
61
|
});
|
|
61
62
|
|
|
62
63
|
if (ongoingToolCall != null) {
|
|
63
|
-
ongoingToolCall.
|
|
64
|
+
ongoingToolCall.validatedContexts[toolName] = newValidatedContext;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
return
|
|
67
|
+
return newValidatedContext;
|
|
67
68
|
};
|
|
68
69
|
|
|
69
70
|
return stream.pipeThrough(
|
|
@@ -80,7 +81,7 @@ export function invokeToolCallbacksFromStream<TOOLS extends ToolSet>({
|
|
|
80
81
|
case 'tool-input-start': {
|
|
81
82
|
ongoingToolCalls[chunk.id] = {
|
|
82
83
|
toolName: chunk.toolName,
|
|
83
|
-
|
|
84
|
+
validatedContexts: createIdMap(),
|
|
84
85
|
};
|
|
85
86
|
|
|
86
87
|
const tool = getOwn(tools, chunk.toolName);
|
|
@@ -120,7 +121,7 @@ export function invokeToolCallbacksFromStream<TOOLS extends ToolSet>({
|
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
case 'tool-call': {
|
|
123
|
-
const toolName =
|
|
124
|
+
const toolName = chunk.toolName;
|
|
124
125
|
const tool = getOwn(tools, toolName);
|
|
125
126
|
|
|
126
127
|
if (!chunk.invalid && tool?.onInputAvailable != null) {
|
|
@@ -12,7 +12,12 @@ import { NoSuchToolError } from '../error/no-such-tool-error';
|
|
|
12
12
|
import { ToolCallRepairError } from '../error/tool-call-repair-error';
|
|
13
13
|
import type { Instructions } from '../prompt';
|
|
14
14
|
import { getOwn } from '../util/get-own';
|
|
15
|
-
import
|
|
15
|
+
import {
|
|
16
|
+
getToolCallInputSchemaInput,
|
|
17
|
+
setToolCallInputSchemaInput,
|
|
18
|
+
type DynamicToolCall,
|
|
19
|
+
type TypedToolCall,
|
|
20
|
+
} from './tool-call';
|
|
16
21
|
import type { ToolCallRepairFunction } from './tool-call-repair-function';
|
|
17
22
|
import type { ToolInputRefinement } from './tool-input-refinement';
|
|
18
23
|
|
|
@@ -167,7 +172,7 @@ async function waitForPromiseWithAbortSignal<T>({
|
|
|
167
172
|
});
|
|
168
173
|
}
|
|
169
174
|
|
|
170
|
-
async function refineParsedToolCallInput<TOOLS extends ToolSet>({
|
|
175
|
+
export async function refineParsedToolCallInput<TOOLS extends ToolSet>({
|
|
171
176
|
toolCall,
|
|
172
177
|
refineToolInput,
|
|
173
178
|
}: {
|
|
@@ -180,10 +185,15 @@ async function refineParsedToolCallInput<TOOLS extends ToolSet>({
|
|
|
180
185
|
return toolCall;
|
|
181
186
|
}
|
|
182
187
|
|
|
183
|
-
|
|
188
|
+
const refinedToolCall = {
|
|
184
189
|
...toolCall,
|
|
185
190
|
input: await refine(toolCall.input as InferToolInput<TOOLS[keyof TOOLS]>),
|
|
186
191
|
} as TypedToolCall<TOOLS>;
|
|
192
|
+
|
|
193
|
+
const inputSchemaInput = getToolCallInputSchemaInput(toolCall);
|
|
194
|
+
return inputSchemaInput == null
|
|
195
|
+
? refinedToolCall
|
|
196
|
+
: setToolCallInputSchemaInput(refinedToolCall, inputSchemaInput.value);
|
|
187
197
|
}
|
|
188
198
|
|
|
189
199
|
async function parseProviderExecutedDynamicToolCall(
|
|
@@ -253,26 +263,29 @@ async function doParseToolCall<TOOLS extends ToolSet>({
|
|
|
253
263
|
});
|
|
254
264
|
}
|
|
255
265
|
|
|
256
|
-
return
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
266
|
+
return setToolCallInputSchemaInput(
|
|
267
|
+
tool.type === 'dynamic'
|
|
268
|
+
? {
|
|
269
|
+
type: 'tool-call',
|
|
270
|
+
toolCallId: toolCall.toolCallId,
|
|
271
|
+
toolName: toolCall.toolName,
|
|
272
|
+
input: parseResult.value,
|
|
273
|
+
providerExecuted: toolCall.providerExecuted,
|
|
274
|
+
providerMetadata: toolCall.providerMetadata,
|
|
275
|
+
...(tool.metadata != null ? { toolMetadata: tool.metadata } : {}),
|
|
276
|
+
dynamic: true,
|
|
277
|
+
title: tool.title,
|
|
278
|
+
}
|
|
279
|
+
: {
|
|
280
|
+
type: 'tool-call',
|
|
281
|
+
toolCallId: toolCall.toolCallId,
|
|
282
|
+
toolName,
|
|
283
|
+
input: parseResult.value,
|
|
284
|
+
providerExecuted: toolCall.providerExecuted,
|
|
285
|
+
providerMetadata: toolCall.providerMetadata,
|
|
286
|
+
...(tool.metadata != null ? { toolMetadata: tool.metadata } : {}),
|
|
287
|
+
title: tool.title,
|
|
288
|
+
},
|
|
289
|
+
parseResult.rawValue,
|
|
290
|
+
);
|
|
278
291
|
}
|
|
@@ -47,7 +47,9 @@ export function pruneMessages({
|
|
|
47
47
|
|
|
48
48
|
return {
|
|
49
49
|
...message,
|
|
50
|
-
content: message.content.filter(
|
|
50
|
+
content: message.content.filter(
|
|
51
|
+
part => part.type !== 'reasoning' && part.type !== 'reasoning-file',
|
|
52
|
+
),
|
|
51
53
|
};
|
|
52
54
|
});
|
|
53
55
|
}
|