新增chatgpt支持

This commit is contained in:
linghaihui 2023-04-02 12:13:33 +08:00
parent d51623b0bb
commit 1b8d864b39
11 changed files with 269 additions and 101 deletions

View File

@ -8,7 +8,7 @@
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTitleText": "New Bing Bot 🤖",
"navigationBarTitleText": "New Bing 🤖",
"navigationBarTextStyle": "black"
},
"sitemapLocation": "sitemap.json",

View File

@ -14,7 +14,12 @@ Component({
addGlobalClass: true,
multipleSlots: true,
},
properties: {},
properties: {
chatType: {
type: String,
value: "bing"
}
},
pageLifetimes: {
show: function () {
this.initMessageHistory()
@ -146,63 +151,6 @@ Component({
"cancelReceive", {}, {}
)
},
deleteAllChat: function () {
var that = this
wx.showModal({
content: "是否删除全部聊天?",
complete: (res) => {
if (res.confirm) {
that.setData({
chatList: [],
})
wx.setStorage({
key: "chatList",
data: [],
})
}
},
})
},
longPress: function (e) {
var that = this
wx.showActionSheet({
itemList: ["删除全部聊天记录", "切换聊天接口方式", that.data.closeShareOnCopy ? "打开复制后分享" : "关闭复制后分享"],
success(res) {
if (res.tapIndex == 0) {
that.deleteAllChat()
} else if (res.tapIndex == 1) {
that.triggerEvent(
"switchRequestMethod", {}, {}
)
} else if (res.tapIndex == 2) {
if (that.data.closeShareOnCopy) {
that.setData({
closeShareOnCopy: false,
})
wx.showToast({
title: "已打开复制后分享",
icon: "none"
})
wx.removeStorage({
key: "closeShareOnCopy",
})
} else {
that.setData({
closeShareOnCopy: true,
})
wx.showToast({
title: "已关闭复制后分享",
icon: "none"
})
wx.setStorage({
key: "closeShareOnCopy",
data: 1,
})
}
}
}
})
},
showOriginContent: function (e) {
var index = e.currentTarget.dataset.index
var data = this.data.chatList[index]
@ -218,7 +166,7 @@ Component({
onPopButtonClick: function (e) {
if (e.detail.t !== "confirm") {
wx.removeStorage({
key: 'shareContent',
key: "shareContent",
})
}
this.setData({

View File

@ -1,34 +1,32 @@
<wxs src="../../tools.wxs" module="tools" />
<view catchlongpress="longPress">
<view wx:if="{{chatList.length == 0}}" style="text-align:center;color: #b4bbc4;font-size: 30rpx;">输入问题开始和New Bing聊天吧~</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}}">
<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}}">
<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 style="display: flex;flex-direction: row;align-items: center;"><text class="dt" style="flex: 1;">{{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>
<view class="content bg-white" catchlongpress="copyContent" data-index="{{index}}" catchtap="{{tools.indexOf(item.originContent, '```markdown') ? 'renderMd': ''}}">
<view class="{{item.blink ? 'blinking': ''}}">
<mp-html content="{{item.content ? item.content: item.originContent}}" preview-img selectable="{{true}}" markdown="{{true}}" img-cache="{{true}}" lazy-load="{{true}}" use-anchor="{{true}}" scroll-table="{{true}}" container-style="margin-top: -1em;" wx:if="{{!item.showOrigin}}" /><text wx:else user-select="{{true}}" decode="{{true}}">{{item.originContent}}</text>
</view>
</view>
<view class="suggest">
<view hover-class="suggest-item-hover" class="suggest-item" catchtap="suggestSubmit" data-suggest="{{suggest}}" wx:key="index" wx:for="{{item.suggests}}" wx:for-item="suggest" wx:if="{{suggest && suggest.length > 0}}">{{suggest}}</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}}">
<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}}">
<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 style="display: flex;flex-direction: row;align-items: center;"><text class="dt" style="flex: 1;">{{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>
<view class="content bg-white" catchlongpress="copyContent" data-index="{{index}}" catchtap="{{tools.indexOf(item.originContent, '```markdown') ? 'renderMd': ''}}">
<view class="{{item.blink ? 'blinking': ''}}">
<mp-html content="{{item.content ? item.content: item.originContent}}" preview-img selectable="{{true}}" markdown="{{true}}" img-cache="{{true}}" lazy-load="{{true}}" use-anchor="{{true}}" scroll-table="{{true}}" container-style="margin-top: -1em;" wx:if="{{!item.showOrigin}}" /><text wx:else user-select="{{true}}" decode="{{true}}">{{item.originContent}}</text>
</view>
</view>
</view>
<view class="chat-item right" wx:if="{{item.type == 'man' }}" id="msg-{{index}}">
<view class="chat-box" style="margin-right: 20rpx;">
<text class="dt" style="display: block;text-align: right">{{item.dt}}</text>
<view class="content bg-green" catchlongpress="copyContent" data-index="{{index}}"><text user-select decode space>{{item.originContent}}</text></view>
<view class="suggest">
<view hover-class="suggest-item-hover" class="suggest-item" catchtap="suggestSubmit" data-suggest="{{suggest}}" wx:key="index" wx:for="{{item.suggests}}" wx:for-item="suggest" wx:if="{{suggest && suggest.length > 0}}">{{suggest}}</view>
</view>
<image class="avatar" src="{{item.avatarUrl}}" catchlongpress="clearChat" data-index="{{index}}"></image>
</view>
</view>
<view id="{{'item'+ autoIncrConversation + 9999}}" style="height: 1em;"></view>
</scroll-view>
<icon wx:if="{{receiveData}}" type="cancel" catchtap="cancelReceive" style="position: absolute;bottom: 142rpx;right:1%;z-index: 10000;" size="20"></icon>
</view>
<view class="chat-item right" wx:if="{{item.type == 'man' }}" id="msg-{{index}}">
<view class="chat-box" style="margin-right: 20rpx;">
<text class="dt" style="display: block;text-align: right">{{item.dt}}</text>
<view class="content bg-green" catchlongpress="copyContent" data-index="{{index}}"><text user-select decode space>{{item.originContent}}</text></view>
</view>
<image class="avatar" src="{{item.avatarUrl}}" catchlongpress="clearChat" data-index="{{index}}"></image>
</view>
</view>
<view id="{{'item'+ autoIncrConversation + 9999}}" style="height: 1em;"></view>
</scroll-view>
<icon wx:if="{{receiveData}}" type="cancel" catchtap="cancelReceive" style="position: absolute;bottom: 145rpx;right:1%;z-index: 10000;" size="20"></icon>
<popup message="是否分享搜索内容?" wx:if="{{showShare}}" bindPopButtonClick="onPopButtonClick" openType="share"></popup>

View File

@ -9,7 +9,7 @@
padding: 20rpx 30rpx 20rpx 30rpx;
box-sizing: border-box;
z-index: 10000;
border: 1rpx dashed;
border: 1px dashed;
}
.bt {

BIN
bingchat/image/chatgpt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -24,6 +24,16 @@ function inputPop() {
// 自增对话
var autoIncrConversation = 0
// 默认采用new bing
var chatType = "bing"
try {
if (wx.getStorageSync("usechatgpt")) {
chatType = "chatgpt"
}
} catch (e) {
chatType = "bing"
}
Date.prototype.format = function (fmt) {
var o = {
"M+": this.getMonth() + 1, //月份
@ -74,6 +84,7 @@ Page({
useWebsocket: useWebsocket,
showSearchPop: false,
searchPopMessage: "",
chatType: chatType,
},
inputFocus(e) {
if (inputPop()) {
@ -120,10 +131,19 @@ Page({
var options = this.getOptions()
if (options && options["q"]) {
var q = decodeURIComponent(options["q"])
var chatType = this.data.chatType
if (options["chatType"]) {
chatType = options["chatType"]
// 聊天方式不同关闭websocket
if (chatType != this.data.chatType) {
this.onCancelReceive()
}
}
this.setData({
searchPopMessage: "即将搜索“" + q + "”",
showSearchPop: true,
q: q,
chatType: chatType,
})
}
const cht = app.globalData.cht
@ -132,6 +152,19 @@ Page({
scrollId: "item" + (cht.data.chatList.length - 2),
})
}
// 切换title
this.switchTitle()
},
switchTitle: function () {
if (this.data.chatType == "bing") {
wx.setNavigationBarTitle({
title: "New Bing 🤖️",
})
} else {
wx.setNavigationBarTitle({
title: "ChatGPT 🤖️",
})
}
},
onLoad() {},
processData: function (data, suggests, content) {
@ -139,7 +172,7 @@ Page({
if (robContent == "Success") {
robContent = data["data"]["text"]
suggests.push(...data["data"]["suggests"])
if (robContent.indexOf("New topic") != -1) {
if (robContent.indexOf("New topic") != -1 && this.data.chatType == "bing") {
robContent += "\n发送“重新对话”开始新的对话"
suggests.push("重新对话!")
suggests.push(content)
@ -157,7 +190,9 @@ Page({
suggests.push(content)
} else {
robContent = "抱歉😭,发生错误:" + msg
suggests.push("重新对话!")
if (this.data.chatType == "bing") {
suggests.push("重新对话!")
}
suggests.push(content)
}
}
@ -177,8 +212,9 @@ Page({
sendHttpRequest: function (content) {
var that = this
const cht = app.globalData.cht
var api = that.data.chatType == "bing" ? "/chat" : "/openai_chat"
app.getSid(sid => {
doRequest("/chat", "POST", {
doRequest(api, "POST", {
q: content,
sid: sid_prefix + sid,
}).then(res => {
@ -226,7 +262,7 @@ Page({
content: "",
lastContent: content,
})
if (content == "重新对话!") {
if (content == "重新对话!" && that.data.chatType == "bing") {
that.resetConversation(() => {
that.pushStorageMessage(cht, "现在我们可以开始新的对话😊", "rob", [], false)
})
@ -244,9 +280,10 @@ Page({
if (pop) {
cht.data.chatList.pop()
}
var rAvatar = this.data.chatType == "bing" ? robAvatar : "../../image/chatgpt.png"
cht.data.chatList.push({
type: role,
avatarUrl: role == "rob" ? robAvatar : personAvatar,
avatarUrl: role == "rob" ? rAvatar : personAvatar,
dt: getNow(),
originContent: this.processContent(content),
suggests: suggests,
@ -281,7 +318,7 @@ Page({
this.submitContent(content)
},
onShareAppMessage() {
var title = "New Bing Bot 🤖"
var title = this.data.chatType == "bing" ? "New Bing 🤖" : "ChatGPT 🤖️"
var content = this.data.content.trim()
if (content.length > 0) {
title = content
@ -299,8 +336,8 @@ Page({
}
return {
title: title,
path: "/pages/index/index?q=" + encodeURIComponent(content),
imageUrl: "../../image/newBing.png"
path: "/pages/index/index?q=" + encodeURIComponent(content) + "&chatType=" + this.data.chatType,
imageUrl: this.data.chatType == "bing" ? "../../image/newBing.png" : "../../image/chatgpt_share.png"
}
},
onSuggestSubmit: function (e) {
@ -318,8 +355,9 @@ Page({
}
var that = this
const cht = app.globalData.cht
var apiPath = that.data.chatType == "bing" ? "/chat" : "/ws_openai_chat"
const socket = wx.connectSocket({
url: SERVER_WSS_HOST + "/chat",
url: SERVER_WSS_HOST + apiPath,
fail: function () {
wx.showToast({
title: "打开websocket失败",
@ -380,7 +418,11 @@ Page({
var robContent = ""
var num_in_conversation = -1
if (!data["final"]) {
robContent = data["data"] + " ..."
if (data["data"]["data"]) {
robContent = data["data"]["data"]["text"] + " ..."
} else {
robContent = data["data"] + " ..."
}
cht.setData({
receiveData: true
})
@ -485,5 +527,91 @@ Page({
}
})
}
}
},
deleteAllChat: function () {
const cht = app.globalData.cht
wx.showModal({
content: "是否删除全部聊天?",
complete: (res) => {
if (res.confirm) {
cht.setData({
chatList: [],
})
wx.setStorage({
key: "chatList",
data: [],
})
}
},
})
},
longPress: function (e) {
var that = this
const cht = app.globalData.cht
wx.showActionSheet({
itemList: ["删除全部聊天记录", "切换聊天接口方式", that.data.chatType == "bing" ? "切换成ChatGPT" : "切换成New Bing", cht.data.closeShareOnCopy ? "打开复制后分享" : "关闭复制后分享"],
success(res) {
if (res.tapIndex == 0) {
that.deleteAllChat()
} else if (res.tapIndex == 1) {
that.switchRequestMethod()
} else if (res.tapIndex == 3) {
if (cht.data.closeShareOnCopy) {
cht.setData({
closeShareOnCopy: false,
})
wx.showToast({
title: "已打开复制后分享",
icon: "none"
})
wx.removeStorage({
key: "closeShareOnCopy",
})
} else {
cht.setData({
closeShareOnCopy: true,
})
wx.showToast({
title: "已关闭复制后分享",
icon: "none"
})
wx.setStorage({
key: "closeShareOnCopy",
data: 1,
})
}
} else if (res.tapIndex == 2) {
if (that.data.chatType == "chatgpt") {
wx.removeStorage({
key: "usechatgpt",
})
that.setData({
chatType: "bing",
})
wx.showToast({
title: "已切换成New Bing",
icon: "none"
})
} else {
wx.setStorage({
key: "usechatgpt",
data: 1,
})
that.setData({
chatType: "chatgpt",
})
wx.showToast({
title: "已切换成ChatGPT",
icon: "none"
})
}
}
// 关闭websocket
that.onCancelReceive()
setTimeout(() => {
that.switchTitle()
}, 100)
}
})
},
})

View File

@ -1,5 +1,5 @@
<chat-box bindsuggestSubmit="onSuggestSubmit" bindcancelReceive="onCancelReceive" bindswitchRequestMethod="switchRequestMethod"></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: 1rpx solid #b4bbc4;': ''}}">
<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;': ''}}">
<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>
<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 File

@ -4,8 +4,10 @@ import json as raw_json
import os
import re
import threading
from collections import defaultdict
from datetime import datetime, timedelta
import openai
import requests
from sanic import Sanic
from sanic.log import logger
@ -38,6 +40,9 @@ app.config.RESPONSE_TIMEOUT = 900
app.config.WEBSOCKET_PING_INTERVAL = 15
app.config.WEBSOCKET_PING_TIMEOUT = 30
# openai conversation
OPENAI_CONVERSATION = defaultdict(lambda: [])
def reset_cookie():
if not LOCK.acquire(blocking=False):
@ -191,5 +196,92 @@ async def openid(request):
return json({'data': requests.get(url).json()})
# #########################################以下是openid接口##################################
@app.websocket('/ws_openai_chat')
async def ws_openai_chat(_, ws):
while True:
try:
data = raw_json.loads(await ws.recv())
logger.info('Websocket receive data: %s', data)
sid = data['sid']
q = data['q']
# 保存30个对话
history_conversation = OPENAI_CONVERSATION[sid][-30:]
history_conversation.append({
'role': 'user',
'content': q,
})
response = openai.ChatCompletion.create(
model='gpt-3.5-turbo',
messages=history_conversation,
temperature=1.2,
stream=True,
)
chunks = []
for chunk in response:
chunk_message = chunk['choices'][0]['delta']
if chunk_message:
if 'content' in chunk_message:
chunks.append(chunk_message['content'])
await ws.send(
raw_json.dumps({
'final': False,
'data': make_response_data('Success', ''.join(chunks), [], '')
})
)
else:
OPENAI_CONVERSATION[sid].append({
'role': 'assistant',
'content': ''.join(chunks)
})
await ws.send(
raw_json.dumps({
'final': True,
'data': make_response_data('Success', ''.join(chunks), [], '')
})
)
except Exception as e:
logger.error(e)
await ws.send(raw_json.dumps({
'final': True,
'data': make_response_data('Error', str(e), [], str(e))
}))
@app.post('/openai_chat')
async def openai_chat(request):
try:
sid = request.json.get('sid')
q = request.json.get('q')
history_conversation = OPENAI_CONVERSATION[sid][-30:]
history_conversation.append({
'role': 'user',
'content': q,
})
response = openai.ChatCompletion.create(
model='gpt-3.5-turbo',
messages=history_conversation,
temperature=1.2,
stream=True,
)
chunks = []
for chunk in response:
chunk_message = chunk['choices'][0]['delta']
if chunk_message:
if 'content' in chunk_message:
chunks.append(chunk_message['content'])
else:
OPENAI_CONVERSATION[sid].append({
'role': 'assistant',
'content': ''.join(chunks)
})
return json(make_response_data('Success', ''.join(chunks), [], ''))
except Exception as e:
logger.error(e)
return json(make_response_data('Error', str(e), [], str(e)))
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)

View File

@ -5,3 +5,4 @@ COOKIE_FILE1=/sanic/cookies/cookie1.json # 备用cookie1, 没有可以删掉
COOKIE_FILE2=/sanic/cookies/cookie2.json # 备用cookie2, 没有可以删掉
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所以最好能加个代理
OPENAI_API_KEY=

View File

@ -3,3 +3,4 @@ requests==2.28.2
asyncio==3.4.3
websockets==10.4
httpx==0.23.3
openai==0.27.2