前言
最近捣鼓小程序,突然发现小程序好像没怎么有ai对话,就突然想写一个对话小程序,记录一下走过的坑。
前端:uniapp 后端:express openai-node
后端实现流对话功能
本来后端用的是egg.js,但是我写的时候发现egg时候使用body返回PassThrough数据就会等待全部数据返回后一起返回,具体原因使用for循环就会出现这种情况,使用setTimeout模拟流数据就不会出现这种情况,由于我对egg不是很熟悉就没有研究这是为什么,知道的朋友可以留言告知我为什么下面是egg.js的代码
ctx.set('Content-Type', 'text/event-stream');
const PassThrough1 = new PassThrough()
ctx.body = PassThrough1
const stream = await openai.chat.completions.create({
messages: [{role: 'user', content: prompt}],
model: 'qwen-plus',
stream: true,
});
for await (const chunk of stream) {
try {
PassThrough1.write(`${JSON.stringify(chunk)}\n\n`)
}catch (e) {
console.log(e)
}
}
随后我又尝试了几次后无法解决就开始改用express,由于现在ChatGPT全面禁用咱们,所以我用的通义千问,虽然还没有node版本的jdk,但是还好支持opneai库只需要修改链接就可以,这样我们只要访问/ai这个接口就会得到流式数据并且渲染他。
const express = require('express')
const {OpenAI} = require('openai')
const app = express()
const port = 3000
const openai = new OpenAI({
apiKey: '********',
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
});
app.get('/ai', async (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/event-stream' }) // 'tex
const prompt = req.query.prompt
const stream = await openai.chat.completions.create({
messages: [{role: 'user', content: prompt}],
model: 'qwen-plus',
stream: true,
});
for await (const chunk of stream) {
// console.log(chunk)
res.write(`${JSON.stringify(chunk.choices[0]?.delta?.content || '')}`)
}
req.on('close', () => {
console.log('req close...')
})
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
uniapp编写小程序
小程序现在并不支持sse,所以我只能用以下方法来进行获取流数据,前端只写了核心代码。
const requestTask = uni.request({
url: 'http://127.0.0.1:3000/ai',
timeout: 15000,
responseType: 'text',
method: 'GET',
enableChunked: true, //配置这里
data: {
prompt: this.content
},
success: response => {
console.log(response)
},
fail: error => {}
})
requestTask.onHeadersReceived(function(res) {
console.log(res.header);
})
requestTask.onChunkReceived((res)=> {
//二进制数据转为字符串
const uint8Array = new Uint8Array(res.data);
let text = String.fromCharCode.apply(null, uint8Array);
text = decodeURIComponent(escape(text)).replace(/\"/g, "");
})