ai 6.0.296 → 6.0.297

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/index.d.mts +36 -5
  3. package/dist/index.d.ts +36 -5
  4. package/dist/index.js +187 -32
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +187 -32
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/internal/index.js +1 -1
  9. package/dist/internal/index.mjs +1 -1
  10. package/docs/04-ai-sdk-ui/02-chatbot.mdx +10 -1
  11. package/docs/04-ai-sdk-ui/03-chatbot-message-persistence.mdx +8 -0
  12. package/docs/04-ai-sdk-ui/03-chatbot-resume-streams.mdx +10 -3
  13. package/docs/04-ai-sdk-ui/03-chatbot-tool-usage.mdx +10 -1
  14. package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +1 -1
  15. package/docs/07-reference/02-ai-sdk-ui/41-create-ui-message-stream-response.mdx +7 -0
  16. package/docs/07-reference/02-ai-sdk-ui/42-pipe-ui-message-stream-to-response.mdx +7 -0
  17. package/package.json +2 -2
  18. package/src/agent/create-agent-ui-stream-response.ts +3 -0
  19. package/src/agent/pipe-agent-ui-stream-to-response.ts +3 -0
  20. package/src/embed/embed-many.ts +5 -3
  21. package/src/generate-image/generate-image.ts +7 -2
  22. package/src/generate-text/stream-text-result.ts +5 -0
  23. package/src/generate-text/stream-text.ts +4 -1
  24. package/src/generate-video/generate-video.ts +6 -4
  25. package/src/registry/provider-registry.ts +1 -1
  26. package/src/ui/chat.ts +13 -4
  27. package/src/ui/http-chat-transport.ts +27 -1
  28. package/src/ui/last-assistant-message-is-complete-with-tool-calls.ts +15 -3
  29. package/src/ui/process-ui-message-stream.ts +51 -14
  30. package/src/ui/ui-messages.ts +14 -0
  31. package/src/ui/validate-ui-messages.ts +2 -0
  32. package/src/ui-message-stream/create-sse-stream-with-keep-alive.ts +89 -0
  33. package/src/ui-message-stream/create-ui-message-stream-response.ts +7 -1
  34. package/src/ui-message-stream/pipe-ui-message-stream-to-response.ts +7 -1
  35. package/src/ui-message-stream/ui-message-stream-response-init.ts +7 -0
  36. package/src/util/get-own.ts +18 -0
  37. package/src/util/set-own.ts +16 -0
@@ -0,0 +1,89 @@
1
+ const STREAM_OPEN_COMMENT = ': stream-open\n\n';
2
+ const KEEP_ALIVE_COMMENT = ': keep-alive\n\n';
3
+
4
+ export function createSseStreamWithKeepAlive({
5
+ stream,
6
+ keepAliveMs,
7
+ }: {
8
+ stream: ReadableStream<string>;
9
+ keepAliveMs: number | undefined;
10
+ }): ReadableStream<string> {
11
+ if (keepAliveMs == null) {
12
+ return stream;
13
+ }
14
+
15
+ if (
16
+ !Number.isFinite(keepAliveMs) ||
17
+ keepAliveMs <= 0 ||
18
+ keepAliveMs > 2_147_483_647
19
+ ) {
20
+ throw new Error(
21
+ 'keepAliveMs must be a positive finite timer duration no greater than 2147483647',
22
+ );
23
+ }
24
+
25
+ const reader = stream.getReader();
26
+ let keepAliveTimeout: ReturnType<typeof setTimeout> | undefined;
27
+ let isCancelled = false;
28
+
29
+ const clearKeepAliveTimeout = () => {
30
+ clearTimeout(keepAliveTimeout);
31
+ keepAliveTimeout = undefined;
32
+ };
33
+
34
+ const scheduleKeepAlive = (
35
+ controller: ReadableStreamDefaultController<string>,
36
+ ) => {
37
+ clearKeepAliveTimeout();
38
+ keepAliveTimeout = setTimeout(() => {
39
+ if (isCancelled) {
40
+ return;
41
+ }
42
+
43
+ if (controller.desiredSize != null && controller.desiredSize > 0) {
44
+ controller.enqueue(KEEP_ALIVE_COMMENT);
45
+ }
46
+
47
+ scheduleKeepAlive(controller);
48
+ }, keepAliveMs);
49
+ };
50
+
51
+ return new ReadableStream<string>({
52
+ start(controller) {
53
+ controller.enqueue(STREAM_OPEN_COMMENT);
54
+ scheduleKeepAlive(controller);
55
+ },
56
+
57
+ pull(controller) {
58
+ return reader.read().then(
59
+ result => {
60
+ clearKeepAliveTimeout();
61
+
62
+ if (isCancelled) {
63
+ return;
64
+ }
65
+
66
+ if (result.done) {
67
+ controller.close();
68
+ } else {
69
+ controller.enqueue(result.value);
70
+ scheduleKeepAlive(controller);
71
+ }
72
+ },
73
+ error => {
74
+ clearKeepAliveTimeout();
75
+
76
+ if (!isCancelled) {
77
+ throw error;
78
+ }
79
+ },
80
+ );
81
+ },
82
+
83
+ async cancel(reason) {
84
+ isCancelled = true;
85
+ clearKeepAliveTimeout();
86
+ await reader.cancel(reason);
87
+ },
88
+ });
89
+ }
@@ -1,4 +1,5 @@
1
1
  import { prepareHeaders } from '../util/prepare-headers';
2
+ import { createSseStreamWithKeepAlive } from './create-sse-stream-with-keep-alive';
2
3
  import { JsonToSseTransformStream } from './json-to-sse-transform-stream';
