ai 6.0.248 → 6.0.249
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 +10 -0
- package/dist/index.d.mts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +85 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +85 -19
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +1 -1
- package/dist/internal/index.mjs +1 -1
- package/package.json +2 -2
- 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
package/dist/internal/index.js
CHANGED
|
@@ -164,7 +164,7 @@ function detectMediaType({
|
|
|
164
164
|
var import_provider_utils2 = require("@ai-sdk/provider-utils");
|
|
165
165
|
|
|
166
166
|
// src/version.ts
|
|
167
|
-
var VERSION = true ? "6.0.
|
|
167
|
+
var VERSION = true ? "6.0.249" : "0.0.0-test";
|
|
168
168
|
|
|
169
169
|
// src/util/download/download.ts
|
|
170
170
|
var download = async ({
|
package/dist/internal/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.249",
|
|
4
4
|
"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.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@opentelemetry/api": "^1.9.0",
|
|
48
|
-
"@ai-sdk/gateway": "3.0.
|
|
48
|
+
"@ai-sdk/gateway": "3.0.169",
|
|
49
49
|
"@ai-sdk/provider": "3.0.15",
|
|
50
50
|
"@ai-sdk/provider-utils": "4.0.44"
|
|
51
51
|
},
|
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
|
@@ -136,6 +136,10 @@ type ActiveResponse<UI_MESSAGE extends UIMessage> = {
|
|
|
136
136
|
abortController: AbortController;
|
|
137
137
|
};
|
|
138
138
|
|
|
139
|
+
type ActiveResumeRequest = {
|
|
140
|
+
abortController: AbortController;
|
|
141
|
+
};
|
|
142
|
+
|
|
139
143
|
export interface ChatState<UI_MESSAGE extends UIMessage> {
|
|
140
144
|
status: ChatStatus;
|
|
141
145
|
|
|
@@ -255,6 +259,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
255
259
|
private sendAutomaticallyWhen?: ChatInit<UI_MESSAGE>['sendAutomaticallyWhen'];
|
|
256
260
|
|
|
257
261
|
private activeResponse: ActiveResponse<UI_MESSAGE> | undefined = undefined;
|
|
262
|
+
private activeResumeRequest: ActiveResumeRequest | undefined = undefined;
|
|
258
263
|
private jobExecutor = new SerialJobExecutor();
|
|
259
264
|
|
|
260
265
|
constructor({
|
|
@@ -585,11 +590,8 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
585
590
|
* Abort the current request immediately, keep the generated tokens if any.
|
|
586
591
|
*/
|
|
587
592
|
stop = async () => {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
if (this.activeResponse?.abortController) {
|
|
591
|
-
this.activeResponse.abortController.abort();
|
|
592
|
-
}
|
|
593
|
+
this.activeResumeRequest?.abortController.abort();
|
|
594
|
+
this.activeResponse?.abortController.abort();
|
|
593
595
|
};
|
|
594
596
|
|
|
595
597
|
private async shouldSendAutomatically(): Promise<boolean> {
|
|
@@ -617,6 +619,25 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
617
619
|
trigger: 'submit-message' | 'resume-stream' | 'regenerate-message';
|
|
618
620
|
messageId?: string;
|
|
619
621
|
} & ChatRequestOptions) {
|
|
622
|
+
const abortController = new AbortController();
|
|
623
|
+
const activeResumeRequest =
|
|
624
|
+
trigger === 'resume-stream' ? { abortController } : undefined;
|
|
625
|
+
|
|
626
|
+
if (activeResumeRequest) {
|
|
627
|
+
this.activeResumeRequest?.abortController.abort();
|
|
628
|
+
this.activeResumeRequest = activeResumeRequest;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const isCurrentRequest = () =>
|
|
632
|
+
activeResumeRequest == null ||
|
|
633
|
+
this.activeResumeRequest === activeResumeRequest;
|
|
634
|
+
|
|
635
|
+
const clearActiveResumeRequest = () => {
|
|
636
|
+
if (this.activeResumeRequest === activeResumeRequest) {
|
|
637
|
+
this.activeResumeRequest = undefined;
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
|
|
620
641
|
// For resume-stream, check if there's an active stream before
|
|
621
642
|
// changing status. This avoids a brief flash of 'submitted' status
|
|
622
643
|
// when there is no stream to resume (e.g. on page load).
|
|
@@ -625,21 +646,49 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
625
646
|
try {
|
|
626
647
|
const reconnect = await this.transport.reconnectToStream({
|
|
627
648
|
chatId: this.id,
|
|
649
|
+
abortSignal: abortController.signal,
|
|
628
650
|
metadata,
|
|
629
651
|
headers,
|
|
630
652
|
body,
|
|
631
653
|
});
|
|
632
654
|
|
|
655
|
+
if (abortController.signal.aborted || !isCurrentRequest()) {
|
|
656
|
+
await reconnect?.cancel().catch(() => {});
|
|
657
|
+
if (isCurrentRequest()) {
|
|
658
|
+
this.setStatus({ status: 'ready' });
|
|
659
|
+
}
|
|
660
|
+
clearActiveResumeRequest();
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
|
|
633
664
|
if (reconnect == null) {
|
|
665
|
+
this.setStatus({ status: 'ready' });
|
|
666
|
+
clearActiveResumeRequest();
|
|
634
667
|
return; // no active stream found, so we do not resume
|
|
635
668
|
}
|
|
636
669
|
|
|
637
670
|
resumeStream = reconnect;
|
|
638
671
|
} catch (err) {
|
|
672
|
+
if (
|
|
673
|
+
abortController.signal.aborted ||
|
|
674
|
+
(err as { name?: string }).name === 'AbortError'
|
|
675
|
+
) {
|
|
676
|
+
if (isCurrentRequest()) {
|
|
677
|
+
this.setStatus({ status: 'ready' });
|
|
678
|
+
}
|
|
679
|
+
clearActiveResumeRequest();
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (!isCurrentRequest()) {
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
|
|
639
687
|
if (this.onError && err instanceof Error) {
|
|
640
688
|
this.onError(err);
|
|
641
689
|
}
|
|
642
690
|
this.setStatus({ status: 'error', error: err as Error });
|
|
691
|
+
clearActiveResumeRequest();
|
|
643
692
|
return;
|
|
644
693
|
}
|
|
645
694
|
}
|
|
@@ -662,7 +711,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
662
711
|
: this.state.snapshot(lastMessage),
|
|
663
712
|
messageId: this.generateId(),
|
|
664
713
|
}),
|
|
665
|
-
abortController
|
|
714
|
+
abortController,
|
|
666
715
|
} as ActiveResponse<UI_MESSAGE>;
|
|
667
716
|
|
|
668
717
|
activeResponse = response;
|
|
@@ -697,10 +746,18 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
697
746
|
}) => Promise<void>,
|
|
698
747
|
) =>
|
|
699
748
|
// serialize the job execution to avoid race conditions:
|
|
700
|
-
this.jobExecutor.run(() =>
|
|
701
|
-
|
|
749
|
+
this.jobExecutor.run(() => {
|
|
750
|
+
if (response.abortController.signal.aborted) {
|
|
751
|
+
return Promise.resolve();
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
return job({
|
|
702
755
|
state: response.state,
|
|
703
756
|
write: () => {
|
|
757
|
+
if (response.abortController.signal.aborted) {
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
|
|
704
761
|
// streaming is set on first write (before it should be "submitted")
|
|
705
762
|
this.setStatus({ status: 'streaming' });
|
|
706
763
|
|
|
@@ -716,8 +773,8 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
716
773
|
this.state.pushMessage(response.state.message);
|
|
717
774
|
}
|
|
718
775
|
},
|
|
719
|
-
})
|
|
720
|
-
);
|
|
776
|
+
});
|
|
777
|
+
});
|
|
721
778
|
|
|
722
779
|
await consumeStream({
|
|
723
780
|
stream: processUIMessageStream({
|
|
@@ -731,17 +788,33 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
731
788
|
throw error;
|
|
732
789
|
},
|
|
733
790
|
}),
|
|
791
|
+
abortSignal: response.abortController.signal,
|
|
734
792
|
onError: error => {
|
|
735
793
|
throw error;
|
|
736
794
|
},
|
|
737
795
|
});
|
|
738
796
|
|
|
739
|
-
|
|
797
|
+
if (isAbort) {
|
|
798
|
+
if (isCurrentRequest()) {
|
|
799
|
+
this.setStatus({ status: 'ready' });
|
|
800
|
+
}
|
|
801
|
+
return null;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
if (isCurrentRequest()) {
|
|
805
|
+
this.setStatus({ status: 'ready' });
|
|
806
|
+
}
|
|
740
807
|
} catch (err) {
|
|
741
808
|
// Ignore abort errors as they are expected.
|
|
742
809
|
if (isAbort || (err as any).name === 'AbortError') {
|
|
743
810
|
isAbort = true;
|
|
744
|
-
|
|
811
|
+
if (isCurrentRequest()) {
|
|
812
|
+
this.setStatus({ status: 'ready' });
|
|
813
|
+
}
|
|
814
|
+
return null;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
if (!isCurrentRequest()) {
|
|
745
818
|
return null;
|
|
746
819
|
}
|
|
747
820
|
|
|
@@ -780,6 +853,8 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
780
853
|
if (this.activeResponse === activeResponse) {
|
|
781
854
|
this.activeResponse = undefined;
|
|
782
855
|
}
|
|
856
|
+
|
|
857
|
+
clearActiveResumeRequest();
|
|
783
858
|
}
|
|
784
859
|
|
|
785
860
|
// 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
|
}
|