Skip to content

Commit ae05fb7

Browse files
lgrammelvalstu
andauthoredMay 17, 2024··
feat (ai/streams): add StreamData support to streamToResponse (#1615)
Co-authored-by: Valtteri Karesto <valtteri.karesto@gmail.com>
1 parent 0705af3 commit ae05fb7

File tree

5 files changed

+98
-31
lines changed

5 files changed

+98
-31
lines changed
 

‎.changeset/unlucky-drinks-fry.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
feat (ai/streams): add StreamData support to streamToResponse

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

+54-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,64 @@ description: Learn to use streamToResponse helper function in your application.
55

66
# `streamToResponse`
77

8-
This method will pipe a ReadableStream to a Node.js ServerResponse object. It can be helpful to combine this with other AI stream utilities, such as OpenAIStream, in Node.js environments. Similar to StreamingTextResponse, it automatically sets the status code to 200 and the Content-Type header to 'text/plain; charset=utf-8'
8+
`streamToResponse` pipes an AI stream to a Node.js `ServerResponse` object and sets the status code and headers.
9+
10+
This is useful to create AI stream responses in environments that use `ServerResponse` objects, such as Node.js HTTP servers.
11+
12+
The status code and headers can be configured using the `options` parameter.
13+
By default, the status code is set to 200 and the Content-Type header is set to `text/plain; charset=utf-8`.
914

1015
## Import
1116

1217
### React
1318

1419
<Snippet text={`import { streamToResponse } from "ai"`} prompt={false} />
1520

16-
<ReferenceTable packageName="streams" functionName="streamToResponse" />
21+
<ObjectTableList
22+
content={{
23+
Parameters: [
24+
{
25+
name: 'stream',
26+
type: 'ReadableStream',
27+
description:
28+
'The Web Stream to pipe to the response. It can be the return value of OpenAIStream, HuggingFaceStream, AnthropicStream, or an AIStream instance.',
29+
},
30+
{
31+
name: 'response',
32+
type: 'ServerResponse',
33+
description:
34+
'The Node.js ServerResponse object to pipe the stream to. This is usually the second argument of a Node.js HTTP request handler.',
35+
},
36+
{
37+
name: 'options',
38+
type: 'Options',
39+
description: 'Configure the response',
40+
properties: [
41+
{
42+
type: 'Options',
43+
parameters: [
44+
{
45+
name: 'status',
46+
type: 'number',
47+
description:
48+
'The status code to set on the response. Defaults to `200`.',
49+
},
50+
{
51+
name: 'headers',
52+
type: 'Record<string, string>',
53+
description:
54+
"Additional headers to set on the response. Defaults to `{ 'Content-Type': 'text/plain; charset=utf-8' }`.",
55+
},
56+
],
57+
},
58+
],
59+
},
60+
{
61+
name: 'data',
62+
type: 'StreamData',
63+
description:
64+
'StreamData object for forwarding additional data to the client.',
65+
},
66+
],
67+
}}
68+
/>

‎packages/core/streams/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ export * from './mistral-stream';
1515
export * from './openai-stream';
1616
export * from './replicate-stream';
1717
export * from './stream-data';
18+
export * from './stream-to-response';
1819
export * from './streaming-react-response';
1920
export * from './streaming-text-response';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { ServerResponse } from 'node:http';
2+
import { StreamData } from './stream-data';
3+
import { mergeStreams } from '../core/util/merge-streams';
4+
5+
/**
6+
* A utility function to stream a ReadableStream to a Node.js response-like object.
7+
*/
8+
export function streamToResponse(
9+
res: ReadableStream,
10+
response: ServerResponse,
11+
init?: { headers?: Record<string, string>; status?: number },
12+
data?: StreamData,
13+
) {
14+
response.writeHead(init?.status ?? 200, {
15+
'Content-Type': 'text/plain; charset=utf-8',
16+
...init?.headers,
17+
});
18+
19+
let processedStream = res;
20+
21+
if (data) {
22+
processedStream = mergeStreams(data.stream, res);
23+
}
24+
25+
const reader = processedStream.getReader();
26+
function read() {
27+
reader.read().then(({ done, value }: { done: boolean; value?: any }) => {
28+
if (done) {
29+
response.end();
30+
return;
31+
}
32+
response.write(value);
33+
read();
34+
});
35+
}
36+
read();
37+
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import type { ServerResponse } from 'node:http';
2-
import { StreamData } from './stream-data';
31
import { mergeStreams } from '../core/util/merge-streams';
2+
import { StreamData } from './stream-data';
43

54
/**
65
* A utility class for streaming text responses.
@@ -23,30 +22,3 @@ export class StreamingTextResponse extends Response {
2322
});
2423
}
2524
}
26-
27-
/**
28-
* A utility function to stream a ReadableStream to a Node.js response-like object.
29-
*/
30-
export function streamToResponse(
31-
res: ReadableStream,
32-
response: ServerResponse,
33-
init?: { headers?: Record<string, string>; status?: number },
34-
) {
35-
response.writeHead(init?.status || 200, {
36-
'Content-Type': 'text/plain; charset=utf-8',
37-
...init?.headers,
38-
});
39-
40-
const reader = res.getReader();
41-
function read() {
42-
reader.read().then(({ done, value }: { done: boolean; value?: any }) => {
43-
if (done) {
44-
response.end();
45-
return;
46-
}
47-
response.write(value);
48-
read();
49-
});
50-
}
51-
read();
52-
}

0 commit comments

Comments
 (0)
Please sign in to comment.