当下chatGPT如此火热,很多开发者都想部署一个 自己的gpt站点,本文不细致讨论gpt部署,只是着重总结一下博主在接入gpt时对于内容流失输出的解决方案,【代码质量并不高,感兴趣的小伙伴可以简单参考】
各个平台兼容性也比较友好,且支持小程序【小程序需要使用其内部自动的socket API,详情请参考其官方文档】
//使用WebSocket连接后端socket服务 注意此处wss 可以理解为https ws为http
socket = new WebSocket("ws://chat.xxx.saybot.net/websocket/" + userStore.uid)
//监听onopen 成功触发表示连接socket服务成功
socket.onopen = function () {
console.log("您已成功接入websocket服务,开始解决你的问题")
socket.send("")
}
//收到消息事件 gpt响应会不断触发onmessage事件,其事件对象中会有流失输出的内容
socket.onmessage = function (event) {
try {
const content = JSON.parse(event.data).content
console.log('gpt 响应内容为',content)
} catch (err) {
switch (event.data) {
case "[INTERRUPTED]":
case "[MODEL_TIMEOUT]":
case "TEMPERATURE_THRESHOLD":
case "[TOKENS]":
console.log("gpt 响应中断或者超时")
event.target.close()
break
case "[DONE]":
console.log("gpt 响应结束了")
event.target.close()
break
default:
console.log("message error", err)
break
}
}
}
//连接关闭事件
socket.onclose = function (event) {
console.log("您已断开")
}
//发生了错误事件
socket.onerror = function (e) {
console.log(""连接错误,请联系开发者")
}
响应数据如下:
SSE方案和websocket类似,但其存在一个比较大的”遗憾“就是不支持小程序
npm i event-source-polyfill //首先需要下载改库到项目
const eventSource = new EventSourcePolyfill("http:/xxxxxx/createSse", {
headers: {
uid: userStore.uid, //此处可以携带你需要的headers
token: localStorage.getItem("accessToken")
}
})
eventSource.onopen = event => {
console.log("SSE 连接成功, 开始请求数据")
}
eventSource.onmessage = async event => {
const { data, lastEventId } = event
switch (lastEventId) {
case "[INTERRUPTED]":
case "[MODEL_TIMEOUT]":
case "TEMPERATURE_THRESHOLD":
console.log("gpt 回复超时或中断")
event.target.close()
beak
case "[TOKENS]":
case "[DONE]":
console.log("gpt 回复完毕")
event.target.close()
break
default:
const content = JSON.parse(data).content
console.log("gpt 回复:",content)
break
}
}
eventSource.onerror = event => {
console.log("gpt onerror :",event)
}
解析输出数据如下:
通过fetch 请求,获取stream流式数据,相对比较麻烦不推荐
export const postMessage = async data => {
const res = await fetch("http://xxxxx/talkByStream", {
method: "POST",
headers: {
"Content-Type": "application/json",
token: userStore.token
},
body: JSON.stringify(data)
})
if (res.status && res.status !== 200) {
console.log('error',res)
}
return res?.body?.getReader(); //getReader()可以让调用者更方便字符内容的处理
}
调用fetch请求
const reader = await postMessage({
conversation: messageListToConversation(messageList.value),
userMessage: { role: "user", content },
temperature: 0.9
})
reader.read().then(async ({ value, done }) => {
if (done) {
return
}
let text = new TextDecoder().decode(value)
const responseStatus = text.match(REGEXP_FINISH_STATUS)
const matchs = text.match(REGEXP_CONTENT_ARR)
for (let i = 0; i < matchs?. length; i++) {
let [, content] = matchs[i].match(REGEXP_CONTENT)
content = content.replace(/\\n/g, "\n")
await sleep(90) //睡眠函数
console.log(content )
}
}
以上方案仅供参考,只是一个基础demo,实际使用需要调整,只是为大家提供一种思路,谢谢