Skip to content

Commit 1e3dfd2

Browse files
authored
feat (ai/core): enhance pipeToData/TextStreamResponse methods (#2934)
1 parent 37f412c commit 1e3dfd2

File tree

17 files changed

+589
-180
lines changed

17 files changed

+589
-180
lines changed

‎.changeset/sixty-rats-build.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
feat (ai/core): enhance pipeToData/TextStreamResponse methods

‎content/docs/07-reference/ai-sdk-core/02-stream-text.mdx‎

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,24 +1084,100 @@ To see `streamText` in action, check out [these examples](#examples).
10841084
},
10851085
{
10861086
name: 'pipeDataStreamToResponse',
1087-
type: '(response: ServerResponse, init?: { headers?: Record<string, string>; status?: number } => void',
1087+
type: '(response: ServerResponse, options: PipeDataStreamToResponseOptions } => void',
10881088
description:
10891089
'Writes stream data output to a Node.js response-like object. It sets a `Content-Type` header to `text/plain; charset=utf-8` and writes each stream data part as a separate chunk.',
1090+
properties: [
1091+
{
1092+
type: 'PipeDataStreamToResponseOptions',
1093+
parameters: [
1094+
{
1095+
name: 'init',
1096+
type: 'ResponseInit',
1097+
optional: true,
1098+
description: 'The response init options.',
1099+
properties: [
1100+
{
1101+
type: 'ResponseInit',
1102+
parameters: [
1103+
{
1104+
name: 'status',
1105+
type: 'number',
1106+
optional: true,
1107+
description: 'The response status code.',
1108+
},
1109+
{
1110+
name: 'statusText',
1111+
type: 'string',
1112+
optional: true,
1113+
description: 'The response status text.',
1114+
},
1115+
{
1116+
name: 'headers',
1117+
type: 'Record<string, string>',
1118+
optional: true,
1119+
description: 'The response headers.',
1120+
},
1121+
],
1122+
},
1123+
],
1124+
},
1125+
{
1126+
name: 'data',
1127+
type: 'StreamData',
1128+
optional: true,
1129+
description: 'The stream data object.',
1130+
},
1131+
{
1132+
name: 'getErrorMessage',
1133+
type: '(error: unknown) => string',
1134+
description:
1135+
'A function to get the error message from the error object. By default, all errors are masked as "" for safety reasons.',
1136+
optional: true,
1137+
},
1138+
],
1139+
},
1140+
],
10901141
},
10911142
{
10921143
name: 'pipeTextStreamToResponse',
1093-
type: '(response: ServerResponse, init?: { headers?: Record<string, string>; status?: number } => void',
1144+
type: '(response: ServerResponse, init?: ResponseInit => void',
10941145
description:
10951146
'Writes text delta output to a Node.js response-like object. It sets a `Content-Type` header to `text/plain; charset=utf-8` and writes each text delta as a separate chunk.',
1147+
properties: [
1148+
{
1149+
type: 'ResponseInit',
1150+
parameters: [
1151+
{
1152+
name: 'status',
1153+
type: 'number',
1154+
optional: true,
1155+
description: 'The response status code.',
1156+
},
1157+
{
1158+
name: 'statusText',
1159+
type: 'string',
1160+
optional: true,
1161+
description: 'The response status text.',
1162+
},
1163+
{
1164+
name: 'headers',
1165+
type: 'Record<string, string>',
1166+
optional: true,
1167+
description: 'The response headers.',
1168+
},
1169+
],
1170+
},
1171+
],
10961172
},
10971173
{
10981174
name: 'toDataStreamResponse',
1099-
type: '(options?: toDataStreamOptions) => Response',
1175+
type: '(options?: ToDataStreamOptions) => Response',
11001176
description:
11011177
'Converts the result to a streamed response object with a stream data part stream. It can be used with the `useChat` and `useCompletion` hooks.',
11021178
properties: [
11031179
{
1104-
type: 'toDataStreamOptions',
1180+
type: 'ToDataStreamOptions',
11051181
parameters: [
11061182
{
11071183
name: 'init',
@@ -1118,6 +1194,12 @@ To see `streamText` in action, check out [these examples](#examples).
11181194
optional: true,
11191195
description: 'The response status code.',
11201196
},
1197+
{
1198+
name: 'statusText',
1199+
type: 'string',
1200+
optional: true,
1201+
description: 'The response status text.',
1202+
},
11211203
{
11221204
name: 'headers',
11231205
type: 'Record<string, string>',
@@ -1150,6 +1232,31 @@ To see `streamText` in action, check out [these examples](#examples).
11501232
type: '(init?: ResponseInit) => Response',
11511233
description:
11521234
'Creates a simple text stream response. Each text delta is encoded as UTF-8 and sent as a separate chunk. Non-text-delta events are ignored.',
1235+
properties: [
1236+
{
1237+
type: 'ResponseInit',
1238+
parameters: [
1239+
{
1240+
name: 'status',
1241+
type: 'number',
1242+
optional: true,
1243+
description: 'The response status code.',
1244+
},
1245+
{
1246+
name: 'statusText',
1247+
type: 'string',
1248+
optional: true,
1249+
description: 'The response status text.',
1250+
},
1251+
{
1252+
name: 'headers',
1253+
type: 'Record<string, string>',
1254+
optional: true,
1255+
description: 'The response headers.',
1256+
},
1257+
],
1258+
},
1259+
],
11531260
},
11541261
]}
11551262
/>

‎content/docs/07-reference/ai-sdk-core/04-stream-object.mdx‎

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -761,15 +761,65 @@ To see `streamObject` in action, check out the [additional examples](#more-examp
761761
},
762762
{
763763
name: 'pipeTextStreamToResponse',
764-
type: '(response: ServerResponse, init?: { headers?: Record<string, string>; status?: number } => void',
764+
type: '(response: ServerResponse, init?: ResponseInit => void',
765765
description:
766766
'Writes text delta output to a Node.js response-like object. It sets a `Content-Type` header to `text/plain; charset=utf-8` and writes each text delta as a separate chunk.',
767+
properties: [
768+
{
769+
type: 'ResponseInit',
770+
parameters: [
771+
{
772+
name: 'status',
773+
type: 'number',
774+
optional: true,
775+
description: 'The response status code.',
776+
},
777+
{
778+
name: 'statusText',
779+
type: 'string',
780+
optional: true,
781+
description: 'The response status text.',
782+
},
783+
{
784+
name: 'headers',
785+
type: 'Record<string, string>',
786+
optional: true,
787+
description: 'The response headers.',
788+
},
789+
],
790+
},
791+
],
767792
},
768793
{
769794
name: 'toTextStreamResponse',
770795
type: '(init?: ResponseInit) => Response',
771796
description:
772797
'Creates a simple text stream response. Each text delta is encoded as UTF-8 and sent as a separate chunk. Non-text-delta events are ignored.',
798+
properties: [
799+
{
800+
type: 'ResponseInit',
801+
parameters: [
802+
{
803+
name: 'status',
804+
type: 'number',
805+
optional: true,
806+
description: 'The response status code.',
807+
},
808+
{
809+
name: 'statusText',
810+
type: 'string',
811+
optional: true,
812+
description: 'The response status text.',
813+
},
814+
{
815+
name: 'headers',
816+
type: 'Record<string, string>',
817+
optional: true,
818+
description: 'The response headers.',
819+
},
820+
],
821+
},
822+
],
773823
},
774824
]}
775825
/>

‎content/docs/07-reference/stream-helpers/05-stream-to-response.mdx‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ description: Learn to use streamToResponse helper function in your application.
55

66
# `streamToResponse`
77

8+
<Note type="warning">
9+
`streamToResponse` is deprecated. Use `pipeDataStreamToResponse` from
10+
[streamText](/docs/reference/ai-sdk-core/stream-text) instead.
11+
</Note>
12+
813
`streamToResponse` pipes an AI stream to a Node.js `ServerResponse` object and sets the status code and headers.
914

1015
This is useful to create AI stream responses in environments that use `ServerResponse` objects, such as Node.js HTTP servers.
@@ -14,8 +19,6 @@ By default, the status code is set to 200 and the Content-Type header is set to
1419

1520
## Import
1621

17-
### React
18-
1922
<Snippet text={`import { streamToResponse } from "ai"`} prompt={false} />
2023

2124
## Example
Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,21 @@
11
import { openai } from '@ai-sdk/openai';
2-
import { StreamData, streamText, streamToResponse } from 'ai';
2+
import { StreamData, streamText } from 'ai';
3+
import 'dotenv/config';
34
import { createServer } from 'http';
4-
import dotenv from 'dotenv';
5-
6-
dotenv.config();
75

86
createServer(async (req, res) => {
7+
// use stream data (optional):
8+
const data = new StreamData();
9+
data.append('initialized call');
10+
911
const result = await streamText({
1012
model: openai('gpt-4-turbo'),
1113
prompt: 'What is the weather in San Francisco?',
14+
onFinish() {
15+
data.append('call completed');
16+
data.close();
17+
},
1218
});
1319

14-
// use stream data
15-
const data = new StreamData();
16-
17-
data.append('initialized call');
18-
19-
streamToResponse(
20-
result.toAIStream({
21-
onFinal() {
22-
data.append('call completed');
23-
data.close();
24-
},
25-
}),
26-
res,
27-
{},
28-
data,
29-
);
20+
result.pipeDataStreamToResponse(res, { data });
3021
}).listen(8080);

‎packages/ai/core/generate-object/stream-object-result.ts‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,20 +85,17 @@ Additional response information.
8585
writes each text delta as a separate chunk.
8686
8787
@param response A Node.js response-like object (ServerResponse).
88-
@param init Optional headers and status code.
88+
@param init Optional headers, status code, and status text.
8989
*/
90-
pipeTextStreamToResponse(
91-
response: ServerResponse,
92-
init?: { headers?: Record<string, string>; status?: number },
93-
): void;
90+
pipeTextStreamToResponse(response: ServerResponse, init?: ResponseInit): void;
9491

9592
/**
9693
Creates a simple text stream response.
9794
The response has a `Content-Type` header set to `text/plain; charset=utf-8`.
9895
Each text delta is encoded as UTF-8 and sent as a separate chunk.
9996
Non-text-delta events are ignored.
10097
101-
@param init Optional headers and status code.
98+
@param init Optional headers, status code, and status text.
10299
*/
103100
toTextStreamResponse(init?: ResponseInit): Response;
104101
}

‎packages/ai/core/generate-object/stream-object.test.ts‎

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -527,28 +527,19 @@ describe('output = "object"', () => {
527527

528528
result.pipeTextStreamToResponse(mockResponse);
529529

530-
// Wait for the stream to finish writing to the mock response
531-
await new Promise(resolve => {
532-
const checkIfEnded = () => {
533-
if (mockResponse.ended) {
534-
resolve(undefined);
535-
} else {
536-
setImmediate(checkIfEnded);
537-
}
538-
};
539-
checkIfEnded();
540-
});
541-
542-
const decoder = new TextDecoder();
530+
await mockResponse.waitForEnd();
543531

544-
assert.strictEqual(mockResponse.statusCode, 200);
545-
assert.deepStrictEqual(mockResponse.headers, {
532+
expect(mockResponse.statusCode).toBe(200);
533+
expect(mockResponse.headers).toEqual({
546534
'Content-Type': 'text/plain; charset=utf-8',
547535
});
548-
assert.deepStrictEqual(
549-
mockResponse.writtenChunks.map(chunk => decoder.decode(chunk)),
550-
['{ ', '"content": "Hello, ', 'world', '!"', ' }'],
551-
);
536+
expect(mockResponse.getDecodedChunks()).toEqual([
537+
'{ ',
538+
'"content": "Hello, ',
539+
'world',
540+
'!"',
541+
' }',
542+
]);
552543
});
553544
});
554545

0 commit comments

Comments
 (0)