Streaming responses¶
Streaming in itty-router can be done by returning the right set of headers and using a TransformStream
.
Here's an example for returning a ChatGPT response via streaming, here words are being sent to the client's as soon as the they get generated by the model, instead of waiting for the complete response.
import { OpenAPIRoute, OpenAPIRouter } from 'chanfana'
import { z } from 'zod'
import OpenAI from 'openai'
import { Context } from 'hono'
export class ChatGPT extends OpenAPIRoute {
schema = {
parameters: {
question: Query(z.string()),
},
response: {
200: {
description: 'ChatGPT response',
contentType: 'text/plain'
},
},
}
async handle(c: Context) {
const { question } = data.query
const { readable, writable } = new TransformStream()
const writer = writable.getWriter()
const encoder = new TextEncoder()
const openai = new OpenAI({
apiKey: 'your-api-key',
})
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [{
role: 'user',
content: question,
}],
temperature: 0,
max_tokens: 256,
stream: true, // send this parameter to let openai know they need to stream the response
})
const fn = async () => {
for await (const message of response) {
const text = message.choices[0]?.delta.content ?? ''
await Promise.all(
Array.from(text).map(async (s) => {
// stream the text chunk
await writer.write(encoder.encode(s))
// wait a minimum of 20 miliseconds before sending another chunk
await new Promise((res) => setTimeout(res, 20))
}),
)
}
}
// When the response is over, close the write stream
fn().finally(async () => {
await writer.close()
})
return new Response(readable, {
headers: { // required headers for streaming responses
'content-type': 'text/plain; charset=UTF-8',
'x-content-type-options': 'nosniff',
'transfer-encoding': 'chunked',
},
})
}
}