最近对接ChatGPT API的时候,发现信息都是一次性返回,十分不优雅。但是官网中确是逐句给出的答案,用户体验极佳😌
一开始,以为官方使用的是websocket,但其实是我想多了。多方查找资料,发现官网是通过Server-sent events(SSE)实现的。
废话不多说,直接上代码:
服务端实现
项目使用Next.js构建, API目录下创建ask-question/[...params].ts文件,写上如下代码
import type { NextApiRequest, NextApiResponse } from 'next'
import { Readable } from 'stream'
import { openai } from '@/lib/chatgpt'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
// 下面这四句是关键,感兴趣的小伙伴可以访问下面地址详细了解
// https://github.com/vercel/next.js/issues/9965
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Content-Type', 'text/event-stream;charset=utf-8')
res.setHeader('Cache-Control', 'no-cache, no-transform')
res.setHeader('X-Accel-Buffering', 'no')
// 这里是拿到客户端传递过来的参数,分别是”模型名称“ 和 ”输入内容“
const {
params: [model, prompt]
} = req.query as { params: string[] }
try {
const streamResponse = await openai.createChatCompletion(
{
model,
messages: [
{
role: 'user',
content: prompt
}
],
temperature: 0.9,
top_p: 1,
max_tokens: 1000,
frequency_penalty: 0,
presence_penalty: 0,
// ** ====> 这里是开启流式返回的属性
stream: true
},
{
timeout: 100000,
// ** ====> 这里要设置成stream
responseType: 'stream'
}
)
const stream = streamResponse.data as unknown as Readable
stream.on('data', (chunk) => {
res.write(chunk)
})
stream.on('end', () => {
res.end()
})
} catch (error: any) {
if (error.response) {
console.log(error.response.status)
console.log(error.response.data)
} else {
console.log(error.message)
}
}
}
客户端实现
// 在这里通过SSE发送请求
const eventSource = new EventSource(`/api/ask-question/${model}/${input}`)
let answer = ''
eventSource.addEventListener('message',async (event) => {
if (event.data === '[DONE]') {
// 信息返回结束
return eventSource.close()
}
const data = JSON.parse(event.data)
if (data.choices[0].delta.content) {
// 这里是服务端返回的信息
answer += data.choices[0].delta.content
}
})
eventSource.addEventListener('error', (err) => {
eventSource.close()
})
完整源码地址:github.com/valcosmos/v…
如发现任何错误,小伙伴可在评论区指出,感谢😬