ai 7.0.134 → 7.0.135
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 +16 -0
- package/dist/index.d.ts +232 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +592 -42
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +4 -0
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +1 -1
- package/docs/03-agents/04-loop-control.mdx +2 -0
- package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +1 -1
- package/docs/03-ai-sdk-core/30-embeddings.mdx +27 -0
- package/docs/04-ai-sdk-ui/21-transport.mdx +73 -0
- package/docs/07-reference/01-ai-sdk-core/05-embed.mdx +14 -0
- package/docs/07-reference/01-ai-sdk-core/06-embed-many.mdx +14 -0
- package/docs/07-reference/02-ai-sdk-ui/51-websocket-chat-transport.mdx +393 -0
- package/docs/07-reference/02-ai-sdk-ui/index.mdx +6 -0
- package/package.json +12 -12
- package/src/agent/default-stop-condition.ts +33 -0
- package/src/agent/tool-loop-agent.ts +2 -2
- package/src/embed/embed-events.ts +6 -0
- package/src/embed/embed-many.ts +18 -0
- package/src/embed/embed.ts +16 -0
- package/src/embed/validate-embedding-dimensions.ts +14 -0
- package/src/ui/chat-transport.ts +16 -0
- package/src/ui/chat.ts +123 -34
- package/src/ui/index.ts +13 -0
- package/src/ui/process-ui-message-stream.ts +6 -2
- package/src/ui/websocket-chat-transport.ts +987 -0
- package/src/util/serial-job-executor.ts +22 -5
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { InvalidArgumentError } from '../error/invalid-argument-error';
|
|
2
|
+
|
|
3
|
+
export function validateEmbeddingDimensions(dimensions: number | undefined) {
|
|
4
|
+
if (
|
|
5
|
+
dimensions !== undefined &&
|
|
6
|
+
(!Number.isInteger(dimensions) || dimensions <= 0)
|
|
7
|
+
) {
|
|
8
|
+
throw new InvalidArgumentError({
|
|
9
|
+
parameter: 'dimensions',
|
|
10
|
+
value: dimensions,
|
|
11
|
+
message: 'dimensions must be a positive integer',
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
}
|
package/src/ui/chat-transport.ts
CHANGED
|
@@ -13,6 +13,22 @@ import type { UIMessage } from './ui-messages';
|
|
|
13
13
|
* @template UI_MESSAGE - The UI message type extending UIMessage
|
|
14
14
|
*/
|
|
15
15
|
export interface ChatTransport<UI_MESSAGE extends UIMessage> {
|
|
16
|
+
/**
|
|
17
|
+
* Whether reconnecting returns the complete response from its beginning.
|
|
18
|
+
* Complete replays must reset the streaming message state before processing
|
|
19
|
+
* the first start chunk.
|
|
20
|
+
*/
|
|
21
|
+
readonly resumeStreamIsReplay?: boolean;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Releases persistent resources owned by the transport.
|
|
25
|
+
*
|
|
26
|
+
* The caller that creates a transport owns its lifecycle and should call
|
|
27
|
+
* this method when no consumers remain. `AbstractChat.dispose()` calls it
|
|
28
|
+
* for directly managed chat instances.
|
|
29
|
+
*/
|
|
30
|
+
close?: () => void;
|
|
31
|
+
|
|
16
32
|
/**
|
|
17
33
|
* Sends messages to the chat API endpoint and returns a streaming response.
|
|
18
34
|
*
|
package/src/ui/chat.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { InvalidArgumentError } from '../error/invalid-argument-error';
|
|
|
8
8
|
import type { FinishReason } from '../types/language-model';
|
|
9
9
|
import type { UIMessageChunk } from '../ui-message-stream/ui-message-chunks';
|
|
10
10
|
import { consumeStream } from '../util/consume-stream';
|
|
11
|
+
import { createResolvablePromise } from '../util/create-resolvable-promise';
|
|
11
12
|
import { SerialJobExecutor } from '../util/serial-job-executor';
|
|
12
13
|
import type { ChatTransport } from './chat-transport';
|
|
13
14
|
import { convertFileListToFileUIParts } from './convert-file-list-to-file-ui-parts';
|
|
@@ -135,12 +136,19 @@ export type ChatStatus = 'submitted' | 'streaming' | 'ready' | 'error';
|
|
|
135
136
|
type ActiveResponse<UI_MESSAGE extends UIMessage> = {
|
|
136
137
|
state: StreamingUIMessageState<UI_MESSAGE>;
|
|
137
138
|
abortController: AbortController;
|
|
139
|
+
completionPromise: Promise<void>;
|
|
138
140
|
};
|
|
139
141
|
|
|
140
142
|
type ActiveResumeRequest = {
|
|
141
143
|
abortController: AbortController;
|
|
144
|
+
completionPromise: Promise<void>;
|
|
142
145
|
};
|
|
143
146
|
|
|
147
|
+
type MakeRequestOptions = {
|
|
148
|
+
trigger: 'submit-message' | 'resume-stream' | 'regenerate-message';
|
|
149
|
+
messageId?: string;
|
|
150
|
+
} & ChatRequestOptions;
|
|
151
|
+
|
|
144
152
|
export interface ChatState<UI_MESSAGE extends UIMessage> {
|
|
145
153
|
status: ChatStatus;
|
|
146
154
|
|
|
@@ -267,6 +275,8 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
267
275
|
| StreamingUIMessageState<UI_MESSAGE>
|
|
268
276
|
| undefined = undefined;
|
|
269
277
|
private jobExecutor = new SerialJobExecutor();
|
|
278
|
+
private activeStopCount = 0;
|
|
279
|
+
private stopGeneration = 0;
|
|
270
280
|
|
|
271
281
|
constructor({
|
|
272
282
|
generateId = generateIdFunc,
|
|
@@ -591,6 +601,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
591
601
|
|
|
592
602
|
// automatically send the message if the sendAutomaticallyWhen function returns true
|
|
593
603
|
if (
|
|
604
|
+
messageIndex !== -1 &&
|
|
594
605
|
this.status !== 'streaming' &&
|
|
595
606
|
this.status !== 'submitted' &&
|
|
596
607
|
this.sendAutomaticallyWhen
|
|
@@ -599,16 +610,18 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
599
610
|
|
|
600
611
|
if (shouldSend) {
|
|
601
612
|
// no await to avoid deadlocking
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
613
|
+
void this.runAutomaticRequest(() => {
|
|
614
|
+
const messageId =
|
|
615
|
+
messageIndex === -1
|
|
616
|
+
? this.lastMessage?.id
|
|
617
|
+
: messages[messageIndex].id;
|
|
618
|
+
|
|
619
|
+
return this.makeRequestForToolApproval({
|
|
620
|
+
messageId,
|
|
621
|
+
messageIndex,
|
|
622
|
+
...options,
|
|
623
|
+
});
|
|
624
|
+
}, shouldSend);
|
|
612
625
|
}
|
|
613
626
|
}
|
|
614
627
|
});
|
|
@@ -684,11 +697,15 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
684
697
|
|
|
685
698
|
if (shouldSend) {
|
|
686
699
|
// no await to avoid deadlocking
|
|
687
|
-
this.
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
700
|
+
void this.runAutomaticRequest(
|
|
701
|
+
() =>
|
|
702
|
+
this.makeRequest({
|
|
703
|
+
trigger: 'submit-message',
|
|
704
|
+
messageId: this.lastMessage?.id,
|
|
705
|
+
...options,
|
|
706
|
+
}),
|
|
707
|
+
shouldSend,
|
|
708
|
+
);
|
|
692
709
|
}
|
|
693
710
|
}
|
|
694
711
|
});
|
|
@@ -697,14 +714,42 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
697
714
|
addToolResult = this.addToolOutput;
|
|
698
715
|
|
|
699
716
|
/**
|
|
700
|
-
* Abort the current request
|
|
717
|
+
* Abort the current request, keep the generated tokens if any, and wait for
|
|
718
|
+
* the request pipeline to finish.
|
|
701
719
|
*/
|
|
702
720
|
stop = async () => {
|
|
703
|
-
|
|
704
|
-
|
|
721
|
+
this.activeStopCount++;
|
|
722
|
+
this.stopGeneration++;
|
|
723
|
+
|
|
724
|
+
try {
|
|
725
|
+
const activeResumeRequest = this.activeResumeRequest;
|
|
726
|
+
const activeResponse = this.activeResponse;
|
|
727
|
+
|
|
728
|
+
for (const controller of this.pendingMessagePreparations) {
|
|
729
|
+
controller.abort();
|
|
730
|
+
}
|
|
731
|
+
activeResumeRequest?.abortController.abort();
|
|
732
|
+
activeResponse?.abortController.abort();
|
|
733
|
+
|
|
734
|
+
await Promise.all([
|
|
735
|
+
activeResumeRequest?.completionPromise,
|
|
736
|
+
activeResponse?.completionPromise,
|
|
737
|
+
]);
|
|
738
|
+
|
|
739
|
+
// Stream cancellation can complete while a processing job is still
|
|
740
|
+
// blocked in onToolCall. Drain that job and any message update it queued.
|
|
741
|
+
await this.jobExecutor.waitForIdle();
|
|
742
|
+
} finally {
|
|
743
|
+
this.activeStopCount--;
|
|
705
744
|
}
|
|
706
|
-
|
|
707
|
-
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Stops active work and releases resources owned by the chat transport.
|
|
749
|
+
*/
|
|
750
|
+
dispose = async (): Promise<void> => {
|
|
751
|
+
await this.stop();
|
|
752
|
+
this.transport.close?.();
|
|
708
753
|
};
|
|
709
754
|
|
|
710
755
|
private async shouldSendAutomatically(): Promise<boolean> {
|
|
@@ -722,6 +767,30 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
722
767
|
return result as boolean;
|
|
723
768
|
}
|
|
724
769
|
|
|
770
|
+
private async runAutomaticRequest(
|
|
771
|
+
request: () => Promise<void>,
|
|
772
|
+
shouldSend?: boolean,
|
|
773
|
+
): Promise<void> {
|
|
774
|
+
const stopGeneration = this.stopGeneration;
|
|
775
|
+
const startedWhileStopping = this.activeStopCount > 0;
|
|
776
|
+
|
|
777
|
+
if (!(shouldSend ?? (await this.shouldSendAutomatically()))) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// A stop invalidates automatic requests that were pending when it started,
|
|
782
|
+
// including requests queued by callback-driven tool updates.
|
|
783
|
+
if (
|
|
784
|
+
startedWhileStopping ||
|
|
785
|
+
stopGeneration !== this.stopGeneration ||
|
|
786
|
+
this.activeStopCount > 0
|
|
787
|
+
) {
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
await request();
|
|
792
|
+
}
|
|
793
|
+
|
|
725
794
|
private async makeRequestForToolApproval({
|
|
726
795
|
messageId,
|
|
727
796
|
messageIndex,
|
|
@@ -752,23 +821,38 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
752
821
|
}
|
|
753
822
|
}
|
|
754
823
|
|
|
755
|
-
private async makeRequest({
|
|
824
|
+
private async makeRequest(options: MakeRequestOptions) {
|
|
825
|
+
const completion = createResolvablePromise<void>();
|
|
826
|
+
|
|
827
|
+
try {
|
|
828
|
+
await this.makeRequestImpl({
|
|
829
|
+
...options,
|
|
830
|
+
completionPromise: completion.promise,
|
|
831
|
+
});
|
|
832
|
+
} finally {
|
|
833
|
+
completion.resolve();
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
private async makeRequestImpl({
|
|
756
838
|
trigger,
|
|
757
839
|
metadata,
|
|
758
840
|
headers,
|
|
759
841
|
body,
|
|
760
842
|
messageId,
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
}
|
|
843
|
+
completionPromise,
|
|
844
|
+
}: MakeRequestOptions & {
|
|
845
|
+
completionPromise: Promise<void>;
|
|
846
|
+
}) {
|
|
765
847
|
if (trigger !== 'resume-stream') {
|
|
766
848
|
this.resumableStreamState = undefined;
|
|
767
849
|
}
|
|
768
850
|
|
|
769
851
|
const abortController = new AbortController();
|
|
770
852
|
const activeResumeRequest =
|
|
771
|
-
trigger === 'resume-stream'
|
|
853
|
+
trigger === 'resume-stream'
|
|
854
|
+
? { abortController, completionPromise }
|
|
855
|
+
: undefined;
|
|
772
856
|
|
|
773
857
|
if (activeResumeRequest) {
|
|
774
858
|
this.activeResumeRequest?.abortController.abort();
|
|
@@ -893,6 +977,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
893
977
|
messageId: this.generateId(),
|
|
894
978
|
}),
|
|
895
979
|
abortController,
|
|
980
|
+
completionPromise,
|
|
896
981
|
} as ActiveResponse<UI_MESSAGE>;
|
|
897
982
|
|
|
898
983
|
activeResponse = response;
|
|
@@ -964,6 +1049,8 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
964
1049
|
stream: processUIMessageStream({
|
|
965
1050
|
stream,
|
|
966
1051
|
resetStateOnMessageIdChange: trigger === 'resume-stream',
|
|
1052
|
+
resetStateOnFirstMessageStart:
|
|
1053
|
+
trigger === 'resume-stream' && this.transport.resumeStreamIsReplay,
|
|
967
1054
|
onToolCall: this.onToolCall,
|
|
968
1055
|
onData: this.onData,
|
|
969
1056
|
messageMetadataSchema: this.messageMetadataSchema,
|
|
@@ -1059,14 +1146,16 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
1059
1146
|
}
|
|
1060
1147
|
|
|
1061
1148
|
// automatically send the message if the sendAutomaticallyWhen function returns true
|
|
1062
|
-
if (!
|
|
1063
|
-
await this.
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1149
|
+
if (!isAbort && !isError) {
|
|
1150
|
+
await this.runAutomaticRequest(() =>
|
|
1151
|
+
this.makeRequest({
|
|
1152
|
+
trigger: 'submit-message',
|
|
1153
|
+
messageId: this.lastMessage?.id,
|
|
1154
|
+
metadata,
|
|
1155
|
+
headers,
|
|
1156
|
+
body,
|
|
1157
|
+
}),
|
|
1158
|
+
);
|
|
1070
1159
|
}
|
|
1071
1160
|
}
|
|
1072
1161
|
}
|
package/src/ui/index.ts
CHANGED
|
@@ -32,6 +32,19 @@ export {
|
|
|
32
32
|
export { lastAssistantMessageIsCompleteWithApprovalResponses } from './last-assistant-message-is-complete-with-approval-responses';
|
|
33
33
|
export { lastAssistantMessageIsCompleteWithToolCalls } from './last-assistant-message-is-complete-with-tool-calls';
|
|
34
34
|
export { TextStreamChatTransport } from './text-stream-chat-transport';
|
|
35
|
+
export {
|
|
36
|
+
safeValidateWebSocketChatTransportRequest,
|
|
37
|
+
WebSocketChatTransport,
|
|
38
|
+
type PrepareWebSocketChatTransportReconnectToStreamRequest,
|
|
39
|
+
type PrepareWebSocketChatTransportSendMessagesRequest,
|
|
40
|
+
type SafeValidateWebSocketChatTransportRequestResult,
|
|
41
|
+
type WebSocketChatTransportAbortRequest,
|
|
42
|
+
type WebSocketChatTransportInitOptions,
|
|
43
|
+
type WebSocketChatTransportRequest,
|
|
44
|
+
type WebSocketChatTransportResponse,
|
|
45
|
+
type WebSocketChatTransportResumeRequest,
|
|
46
|
+
type WebSocketChatTransportSendRequest,
|
|
47
|
+
} from './websocket-chat-transport';
|
|
35
48
|
export {
|
|
36
49
|
getStaticToolName,
|
|
37
50
|
getToolName,
|
|
@@ -121,6 +121,7 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
|
|
|
121
121
|
onToolCall,
|
|
122
122
|
onData,
|
|
123
123
|
resetStateOnMessageIdChange = false,
|
|
124
|
+
resetStateOnFirstMessageStart = false,
|
|
124
125
|
}: {
|
|
125
126
|
// input stream is not fully typed yet:
|
|
126
127
|
stream: ReadableStream<UIMessageChunk>;
|
|
@@ -139,6 +140,8 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
|
|
|
139
140
|
onError: ErrorHandler;
|
|
140
141
|
// During resume, a different message ID identifies a separate response.
|
|
141
142
|
resetStateOnMessageIdChange?: boolean;
|
|
143
|
+
// A transport can replay a response from the beginning using the same ID.
|
|
144
|
+
resetStateOnFirstMessageStart?: boolean;
|
|
142
145
|
}): ReadableStream<InferUIMessageChunk<UI_MESSAGE>> {
|
|
143
146
|
return stream.pipeThrough(
|
|
144
147
|
new TransformStream<UIMessageChunk, InferUIMessageChunk<UI_MESSAGE>>({
|
|
@@ -957,9 +960,10 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
|
|
|
957
960
|
|
|
958
961
|
case 'start': {
|
|
959
962
|
if (
|
|
960
|
-
resetStateOnMessageIdChange &&
|
|
961
963
|
chunk.messageId != null &&
|
|
962
|
-
|
|
964
|
+
(resetStateOnFirstMessageStart ||
|
|
965
|
+
(resetStateOnMessageIdChange &&
|
|
966
|
+
chunk.messageId !== state.message.id))
|
|
963
967
|
) {
|
|
964
968
|
// Start the separate response with empty parts and metadata.
|
|
965
969
|
// Reset the active-part maps too, so earlier chunks stay with
|