3
4
  import { UI_MESSAGE_STREAM_HEADERS } from './ui-message-stream-headers';
4
5
  import type { UIMessageChunk } from './ui-message-chunks';
@@ -12,6 +13,7 @@ import type { UIMessageStreamResponseInit } from './ui-message-stream-response-i
12
13
  * @param options.statusText - The HTTP status text for the response.
13
14
  * @param options.headers - Additional HTTP headers to include in the response.
14
15
  * @param options.stream - The UI message chunk stream to send.
16
+ * @param options.keepAliveMs - Optional interval for sending SSE keep-alive comments.
15
17
  * @param options.consumeSseStream - Optional callback to consume a copy of the SSE stream independently.
16
18
  *
17
19
  * @returns A `Response` object with the UI message stream as the body.
@@ -21,11 +23,15 @@ export function createUIMessageStreamResponse({
21
23
  statusText,
22
24
  headers,
23
25
  stream,
26
+ keepAliveMs,
24
27
  consumeSseStream,
25
28
  }: UIMessageStreamResponseInit & {
26
29
  stream: ReadableStream<UIMessageChunk>;
27
30
  }): Response {
28
- let sseStream = stream.pipeThrough(new JsonToSseTransformStream());
31
+ let sseStream = createSseStreamWithKeepAlive({
32
+ stream: stream.pipeThrough(new JsonToSseTransformStream()),
33
+ keepAliveMs,
34
+ });
29
35
 
30
36
  // when the consumeSseStream is provided, we need to tee the stream
31
37
  // and send the second part to the consumeSseStream function
@@ -1,6 +1,7 @@
1
1
  import type { ServerResponse } from 'node:http';
2
2
  import { prepareHeaders } from '../util/prepare-headers';
3
3
  import { writeToServerResponse } from '../util/write-to-server-response';
4
+ import { createSseStreamWithKeepAlive } from './create-sse-stream-with-keep-alive';
4
5
  import { JsonToSseTransformStream } from './json-to-sse-transform-stream';
5
6
  import { UI_MESSAGE_STREAM_HEADERS } from './ui-message-stream-headers';
6
7
  import type { UIMessageChunk } from './ui-message-chunks';
@@ -15,6 +16,7 @@ import type { UIMessageStreamResponseInit } from './ui-message-stream-response-i
15
16
  * @param options.statusText - The HTTP status text for the response.
16
17
  * @param options.headers - Additional HTTP headers to include in the response.
17
18
  * @param options.stream - The UI message chunk stream to send.
19
+ * @param options.keepAliveMs - Optional interval for sending SSE keep-alive comments.
18
20
  * @param options.consumeSseStream - Optional callback to consume a copy of the SSE stream independently.
19
21
  * @returns A promise that resolves when the stream has been written.
20
22
  */
@@ -24,12 +26,16 @@ export function pipeUIMessageStreamToResponse({
24
26
  statusText,
25
27
  headers,
26
28
  stream,
29
+ keepAliveMs,
27
30
  consumeSseStream,
28
31
  }: {
29
32
  response: ServerResponse;
30
33
  stream: ReadableStream<UIMessageChunk>;
31
34
  } & UIMessageStreamResponseInit): Promise<void> {
32
- let sseStream = stream.pipeThrough(new JsonToSseTransformStream());
35
+ let sseStream = createSseStreamWithKeepAlive({
36
+ stream: stream.pipeThrough(new JsonToSseTransformStream()),
37
+ keepAliveMs,
38
+ });
33
39
 
34
40
  // when the consumeSseStream is provided, we need to tee the stream
35
41
  // and send the second part to the consumeSseStream function
@@ -3,6 +3,13 @@
3
3
  * Extends the standard `ResponseInit` with additional streaming options.
4
4
  */
5
5
  export type UIMessageStreamResponseInit = ResponseInit & {
6
+ /**
7
+ * Optional interval in milliseconds for sending SSE keep-alive comments.
8
+ * When set, an opening comment is sent immediately and additional comments
9
+ * are sent after the stream has been idle for the configured interval.
10
+ */
11
+ keepAliveMs?: number;
12
+
6
13
  /**
7
14
  * Optional callback to consume a copy of the SSE stream independently.
8
15
  * This is useful for logging, debugging, or processing the stream in parallel.
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Reads a property by an untrusted key, ignoring inherited prototype members.
3
+ *
4
+ * Tool sets, tool contexts, and similar lookup objects are indexed by names
5
+ * that can come from model output or client-supplied message history. Plain
6
+ * bracket access (`obj[name]`) resolves names such as `constructor`,
7
+ * `toString`, or `__proto__` to values on `Object.prototype`, which would slip
8
+ * past the `== null` / `!value` guards that treat an unknown name as "not
9
+ * present". This helper returns `undefined` unless `key` is an own property.
10
+ */
11
+ export function getOwn<T extends object>(
12
+ obj: T | undefined | null,
13
+ key: string,
14
+ ): T[keyof T] | undefined {
15
+ return obj != null && Object.hasOwn(obj, key)
16
+ ? obj[key as keyof T]
17
+ : undefined;
18
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Writes an enumerable own property without invoking inherited setters such as
3
+ * `Object.prototype.__proto__`. Keeps the target object's prototype unchanged.
4
+ */
5
+ export function setOwn<T>(
6
+ object: Record<string, T>,
7
+ key: string,
8
+ value: T,
9
+ ): void {
10
+ Object.defineProperty(object, key, {
11
+ value,
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ });
16
+ }