How to send readable stream from Next JS route handler?
Answered
English Angora posted this in #help-forum
English AngoraOP
As in title. I know there is a tutorial about the openAI usage for streaming chat msg. I only want to know how to send any kind of stream back to the client. Regardless if it is open ai and I want to chunk some other data.
Answered by Ray
something like this
export async function GET() {
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder();
const chunks = [1, 2, 3, 4];
for (const chunk of chunks) {
controller.enqueue(encoder.encode(`data: ${chunk}\n\n`));
await new Promise((resolve) => setTimeout(resolve, 2000));
}
controller.close();
},
});
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
},
});
}17 Replies
@English Angora As in title. I know there is a tutorial about the openAI usage for streaming chat msg. I only want to know how to send any kind of stream back to the client. Regardless if it is open ai and I want to chunk some other data.
something like this
export async function GET() {
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder();
const chunks = [1, 2, 3, 4];
for (const chunk of chunks) {
controller.enqueue(encoder.encode(`data: ${chunk}\n\n`));
await new Promise((resolve) => setTimeout(resolve, 2000));
}
controller.close();
},
});
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
},
});
}Answer
English AngoraOP
@Ray Thank for this. Yes it does make a stream and I was able to read it on the client. However, for some reason it still waits for some time, and then gives all the chunks at once. It does not start streaming as soon as I fetch the data on the client, which is the whole purpose of a stream, not to wait for the whole data to be transfered 😕
English AngoraOP
const document = new Document({ text: JSON.stringify(data) });
// Split text and create embeddings. Store them in a VectorStoreIndex
const index = await VectorStoreIndex.fromDocuments([document]);
// Query the index
const queryEngine = index.asQueryEngine();
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder();
const chunks = await queryEngine.query({ query, stream: true });
for await (const chunk of chunks) {
controller.enqueue(encoder.encode(chunk.response));
}
controller.close();
},
});I already have a "stream" from llama-index, but really it is a wrapper around openAI.
English AngoraOP
ok so something is off with the code related to llama index. Your code example does chunk as expected! Thanks this was already a massive help. Wonder why this hasn't been documented? Could be that it has nothing to do with Next js, but it is rather regular web api of node?
@English Angora ok so something is off with the code related to llama index. Your code example does chunk as expected! Thanks this was already a massive help. Wonder why this hasn't been documented? Could be that it has nothing to do with Next js, but it is rather regular web api of node?
yes the app router is build on top of the WEB API
https://developer.mozilla.org/en-US/docs/Web/API
https://developer.mozilla.org/en-US/docs/Web/API
@English Angora ok so something is off with the code related to llama index. Your code example does chunk as expected! Thanks this was already a massive help. Wonder why this hasn't been documented? Could be that it has nothing to do with Next js, but it is rather regular web api of node?
controller.enqueue(encoder.encode(`data: ${chunk}\n\n`));btw, the
data: \n\n is important, maybe try adding it to your codeEnglish AngoraOP
It actually work without it somehow.
In my case I suspect that this line tames a long time to execute and it actually does pause for some time:
In my case I suspect that this line tames a long time to execute and it actually does pause for some time:
const chunks = await queryEngine.query({ query, stream: true });English AngoraOP
I am a noob man, sorry for uggly code format
hm not sure
look here in the docs >> https://ts.llamaindex.ai/modules/high_level/query_engine
chunk.response is a string type
look here in the docs >> https://ts.llamaindex.ai/modules/high_level/query_engine
chunk.response is a string type
do you get stream response with this?
@English Angora hm not sure
look here in the docs >> https://ts.llamaindex.ai/modules/high_level/query_engine
chunk.response is a string type
well I think you can just return the stream here
const stream = await queryEngine.query({ query: "query string", stream: true });
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
},
});English AngoraOP
Yeah, one of the very first thing I tried.
Argument of type 'AsyncIterable<Response>' is not assignable to parameter of type 'BodyInit | null | undefined'.
Getting this from "Response"
Maybe I should be using somehting else? I have also tried NextApiResponse and NextResponse, none of them takes 'AsyncIterable<Response>' as type 😕
Argument of type 'AsyncIterable<Response>' is not assignable to parameter of type 'BodyInit | null | undefined'.
Getting this from "Response"
Maybe I should be using somehting else? I have also tried NextApiResponse and NextResponse, none of them takes 'AsyncIterable<Response>' as type 😕
oh
English AngoraOP
btw you helped me a lot, so thanks, I got it to work a bit faster with streaming and chunking but it does suck that it still pauses a bit.
Have a nice rest of the dat @Ray ðŸ™
Have a nice rest of the dat @Ray ðŸ™