持久化聊天数据到redis
This commit is contained in:
parent
1b8d864b39
commit
68fd5a79d7
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
doRequest
|
doRequest,
|
||||||
|
sid_prefix
|
||||||
} from "./config"
|
} from "./config"
|
||||||
|
|
||||||
App({
|
App({
|
||||||
@ -8,8 +9,45 @@ App({
|
|||||||
this.getSid(sid => {
|
this.getSid(sid => {
|
||||||
console.log(sid)
|
console.log(sid)
|
||||||
})
|
})
|
||||||
|
this.upload_conversation()
|
||||||
|
},
|
||||||
|
onHide: function () {
|
||||||
|
this.upload_conversation()
|
||||||
},
|
},
|
||||||
globalData: {},
|
globalData: {},
|
||||||
|
upload_cache_conversation: function (sid) {
|
||||||
|
wx.getStorage({
|
||||||
|
key: "chatList",
|
||||||
|
success: function (res) {
|
||||||
|
var data = res.data
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
doRequest("/save", "POST", {
|
||||||
|
"sid": sid_prefix + sid,
|
||||||
|
"conversations": data
|
||||||
|
}).then(res => {
|
||||||
|
console.log("upload " + data.length + " conversations success!")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
upload_conversation: function (conversations = []) {
|
||||||
|
var that = this
|
||||||
|
if (conversations.length == 0) {
|
||||||
|
that.getSid(sid => {
|
||||||
|
that.upload_cache_conversation(sid)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
that.getSid(sid => {
|
||||||
|
doRequest("/save", "POST", {
|
||||||
|
"sid": sid_prefix + sid,
|
||||||
|
"conversations": conversations,
|
||||||
|
}).then(res => {
|
||||||
|
console.log("upload " + conversations.length + " conversations success!")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
getSid: function (callback) {
|
getSid: function (callback) {
|
||||||
var that = this
|
var that = this
|
||||||
if (!this.globalData.sid) {
|
if (!this.globalData.sid) {
|
||||||
|
|||||||
@ -1,5 +1,11 @@
|
|||||||
const app = getApp()
|
const app = getApp()
|
||||||
|
|
||||||
|
import {
|
||||||
|
doRequest,
|
||||||
|
sid_prefix,
|
||||||
|
systemInfo
|
||||||
|
} from "../../config"
|
||||||
|
|
||||||
var closeShareOnCopy = false
|
var closeShareOnCopy = false
|
||||||
try {
|
try {
|
||||||
if (wx.getStorageSync("closeShareOnCopy")) {
|
if (wx.getStorageSync("closeShareOnCopy")) {
|
||||||
@ -22,14 +28,14 @@ Component({
|
|||||||
},
|
},
|
||||||
pageLifetimes: {
|
pageLifetimes: {
|
||||||
show: function () {
|
show: function () {
|
||||||
this.initMessageHistory()
|
// this.initMessageHistory()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lifetimes: {
|
lifetimes: {
|
||||||
attached() {
|
attached() {
|
||||||
var that = this
|
var that = this
|
||||||
app.globalData.cht = that
|
app.globalData.cht = that
|
||||||
//that.initMessageHistory()
|
that.initMessageHistory()
|
||||||
wx.getSystemInfo({
|
wx.getSystemInfo({
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
that.setData({
|
that.setData({
|
||||||
@ -48,22 +54,69 @@ Component({
|
|||||||
autoIncrConversation: 1,
|
autoIncrConversation: 1,
|
||||||
closeShareOnCopy: closeShareOnCopy,
|
closeShareOnCopy: closeShareOnCopy,
|
||||||
showShare: false,
|
showShare: false,
|
||||||
|
loadingData: false,
|
||||||
|
height: systemInfo.windowHeight - parseInt(100 / 750 * systemInfo.windowWidth) - ((systemInfo.platform == "ios" || systemInfo.platform == "android") ? 22 : 5)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
initMessageHistory() {
|
bindscrolltoupper: function (e) {
|
||||||
var that = this
|
var that = this
|
||||||
var data = wx.getStorageSync("chatList")
|
if (that.data.loadingData) {
|
||||||
data = data ? data : []
|
return
|
||||||
data.forEach((v) => {
|
|
||||||
if (v["suggests"] === undefined) {
|
|
||||||
v["suggests"] = []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (data.length > 0) {
|
|
||||||
that.setData({
|
|
||||||
chatList: data,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
that.setData({
|
||||||
|
loadingData: true
|
||||||
|
})
|
||||||
|
wx.showLoading({
|
||||||
|
title: "加载历史记录...",
|
||||||
|
})
|
||||||
|
app.getSid(sid => {
|
||||||
|
var page = 1
|
||||||
|
if (that.data.chatList.length > 0) {
|
||||||
|
page = Math.ceil((that.data.chatList.length + 1) / 10)
|
||||||
|
}
|
||||||
|
doRequest("/query", "GET", {
|
||||||
|
"sid": sid_prefix + sid,
|
||||||
|
"page": page,
|
||||||
|
"size": 10,
|
||||||
|
}).then(res => {
|
||||||
|
var data = res.data["data"]
|
||||||
|
data.reverse()
|
||||||
|
var oldData = that.data.chatList
|
||||||
|
var filterData = []
|
||||||
|
if (oldData.length > 0) {
|
||||||
|
data.forEach(k => {
|
||||||
|
if (k["dt"] < oldData[0]["dt"]) {
|
||||||
|
filterData.push(k)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
filterData = data
|
||||||
|
}
|
||||||
|
var newData = filterData.concat(oldData)
|
||||||
|
that.setData({
|
||||||
|
chatList: newData,
|
||||||
|
loadingData: false
|
||||||
|
}, () => {
|
||||||
|
if (filterData.length == 0 && e) {
|
||||||
|
setTimeout(() => {
|
||||||
|
wx.showToast({
|
||||||
|
title: "已加载完成"
|
||||||
|
})
|
||||||
|
}, 300)
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
wx.hideLoading()
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(res => {
|
||||||
|
wx.hideLoading()
|
||||||
|
console.log(res)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
initMessageHistory() {
|
||||||
|
this.bindscrolltoupper()
|
||||||
},
|
},
|
||||||
clearChat: function (e) {
|
clearChat: function (e) {
|
||||||
var that = this
|
var that = this
|
||||||
@ -73,13 +126,18 @@ Component({
|
|||||||
content: "是否删除该条聊天?",
|
content: "是否删除该条聊天?",
|
||||||
complete: (res) => {
|
complete: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
|
var deleteData = data[index]
|
||||||
data.splice(index, 1)
|
data.splice(index, 1)
|
||||||
that.setData({
|
that.setData({
|
||||||
chatList: data,
|
chatList: data,
|
||||||
})
|
})
|
||||||
wx.setStorage({
|
app.getSid(sid => {
|
||||||
key: "chatList",
|
doRequest("/delete", "POST", {
|
||||||
data: data,
|
"sid": sid_prefix + sid,
|
||||||
|
"conversation": deleteData
|
||||||
|
}).then(res => {
|
||||||
|
console.log(res)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
<wxs src="../../tools.wxs" module="tools" />
|
<wxs src="../../tools.wxs" module="tools" />
|
||||||
<view wx:if="{{chatList.length == 0}}" style="text-align:center;color: #b4bbc4;font-size: 30rpx;">输入问题开始和{{chatType == "bing" ? "New Bing" : "ChatGPT"}}聊天吧~</view>
|
<view wx:if="{{chatList.length == 0}}" style="text-align:center;color: #b4bbc4;font-size: 30rpx;">输入问题开始和{{chatType == "bing" ? "New Bing" : "ChatGPT"}}聊天吧~</view>
|
||||||
<scroll-view class="chat" scroll-y="{{true}}" scroll-into-view="{{scrollId}}" style="height:{{systemInfo.windowHeight - 70}}px;" enable-back-to-top="{{true}}" scroll-anchoring="{{true}}" enhanced="{{true}}" enable-passive="{{true}}" show-scrollbar="{{false}}" enable-flex="{{true}}">
|
<scroll-view class="chat" scroll-y="{{true}}" scroll-into-view="{{scrollId}}" style="height:{{height}}px;" enable-back-to-top="{{true}}" scroll-anchoring="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}" enable-flex="{{true}}" bindrefresherrefresh="bindscrolltoupper" scroll-with-animation="{{true}}" refresher-enabled="{{ systemInfo.platform == 'ios' || systemInfo.platform == 'android'}}" refresher-triggered="{{loadingData}}" refresher-threshold="80" bindscrolltoupper="{{!(systemInfo.platform == 'ios' || systemInfo.platform == 'android') ? 'bindscrolltoupper': ''}}">
|
||||||
<view wx:for="{{chatList}}" wx:key="index" wx:for-item="item" id="{{'item'+index}}">
|
<view wx:for="{{chatList}}" wx:key="index" wx:for-item="item" id="{{'item'+index}}">
|
||||||
<view class="chat-item left" wx:if="{{item.type != 'man'}}" id="msg-{{index}}">
|
<view class="chat-item left" wx:if="{{item.type != 'man'}}" id="msg-{{index}}">
|
||||||
<image class="avatar" src="{{item.avatarUrl}}" style="display: flex;" catchlongpress="clearChat" data-index="{{index}}" catchtap="showOriginContent" data-index="{{index}}"></image>
|
<image class="avatar" src="{{item.avatarUrl}}" style="display: flex;" catchlongpress="clearChat" data-index="{{index}}" catchtap="showOriginContent" data-index="{{index}}"></image>
|
||||||
<view class="chat-box" style="margin-left: 20rpx;">
|
<view class="chat-box" style="margin-left: 20rpx;">
|
||||||
<view style="display: flex;flex-direction: row;align-items: center;"><text class="dt" style="flex: 1;">{{item.dt}}</text>
|
<view style="display: flex;flex-direction: row;align-items: center;"><text class="dt" style="flex: 2;">{{item.dt}}</text>
|
||||||
<view wx:if="{{item.num_in_conversation && item.num_in_conversation != -1}}" style="display:flex;justify-content:flex-end; flex: 1;align-items: center;"><text class="conversation_num">{{item.num_in_conversation}}</text></view>
|
<view wx:if="{{item.num_in_conversation && item.num_in_conversation != -1}}" style="display:flex;justify-content:flex-end; flex: 1;align-items: center;"><text class="conversation_num">{{item.num_in_conversation}}</text></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="content bg-white" catchlongpress="copyContent" data-index="{{index}}" catchtap="{{tools.indexOf(item.originContent, '```markdown') ? 'renderMd': ''}}">
|
<view class="content bg-white" catchlongpress="copyContent" data-index="{{index}}" catchtap="{{tools.indexOf(item.originContent, '```markdown') ? 'renderMd': ''}}">
|
||||||
@ -28,5 +28,5 @@
|
|||||||
</view>
|
</view>
|
||||||
<view id="{{'item'+ autoIncrConversation + 9999}}" style="height: 1em;"></view>
|
<view id="{{'item'+ autoIncrConversation + 9999}}" style="height: 1em;"></view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
<icon wx:if="{{receiveData}}" type="cancel" catchtap="cancelReceive" style="position: absolute;bottom: 145rpx;right:1%;z-index: 10000;" size="20"></icon>
|
<icon wx:if="{{receiveData}}" type="cancel" catchtap="cancelReceive" style="position: absolute;bottom: 148rpx;right:1%;z-index: 10000;" size="22"></icon>
|
||||||
<popup message="是否分享搜索内容?" wx:if="{{showShare}}" bindPopButtonClick="onPopButtonClick" openType="share"></popup>
|
<popup message="是否分享搜索内容?" wx:if="{{showShare}}" bindPopButtonClick="onPopButtonClick" openType="share"></popup>
|
||||||
@ -102,4 +102,4 @@
|
|||||||
font-size: 18rpx;
|
font-size: 18rpx;
|
||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
doRequest,
|
doRequest,
|
||||||
SERVER_WSS_HOST
|
SERVER_WSS_HOST,
|
||||||
|
systemInfo,
|
||||||
|
sid_prefix
|
||||||
} from "../../config"
|
} from "../../config"
|
||||||
|
|
||||||
const systemInfo = wx.getSystemInfoSync()
|
|
||||||
// 各平台对话分离
|
|
||||||
const sid_prefix = systemInfo.platform == "ios" || systemInfo.platform == "android" ? "" : systemInfo.platform
|
|
||||||
const initHeight = inputPop() ? 22 : 5
|
const initHeight = inputPop() ? 22 : 5
|
||||||
// 是否使用websocket请求
|
// 是否使用websocket请求
|
||||||
var useWebsocket = true
|
var useWebsocket = true
|
||||||
@ -146,12 +145,6 @@ Page({
|
|||||||
chatType: chatType,
|
chatType: chatType,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const cht = app.globalData.cht
|
|
||||||
if (cht.data.chatList.length > 1) {
|
|
||||||
cht.setData({
|
|
||||||
scrollId: "item" + (cht.data.chatList.length - 2),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 切换title
|
// 切换title
|
||||||
this.switchTitle()
|
this.switchTitle()
|
||||||
},
|
},
|
||||||
@ -166,7 +159,24 @@ Page({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {},
|
scrollBottom: function () {
|
||||||
|
const cht = app.globalData.cht
|
||||||
|
if (cht.data.chatList.length > 1 && !this.data.textareaFocus) {
|
||||||
|
cht.setData({
|
||||||
|
scrollId: "item" + (cht.data.chatList.length - 2),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
const cht = app.globalData.cht
|
||||||
|
setTimeout(() => {
|
||||||
|
if (cht.data.chatList.length > 1) {
|
||||||
|
cht.setData({
|
||||||
|
scrollId: "item" + (cht.data.chatList.length - 2),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 1500)
|
||||||
|
},
|
||||||
processData: function (data, suggests, content) {
|
processData: function (data, suggests, content) {
|
||||||
var robContent = data["data"]["status"]
|
var robContent = data["data"]["status"]
|
||||||
if (robContent == "Success") {
|
if (robContent == "Success") {
|
||||||
@ -268,7 +278,7 @@ Page({
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
that.pushStorageMessage(cht, "搜索中🔍...", "rob", [], true)
|
that.pushStorageMessage(cht, "搜索中🔍...", "rob", [], true, false, -1, false)
|
||||||
}
|
}
|
||||||
if (that.data.useWebsocket) {
|
if (that.data.useWebsocket) {
|
||||||
that.sendWSRequest(content)
|
that.sendWSRequest(content)
|
||||||
@ -300,10 +310,14 @@ Page({
|
|||||||
searching: false
|
searching: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 只保留最新的10条
|
||||||
wx.setStorage({
|
wx.setStorage({
|
||||||
key: "chatList",
|
key: "chatList",
|
||||||
data: cht.data.chatList,
|
data: cht.data.chatList.slice(cht.data.chatList.length - 10),
|
||||||
})
|
})
|
||||||
|
if (final) {
|
||||||
|
app.upload_conversation(cht.data.chatList.slice(cht.data.chatList.length - 1))
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
cht.setData({
|
cht.setData({
|
||||||
scrollId: "item" + (autoIncrConversation + "9999"),
|
scrollId: "item" + (autoIncrConversation + "9999"),
|
||||||
@ -537,9 +551,12 @@ Page({
|
|||||||
cht.setData({
|
cht.setData({
|
||||||
chatList: [],
|
chatList: [],
|
||||||
})
|
})
|
||||||
wx.setStorage({
|
app.getSid(sid => {
|
||||||
key: "chatList",
|
doRequest("/delete_all", "POST", {
|
||||||
data: [],
|
"sid": sid_prefix + sid
|
||||||
|
}).then(res => {
|
||||||
|
console.log("delete all")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<chat-box bindsuggestSubmit="onSuggestSubmit" bindcancelReceive="onCancelReceive" bindswitchRequestMethod="switchRequestMethod" catchlongpress="longPress" chatType="{{chatType}}"></chat-box>
|
<chat-box bindsuggestSubmit="onSuggestSubmit" bindcancelReceive="onCancelReceive" bindswitchRequestMethod="switchRequestMethod" catchlongpress="longPress" chatType="{{chatType}}"></chat-box>
|
||||||
<view style="bottom:{{inputBottom}}px; border-radius: 20rpx;margin-left: 1%;width: 98%;min-height: 100rpx;position: fixed;background-color: #f4f6f8;display: flex;align-items:flex-start; justify-content: space-between;{{textareaFocus ? 'border: 1px solid #b4bbc4;': ''}}">
|
<view style="bottom:{{inputBottom}}px; border-radius: 20rpx;margin-left: 1%;width: 98%;min-height: 100rpx;position: fixed;background-color: #f4f6f8;display: flex;align-items:flex-start; justify-content: space-between;{{textareaFocus ? 'border: 1px solid #b4bbc4;': ''}}">
|
||||||
<textarea bindfocus="inputFocus" bindblur="inputBlur" value="{{content}}" adjust-position="{{false}}" focus="{{textareaFocus}}" maxlength="2000" auto-height="{{true}}" cursor-spacing="10" bindconfirm="submit" fixed="{{true}}" show-confirm-bar="{{false}}" confirm-type="send" placeholder="{{systemInfo.platform == 'mac' || systemInfo.platform == 'windows' ? '请输入问题,输入>>>提交...': '请输入问题...'}}" style="padding: 10rpx;flex: 9;line-height: normal;" placeholder-style="color: #b4bbc4" catchtap="focus" bindinput="inputData"></textarea>
|
<textarea bindfocus="inputFocus" bindblur="inputBlur" value="{{content}}" adjust-position="{{false}}" focus="{{textareaFocus}}" maxlength="2000" auto-height="{{true}}" cursor-spacing="10" bindconfirm="submit" fixed="{{true}}" show-confirm-bar="{{false}}" confirm-type="send" placeholder="{{systemInfo.platform == 'mac' || systemInfo.platform == 'windows' ? '请输入问题,输入>>>提交...': '请输入问题...'}}" style="padding: 10rpx;flex: 9;line-height: normal;" placeholder-style="color: #b4bbc4" catchtap="focus" bindinput="inputData" catchlongpress="scrollBottom"></textarea>
|
||||||
<view style="background-color: #f4f6f8;color: {{content ? black : '#b4bbc4'}};border-radius: 0 20rpx 20rpx 0;height: 90rpx;cursor: pointer;margin-right: 15rpx;padding-top:10rpx;font-size: 32rpx;" catchtap="submit" wx:if="{{systemInfo.platform != 'ios'}}">发送</view>
|
<view style="background-color: #f4f6f8;color: {{content ? black : '#b4bbc4'}};border-radius: 0 20rpx 20rpx 0;height: 90rpx;cursor: pointer;margin-right: 15rpx;padding-top:10rpx;font-size: 32rpx;" catchtap="submit" wx:if="{{systemInfo.platform != 'ios'}}">发送</view>
|
||||||
</view>
|
</view>
|
||||||
<popup message="{{searchPopMessage}}" wx:if="{{showSearchPop}}" bindPopButtonClick="onPopButtonClick" data-q="{{q}}"></popup>
|
<popup message="{{searchPopMessage}}" wx:if="{{showSearchPop}}" bindPopButtonClick="onPopButtonClick" data-q="{{q}}"></popup>
|
||||||
@ -2,7 +2,7 @@ FROM sanicframework/sanic:3.11-latest
|
|||||||
|
|
||||||
WORKDIR /sanic
|
WORKDIR /sanic
|
||||||
|
|
||||||
COPY app.py EdgeGPT.py requirements.txt /sanic/
|
COPY app.py EdgeGPT.py conversation_ctr.py requirements.txt /sanic/
|
||||||
|
|
||||||
RUN pip install -r requirements.txt
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
|||||||
@ -22,20 +22,20 @@ HEADERS = {
|
|||||||
"accept": "application/json",
|
"accept": "application/json",
|
||||||
"accept-language": "en-US,en;q=0.9",
|
"accept-language": "en-US,en;q=0.9",
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
"sec-ch-ua": '"Not_A Brand";v="99", "Microsoft Edge";v="110", "Chromium";v="110"',
|
"sec-ch-ua": '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
|
||||||
"sec-ch-ua-arch": '"x86"',
|
"sec-ch-ua-arch": '"x86"',
|
||||||
"sec-ch-ua-bitness": '"64"',
|
"sec-ch-ua-bitness": '"64"',
|
||||||
"sec-ch-ua-full-version": '"109.0.1518.78"',
|
"sec-ch-ua-full-version": '"111.0.1661.43"',
|
||||||
"sec-ch-ua-full-version-list": '"Chromium";v="110.0.5481.192", "Not A(Brand";v="24.0.0.0", "Microsoft Edge";v="110.0.1587.69"',
|
"sec-ch-ua-full-version-list": '"Microsoft Edge";v="111.0.1661.43", "Not(A:Brand";v="8.0.0.0", "Chromium";v="111.0.5563.64"',
|
||||||
"sec-ch-ua-mobile": "?0",
|
"sec-ch-ua-mobile": "?0",
|
||||||
"sec-ch-ua-model": "",
|
"sec-ch-ua-model": "",
|
||||||
"sec-ch-ua-platform": '"Windows"',
|
"sec-ch-ua-platform": '"macOS"',
|
||||||
"sec-ch-ua-platform-version": '"15.0.0"',
|
"sec-ch-ua-platform-version": '"11.7.3"',
|
||||||
"sec-fetch-dest": "empty",
|
"sec-fetch-dest": "empty",
|
||||||
"sec-fetch-mode": "cors",
|
"sec-fetch-mode": "cors",
|
||||||
"sec-fetch-site": "same-origin",
|
"sec-fetch-site": "same-origin",
|
||||||
"x-ms-client-request-id": str(uuid.uuid4()),
|
"x-ms-client-request-id": str(uuid.uuid4()),
|
||||||
"x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/Win32",
|
"x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/MacIntel",
|
||||||
"Referer": "https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx",
|
"Referer": "https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx",
|
||||||
"Referrer-Policy": "origin-when-cross-origin",
|
"Referrer-Policy": "origin-when-cross-origin",
|
||||||
"x-forwarded-for": FORWARDED_IP,
|
"x-forwarded-for": FORWARDED_IP,
|
||||||
@ -46,21 +46,21 @@ HEADERS_INIT_CONVER = {
|
|||||||
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
||||||
"accept-language": "en-US,en;q=0.9",
|
"accept-language": "en-US,en;q=0.9",
|
||||||
"cache-control": "max-age=0",
|
"cache-control": "max-age=0",
|
||||||
"sec-ch-ua": '"Chromium";v="110", "Not A(Brand";v="24", "Microsoft Edge";v="110"',
|
"sec-ch-ua": '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
|
||||||
"sec-ch-ua-arch": '"x86"',
|
"sec-ch-ua-arch": '"x86"',
|
||||||
"sec-ch-ua-bitness": '"64"',
|
"sec-ch-ua-bitness": '"64"',
|
||||||
"sec-ch-ua-full-version": '"110.0.1587.69"',
|
"sec-ch-ua-full-version": '"111.0.1661.43"',
|
||||||
"sec-ch-ua-full-version-list": '"Chromium";v="110.0.5481.192", "Not A(Brand";v="24.0.0.0", "Microsoft Edge";v="110.0.1587.69"',
|
"sec-ch-ua-full-version-list": '"Microsoft Edge";v="111.0.1661.43", "Not(A:Brand";v="8.0.0.0", "Chromium";v="111.0.5563.64"',
|
||||||
"sec-ch-ua-mobile": "?0",
|
"sec-ch-ua-mobile": "?0",
|
||||||
"sec-ch-ua-model": '""',
|
"sec-ch-ua-model": '""',
|
||||||
"sec-ch-ua-platform": '"Windows"',
|
"sec-ch-ua-platform": '"macOS"',
|
||||||
"sec-ch-ua-platform-version": '"15.0.0"',
|
"sec-ch-ua-platform-version": '"11.7.3"',
|
||||||
"sec-fetch-dest": "document",
|
"sec-fetch-dest": "document",
|
||||||
"sec-fetch-mode": "navigate",
|
"sec-fetch-mode": "navigate",
|
||||||
"sec-fetch-site": "none",
|
"sec-fetch-site": "none",
|
||||||
"sec-fetch-user": "?1",
|
"sec-fetch-user": "?1",
|
||||||
"upgrade-insecure-requests": "1",
|
"upgrade-insecure-requests": "1",
|
||||||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.69",
|
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.43",
|
||||||
"x-edge-shopping-flag": "1",
|
"x-edge-shopping-flag": "1",
|
||||||
"x-forwarded-for": "1.1.1.1",
|
"x-forwarded-for": "1.1.1.1",
|
||||||
}
|
}
|
||||||
@ -288,8 +288,6 @@ class ChatHub:
|
|||||||
elif response.get("type") == 2:
|
elif response.get("type") == 2:
|
||||||
final = True
|
final = True
|
||||||
yield True, response
|
yield True, response
|
||||||
else:
|
|
||||||
print(response)
|
|
||||||
|
|
||||||
async def __initial_handshake(self):
|
async def __initial_handshake(self):
|
||||||
await self.wss.send(append_identifier({
|
await self.wss.send(append_identifier({
|
||||||
@ -367,4 +365,5 @@ class Chatbot:
|
|||||||
Reset the conversation
|
Reset the conversation
|
||||||
"""
|
"""
|
||||||
await self.close()
|
await self.close()
|
||||||
|
HEADERS["x-ms-client-request-id"] = str(uuid.uuid4())
|
||||||
self.chat_hub = ChatHub(Conversation())
|
self.chat_hub = ChatHub(Conversation())
|
||||||
|
|||||||
@ -13,6 +13,7 @@ from sanic import Sanic
|
|||||||
from sanic.log import logger
|
from sanic.log import logger
|
||||||
from sanic.response import json
|
from sanic.response import json
|
||||||
|
|
||||||
|
from conversation_ctr import conversation_ctr
|
||||||
from EdgeGPT import Chatbot, ConversationStyle
|
from EdgeGPT import Chatbot, ConversationStyle
|
||||||
|
|
||||||
APPID = os.environ.get('WXAPPID')
|
APPID = os.environ.get('WXAPPID')
|
||||||
@ -80,9 +81,10 @@ async def ws_chat(_, ws):
|
|||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
data = raw_json.loads(await ws.recv())
|
data = raw_json.loads(await ws.recv())
|
||||||
logger.info('Websocket receive data: %s', data)
|
logger.info('[bing] Websocket receive data: %s', data)
|
||||||
sid = data['sid']
|
sid = data['sid']
|
||||||
q = data['q']
|
q = data['q']
|
||||||
|
index = 0
|
||||||
async for response in get_bot(sid).ask_stream(q, conversation_style=ConversationStyle.creative):
|
async for response in get_bot(sid).ask_stream(q, conversation_style=ConversationStyle.creative):
|
||||||
final, res = response
|
final, res = response
|
||||||
if final:
|
if final:
|
||||||
@ -96,10 +98,12 @@ async def ws_chat(_, ws):
|
|||||||
'data': processed_data
|
'data': processed_data
|
||||||
}))
|
}))
|
||||||
else:
|
else:
|
||||||
await ws.send(raw_json.dumps({
|
index += 1
|
||||||
'final': final,
|
if index % 3 == 1:
|
||||||
'data': res
|
await ws.send(raw_json.dumps({
|
||||||
}))
|
'final': final,
|
||||||
|
'data': res
|
||||||
|
}))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
await ws.send(raw_json.dumps({
|
await ws.send(raw_json.dumps({
|
||||||
@ -127,7 +131,7 @@ async def reset_conversation(sid):
|
|||||||
|
|
||||||
|
|
||||||
async def do_chat(request):
|
async def do_chat(request):
|
||||||
logger.info('Http request payload: %s', request.json)
|
logger.info('[bing] Http request payload: %s', request.json)
|
||||||
return await get_bot(request.json.get('sid')).ask(
|
return await get_bot(request.json.get('sid')).ask(
|
||||||
request.json.get('q'), conversation_style=ConversationStyle.creative
|
request.json.get('q'), conversation_style=ConversationStyle.creative
|
||||||
)
|
)
|
||||||
@ -157,9 +161,6 @@ async def process_data(res, q, sid, auto_reset=None):
|
|||||||
text = '抱歉,未搜索到结果。'
|
text = '抱歉,未搜索到结果。'
|
||||||
logger.error('响应异常:%s', res)
|
logger.error('响应异常:%s', res)
|
||||||
suggests = [q]
|
suggests = [q]
|
||||||
if res['type'] == 2:
|
|
||||||
await reset_conversation(sid)
|
|
||||||
text += '\n已结束本轮对话。'
|
|
||||||
msg = res['item']['result']['message'] if 'message' in res['item']['result'] else ''
|
msg = res['item']['result']['message'] if 'message' in res['item']['result'] else ''
|
||||||
if auto_reset and ('New topic' in text or 'has expired' in msg):
|
if auto_reset and ('New topic' in text or 'has expired' in msg):
|
||||||
await reset_conversation(sid)
|
await reset_conversation(sid)
|
||||||
@ -204,7 +205,7 @@ async def ws_openai_chat(_, ws):
|
|||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
data = raw_json.loads(await ws.recv())
|
data = raw_json.loads(await ws.recv())
|
||||||
logger.info('Websocket receive data: %s', data)
|
logger.info('[openai] Websocket receive data: %s', data)
|
||||||
sid = data['sid']
|
sid = data['sid']
|
||||||
q = data['q']
|
q = data['q']
|
||||||
# 保存30个对话
|
# 保存30个对话
|
||||||
@ -220,11 +221,14 @@ async def ws_openai_chat(_, ws):
|
|||||||
stream=True,
|
stream=True,
|
||||||
)
|
)
|
||||||
chunks = []
|
chunks = []
|
||||||
|
index = 0
|
||||||
for chunk in response:
|
for chunk in response:
|
||||||
chunk_message = chunk['choices'][0]['delta']
|
chunk_message = chunk['choices'][0]['delta']
|
||||||
if chunk_message:
|
if chunk_message:
|
||||||
if 'content' in chunk_message:
|
if 'content' in chunk_message:
|
||||||
chunks.append(chunk_message['content'])
|
chunks.append(chunk_message['content'])
|
||||||
|
index += 1
|
||||||
|
if index % 5 == 1:
|
||||||
await ws.send(
|
await ws.send(
|
||||||
raw_json.dumps({
|
raw_json.dumps({
|
||||||
'final': False,
|
'final': False,
|
||||||
@ -253,6 +257,7 @@ async def ws_openai_chat(_, ws):
|
|||||||
@app.post('/openai_chat')
|
@app.post('/openai_chat')
|
||||||
async def openai_chat(request):
|
async def openai_chat(request):
|
||||||
try:
|
try:
|
||||||
|
logger.info('[openai] Http request payload: %s', request.json)
|
||||||
sid = request.json.get('sid')
|
sid = request.json.get('sid')
|
||||||
q = request.json.get('q')
|
q = request.json.get('q')
|
||||||
history_conversation = OPENAI_CONVERSATION[sid][-30:]
|
history_conversation = OPENAI_CONVERSATION[sid][-30:]
|
||||||
@ -283,5 +288,36 @@ async def openai_chat(request):
|
|||||||
return json(make_response_data('Error', str(e), [], str(e)))
|
return json(make_response_data('Error', str(e), [], str(e)))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/last_sync_time')
|
||||||
|
async def last_sync_time(request):
|
||||||
|
return json({'last_sync_time': conversation_ctr.get_last_sync_time(request.args.get('sid'))})
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/save')
|
||||||
|
async def save(request):
|
||||||
|
conversation_ctr.save(request.json.get('sid'), request.json.get('conversations'))
|
||||||
|
return json({})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/query')
|
||||||
|
async def query(request):
|
||||||
|
data = conversation_ctr.get_by_page(
|
||||||
|
request.args.get('sid'), int(request.args.get('page', '1')), int(request.args.get('size', '20'))
|
||||||
|
)
|
||||||
|
return json({'data': data})
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/delete')
|
||||||
|
async def delete(request):
|
||||||
|
conversation_ctr.delete(request.json.get('sid'), request.json.get('conversation'))
|
||||||
|
return json({})
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/delete_all')
|
||||||
|
async def delete_all(request):
|
||||||
|
conversation_ctr.delete_all(request.json.get('sid'))
|
||||||
|
return json({})
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(host='0.0.0.0', port=8000)
|
app.run(host='0.0.0.0', port=8000)
|
||||||
|
|||||||
58
new-bing/conversation_ctr.py
Normal file
58
new-bing/conversation_ctr.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
import redis
|
||||||
|
|
||||||
|
REDIS_HOST = os.environ.get('REDIS_HOST', '127.0.0.1')
|
||||||
|
REDIS_PORT = int(os.environ.get('REDIS_PORT', 6379))
|
||||||
|
REDIS_PASSWD = os.environ.get('REDIS_PASSWD', '123456')
|
||||||
|
REDIS_DB = int(os.environ.get('REDIS_DB', 0))
|
||||||
|
|
||||||
|
|
||||||
|
class ConversationCtr:
|
||||||
|
|
||||||
|
LAST_SYNC_TIME_KEY = 'bing:last_sync_time:%s'
|
||||||
|
CONVERSATION_LIST_KEY = 'bing:conversation_list:%s'
|
||||||
|
|
||||||
|
def __init__(self, client=None) -> None:
|
||||||
|
self.redis_client = client
|
||||||
|
if client is None:
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
if self.redis_client is not None:
|
||||||
|
return
|
||||||
|
pool = redis.ConnectionPool(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB, password=REDIS_PASSWD)
|
||||||
|
self.redis_client = redis.Redis(connection_pool=pool)
|
||||||
|
|
||||||
|
def get_last_sync_time(self, sid):
|
||||||
|
key = self.LAST_SYNC_TIME_KEY % sid
|
||||||
|
v = self.redis_client.get(key)
|
||||||
|
return v.decode() if v else ''
|
||||||
|
|
||||||
|
def get_by_page(self, sid, page=1, size=20):
|
||||||
|
offset = size * (page - 1) if page > 0 else 0
|
||||||
|
key = self.CONVERSATION_LIST_KEY % sid
|
||||||
|
return [json.loads(x) for x in self.redis_client.lrange(key, offset, offset + size - 1)]
|
||||||
|
|
||||||
|
def save(self, sid, conversations):
|
||||||
|
_last_sync_time = self.get_last_sync_time(sid)
|
||||||
|
conversations = [x for x in conversations if x['dt'] > _last_sync_time]
|
||||||
|
if len(conversations) <= 0:
|
||||||
|
return
|
||||||
|
key = self.CONVERSATION_LIST_KEY % sid
|
||||||
|
self.redis_client.lpush(key, *[json.dumps(x) for x in conversations])
|
||||||
|
self.redis_client.set(self.LAST_SYNC_TIME_KEY % sid, conversations[-1]['dt'])
|
||||||
|
|
||||||
|
def delete(self, sid, conversation):
|
||||||
|
key = self.CONVERSATION_LIST_KEY % sid
|
||||||
|
self.redis_client.lrem(key, 0, json.dumps(conversation))
|
||||||
|
|
||||||
|
def delete_all(self, sid):
|
||||||
|
key = self.CONVERSATION_LIST_KEY % sid
|
||||||
|
self.redis_client.delete(key)
|
||||||
|
|
||||||
|
|
||||||
|
conversation_ctr = ConversationCtr()
|
||||||
@ -6,3 +6,6 @@ COOKIE_FILE2=/sanic/cookies/cookie2.json # 备用cookie2, 没有可以删掉
|
|||||||
COOKIE_FILES=["/sanic/cookies/cookie.json", "/sanic/cookies/cookie1.json", "/sanic/cookies/cookie2.json"] #cookie列表,配置了此环境变量,会优先使用此变量,直接忽略上面的3个环境变量
|
COOKIE_FILES=["/sanic/cookies/cookie.json", "/sanic/cookies/cookie1.json", "/sanic/cookies/cookie2.json"] #cookie列表,配置了此环境变量,会优先使用此变量,直接忽略上面的3个环境变量
|
||||||
https_proxy=http://127.0.0.1:1080 # 目前中国大陆的IP会返回404,所以最好能加个代理
|
https_proxy=http://127.0.0.1:1080 # 目前中国大陆的IP会返回404,所以最好能加个代理
|
||||||
OPENAI_API_KEY=
|
OPENAI_API_KEY=
|
||||||
|
REDIS_HOST=
|
||||||
|
REDIS_PORT=
|
||||||
|
REDIS_PASSWD=
|
||||||
|
|||||||
@ -4,3 +4,5 @@ asyncio==3.4.3
|
|||||||
websockets==10.4
|
websockets==10.4
|
||||||
httpx==0.23.3
|
httpx==0.23.3
|
||||||
openai==0.27.2
|
openai==0.27.2
|
||||||
|
redis==4.5.1
|
||||||
|
hiredis==2.2.2
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user