有開發(fā)過ChatGPT相關(guān)應(yīng)用的都知道,小程序是不支持流式請求的,目前市面上大多數(shù)開發(fā)者的解決方案都是使用websocket來解決。
還有一部分開發(fā)者是小程序嵌套網(wǎng)頁解決這個問題,前者對于我們軟件銷售型的團隊來說,交付會很麻煩,而且問題也會很多,而后者主要是體驗不怎么好,而且需要設(shè)置網(wǎng)頁授權(quán)域名。
作為ChatGPT最早期的開發(fā)者,我們開發(fā)的ChatGPT分銷版占據(jù)了市面上60%左右的市場,和下面圖片相似的都是我們的ChatGPT分銷版。
?在開發(fā)這個項目之前,我們開源了這個產(chǎn)品的前端模板,現(xiàn)在市面上很多雷同的產(chǎn)品都是基于我們的模板進行開發(fā)的,或者是借鑒開發(fā)的。
有客戶運營我們的產(chǎn)品,目前已經(jīng)累計200w+的充值金額,凈利潤預(yù)估至少100w+,我們的產(chǎn)品質(zhì)量源自于這些客戶的數(shù)據(jù)支撐。
我們的后端使用ThinkPHP5.0進行開發(fā),Saas架構(gòu),界面看上去很大氣,如下圖所示。
?顏值絕對秒殺市面上的所有ChatGPT應(yīng)用,當然我們的定價和服務(wù)也是相當超值的。
廢話不多說,接下來我來詳細介紹下我是怎么實現(xiàn)微信小程序的流式請求的。
一、設(shè)置請求頭
我的微信小程序和網(wǎng)頁H5都是用的同一個接口,而微信小程序不支持stream的方式,只能使用分段傳輸?shù)姆绞健?/p>
所以在這一步你需要做一個接口的兼容,我是傳一個參數(shù)代表是微信小程序請求還是網(wǎng)頁請求,從而設(shè)置不同的請求頭。
網(wǎng)頁H5的我就不給出header了,這里主要給出小程序的請求頭,如下所示。
// 設(shè)置響應(yīng)頭信息
header('Access-Control-Allow-Credentials: true');
// 設(shè)置響應(yīng)頭信息
header('Transfer-Encoding: chunked');
header('Cache-Control: no-cache');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
header('Connection: keep-alive');
header('X-Accel-Buffering: no');
二,設(shè)置回復(fù)兼容
網(wǎng)頁H5的流式請求我是直接返回的官方的響應(yīng)數(shù)據(jù),所以這里不能動之前的數(shù)據(jù)格式,在小程序請求這個接口時,需要單獨返回對應(yīng)的數(shù)據(jù)格式。
if ($is_wxapp) {
echo "success: " . json_encode(['content' => $content]) . "\r\n";
}
結(jié)尾使用“\r\n”,并且當所有數(shù)據(jù)響應(yīng)完成之后一定要輸出0,如下圖所示。
if ($is_wxapp) {
echo "0\r\n\r\n";
ob_flush();
flush();
}
我這里為了兼容網(wǎng)頁H5的流式請求,也是同樣的加了判斷之后輸出的。
三,進入前端請求代碼
以下是我完整的小程序請求方法,里面包含了我很多的業(yè)務(wù)邏輯,你可以根據(jù)你的業(yè)務(wù)進行修改,后面我會挑幾個注意事項進行簡述。
async onChatApplet() {
let _this = this;
let token = uni.getStorageSync('token');
const url = "";
const requestTask = uni.request({
url: url,
timeout: 15000,
responseType: 'text',
method: 'GET',
enableChunked: true,
data: {},
success: response => {
// console.log(response)
},
fail: error => {}
})
requestTask.onHeadersReceived(function(res) {
// console.log(res.header);
});
requestTask.onChunkReceived(function(response) {
const arrayBuffer = response.data;
const uint8Array = new Uint8Array(arrayBuffer);
let text = uni.arrayBufferToBase64(uint8Array)
text = new Buffer(text, 'base64')
text = text.toString('utf8');
if (text.indexOf('error') > 0) {
let error = text.replace("event: error\ndata: ", "").replace("\r\n", "")
error = JSON.parse(error)
let len = _this.question.length
_this.disabled = false
if (error.code == 0) {
_this.$refs.uToast.show({
type: 'error',
message: error.msg
})
if (len > 0) {
_this.question[len - 1].content = error.msg
} else {
_this.question[0].content = error.msg
}
return false;
}
if (in_array(error.code, [10001, 11000, 500, 404])) {
let length1 = 0
if (length1 > 0) {
_this.question[length1 - 1].content = error.msg
} else {
_this.question[0].content = error.msg
}
_this.$refs.uToast.show({
type: 'error',
message: error.msg
})
return false;
}
switch (error.code) {
case -1:
_this.question.pop()
uni.navigateTo({
url: '/pages/user/passport/login'
})
break;
case 101:
_this.question.pop()
_this.$refs.uToast.show({
type: 'error',
message: error.msg,
complete() {
uni.navigateTo({
url: '/pages/user/recharge/index'
})
}
})
break;
case 102:
let length = _this.question.length
if (length > 0) {
_this.question[length - 1].content = error.msg
} else {
_this.question[0].content = error.msg
}
_this.$refs.uToast.show({
type: 'error',
message: error.msg
})
break;
default:
break;
}
return false;
}
if (text.indexOf('success') != -1) {
let json = text.split('success: ');
json.forEach(function(element) {
if (element) {
element = JSON.parse(element)
let index = 0
if (_this.question.length > 0) {
index = _this.question.length - 1
}
_this.question[index].content += element.content;
_this.$nextTick(() => {
uni.pageScrollTo({
scrollTop: 2000000,
duration: 0
});
});
}
})
return false;
}
let done = text.replace("\r\n", "")
if (done == 0) {
_this.disabled = false
}
})
}
注意事項:編碼
const arrayBuffer = response.data;
const uint8Array = new Uint8Array(arrayBuffer);
let text = uni.arrayBufferToBase64(uint8Array)
text = new Buffer(text, 'base64')
text = text.toString('utf8');
我這個方式比較簡單粗暴,我在網(wǎng)上看到有人使用了第三方庫,但是我測試下來行不通,就使用了uni官方這個轉(zhuǎn)成Base64,然后再進行轉(zhuǎn)碼。
以上就是整個小程序的流式響應(yīng)回復(fù)所需要用到的技術(shù),也是最直接有效的方法,如果你現(xiàn)在掌握這門技術(shù),再加上ChatGPT目前的勢頭,我相信你也能做出一些事情。
好了,就這樣,做一個小小的記錄,后期如果有空,我會繼續(xù)分享我在開發(fā)ChatGPT產(chǎn)品的其他思路。
個人公眾號:程序員在囧途文章來源:http://www.zghlxwxcb.cn/news/detail-455573.html
歡迎大佬交流合作,交個朋友也行。文章來源地址http://www.zghlxwxcb.cn/news/detail-455573.html
到了這里,關(guān)于在微信小程序中如何支持使用流模式(stream),打造ChatGPT實時回復(fù)機器人,最詳細講解。的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!