ai 7.0.92 → 7.0.94
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 +33 -0
- package/dist/index.d.ts +239 -214
- package/dist/index.js +391 -138
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +3 -2
- package/dist/internal/index.js +8 -5
- package/dist/internal/index.js.map +1 -1
- package/docs/02-foundations/02-providers-and-models.mdx +1 -1
- package/docs/03-agents/04-loop-control.mdx +5 -3
- package/docs/03-agents/07-workflow-agent.mdx +27 -6
- package/docs/03-ai-sdk-core/10-generating-structured-data.mdx +15 -3
- package/docs/03-ai-sdk-core/16-mcp-tools.mdx +64 -1
- package/docs/03-ai-sdk-core/35-image-generation.mdx +9 -0
- package/docs/03-ai-sdk-core/36-transcription.mdx +36 -35
- package/docs/03-ai-sdk-core/37-speech.mdx +0 -17
- package/docs/03-ai-sdk-harnesses/02-harness-agent.mdx +36 -0
- package/docs/04-ai-sdk-ui/20-streaming-data.mdx +11 -6
- package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +14 -0
- package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +15 -1
- package/docs/07-reference/01-ai-sdk-core/10-generate-image.mdx +2 -1
- package/docs/07-reference/01-ai-sdk-core/28-output.mdx +27 -1
- 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 +4 -0
- package/docs/07-reference/02-ai-sdk-ui/41-create-ui-message-stream-response.mdx +6 -1
- package/docs/07-reference/04-ai-sdk-workflow/01-workflow-agent.mdx +42 -28
- package/docs/07-reference/05-ai-sdk-errors/ai-no-image-generated-error.mdx +7 -0
- package/package.json +12 -12
- package/src/agent/tool-loop-agent-settings.ts +15 -0
- package/src/batch/batch-types.ts +76 -68
- package/src/batch/batch.ts +169 -101
- package/src/batch/index.ts +9 -7
- package/src/embed/embed-many.ts +27 -2
- package/src/error/no-image-generated-error.ts +9 -0
- package/src/generate-image/generate-image.ts +51 -10
- package/src/generate-text/output.ts +111 -1
- package/src/generate-text/stream-language-model-call.ts +82 -0
- package/src/generate-text/stream-text.ts +5 -1
- package/src/ui/chat.ts +1 -1
- package/src/ui/convert-to-model-messages.ts +8 -2
- package/src/ui/validate-ui-messages.ts +14 -0
- package/src/util/data-url.ts +1 -1
- package/src/util/merge-abort-signals.ts +1 -1
- package/src/util/prepare-retries.ts +7 -1
- package/src/util/retry-with-exponential-backoff.ts +9 -4
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
safeValidateTypes,
|
|
12
12
|
type FlexibleSchema,
|
|
13
13
|
} from '@ai-sdk/provider-utils';
|
|
14
|
+
import { InvalidArgumentError } from '../error/invalid-argument-error';
|
|
14
15
|
import { NoObjectGeneratedError } from '../error/no-object-generated-error';
|
|
15
16
|
import type { FinishReason } from '../types/language-model';
|
|
16
17
|
import type { LanguageModelResponseMetadata } from '../types/language-model-response-metadata';
|
|
@@ -189,6 +190,8 @@ export const object = <OBJECT>({
|
|
|
189
190
|
* When the model generates a text response, it will return an array of elements.
|
|
190
191
|
*
|
|
191
192
|
* @param element - The schema of the array elements to generate.
|
|
193
|
+
* @param minItems - Optional minimum number of elements to generate.
|
|
194
|
+
* @param maxItems - Optional maximum number of elements to generate.
|
|
192
195
|
* @param name - Optional name of the output that should be generated. Used by some providers for additional LLM guidance, e.g. via tool or schema name.
|
|
193
196
|
* @param description - Optional description of the output that should be generated. Used by some providers for additional LLM guidance, e.g. via tool or schema description.
|
|
194
197
|
*
|
|
@@ -196,10 +199,20 @@ export const object = <OBJECT>({
|
|
|
196
199
|
*/
|
|
197
200
|
export const array = <ELEMENT>({
|
|
198
201
|
element: inputElementSchema,
|
|
202
|
+
minItems,
|
|
203
|
+
maxItems,
|
|
199
204
|
name,
|
|
200
205
|
description,
|
|
201
206
|
}: {
|
|
202
207
|
element: FlexibleSchema<ELEMENT>;
|
|
208
|
+
/**
|
|
209
|
+
* Optional minimum number of elements to generate.
|
|
210
|
+
*/
|
|
211
|
+
minItems?: number;
|
|
212
|
+
/**
|
|
213
|
+
* Optional maximum number of elements to generate.
|
|
214
|
+
*/
|
|
215
|
+
maxItems?: number;
|
|
203
216
|
/**
|
|
204
217
|
* Optional name of the output that should be generated.
|
|
205
218
|
* Used by some providers for additional LLM guidance, e.g. via tool or schema name.
|
|
@@ -211,6 +224,17 @@ export const array = <ELEMENT>({
|
|
|
211
224
|
*/
|
|
212
225
|
description?: string;
|
|
213
226
|
}): Output<Array<ELEMENT>, Array<ELEMENT>, ELEMENT> => {
|
|
227
|
+
validateArrayBound({ name: 'minItems', value: minItems });
|
|
228
|
+
validateArrayBound({ name: 'maxItems', value: maxItems });
|
|
229
|
+
|
|
230
|
+
if (minItems != null && maxItems != null && minItems > maxItems) {
|
|
231
|
+
throw new InvalidArgumentError({
|
|
232
|
+
parameter: 'minItems',
|
|
233
|
+
value: minItems,
|
|
234
|
+
message: 'minItems must be less than or equal to maxItems',
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
214
238
|
const elementSchema = asSchema(inputElementSchema);
|
|
215
239
|
|
|
216
240
|
return {
|
|
@@ -236,7 +260,12 @@ export const array = <ELEMENT>({
|
|
|
236
260
|
...($defs != null && { $defs }),
|
|
237
261
|
type: 'object',
|
|
238
262
|
properties: {
|
|
239
|
-
elements: {
|
|
263
|
+
elements: {
|
|
264
|
+
type: 'array',
|
|
265
|
+
items: itemSchema,
|
|
266
|
+
...(minItems != null && { minItems }),
|
|
267
|
+
...(maxItems != null && { maxItems }),
|
|
268
|
+
},
|
|
240
269
|
},
|
|
241
270
|
required: ['elements'],
|
|
242
271
|
additionalProperties: false,
|
|
@@ -288,6 +317,23 @@ export const array = <ELEMENT>({
|
|
|
288
317
|
});
|
|
289
318
|
}
|
|
290
319
|
|
|
320
|
+
const lengthValidationError = getArrayLengthValidationError({
|
|
321
|
+
value: outerValue.elements,
|
|
322
|
+
minItems,
|
|
323
|
+
maxItems,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
if (lengthValidationError != null) {
|
|
327
|
+
throw new NoObjectGeneratedError({
|
|
328
|
+
message: 'No object generated: response did not match schema.',
|
|
329
|
+
cause: lengthValidationError,
|
|
330
|
+
text,
|
|
331
|
+
response: context.response,
|
|
332
|
+
usage: context.usage,
|
|
333
|
+
finishReason: context.finishReason,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
291
337
|
const validatedElements: Array<ELEMENT> = [];
|
|
292
338
|
for (const element of outerValue.elements) {
|
|
293
339
|
const validationResult = await safeValidateTypes({
|
|
@@ -372,6 +418,16 @@ export const array = <ELEMENT>({
|
|
|
372
418
|
publishedElements < partialOutput.length;
|
|
373
419
|
publishedElements++
|
|
374
420
|
) {
|
|
421
|
+
if (maxItems != null && publishedElements >= maxItems) {
|
|
422
|
+
controller.error(
|
|
423
|
+
getArrayLengthValidationError({
|
|
424
|
+
value: partialOutput,
|
|
425
|
+
maxItems,
|
|
426
|
+
}),
|
|
427
|
+
);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
|
|
375
431
|
controller.enqueue(partialOutput[publishedElements]);
|
|
376
432
|
}
|
|
377
433
|
}
|
|
@@ -381,6 +437,60 @@ export const array = <ELEMENT>({
|
|
|
381
437
|
};
|
|
382
438
|
};
|
|
383
439
|
|
|
440
|
+
function validateArrayBound({
|
|
441
|
+
name,
|
|
442
|
+
value,
|
|
443
|
+
}: {
|
|
444
|
+
name: 'minItems' | 'maxItems';
|
|
445
|
+
value: number | undefined;
|
|
446
|
+
}) {
|
|
447
|
+
if (value == null) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (!Number.isInteger(value)) {
|
|
452
|
+
throw new InvalidArgumentError({
|
|
453
|
+
parameter: name,
|
|
454
|
+
value,
|
|
455
|
+
message: `${name} must be an integer`,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (value < 0) {
|
|
460
|
+
throw new InvalidArgumentError({
|
|
461
|
+
parameter: name,
|
|
462
|
+
value,
|
|
463
|
+
message: `${name} must be greater than or equal to 0`,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function getArrayLengthValidationError({
|
|
469
|
+
value,
|
|
470
|
+
minItems,
|
|
471
|
+
maxItems,
|
|
472
|
+
}: {
|
|
473
|
+
value: Array<unknown>;
|
|
474
|
+
minItems?: number;
|
|
475
|
+
maxItems?: number;
|
|
476
|
+
}): TypeValidationError | undefined {
|
|
477
|
+
if (minItems != null && value.length < minItems) {
|
|
478
|
+
return new TypeValidationError({
|
|
479
|
+
value,
|
|
480
|
+
cause: `elements array must contain at least ${minItems} items`,
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (maxItems != null && value.length > maxItems) {
|
|
485
|
+
return new TypeValidationError({
|
|
486
|
+
value,
|
|
487
|
+
cause: `elements array must contain at most ${maxItems} items`,
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return undefined;
|
|
492
|
+
}
|
|
493
|
+
|
|
384
494
|
/**
|
|
385
495
|
* Output specification for choice generation.
|
|
386
496
|
* When the model generates a text response, it will return a one of the choice options.
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getErrorMessage,
|
|
3
|
+
type LanguageModelV4Content,
|
|
3
4
|
type LanguageModelV4Prompt,
|
|
4
5
|
type LanguageModelV4StreamPart,
|
|
6
|
+
type LanguageModelV4ToolChoice,
|
|
5
7
|
type SharedV4Headers,
|
|
6
8
|
} from '@ai-sdk/provider';
|
|
7
9
|
import {
|
|
@@ -40,6 +42,7 @@ import {
|
|
|
40
42
|
import type { DownloadFunction } from '../util/download/download-function';
|
|
41
43
|
import { notify } from '../util/notify';
|
|
42
44
|
import { now as originalNow } from '../util/now';
|
|
45
|
+
import { ToolChoiceViolationError } from '../error';
|
|
43
46
|
import { calculateTokensPerSecond } from './calculate-tokens-per-second';
|
|
44
47
|
import type { ContentPart } from './content-part';
|
|
45
48
|
import { DefaultGeneratedFileWithType } from './generated-file';
|
|
@@ -372,6 +375,7 @@ export async function streamLanguageModelCall<
|
|
|
372
375
|
callId: effectiveCallId,
|
|
373
376
|
provider: resolvedModel.provider,
|
|
374
377
|
modelId: resolvedModel.modelId,
|
|
378
|
+
toolChoice: stepToolChoice,
|
|
375
379
|
generateId,
|
|
376
380
|
now,
|
|
377
381
|
callStartTimestampMs,
|
|
@@ -398,6 +402,7 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
398
402
|
callId,
|
|
399
403
|
provider,
|
|
400
404
|
modelId,
|
|
405
|
+
toolChoice,
|
|
401
406
|
generateId,
|
|
402
407
|
now,
|
|
403
408
|
callStartTimestampMs,
|
|
@@ -411,6 +416,7 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
411
416
|
callId: string;
|
|
412
417
|
provider: string;
|
|
413
418
|
modelId: string;
|
|
419
|
+
toolChoice: LanguageModelV4ToolChoice;
|
|
414
420
|
generateId: IdGenerator;
|
|
415
421
|
now: () => number;
|
|
416
422
|
callStartTimestampMs: number;
|
|
@@ -420,8 +426,11 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
420
426
|
// keep track of tool inputs for provider-side tool results
|
|
421
427
|
const toolCallsByToolCallId = new Map<string, TypedToolCall<TOOLS>>();
|
|
422
428
|
const modelCallContent: Array<ContentPart<TOOLS>> = [];
|
|
429
|
+
const rawModelCallContent: Array<LanguageModelV4Content> = [];
|
|
423
430
|
const textPartIndexes = new Map<string, number>();
|
|
424
431
|
const reasoningPartIndexes = new Map<string, number>();
|
|
432
|
+
const rawTextPartIndexes = new Map<string, number>();
|
|
433
|
+
const rawReasoningPartIndexes = new Map<string, number>();
|
|
425
434
|
let responseId = generateId();
|
|
426
435
|
let responseModelId = modelId;
|
|
427
436
|
let timeToFirstOutputMs: number | undefined;
|
|
@@ -458,7 +467,9 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
458
467
|
case 'text-start':
|
|
459
468
|
upsertTextContentPart({
|
|
460
469
|
content: modelCallContent,
|
|
470
|
+
rawContent: rawModelCallContent,
|
|
461
471
|
partIndexes: textPartIndexes,
|
|
472
|
+
rawPartIndexes: rawTextPartIndexes,
|
|
462
473
|
id: chunk.id,
|
|
463
474
|
type: 'text',
|
|
464
475
|
providerMetadata: chunk.providerMetadata,
|
|
@@ -469,7 +480,9 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
469
480
|
case 'text-delta':
|
|
470
481
|
upsertTextContentPart({
|
|
471
482
|
content: modelCallContent,
|
|
483
|
+
rawContent: rawModelCallContent,
|
|
472
484
|
partIndexes: textPartIndexes,
|
|
485
|
+
rawPartIndexes: rawTextPartIndexes,
|
|
473
486
|
id: chunk.id,
|
|
474
487
|
type: 'text',
|
|
475
488
|
textDelta: chunk.delta,
|
|
@@ -486,19 +499,24 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
486
499
|
case 'text-end':
|
|
487
500
|
upsertTextContentPart({
|
|
488
501
|
content: modelCallContent,
|
|
502
|
+
rawContent: rawModelCallContent,
|
|
489
503
|
partIndexes: textPartIndexes,
|
|
504
|
+
rawPartIndexes: rawTextPartIndexes,
|
|
490
505
|
id: chunk.id,
|
|
491
506
|
type: 'text',
|
|
492
507
|
providerMetadata: chunk.providerMetadata,
|
|
493
508
|
});
|
|
494
509
|
textPartIndexes.delete(chunk.id);
|
|
510
|
+
rawTextPartIndexes.delete(chunk.id);
|
|
495
511
|
controller.enqueue(chunk);
|
|
496
512
|
break;
|
|
497
513
|
|
|
498
514
|
case 'reasoning-start':
|
|
499
515
|
upsertTextContentPart({
|
|
500
516
|
content: modelCallContent,
|
|
517
|
+
rawContent: rawModelCallContent,
|
|
501
518
|
partIndexes: reasoningPartIndexes,
|
|
519
|
+
rawPartIndexes: rawReasoningPartIndexes,
|
|
502
520
|
id: chunk.id,
|
|
503
521
|
type: 'reasoning',
|
|
504
522
|
providerMetadata: chunk.providerMetadata,
|
|
@@ -509,7 +527,9 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
509
527
|
case 'reasoning-delta':
|
|
510
528
|
upsertTextContentPart({
|
|
511
529
|
content: modelCallContent,
|
|
530
|
+
rawContent: rawModelCallContent,
|
|
512
531
|
partIndexes: reasoningPartIndexes,
|
|
532
|
+
rawPartIndexes: rawReasoningPartIndexes,
|
|
513
533
|
id: chunk.id,
|
|
514
534
|
type: 'reasoning',
|
|
515
535
|
textDelta: chunk.delta,
|
|
@@ -526,12 +546,15 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
526
546
|
case 'reasoning-end':
|
|
527
547
|
upsertTextContentPart({
|
|
528
548
|
content: modelCallContent,
|
|
549
|
+
rawContent: rawModelCallContent,
|
|
529
550
|
partIndexes: reasoningPartIndexes,
|
|
551
|
+
rawPartIndexes: rawReasoningPartIndexes,
|
|
530
552
|
id: chunk.id,
|
|
531
553
|
type: 'reasoning',
|
|
532
554
|
providerMetadata: chunk.providerMetadata,
|
|
533
555
|
});
|
|
534
556
|
reasoningPartIndexes.delete(chunk.id);
|
|
557
|
+
rawReasoningPartIndexes.delete(chunk.id);
|
|
535
558
|
controller.enqueue(chunk);
|
|
536
559
|
break;
|
|
537
560
|
|
|
@@ -552,6 +575,7 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
552
575
|
? { providerMetadata: chunk.providerMetadata }
|
|
553
576
|
: {}),
|
|
554
577
|
});
|
|
578
|
+
rawModelCallContent.push(chunk);
|
|
555
579
|
|
|
556
580
|
controller.enqueue({
|
|
557
581
|
type: chunk.type,
|
|
@@ -612,6 +636,9 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
612
636
|
callbacks: onLanguageModelCallEnd,
|
|
613
637
|
});
|
|
614
638
|
|
|
639
|
+
// Preserve the completed model call's usage, metadata, and
|
|
640
|
+
// performance even when response validation below surfaces a
|
|
641
|
+
// semantic error.
|
|
615
642
|
controller.enqueue({
|
|
616
643
|
type: 'model-call-end',
|
|
617
644
|
finishReason: chunk.finishReason.unified,
|
|
@@ -620,10 +647,38 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
620
647
|
providerMetadata: chunk.providerMetadata,
|
|
621
648
|
performance,
|
|
622
649
|
});
|
|
650
|
+
|
|
651
|
+
const enforcedToolChoice =
|
|
652
|
+
toolChoice.type === 'required' || toolChoice.type === 'tool'
|
|
653
|
+
? toolChoice
|
|
654
|
+
: undefined;
|
|
655
|
+
|
|
656
|
+
if (
|
|
657
|
+
enforcedToolChoice != null &&
|
|
658
|
+
![...toolCallsByToolCallId.values()].some(
|
|
659
|
+
toolCall =>
|
|
660
|
+
enforcedToolChoice.type === 'required' ||
|
|
661
|
+
toolCall.toolName === enforcedToolChoice.toolName,
|
|
662
|
+
)
|
|
663
|
+
) {
|
|
664
|
+
controller.enqueue({
|
|
665
|
+
type: 'error',
|
|
666
|
+
error: new ToolChoiceViolationError({
|
|
667
|
+
toolChoice: enforcedToolChoice,
|
|
668
|
+
finishReason: chunk.finishReason.unified,
|
|
669
|
+
provider,
|
|
670
|
+
modelId,
|
|
671
|
+
content: rawModelCallContent,
|
|
672
|
+
}),
|
|
673
|
+
});
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
623
676
|
break;
|
|
624
677
|
}
|
|
625
678
|
|
|
626
679
|
case 'tool-call': {
|
|
680
|
+
rawModelCallContent.push(chunk);
|
|
681
|
+
|
|
627
682
|
try {
|
|
628
683
|
const toolCall = await parseToolCall({
|
|
629
684
|
toolCall: chunk,
|
|
@@ -663,6 +718,8 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
663
718
|
}
|
|
664
719
|
|
|
665
720
|
case 'tool-approval-request': {
|
|
721
|
+
rawModelCallContent.push(chunk);
|
|
722
|
+
|
|
666
723
|
const toolCall = toolCallsByToolCallId.get(chunk.toolCallId);
|
|
667
724
|
|
|
668
725
|
if (toolCall == null) {
|
|
@@ -688,6 +745,8 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
688
745
|
}
|
|
689
746
|
|
|
690
747
|
case 'tool-result': {
|
|
748
|
+
rawModelCallContent.push(chunk);
|
|
749
|
+
|
|
691
750
|
const toolName = chunk.toolName as keyof TOOLS & string;
|
|
692
751
|
const toolCall = toolCallsByToolCallId.get(chunk.toolCallId);
|
|
693
752
|
|
|
@@ -765,6 +824,7 @@ function createLanguageModelV4StreamPartToLanguageModelStreamPartTransform<
|
|
|
765
824
|
default:
|
|
766
825
|
if (chunk.type === 'custom' || chunk.type === 'source') {
|
|
767
826
|
modelCallContent.push(chunk);
|
|
827
|
+
rawModelCallContent.push(chunk);
|
|
768
828
|
}
|
|
769
829
|
|
|
770
830
|
controller.enqueue(chunk);
|
|
@@ -818,14 +878,18 @@ function calculateNearestRankPercentile(
|
|
|
818
878
|
*/
|
|
819
879
|
function upsertTextContentPart<TOOLS extends ToolSet>({
|
|
820
880
|
content,
|
|
881
|
+
rawContent,
|
|
821
882
|
partIndexes,
|
|
883
|
+
rawPartIndexes,
|
|
822
884
|
id,
|
|
823
885
|
type,
|
|
824
886
|
textDelta,
|
|
825
887
|
providerMetadata,
|
|
826
888
|
}: {
|
|
827
889
|
content: Array<ContentPart<TOOLS>>;
|
|
890
|
+
rawContent: Array<LanguageModelV4Content>;
|
|
828
891
|
partIndexes: Map<string, number>;
|
|
892
|
+
rawPartIndexes: Map<string, number>;
|
|
829
893
|
id: string;
|
|
830
894
|
type: 'text' | 'reasoning';
|
|
831
895
|
textDelta?: string;
|
|
@@ -843,16 +907,34 @@ function upsertTextContentPart<TOOLS extends ToolSet>({
|
|
|
843
907
|
partIndexes.set(id, partIndex);
|
|
844
908
|
}
|
|
845
909
|
|
|
910
|
+
let rawPartIndex = rawPartIndexes.get(id);
|
|
911
|
+
|
|
912
|
+
if (rawPartIndex == null) {
|
|
913
|
+
rawPartIndex =
|
|
914
|
+
rawContent.push({
|
|
915
|
+
type,
|
|
916
|
+
text: '',
|
|
917
|
+
...(providerMetadata != null ? { providerMetadata } : {}),
|
|
918
|
+
}) - 1;
|
|
919
|
+
rawPartIndexes.set(id, rawPartIndex);
|
|
920
|
+
}
|
|
921
|
+
|
|
846
922
|
const part = content[partIndex] as {
|
|
847
923
|
text: string;
|
|
848
924
|
providerMetadata?: ProviderMetadata;
|
|
849
925
|
};
|
|
926
|
+
const rawPart = rawContent[rawPartIndex] as {
|
|
927
|
+
text: string;
|
|
928
|
+
providerMetadata?: ProviderMetadata;
|
|
929
|
+
};
|
|
850
930
|
|
|
851
931
|
if (textDelta != null) {
|
|
852
932
|
part.text += textDelta;
|
|
933
|
+
rawPart.text += textDelta;
|
|
853
934
|
}
|
|
854
935
|
|
|
855
936
|
if (providerMetadata != null) {
|
|
856
937
|
part.providerMetadata = providerMetadata;
|
|
938
|
+
rawPart.providerMetadata = providerMetadata;
|
|
857
939
|
}
|
|
858
940
|
}
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
type ToolSet,
|
|
23
23
|
} from '@ai-sdk/provider-utils';
|
|
24
24
|
import type { ServerResponse } from 'node:http';
|
|
25
|
-
import { NoOutputGeneratedError } from '../error';
|
|
25
|
+
import { NoOutputGeneratedError, ToolChoiceViolationError } from '../error';
|
|
26
26
|
import { logWarnings } from '../logger/log-warnings';
|
|
27
27
|
import { resolveLanguageModel } from '../model/resolve-model';
|
|
28
28
|
import { cloneModelMessages } from '../prompt/clone-model-message';
|
|
@@ -2573,6 +2573,8 @@ class DefaultStreamTextResult<
|
|
|
2573
2573
|
callbacks: onChunk,
|
|
2574
2574
|
});
|
|
2575
2575
|
const error = wrapGatewayError(value.error);
|
|
2576
|
+
const isToolChoiceViolation =
|
|
2577
|
+
ToolChoiceViolationError.isInstance(error);
|
|
2576
2578
|
let onErrorResult: unknown;
|
|
2577
2579
|
try {
|
|
2578
2580
|
onErrorResult = await onError({ error });
|
|
@@ -2584,8 +2586,10 @@ class DefaultStreamTextResult<
|
|
|
2584
2586
|
'retry' in onErrorResult &&
|
|
2585
2587
|
onErrorResult.retry === true;
|
|
2586
2588
|
const automaticRetry =
|
|
2589
|
+
!isToolChoiceViolation &&
|
|
2587
2590
|
automaticStreamRetryCount < streamRetries;
|
|
2588
2591
|
const callbackRetry =
|
|
2592
|
+
!isToolChoiceViolation &&
|
|
2589
2593
|
!automaticRetry &&
|
|
2590
2594
|
callbackRequestedRetry &&
|
|
2591
2595
|
callbackStreamRetryCount < 1;
|
package/src/ui/chat.ts
CHANGED
|
@@ -420,8 +420,8 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
420
420
|
|
|
421
421
|
// update the message with the new content
|
|
422
422
|
this.state.replaceMessage(messageIndex, {
|
|
423
|
-
...uiMessage,
|
|
424
423
|
id: message.messageId,
|
|
424
|
+
...uiMessage,
|
|
425
425
|
role: uiMessage.role ?? 'user',
|
|
426
426
|
metadata: message.metadata,
|
|
427
427
|
} as UI_MESSAGE);
|
|
@@ -212,6 +212,12 @@ export async function convertToModelMessages<UI_MESSAGE extends UIMessage>(
|
|
|
212
212
|
const toolName = getToolName(part);
|
|
213
213
|
|
|
214
214
|
if (part.state !== 'input-streaming') {
|
|
215
|
+
const callProviderMetadata =
|
|
216
|
+
part.callProviderMetadata ??
|
|
217
|
+
(part.state === 'output-error'
|
|
218
|
+
? part.resultProviderMetadata
|
|
219
|
+
: undefined);
|
|
220
|
+
|
|
215
221
|
content.push({
|
|
216
222
|
type: 'tool-call' as const,
|
|
217
223
|
toolCallId: part.toolCallId,
|
|
@@ -222,8 +228,8 @@ export async function convertToModelMessages<UI_MESSAGE extends UIMessage>(
|
|
|
222
228
|
('rawInput' in part ? part.rawInput : undefined))
|
|
223
229
|
: part.input,
|
|
224
230
|
providerExecuted: part.providerExecuted,
|
|
225
|
-
...(
|
|
226
|
-
? { providerOptions:
|
|
231
|
+
...(callProviderMetadata != null
|
|
232
|
+
? { providerOptions: callProviderMetadata }
|
|
227
233
|
: {}),
|
|
228
234
|
});
|
|
229
235
|
|
|
@@ -117,6 +117,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
117
117
|
type: z.literal('dynamic-tool'),
|
|
118
118
|
toolName: z.string(),
|
|
119
119
|
toolCallId: z.string(),
|
|
120
|
+
title: z.string().optional(),
|
|
120
121
|
toolMetadata: toolMetadataSchema.optional(),
|
|
121
122
|
state: z.literal('input-streaming'),
|
|
122
123
|
input: z.unknown().optional(),
|
|
@@ -130,6 +131,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
130
131
|
type: z.literal('dynamic-tool'),
|
|
131
132
|
toolName: z.string(),
|
|
132
133
|
toolCallId: z.string(),
|
|
134
|
+
title: z.string().optional(),
|
|
133
135
|
toolMetadata: toolMetadataSchema.optional(),
|
|
134
136
|
state: z.literal('input-available'),
|
|
135
137
|
input: z.unknown(),
|
|
@@ -143,6 +145,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
143
145
|
type: z.literal('dynamic-tool'),
|
|
144
146
|
toolName: z.string(),
|
|
145
147
|
toolCallId: z.string(),
|
|
148
|
+
title: z.string().optional(),
|
|
146
149
|
toolMetadata: toolMetadataSchema.optional(),
|
|
147
150
|
state: z.literal('approval-requested'),
|
|
148
151
|
input: z.unknown(),
|
|
@@ -164,6 +167,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
164
167
|
type: z.literal('dynamic-tool'),
|
|
165
168
|
toolName: z.string(),
|
|
166
169
|
toolCallId: z.string(),
|
|
170
|
+
title: z.string().optional(),
|
|
167
171
|
toolMetadata: toolMetadataSchema.optional(),
|
|
168
172
|
state: z.literal('approval-responded'),
|
|
169
173
|
input: z.unknown(),
|
|
@@ -185,6 +189,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
185
189
|
type: z.literal('dynamic-tool'),
|
|
186
190
|
toolName: z.string(),
|
|
187
191
|
toolCallId: z.string(),
|
|
192
|
+
title: z.string().optional(),
|
|
188
193
|
toolMetadata: toolMetadataSchema.optional(),
|
|
189
194
|
state: z.literal('output-available'),
|
|
190
195
|
input: z.unknown(),
|
|
@@ -210,6 +215,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
210
215
|
type: z.literal('dynamic-tool'),
|
|
211
216
|
toolName: z.string(),
|
|
212
217
|
toolCallId: z.string(),
|
|
218
|
+
title: z.string().optional(),
|
|
213
219
|
toolMetadata: toolMetadataSchema.optional(),
|
|
214
220
|
state: z.literal('output-error'),
|
|
215
221
|
input: z.unknown().optional(),
|
|
@@ -235,6 +241,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
235
241
|
type: z.literal('dynamic-tool'),
|
|
236
242
|
toolName: z.string(),
|
|
237
243
|
toolCallId: z.string(),
|
|
244
|
+
title: z.string().optional(),
|
|
238
245
|
toolMetadata: toolMetadataSchema.optional(),
|
|
239
246
|
state: z.literal('output-denied'),
|
|
240
247
|
input: z.unknown(),
|
|
@@ -255,6 +262,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
255
262
|
z.object({
|
|
256
263
|
type: z.string().startsWith('tool-'),
|
|
257
264
|
toolCallId: z.string(),
|
|
265
|
+
title: z.string().optional(),
|
|
258
266
|
toolMetadata: toolMetadataSchema.optional(),
|
|
259
267
|
state: z.literal('input-streaming'),
|
|
260
268
|
providerExecuted: z.boolean().optional(),
|
|
@@ -267,6 +275,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
267
275
|
z.object({
|
|
268
276
|
type: z.string().startsWith('tool-'),
|
|
269
277
|
toolCallId: z.string(),
|
|
278
|
+
title: z.string().optional(),
|
|
270
279
|
toolMetadata: toolMetadataSchema.optional(),
|
|
271
280
|
state: z.literal('input-available'),
|
|
272
281
|
providerExecuted: z.boolean().optional(),
|
|
@@ -279,6 +288,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
279
288
|
z.object({
|
|
280
289
|
type: z.string().startsWith('tool-'),
|
|
281
290
|
toolCallId: z.string(),
|
|
291
|
+
title: z.string().optional(),
|
|
282
292
|
toolMetadata: toolMetadataSchema.optional(),
|
|
283
293
|
state: z.literal('approval-requested'),
|
|
284
294
|
input: z.unknown(),
|
|
@@ -299,6 +309,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
299
309
|
z.object({
|
|
300
310
|
type: z.string().startsWith('tool-'),
|
|
301
311
|
toolCallId: z.string(),
|
|
312
|
+
title: z.string().optional(),
|
|
302
313
|
toolMetadata: toolMetadataSchema.optional(),
|
|
303
314
|
state: z.literal('approval-responded'),
|
|
304
315
|
input: z.unknown(),
|
|
@@ -319,6 +330,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
319
330
|
z.object({
|
|
320
331
|
type: z.string().startsWith('tool-'),
|
|
321
332
|
toolCallId: z.string(),
|
|
333
|
+
title: z.string().optional(),
|
|
322
334
|
toolMetadata: toolMetadataSchema.optional(),
|
|
323
335
|
state: z.literal('output-available'),
|
|
324
336
|
providerExecuted: z.boolean().optional(),
|
|
@@ -343,6 +355,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
343
355
|
z.object({
|
|
344
356
|
type: z.string().startsWith('tool-'),
|
|
345
357
|
toolCallId: z.string(),
|
|
358
|
+
title: z.string().optional(),
|
|
346
359
|
toolMetadata: toolMetadataSchema.optional(),
|
|
347
360
|
state: z.literal('output-error'),
|
|
348
361
|
providerExecuted: z.boolean().optional(),
|
|
@@ -367,6 +380,7 @@ const uiMessagesSchema = lazySchema(() =>
|
|
|
367
380
|
z.object({
|
|
368
381
|
type: z.string().startsWith('tool-'),
|
|
369
382
|
toolCallId: z.string(),
|
|
383
|
+
title: z.string().optional(),
|
|
370
384
|
toolMetadata: toolMetadataSchema.optional(),
|
|
371
385
|
state: z.literal('output-denied'),
|
|
372
386
|
providerExecuted: z.boolean().optional(),
|
package/src/util/data-url.ts
CHANGED
|
@@ -14,7 +14,7 @@ export function mergeAbortSignals(
|
|
|
14
14
|
...signals: (AbortSignal | null | undefined | number)[]
|
|
15
15
|
): AbortSignal | undefined {
|
|
16
16
|
const validSignals = filterNullable(...signals).map(signal =>
|
|
17
|
-
signal
|
|
17
|
+
typeof signal === 'number' ? AbortSignal.timeout(signal) : signal,
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
return validSignals.length === 0
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { InvalidArgumentError } from '../error/invalid-argument-error';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
RetryFunction,
|
|
4
|
+
ShouldRetryFunction,
|
|
5
|
+
} from '@ai-sdk/provider-utils';
|
|
3
6
|
import { retryWithExponentialBackoffRespectingRetryHeaders } from '../util/retry-with-exponential-backoff';
|
|
4
7
|
/**
|
|
5
8
|
* Validate and prepare retries.
|
|
@@ -7,11 +10,13 @@ import { retryWithExponentialBackoffRespectingRetryHeaders } from '../util/retry
|
|
|
7
10
|
export function prepareRetries({
|
|
8
11
|
maxRetries,
|
|
9
12
|
abortSignal,
|
|
13
|
+
additionalRetryableError,
|
|
10
14
|
parameter = 'maxRetries',
|
|
11
15
|
defaultMaxRetries = 2,
|
|
12
16
|
}: {
|
|
13
17
|
maxRetries: number | undefined;
|
|
14
18
|
abortSignal: AbortSignal | undefined;
|
|
19
|
+
additionalRetryableError?: ShouldRetryFunction;
|
|
15
20
|
parameter?: string;
|
|
16
21
|
defaultMaxRetries?: number;
|
|
17
22
|
}): {
|
|
@@ -43,6 +48,7 @@ export function prepareRetries({
|
|
|
43
48
|
retry: retryWithExponentialBackoffRespectingRetryHeaders({
|
|
44
49
|
maxRetries: maxRetriesResult,
|
|
45
50
|
abortSignal,
|
|
51
|
+
additionalRetryableError,
|
|
46
52
|
}),
|
|
47
53
|
};
|
|
48
54
|
}
|
|
@@ -3,6 +3,7 @@ import { GatewayError } from '@ai-sdk/gateway';
|
|
|
3
3
|
import {
|
|
4
4
|
retryWithExponentialBackoff,
|
|
5
5
|
type RetryFunction,
|
|
6
|
+
type ShouldRetryFunction,
|
|
6
7
|
} from '@ai-sdk/provider-utils';
|
|
7
8
|
import { RetryError } from './retry-error';
|
|
8
9
|
|
|
@@ -66,21 +67,25 @@ export const retryWithExponentialBackoffRespectingRetryHeaders = ({
|
|
|
66
67
|
initialDelayInMs = 2000,
|
|
67
68
|
backoffFactor = 2,
|
|
68
69
|
abortSignal,
|
|
70
|
+
additionalRetryableError,
|
|
69
71
|
}: {
|
|
70
72
|
maxRetries?: number;
|
|
71
73
|
initialDelayInMs?: number;
|
|
72
74
|
backoffFactor?: number;
|
|
73
75
|
abortSignal?: AbortSignal;
|
|
76
|
+
additionalRetryableError?: ShouldRetryFunction;
|
|
74
77
|
} = {}): RetryFunction =>
|
|
75
78
|
retryWithExponentialBackoff({
|
|
76
79
|
maxRetries,
|
|
77
80
|
initialDelayInMs,
|
|
78
81
|
backoffFactor,
|
|
79
82
|
abortSignal,
|
|
80
|
-
shouldRetry: error =>
|
|
81
|
-
error instanceof Error &&
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
shouldRetry: async error =>
|
|
84
|
+
(error instanceof Error &&
|
|
85
|
+
((APICallError.isInstance(error) && error.isRetryable === true) ||
|
|
86
|
+
(GatewayError.isInstance(error) && error.isRetryable === true))) ||
|
|
87
|
+
(additionalRetryableError != null &&
|
|
88
|
+
(await additionalRetryableError(error))),
|
|
84
89
|
getDelayInMs: ({ error, exponentialBackoffDelay }) =>
|
|
85
90
|
getRetryDelayInMs({
|
|
86
91
|
error: error as APICallError | GatewayError,
|