ai 7.0.54 → 7.0.56
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 +31 -0
- package/dist/index.d.ts +128 -3
- package/dist/index.js +407 -83
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +2 -0
- package/dist/internal/index.js +1 -1
- package/docs/03-ai-sdk-core/05-generating-text.mdx +2 -2
- package/docs/03-ai-sdk-core/60-telemetry.mdx +2 -2
- package/docs/03-ai-sdk-core/65-lifecycle-callbacks.mdx +18 -1
- package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +6 -0
- package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +6 -0
- package/package.json +4 -4
- package/src/batch/batch-types.ts +134 -0
- package/src/batch/batch.ts +336 -0
- package/src/batch/index.ts +19 -0
- package/src/generate-text/generate-text.ts +5 -0
- package/src/generate-text/language-model-events.ts +4 -0
- package/src/generate-text/stream-language-model-call.ts +3 -0
- package/src/generate-video/generate-video.ts +17 -6
- package/src/index.ts +1 -0
- package/src/ui/chat-transport.ts +3 -0
- package/src/ui/chat.ts +87 -12
- package/src/ui/http-chat-transport.ts +1 -0
- package/src/util/consume-stream.ts +13 -0
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
import {
|
|
12
12
|
convertBase64ToUint8Array,
|
|
13
13
|
delay as defaultDelay,
|
|
14
|
+
generateId,
|
|
14
15
|
withUserAgentSuffix,
|
|
15
16
|
type DataContent,
|
|
16
17
|
detectMediaType,
|
|
@@ -553,13 +554,23 @@ async function executeStartStatusFlow({
|
|
|
553
554
|
}
|
|
554
555
|
}
|
|
555
556
|
|
|
556
|
-
// 2. Start the generation
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}),
|
|
557
|
+
// 2. Start the generation. `doStart` is billable: mint one idempotency token
|
|
558
|
+
// per logical start, outside the retry closure; a caller-supplied key wins.
|
|
559
|
+
const callerIdempotencyKey = Object.entries(callOptions.headers ?? {}).find(
|
|
560
|
+
([key, value]) =>
|
|
561
|
+
key.toLowerCase() === 'idempotency-key' && value !== undefined,
|
|
562
562
|
);
|
|
563
|
+
const startCallOptions = {
|
|
564
|
+
...callOptions,
|
|
565
|
+
headers: {
|
|
566
|
+
...callOptions.headers,
|
|
567
|
+
...(callerIdempotencyKey
|
|
568
|
+
? {}
|
|
569
|
+
: { 'idempotency-key': `aisdk_vid_${generateId()}` }),
|
|
570
|
+
},
|
|
571
|
+
webhookUrl,
|
|
572
|
+
};
|
|
573
|
+
const startResult = await retry(() => model.doStart!(startCallOptions));
|
|
563
574
|
|
|
564
575
|
const allWarnings = [...earlyWarnings, ...startResult.warnings];
|
|
565
576
|
let operationProviderMetadata =
|
package/src/index.ts
CHANGED
package/src/ui/chat-transport.ts
CHANGED
|
@@ -64,6 +64,7 @@ export interface ChatTransport<UI_MESSAGE extends UIMessage> {
|
|
|
64
64
|
*
|
|
65
65
|
* @param options - Configuration object containing:
|
|
66
66
|
* @param options.chatId - Unique identifier for the chat session to reconnect to
|
|
67
|
+
* @param options.abortSignal - Signal to abort the reconnection request if needed
|
|
67
68
|
* @param options.headers - Additional HTTP headers to include in the reconnection request
|
|
68
69
|
* @param options.body - Additional JSON properties to include in the request body
|
|
69
70
|
* @param options.metadata - Custom metadata to attach to the request
|
|
@@ -78,6 +79,8 @@ export interface ChatTransport<UI_MESSAGE extends UIMessage> {
|
|
|
78
79
|
options: {
|
|
79
80
|
/** Unique identifier for the chat session to reconnect to */
|
|
80
81
|
chatId: string;
|
|
82
|
+
/** Signal to abort the reconnection request if needed */
|
|
83
|
+
abortSignal?: AbortSignal;
|
|
81
84
|
} & ChatRequestOptions,
|
|
82
85
|
) => Promise<ReadableStream<UIMessageChunk> | null>;
|
|
83
86
|
}
|
package/src/ui/chat.ts
CHANGED
|
@@ -135,6 +135,10 @@ type ActiveResponse<UI_MESSAGE extends UIMessage> = {
|
|
|
135
135
|
abortController: AbortController;
|
|
136
136
|
};
|
|
137
137
|
|
|
138
|
+
type ActiveResumeRequest = {
|
|
139
|
+
abortController: AbortController;
|
|
140
|
+
};
|
|
141
|
+
|
|
138
142
|
export interface ChatState<UI_MESSAGE extends UIMessage> {
|
|
139
143
|
status: ChatStatus;
|
|
140
144
|
|
|
@@ -254,6 +258,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
254
258
|
private sendAutomaticallyWhen?: ChatInit<UI_MESSAGE>['sendAutomaticallyWhen'];
|
|
255
259
|
|
|
256
260
|
private activeResponse: ActiveResponse<UI_MESSAGE> | undefined = undefined;
|
|
261
|
+
private activeResumeRequest: ActiveResumeRequest | undefined = undefined;
|
|
257
262
|
private jobExecutor = new SerialJobExecutor();
|
|
258
263
|
|
|
259
264
|
constructor({
|
|
@@ -584,11 +589,8 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
584
589
|
* Abort the current request immediately, keep the generated tokens if any.
|
|
585
590
|
*/
|
|
586
591
|
stop = async () => {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
if (this.activeResponse?.abortController) {
|
|
590
|
-
this.activeResponse.abortController.abort();
|
|
591
|
-
}
|
|
592
|
+
this.activeResumeRequest?.abortController.abort();
|
|
593
|
+
this.activeResponse?.abortController.abort();
|
|
592
594
|
};
|
|
593
595
|
|
|
594
596
|
private async shouldSendAutomatically(): Promise<boolean> {
|
|
@@ -616,6 +618,25 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
616
618
|
trigger: 'submit-message' | 'resume-stream' | 'regenerate-message';
|
|
617
619
|
messageId?: string;
|
|
618
620
|
} & ChatRequestOptions) {
|
|
621
|
+
const abortController = new AbortController();
|
|
622
|
+
const activeResumeRequest =
|
|
623
|
+
trigger === 'resume-stream' ? { abortController } : undefined;
|
|
624
|
+
|
|
625
|
+
if (activeResumeRequest) {
|
|
626
|
+
this.activeResumeRequest?.abortController.abort();
|
|
627
|
+
this.activeResumeRequest = activeResumeRequest;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const isCurrentRequest = () =>
|
|
631
|
+
activeResumeRequest == null ||
|
|
632
|
+
this.activeResumeRequest === activeResumeRequest;
|
|
633
|
+
|
|
634
|
+
const clearActiveResumeRequest = () => {
|
|
635
|
+
if (this.activeResumeRequest === activeResumeRequest) {
|
|
636
|
+
this.activeResumeRequest = undefined;
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
|
|
619
640
|
// For resume-stream, check if there's an active stream before
|
|
620
641
|
// changing status. This avoids a brief flash of 'submitted' status
|
|
621
642
|
// when there is no stream to resume (e.g. on page load).
|
|
@@ -624,21 +645,49 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
624
645
|
try {
|
|
625
646
|
const reconnect = await this.transport.reconnectToStream({
|
|
626
647
|
chatId: this.id,
|
|
648
|
+
abortSignal: abortController.signal,
|
|
627
649
|
metadata,
|
|
628
650
|
headers,
|
|
629
651
|
body,
|
|
630
652
|
});
|
|
631
653
|
|
|
654
|
+
if (abortController.signal.aborted || !isCurrentRequest()) {
|
|
655
|
+
await reconnect?.cancel().catch(() => {});
|
|
656
|
+
if (isCurrentRequest()) {
|
|
657
|
+
this.setStatus({ status: 'ready' });
|
|
658
|
+
}
|
|
659
|
+
clearActiveResumeRequest();
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
632
663
|
if (reconnect == null) {
|
|
664
|
+
this.setStatus({ status: 'ready' });
|
|
665
|
+
clearActiveResumeRequest();
|
|
633
666
|
return; // no active stream found, so we do not resume
|
|
634
667
|
}
|
|
635
668
|
|
|
636
669
|
resumeStream = reconnect;
|
|
637
670
|
} catch (err) {
|
|
671
|
+
if (
|
|
672
|
+
abortController.signal.aborted ||
|
|
673
|
+
(err as { name?: string }).name === 'AbortError'
|
|
674
|
+
) {
|
|
675
|
+
if (isCurrentRequest()) {
|
|
676
|
+
this.setStatus({ status: 'ready' });
|
|
677
|
+
}
|
|
678
|
+
clearActiveResumeRequest();
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
if (!isCurrentRequest()) {
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
|
|
638
686
|
if (this.onError && err instanceof Error) {
|
|
639
687
|
this.onError(err);
|
|
640
688
|
}
|
|
641
689
|
this.setStatus({ status: 'error', error: err as Error });
|
|
690
|
+
clearActiveResumeRequest();
|
|
642
691
|
return;
|
|
643
692
|
}
|
|
644
693
|
}
|
|
@@ -661,7 +710,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
661
710
|
: this.state.snapshot(lastMessage),
|
|
662
711
|
messageId: this.generateId(),
|
|
663
712
|
}),
|
|
664
|
-
abortController
|
|
713
|
+
abortController,
|
|
665
714
|
} as ActiveResponse<UI_MESSAGE>;
|
|
666
715
|
|
|
667
716
|
activeResponse = response;
|
|
@@ -696,10 +745,18 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
696
745
|
}) => Promise<void>,
|
|
697
746
|
) =>
|
|
698
747
|
// serialize the job execution to avoid race conditions:
|
|
699
|
-
this.jobExecutor.run(() =>
|
|
700
|
-
|
|
748
|
+
this.jobExecutor.run(() => {
|
|
749
|
+
if (response.abortController.signal.aborted) {
|
|
750
|
+
return Promise.resolve();
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return job({
|
|
701
754
|
state: response.state,
|
|
702
755
|
write: () => {
|
|
756
|
+
if (response.abortController.signal.aborted) {
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
|
|
703
760
|
// streaming is set on first write (before it should be "submitted")
|
|
704
761
|
this.setStatus({ status: 'streaming' });
|
|
705
762
|
|
|
@@ -715,8 +772,8 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
715
772
|
this.state.pushMessage(response.state.message);
|
|
716
773
|
}
|
|
717
774
|
},
|
|
718
|
-
})
|
|
719
|
-
);
|
|
775
|
+
});
|
|
776
|
+
});
|
|
720
777
|
|
|
721
778
|
await consumeStream({
|
|
722
779
|
stream: processUIMessageStream({
|
|
@@ -730,17 +787,33 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
730
787
|
throw error;
|
|
731
788
|
},
|
|
732
789
|
}),
|
|
790
|
+
abortSignal: response.abortController.signal,
|
|
733
791
|
onError: error => {
|
|
734
792
|
throw error;
|
|
735
793
|
},
|
|
736
794
|
});
|
|
737
795
|
|
|
738
|
-
|
|
796
|
+
if (isAbort) {
|
|
797
|
+
if (isCurrentRequest()) {
|
|
798
|
+
this.setStatus({ status: 'ready' });
|
|
799
|
+
}
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
if (isCurrentRequest()) {
|
|
804
|
+
this.setStatus({ status: 'ready' });
|
|
805
|
+
}
|
|
739
806
|
} catch (err) {
|
|
740
807
|
// Ignore abort errors as they are expected.
|
|
741
808
|
if (isAbort || (err as any).name === 'AbortError') {
|
|
742
809
|
isAbort = true;
|
|
743
|
-
|
|
810
|
+
if (isCurrentRequest()) {
|
|
811
|
+
this.setStatus({ status: 'ready' });
|
|
812
|
+
}
|
|
813
|
+
return null;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
if (!isCurrentRequest()) {
|
|
744
817
|
return null;
|
|
745
818
|
}
|
|
746
819
|
|
|
@@ -779,6 +852,8 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
779
852
|
if (this.activeResponse === activeResponse) {
|
|
780
853
|
this.activeResponse = undefined;
|
|
781
854
|
}
|
|
855
|
+
|
|
856
|
+
clearActiveResumeRequest();
|
|
782
857
|
}
|
|
783
858
|
|
|
784
859
|
// automatically send the message if the sendAutomaticallyWhen function returns true
|
|
@@ -13,11 +13,23 @@
|
|
|
13
13
|
export async function consumeStream({
|
|
14
14
|
stream,
|
|
15
15
|
onError,
|
|
16
|
+
abortSignal,
|
|
16
17
|
}: {
|
|
17
18
|
stream: ReadableStream;
|
|
18
19
|
onError?: (error: unknown) => void;
|
|
20
|
+
abortSignal?: AbortSignal;
|
|
19
21
|
}): Promise<void> {
|
|
20
22
|
const reader = stream.getReader();
|
|
23
|
+
const cancelOnAbort = () => {
|
|
24
|
+
reader.cancel().catch(() => {});
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
if (abortSignal?.aborted) {
|
|
28
|
+
cancelOnAbort();
|
|
29
|
+
} else {
|
|
30
|
+
abortSignal?.addEventListener('abort', cancelOnAbort, { once: true });
|
|
31
|
+
}
|
|
32
|
+
|
|
21
33
|
try {
|
|
22
34
|
while (true) {
|
|
23
35
|
const { done } = await reader.read();
|
|
@@ -26,6 +38,7 @@ export async function consumeStream({
|
|
|
26
38
|
} catch (error) {
|
|
27
39
|
onError?.(error);
|
|
28
40
|
} finally {
|
|
41
|
+
abortSignal?.removeEventListener('abort', cancelOnAbort);
|
|
29
42
|
reader.releaseLock();
|
|
30
43
|
}
|
|
31
44
|
}
|