hello大家好,我是Range。
自去年以来,ChatGPT为代表的大模型方兴未艾,一个又一个的通用大模型不断刷新我们的认知。很多国内的公司,虽然没有自己开发大模型的能力,但是基于大模型来做上层应用的能力还是有的,而且很大。相信不少小伙伴,已经接触过类似的前端应用开发了,主要的技术点就包括:网络层面使用 Event Stream(SSE)从后端接收流式数据、前端渲染的打字机效果。
今天带来一篇在微信小程序里,接入 SSE 的文章,小程序里需要借助一些开源类库来实现。特别注意的是,流式数据,不再和我们通常的http请求一样,一次就拿到了全部的body数据,在处理流式数据过程中,一定要考虑到,我们拿到的数据有可能是JSON的某一部分,甚至一段文本也被会截取到多个响应回调中,要做好容错处理!
下面是正文部分。
近期忙于将公司内部的AI产品接入到小程序中
发现通过调用内部AI接口发现接口返回的格式是「event stream」的形式,而小程序则是通过开启wx.request中的「enableChunked」的方式来实现
关于wx.request的官方说明
相比于SSE之前更熟悉Websocket,那么两种有什么区别呢?
SSE本质上「还是属于http协议」,而Websocket并不属于http协议
SSE是单向通信,「只能由服务端向客户端发送请求」。
以及SSE是通过保持一个持久的 HTTP 连接来实现推送,「属于长连接」,而Websocket是全双工的。
前文已经提到,核心在于配置wx.request中的「enableChunked」参数
const requestTask = wx.request({
url,
method: 'POST',
data: params,
enableChunked: true,
header: {
token,
}
})
微信官方并且提供了「监听」与「取消」的API
//监听的api
requestTask.onChunkReceived((res) => {
//xxxxxxxxxx
});
//取消的api
requestTask.offChunkReceived()
可以看到返回的结果为「Uint8Array」,而在「小程序中由于不支持TextEncoder/TextDecoder对象」我的解决方式
先将unit8Array转化为十六进制
将十六进制转为中文字符串
其中需要引入「encoding.js和encoding-indexes.js」两个文件
这两个文件的来源为text-encoding下lib文件下的两个文件。
import encoding from './encoding.js'
decodeUint8Array(uint8Array) {
const data16 = this.buf2hex(uint8Array);
const requestData = this.hexToStr(data16)
return requestData
},
buf2hex(arrayBuffer) {
return Array.prototype.map.call(new Uint8Array(arrayBuffer), x => ('00' + x.toString(16)).slice(-2)).join('');
},
/**
* 十六进制字符串转中文
* @param {String} hex 为十六进制字符串
* @return {String} 包含中文的字符串
*/
hexToStr(hex) {
// 去掉字符串首尾空格
let trimedStr = hex.trim()
// 判断trimedStr前两个字符是否为0x,如果是则截取从第三个字符及后面所有,否则返回全部字符
let rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr
// 得到rawStr的长度
let len = rawStr.length
// 如果长度不能被2整除,那么传入的十六进制值有误,返回空字符
if (len % 2 !== 0) {
return ""
}
let curCharCode // 接收每次循环得到的字符
let resultStr = [] // 存转换后的十进制值数组
for (let i = 0; i < len; i = i + 2) {
curCharCode = parseInt(rawStr.substr(i, 2), 16)
resultStr.push(curCharCode)
}
// encoding为空时默认为utf-8
let bytesView = new Uint8Array(resultStr) // 8 位无符号整数值的类型化数组
let str = new encoding.TextDecoder().decode(bytesView)
return str
},
最终调用的伪代码,其中的「chunkText」为处理好的字符串。
requestTask.onChunkReceived((res) => {
let chunkText = this.decodeUint8Array(res.data);
});
「tips」:不知道为什么在微信开发者工具的控制台中,即使接口响应成功还是「看不到SSE的返回」,如下图,只能通过console的方式来查看返回的数据。
在我们熟悉的大多AI产品中,它们的回答都是逐字出现的,这种效果我们也称为「打字机效果」打字机的核心代码
startTyping(text) {
const strLength = text.length;
let currentIndex = 0;
const intervalTime = 100;
let msg = '';
let typingInterval = setInterval(() => {
if (currentIndex < strLength) {
//获取当前索引对应的字
let currentChar = text.charAt(currentIndex);
msg += currentChar
//将msg赋值给你需要的数据项即可
this.setData({
test:msg
});
currentIndex++;
} else {
clearInterval(typingInterval);
}
}, intervalTime);
},
最后
还没有使用过我们刷题网站(https://fe.ecool.fun/)或者刷题小程序的同学,如果近期准备或者正在找工作,千万不要错过,题库主打题全和更新快哦~。
有会员购买、辅导咨询的小伙伴,可以通过下面的二维码,联系我们的小助手。