Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
DCloud
uni-ai-chat
提交
8b19d05b
U
uni-ai-chat
项目概览
DCloud
/
uni-ai-chat
通知
893
Star
11
Fork
8
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
2
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
U
uni-ai-chat
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
2
Issue
2
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
8b19d05b
编写于
6月 16, 2023
作者:
DCloud_JSON
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
1.2.2
上级
ccbbf5f3
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
205 addition
and
150 deletion
+205
-150
changelog.md
changelog.md
+4
-0
components/uni-ai-msg/uni-ai-msg.scss
components/uni-ai-msg/uni-ai-msg.scss
+11
-0
components/uni-ai-msg/uni-ai-msg.vue
components/uni-ai-msg/uni-ai-msg.vue
+2
-18
package.json
package.json
+1
-1
pages/chat/chat.vue
pages/chat/chat.vue
+186
-129
uniCloud-aliyun/cloudfunctions/uni-ai-chat/index.obj.js
uniCloud-aliyun/cloudfunctions/uni-ai-chat/index.obj.js
+1
-2
未找到文件。
changelog.md
浏览文件 @
8b19d05b
## 1.2.2(2023-06-16)
-
修复 部分情况下,通过 uni-ai 计费网关发起调用,“服务商接口抛出错误”会导致界面卡住的问题
-
修复 部分情况下,客户端关闭广告事件触发多次的问题
-
修复 AI返回表格格式的文档,没有边框线的问题
## 1.2.1(2023-06-14)
## 1.2.1(2023-06-14)
-
更新 大语言模型provider的值默认为:minimax
-
更新 大语言模型provider的值默认为:minimax
## 1.2.0(2023-06-14)
## 1.2.0(2023-06-14)
...
...
components/uni-ai-msg/uni-ai-msg.scss
浏览文件 @
8b19d05b
...
@@ -9,6 +9,17 @@
...
@@ -9,6 +9,17 @@
margin
:
5px
0
;
margin
:
5px
0
;
overflow
:
auto
;
overflow
:
auto
;
}
}
.
rich-text-box
:
:
v-deep
table
{
border-spacing
:
0
;
}
.
rich-text-box
:
:
v-deep
th
,
.
rich-text-box
::
v-deep
td
{
border
:
1px
solid
#666
;
padding
:
3px
5px
;
}
.cursor
{
.cursor
{
display
:
none
;
display
:
none
;
...
...
components/uni-ai-msg/uni-ai-msg.vue
浏览文件 @
8b19d05b
...
@@ -11,17 +11,7 @@
...
@@ -11,17 +11,7 @@
<view
v-if=
"msg.isAi"
class=
"rich-text-box"
:class=
"
{'show-cursor':showCursor}" ref="rich-text-box">
<view
v-if=
"msg.isAi"
class=
"rich-text-box"
:class=
"
{'show-cursor':showCursor}" ref="rich-text-box">
<rich-text
v-if=
"nodes&&nodes.length"
space=
"nbsp"
:nodes=
"nodes"
@
itemclick=
"trOnclick"
></rich-text>
<rich-text
v-if=
"nodes&&nodes.length"
space=
"nbsp"
:nodes=
"nodes"
@
itemclick=
"trOnclick"
></rich-text>
</view>
</view>
<view
v-else
>
<view
v-else
>
{{
msgContent
}}
</view>
{{
msgContent
}}
</view>
<view
v-if=
"isLastMsg && adpid && msg.insufficientScore"
>
<text
style=
"color: red;"
>
默认不启用广告组件(被注释),如需使用,请"去掉注释"(“重新运行”后生效)
位置:/components/uni-ai-msg/uni-ai-msg.vue 第28行,或全局搜索 uni-ad-rewarded-video
</text>
<!--
<uni-ad-rewarded-video
:adpid=
"adpid"
@
onAdClose=
"onAdClose"
></uni-ad-rewarded-video>
-->
</view>
<view
class=
"menu-box"
:class=
'
{"menu-box-ai":msg.isAi}'>
<view
class=
"menu-box"
:class=
'
{"menu-box-ai":msg.isAi}'>
<text
v-if=
"isLastMsg && msg.isAi"
title=
"换一个答案"
@
click=
"changeAnswer"
class=
"pointer change-answer"
>
⟳
</text>
<text
v-if=
"isLastMsg && msg.isAi"
title=
"换一个答案"
@
click=
"changeAnswer"
class=
"pointer change-answer"
>
⟳
</text>
<view
@
click=
"showMoreMenu = !showMoreMenu"
class=
"more-icon-box"
>
<view
@
click=
"showMoreMenu = !showMoreMenu"
class=
"more-icon-box"
>
...
@@ -214,9 +204,6 @@
...
@@ -214,9 +204,6 @@
changeAnswer
(){
changeAnswer
(){
this
.
$emit
(
'
changeAnswer
'
)
this
.
$emit
(
'
changeAnswer
'
)
},
},
retriesSendMsg
(){
this
.
$emit
(
'
retriesSendMsg
'
)
},
// 复制文本内容到系统剪切板
// 复制文本内容到系统剪切板
copy
()
{
copy
()
{
uni
.
setClipboardData
({
uni
.
setClipboardData
({
...
@@ -235,10 +222,7 @@
...
@@ -235,10 +222,7 @@
removeMsg
(){
removeMsg
(){
this
.
$emit
(
'
removeMsg
'
)
this
.
$emit
(
'
removeMsg
'
)
this
.
showMoreMenu
=
false
this
.
showMoreMenu
=
false
},
}
onAdClose
(
e
){
this
.
$emit
(
'
onAdClose
'
,
e
)
}
}
}
}
}
</
script
>
</
script
>
...
...
package.json
浏览文件 @
8b19d05b
{
{
"id"
:
"uni-ai-chat"
,
"id"
:
"uni-ai-chat"
,
"name"
:
"uni-ai-chat"
,
"name"
:
"uni-ai-chat"
,
"version"
:
"1.2.
1
"
,
"version"
:
"1.2.
2
"
,
"description"
:
"基于uni-ai的聊天示例项目,支持流式、支持前文总结,云端一体"
,
"description"
:
"基于uni-ai的聊天示例项目,支持流式、支持前文总结,云端一体"
,
"main"
:
"main.js"
,
"main"
:
"main.js"
,
"scripts"
:
{
"scripts"
:
{
...
...
pages/chat/chat.vue
浏览文件 @
8b19d05b
...
@@ -5,14 +5,13 @@
...
@@ -5,14 +5,13 @@
<!-- #endif -->
<!-- #endif -->
<text
class=
"noData"
v-if=
"msgList.length === 0"
>
没有对话记录
</text>
<text
class=
"noData"
v-if=
"msgList.length === 0"
>
没有对话记录
</text>
<scroll-view
:scroll-into-view=
"scrollIntoView"
scroll-y=
"true"
class=
"msg-list"
:enable-flex=
"true"
>
<scroll-view
:scroll-into-view=
"scrollIntoView"
scroll-y=
"true"
class=
"msg-list"
:enable-flex=
"true"
>
<uni-ai-msg
ref=
"msg"
v-for=
"(msg,index) in msgList"
:key=
"index"
:msg=
"msg"
<uni-ai-msg
ref=
"msg"
v-for=
"(msg,index) in msgList"
:key=
"index"
:msg=
"msg"
@
changeAnswer=
"changeAnswer"
@
retriesSendMsg=
"retriesSendMsg"
@
changeAnswer=
"changeAnswer"
@
onAdClose=
"onAdClose"
:show-cursor=
"index == msgList.length - 1 && msgList.length%2 === 0 && sseIndex"
:show-cursor=
"index == msgList.length - 1 && msgList.length%2 === 0 && sseIndex"
:isLastMsg=
"index == msgList.length - 1"
@
removeMsg=
"removeMsg(index)"
></uni-ai-msg>
:isLastMsg=
"index == msgList.length - 1"
@
removeMsg=
"removeMsg(index)"
></uni-ai-msg>
<template
v-if=
"msgList.length%2 !== 0"
>
<template
v-if=
"msgList.length%2 !== 0"
>
<view
v-if=
"
lastMsg
State == -100"
class=
"retries-box"
>
<view
v-if=
"
request
State == -100"
class=
"retries-box"
>
<text>
消息发送失败
</text>
<text>
消息发送失败
</text>
<uni-icons
@
click=
"
retriesSendMsg
"
color=
"#d22"
type=
"refresh-filled"
class=
"retries-icon"
></uni-icons>
<uni-icons
@
click=
"
send
"
color=
"#d22"
type=
"refresh-filled"
class=
"retries-icon"
></uni-icons>
</view>
</view>
<view
class=
"tip-ai-ing"
v-else-if=
"msgList.length"
>
<view
class=
"tip-ai-ing"
v-else-if=
"msgList.length"
>
<text>
uni-ai正在思考中...
</text>
<text>
uni-ai正在思考中...
</text>
...
@@ -22,7 +21,14 @@
...
@@ -22,7 +21,14 @@
</view>
</view>
</view>
</view>
</
template
>
</
template
>
<view
@
click=
"closeSseChannel"
class=
"stop-responding"
v-if=
"enableStream && sseIndex"
>
▣ 停止响应
</view>
<view
v-if=
"adpid"
class=
"open-ad-btn-box"
>
<!-- <text style="color: red;">
默认不启用广告组件(被注释),如需使用,请"去掉注释"(“重新运行”后生效)
位置:/pages/chat/chat.vue 第30行,或全局搜索 uni-ad-rewarded-video
</text> -->
<uni-ad-rewarded-video
v-show=
"insufficientScore"
:adpid=
"adpid"
@
onAdClose=
"onAdClose"
></uni-ad-rewarded-video>
</view>
<view
@
click=
"closeSseChannel"
class=
"stop-responding"
v-if=
"sseIndex"
>
▣ 停止响应
</view>
<view
id=
"last-msg-item"
style=
"height: 1px;"
></view>
<view
id=
"last-msg-item"
style=
"height: 1px;"
></view>
</scroll-view>
</scroll-view>
...
@@ -52,7 +58,7 @@
...
@@ -52,7 +58,7 @@
<!-- #ifdef H5 -->
<!-- #ifdef H5 -->
<text
v-if=
"isWidescreen"
class=
"send-btn-tip"
>
↵ 发送 / shift + ↵ 换行
</text>
<text
v-if=
"isWidescreen"
class=
"send-btn-tip"
>
↵ 发送 / shift + ↵ 换行
</text>
<!-- #endif -->
<!-- #endif -->
<button
@
click=
"beforeSend
Msg
"
:disabled=
"inputBoxDisabled || !content"
class=
"send"
<button
@
click=
"beforeSend"
:disabled=
"inputBoxDisabled || !content"
class=
"send"
type=
"primary"
>
发送
</button>
type=
"primary"
>
发送
</button>
</view>
</view>
</view>
</view>
...
@@ -93,7 +99,11 @@
...
@@ -93,7 +99,11 @@
// 使聊天窗口滚动到指定元素id的值
// 使聊天窗口滚动到指定元素id的值
scrollIntoView
:
""
,
scrollIntoView
:
""
,
// 消息列表数据
// 消息列表数据
msgList
:
[],
msgList
:
[],
// 通讯请求状态
requestState
:
0
,
//0发送中 100发送成功 -100发送失败
// 本地对话是否因积分不足而终止
insufficientScore
:
false
,
// 输入框的消息内容
// 输入框的消息内容
content
:
""
,
content
:
""
,
// 记录流式响应次数
// 记录流式响应次数
...
@@ -130,15 +140,6 @@
...
@@ -130,15 +140,6 @@
NODE_ENV
()
{
NODE_ENV
()
{
return
process
.
env
.
NODE_ENV
return
process
.
env
.
NODE_ENV
},
},
//最后一条消息的状态
lastMsgState
()
{
let
mLength
=
this
.
msgList
.
length
if
(
mLength
)
{
return
this
.
msgList
[
mLength
-
1
].
state
}
else
{
return
false
}
},
footBoxPaddingBottom
()
{
footBoxPaddingBottom
()
{
return
(
this
.
keyboardHeight
||
10
)
+
'
px
'
return
(
this
.
keyboardHeight
||
10
)
+
'
px
'
}
}
...
@@ -155,6 +156,12 @@
...
@@ -155,6 +156,12 @@
},
},
// 深度监听msgList变化
// 深度监听msgList变化
deep
:
true
deep
:
true
},
insufficientScore
(
insufficientScore
){
uni
.
setStorage
({
"
key
"
:
"
uni-ai-chat-insufficientScore
"
,
"
data
"
:
insufficientScore
})
},
},
llmModel
(
llmModel
)
{
llmModel
(
llmModel
)
{
let
title
=
'
uni-ai-chat
'
let
title
=
'
uni-ai-chat
'
...
@@ -215,14 +222,17 @@
...
@@ -215,14 +222,17 @@
this
.
msgList
=
uni
.
getStorageSync
(
'
uni-ai-msg
'
)
||
[];
this
.
msgList
=
uni
.
getStorageSync
(
'
uni-ai-msg
'
)
||
[];
// 获得之前设置的llmModel
// 获得之前设置的llmModel
this
.
llmModel
=
uni
.
getStorageSync
(
'
uni-ai-chat-llmModel
'
)
this
.
llmModel
=
uni
.
getStorageSync
(
'
uni-ai-chat-llmModel
'
)
// 获得之前设置的uni-ai-chat-insufficientScore
this
.
insufficientScore
=
uni
.
getStorageSync
(
'
uni-ai-chat-insufficientScore
'
)
||
false
// 如果上一次对话中 最后一条消息ai未回复。则一启动就自动重发。
// 如果上一次对话中 最后一条消息ai未回复。则一启动就自动重发。
let
length
=
this
.
msgList
.
length
let
length
=
this
.
msgList
.
length
if
(
length
)
{
if
(
length
)
{
let
lastMsg
=
this
.
msgList
[
length
-
1
]
let
lastMsg
=
this
.
msgList
[
length
-
1
]
if
(
!
lastMsg
.
isAi
)
{
if
(
!
lastMsg
.
isAi
)
{
this
.
retriesSendMsg
()
this
.
send
()
}
}
}
}
...
@@ -250,7 +260,7 @@
...
@@ -250,7 +260,7 @@
e
.
preventDefault
()
e
.
preventDefault
()
// 执行发送
// 执行发送
setTimeout
(()
=>
{
setTimeout
(()
=>
{
this
.
beforeSend
Msg
();
this
.
beforeSend
();
},
300
)
},
300
)
}
}
};
};
...
@@ -338,20 +348,6 @@
...
@@ -338,20 +348,6 @@
}
}
this
.
msgList
.
splice
(
length
-
1
,
1
,
lastMsg
)
this
.
msgList
.
splice
(
length
-
1
,
1
,
lastMsg
)
},
},
setSummarize
(
summarizeData
)
{
// console.log('setSummarize');
let
length
=
this
.
msgList
.
length
;
let
index
=
length
-
2
// 从 v1.1.0起,总结可能是总结倒数第二条之前的内容
if
(
index
<
0
)
{
index
=
0
}
let
msg
=
this
.
msgList
[
index
]
msg
.
summarize
=
summarizeData
this
.
msgList
.
splice
(
index
,
1
,
msg
)
// console.log('setSummarize this.msgList',this.msgList);
},
// 广告关闭事件
// 广告关闭事件
onAdClose
(
e
)
{
onAdClose
(
e
)
{
console
.
log
(
'
onAdClose e.detail.isEnded
'
,
e
.
detail
.
isEnded
);
console
.
log
(
'
onAdClose e.detail.isEnded
'
,
e
.
detail
.
isEnded
);
...
@@ -383,12 +379,13 @@
...
@@ -383,12 +379,13 @@
clearInterval
(
myIntive
)
clearInterval
(
myIntive
)
// 隐藏加载提示
// 隐藏加载提示
uni
.
hideLoading
()
uni
.
hideLoading
()
if
(
score
>
0
)
{
if
(
score
>
0
)
{
this
.
insufficientScore
=
false
// 移除最后一条消息
// 移除最后一条消息
this
.
msgList
.
pop
()
this
.
msgList
.
pop
()
this
.
$nextTick
(()
=>
{
this
.
$nextTick
(()
=>
{
// 重发消息
// 重发消息
this
.
retriesSendMsg
()
this
.
send
()
uni
.
showToast
({
uni
.
showToast
({
title
:
'
积分余额:
'
+
score
,
title
:
'
积分余额:
'
+
score
,
icon
:
'
none
'
icon
:
'
none
'
...
@@ -399,34 +396,23 @@
...
@@ -399,34 +396,23 @@
},
2000
);
},
2000
);
}
}
},
},
async
retriesSendMsg
()
{
// 检查是否开通uni-push;决定是否启用enableStream
await
this
.
checkIsOpenPush
()
// 更新最后一条消息的状态为0 表示消息正在发送中
this
.
updateLastMsg
({
state
:
0
})
// 发送消息
this
.
send
()
},
// 换一个答案
// 换一个答案
async
changeAnswer
()
{
async
changeAnswer
()
{
// 如果问题还在回答中需要先关闭
// 如果问题还在回答中需要先关闭
if
(
this
.
sseIndex
)
{
if
(
this
.
sseIndex
)
{
this
.
closeSseChannel
()
this
.
closeSseChannel
()
}
}
//删除旧的回答
//删除旧的回答
this
.
msgList
.
pop
()
this
.
msgList
.
pop
()
this
.
updateLastMsg
({
this
.
updateLastMsg
({
// 防止 偶发答案涉及敏感,重复回答时。提问内容 被卡掉无法重新问
// 防止 偶发答案涉及敏感,重复回答时。提问内容 被卡掉无法重新问
illegal
:
false
,
illegal
:
false
,
// 多设备登录时其他设备看广告后点击重新回答,insufficientScore应当设置为 false
})
insufficientScore
:
false
// 多设备登录时其他设备看广告后点击重新回答,insufficientScore应当设置为 false
})
this
.
insufficientScore
=
false
this
.
send
()
this
.
send
()
},
},
//当消息涉及敏感
removeMsg
(
index
)
{
removeMsg
(
index
)
{
// 如果问题还在回答中需要先关闭
// 如果问题还在回答中需要先关闭
if
(
this
.
sseIndex
)
{
if
(
this
.
sseIndex
)
{
...
@@ -438,7 +424,7 @@
...
@@ -438,7 +424,7 @@
}
}
this
.
msgList
.
splice
(
index
,
2
)
this
.
msgList
.
splice
(
index
,
2
)
},
},
async
beforeSend
Msg
()
{
async
beforeSend
()
{
if
(
this
.
inputBoxDisabled
)
{
if
(
this
.
inputBoxDisabled
)
{
return
uni
.
showToast
({
return
uni
.
showToast
({
title
:
'
ai正在回复中不能发送
'
,
title
:
'
ai正在回复中不能发送
'
,
...
@@ -511,8 +497,6 @@
...
@@ -511,8 +497,6 @@
isAi
:
false
,
isAi
:
false
,
// 消息内容
// 消息内容
content
:
this
.
content
,
content
:
this
.
content
,
// 消息状态为0,表示正在发送中
state
:
0
,
// 消息创建时间
// 消息创建时间
create_time
:
Date
.
now
()
create_time
:
Date
.
now
()
})
})
...
@@ -524,17 +508,18 @@
...
@@ -524,17 +508,18 @@
this
.
content
=
''
this
.
content
=
''
})
})
this
.
send
()
// 发送消息
this
.
send
()
// 发送消息
},
},
async
send
()
{
async
send
()
{
// 流式响应和云对象的请求状态
// 请求状态归零
let
state
=
{
this
.
requestState
=
0
sse
:
0
,
// 防止重复发起,关闭之前的
co
:
0
uniCoTaskList
.
clear
()
}
// 清除旧的afterChatCompletion(如果存在)
if
(
this
.
afterChatCompletion
&&
this
.
afterChatCompletion
.
clear
)
this
.
afterChatCompletion
.
clear
()
let
messages
=
[]
let
messages
=
[]
// 复制一份,消息列表数据
// 复制一份,消息列表数据
let
msgs
=
JSON
.
parse
(
JSON
.
stringify
(
this
.
msgList
))
.
filter
(
i
=>
i
.
isDelete
!==
true
)
let
msgs
=
JSON
.
parse
(
JSON
.
stringify
(
this
.
msgList
))
// 带总结的消息 index
// 带总结的消息 index
let
findIndex
=
[...
msgs
].
reverse
().
findIndex
(
item
=>
item
.
summarize
)
let
findIndex
=
[...
msgs
].
reverse
().
findIndex
(
item
=>
item
.
summarize
)
// console.log('findIndex', findIndex)
// console.log('findIndex', findIndex)
...
@@ -549,10 +534,8 @@
...
@@ -549,10 +534,8 @@
// 如果未总结过就直接从末尾拿10条
// 如果未总结过就直接从末尾拿10条
msgs
=
msgs
.
splice
(
-
10
)
msgs
=
msgs
.
splice
(
-
10
)
}
}
// 过滤涉敏问题
// 过滤涉敏问题
msgs
=
msgs
.
filter
(
msg
=>
!
msg
.
illegal
)
msgs
=
msgs
.
filter
(
msg
=>
!
msg
.
illegal
)
// 根据数据内容设置角色
// 根据数据内容设置角色
messages
=
msgs
.
map
(
item
=>
{
messages
=
msgs
.
map
(
item
=>
{
// 角色默认为用户
// 角色默认为用户
...
@@ -574,12 +557,13 @@
...
@@ -574,12 +557,13 @@
await
this
.
checkIsOpenPush
()
await
this
.
checkIsOpenPush
()
// console.log('this.enableStream',this.enableStream);
// console.log('this.enableStream',this.enableStream);
// 流式响应和云对象的请求结束回调函数
let
sseEnd
,
requestEnd
;
// 判断是否开启了流式响应模式
// 判断是否开启了流式响应模式
if
(
this
.
enableStream
)
{
if
(
this
.
enableStream
)
{
// 创建消息通道
// 创建消息通道
sseChannel
=
new
uniCloud
.
SSEChannel
()
sseChannel
=
new
uniCloud
.
SSEChannel
()
// console.log('sseChannel',sseChannel);
// console.log('sseChannel',sseChannel);
// 监听message事件
// 监听message事件
sseChannel
.
on
(
'
message
'
,
(
message
)
=>
{
sseChannel
.
on
(
'
message
'
,
(
message
)
=>
{
// console.log('on message', message);
// console.log('on message', message);
...
@@ -592,13 +576,12 @@
...
@@ -592,13 +576,12 @@
content
:
message
,
content
:
message
,
create_time
:
Date
.
now
()
create_time
:
Date
.
now
()
})
})
this
.
showLastMsg
()
}
else
{
}
else
{
this
.
updateLastMsg
(
lastMsg
=>
{
this
.
updateLastMsg
(
lastMsg
=>
{
lastMsg
.
content
+=
message
lastMsg
.
content
+=
message
})
})
this
.
showLastMsg
()
}
}
this
.
showLastMsg
()
// 让流式响应计数值递增
// 让流式响应计数值递增
this
.
sseIndex
++
this
.
sseIndex
++
})
})
...
@@ -606,21 +589,85 @@
...
@@ -606,21 +589,85 @@
// 监听end事件,如果云端执行end时传了message,会在客户端end事件内收到传递的消息
// 监听end事件,如果云端执行end时传了message,会在客户端end事件内收到传递的消息
sseChannel
.
on
(
'
end
'
,
(
e
)
=>
{
sseChannel
.
on
(
'
end
'
,
(
e
)
=>
{
console
.
log
(
'
sse 结束
'
,
e
)
console
.
log
(
'
sse 结束
'
,
e
)
state
.
sse
=
1
if
(
e
&&
typeof
e
==
'
object
'
&&
e
.
errCode
){
if
(
state
.
sse
===
1
&&
state
.
co
===
1
){
let
setLastAiMsgContent
=
(
content
)
=>
{
// console.error('通过 sse end 结束',state);
// console.log(content);
// 如果最后一项不是ai的就添加,否则就执行更新最后一条消息
if
(
this
.
sseIndex
===
0
)
{
this
.
msgList
.
push
({
isAi
:
true
,
content
,
create_time
:
Date
.
now
()
})
}
else
{
this
.
updateLastMsg
(
lastMsg
=>
{
lastMsg
.
content
+=
content
})
}
this
.
showLastMsg
()
}
if
(
e
.
errCode
==
60004
){
//服务商检测到AI输出了敏感内容
let
length
=
this
.
msgList
.
length
//如果最后一项不是ai,就创建一项
if
(
length
%
2
){
this
.
msgList
.
push
({
isAi
:
true
,
content
:
"
内容涉及敏感
"
,
illegal
:
true
,
create_time
:
Date
.
now
()
})
length
+=
1
}
// 更新倒数第2项 用户提的问题
this
.
msgList
[
length
-
2
].
illegal
=
true
// 更新倒数第1项 ai 回答的内容
this
.
msgList
[
length
-
1
].
illegal
=
true
this
.
msgList
[
length
-
1
].
content
=
"
内容涉及敏感
"
}
else
{
setLastAiMsgContent
(
e
.
errMsg
)
}
}
sseEnd
()
})
await
sseChannel
.
open
();
// 等待通道开启
// 等待对话完成(云函数请求完成,sse 执行了 end)之后
(
function
fnSelf
(
that
){
fnSelf
.
clear
=
()
=>
{
// console.log('do fnSelf.clear');
if
(
fnSelf
.
clear
.
sse
){
// console.log('fnSelf.clear.sse();')
fnSelf
.
clear
.
sse
();
}
if
(
fnSelf
.
clear
.
request
){
// console.log('fnSelf.clear.request();')
fnSelf
.
clear
.
request
();
}
}
Promise
.
all
([
new
Promise
((
resolve
,
reject
)
=>
{
sseEnd
=
resolve
;
fnSelf
.
clear
.
sse
=
reject
;
}),
new
Promise
((
resolve
,
reject
)
=>
{
requestEnd
=
resolve
;
fnSelf
.
clear
.
request
=
reject
;
})
]).
then
((
e
)
=>
{
// console.log('sseEnd && requestEnd');
//当两个都结束时
//当两个都结束时
sseChannel
.
close
()
sseChannel
.
close
()
// 结束流式响应 将流式响应计数值 设置为 0
// 结束流式响应 将流式响应计数值 设置为 0
this
.
sseIndex
=
0
that
.
sseIndex
=
0
state
=
{
sse
:
0
,
co
:
0
}
}).
catch
((
err
)
=>
{
}
else
{
// console.log('afterChatCompletion is close',err);
// console.log(1,state);
})
}
that
.
afterChatCompletion
=
fnSelf
})
})(
this
)
await
sseChannel
.
open
()
// 等待通道开启
}
}
// 导入uni-ai-chat模块,并设置customUI为true
// 导入uni-ai-chat模块,并设置customUI为true
let
task
=
uniCoTask
({
let
task
=
uniCoTask
({
coName
:
"
uni-ai-chat
"
,
coName
:
"
uni-ai-chat
"
,
...
@@ -633,41 +680,29 @@
...
@@ -633,41 +680,29 @@
config
:
{
config
:
{
customUI
:
true
customUI
:
true
},
},
success
:
res
=>
{
success
:
res
=>
{
console
.
log
(
"
success
"
,
res
);
// 更新 通讯状态为100(发送成功)
if
(
!
res
.
data
)
{
this
.
requestState
=
100
return
// console.log("success",res);
}
if
(
!
res
.
data
)
return
// 更新最后一条消息的状态为100(发送成功)
this
.
updateLastMsg
({
state
:
100
})
let
{
let
{
"
reply
"
:
content
,
reply
,
summarize
,
summarize
,
insufficientScore
,
insufficientScore
,
illegal
illegal
}
=
res
.
data
}
=
res
.
data
// 特殊处理 - start
// 特殊处理 - start
if
(
this
.
enableStream
==
false
&&
!
content
){
if
(
this
.
enableStream
==
false
&&
!
reply
){
//服务商检测到AI输出了敏感内容
illegal
=
true
illegal
=
true
content
=
"
内容涉及敏感
"
reply
=
"
内容涉及敏感
"
}
}
// 特殊处理 - end
// 特殊处理 - end
if
(
illegal
)
{
// 如果返回的数据包含illegal属性,就更新最后一条消息(用户输入的问题)的illegal属性为true
this
.
updateLastMsg
({
// 添加消息涉敏标记
illegal
:
true
})
}
// 非流式模式 或者流式模式,但列表还没有数据且已经进入异常的情况下
// 非流式模式 或者流式模式,但列表还没有数据且已经进入异常的情况下
if
(
this
.
enableStream
==
false
||
this
.
sseIndex
==
0
&&
(
illegal
||
insufficientScore
))
{
if
(
this
.
enableStream
==
false
||
this
.
sseIndex
==
0
&&
(
illegal
||
insufficientScore
))
{
// 将从云端接收到的消息添加到消息列表中
// 将从云端接收到的消息添加到消息列表中
this
.
msgList
.
push
({
this
.
msgList
.
push
({
// 消息创建时间
// 消息创建时间
...
@@ -675,36 +710,50 @@
...
@@ -675,36 +710,50 @@
// 标记消息为来自AI机器人
// 标记消息为来自AI机器人
isAi
:
true
,
isAi
:
true
,
// 消息内容
// 消息内容
content
,
content
:
reply
,
// 消息是否涉敏标记
// 消息是否涉敏标记
illegal
,
illegal
// 本地对话是否因积分不足而终止
insufficientScore
})
})
}
if
(
insufficientScore
){
// 积分不足
this
.
insufficientScore
=
true
}
}
// console.log(res, res.reply);
// 如果回调包含总结的内容,就设置总结
// 如果回调包含总结的内容,就设置总结
if
(
summarize
){
if
(
summarize
){
console
.
log
(
'
拿到总结
'
,
summarize
);
console
.
log
(
'
拿到总结
'
,
summarize
);
this
.
setSummarize
(
summarize
)
// 总结的内容是上一轮对话的
// console.log('setSummarize');
let
index
=
this
.
msgList
.
length
;
// 如果最后一项是ai就往前退2项,否则退一项(流式响应的时候,回答可能晚于总结)
if
(
index
%
2
===
0
){
index
-=
2
}
else
{
index
-=
1
}
// 假如第一次提问就需要总结
if
(
index
<
0
)
{
index
=
0
}
let
msg
=
this
.
msgList
[
index
]
msg
.
summarize
=
summarize
this
.
msgList
.
splice
(
index
,
1
,
msg
)
// console.log('setSummarize this.msgList',this.msgList);
}
if
(
illegal
)
{
console
.
error
(
'
内容涉及敏感
'
);
this
.
updateLastMsg
({
// 添加消息涉敏标记
illegal
:
true
})
}
}
},
},
complete
:
e
=>
{
complete
:
e
=>
{
// console.log('complete:',e);
if
(
this
.
enableStream
)
{
if
(
sseChannel
)
{
requestEnd
()
state
.
co
=
1
if
(
state
.
sse
===
1
&&
state
.
co
===
1
){
// console.error('通过 co complete 结束');
//当两个都结束时
sseChannel
.
close
()
// 结束流式响应 将流式响应计数值 设置为 0
this
.
sseIndex
=
0
state
=
{
sse
:
0
,
co
:
0
}
}
else
{
// console.log(2,state);
}
}
}
// console.log('complete:',e);
// 滚动窗口以显示最新的一条消息
// 滚动窗口以显示最新的一条消息
this
.
$nextTick
(()
=>
{
this
.
$nextTick
(()
=>
{
this
.
showLastMsg
()
this
.
showLastMsg
()
...
@@ -712,15 +761,17 @@
...
@@ -712,15 +761,17 @@
},
},
fail
:
e
=>
{
fail
:
e
=>
{
console
.
error
(
e
);
console
.
error
(
e
);
// 更新最后一条消息的状态为-100(发送失败)
// 更新 通讯状态为-100(发送失败)
this
.
updateLastMsg
({
this
.
requestState
=
-
100
state
:
-
100
})
// 弹框提示用户错误原因
// 弹框提示用户错误原因
uni
.
showModal
({
uni
.
showModal
({
content
:
JSON
.
stringify
(
e
.
message
),
content
:
JSON
.
stringify
(
e
.
message
),
showCancel
:
false
showCancel
:
false
});
});
// 如果启用流式,云函数出错了,sse 也应当被终止
if
(
this
.
enableStream
)
{
sseEnd
()
}
}
}
})
})
uniCoTaskList
.
push
(
task
)
uniCoTaskList
.
push
(
task
)
...
@@ -728,7 +779,8 @@
...
@@ -728,7 +779,8 @@
closeSseChannel
()
{
closeSseChannel
()
{
// 如果存在消息通道,就关闭消息通道
// 如果存在消息通道,就关闭消息通道
if
(
sseChannel
)
{
if
(
sseChannel
)
{
sseChannel
.
close
()
sseChannel
.
close
()
// 设置为 false 防止重复调用closeSseChannel时出错
sseChannel
=
false
sseChannel
=
false
}
}
// 清空历史网络请求(调用云对象)任务
// 清空历史网络请求(调用云对象)任务
...
@@ -922,6 +974,11 @@
...
@@ -922,6 +974,11 @@
color
:
#aaa
;
color
:
#aaa
;
font-size
:
12px
;
font-size
:
12px
;
justify-content
:
center
;
justify-content
:
center
;
}
.open-ad-btn-box
{
justify-content
:
center
;
margin
:
10px
0
;
}
}
.tip-ai-ing
{
.tip-ai-ing
{
...
...
uniCloud-aliyun/cloudfunctions/uni-ai-chat/index.obj.js
浏览文件 @
8b19d05b
...
@@ -174,10 +174,9 @@ module.exports = {
...
@@ -174,10 +174,9 @@ module.exports = {
// 如果是积分不足错误
// 如果是积分不足错误
else
if
(
error
==
'
insufficientScore
'
)
{
else
if
(
error
==
'
insufficientScore
'
)
{
// 设置回复内容
// 设置回复内容
let
reply
=
"
积分不足,请看完激励视频广告后再试
"
return
{
return
{
"
data
"
:
{
"
data
"
:
{
reply
,
"
reply
"
:
"
积分不足,请看完激励视频广告后再试
"
,
"
insufficientScore
"
:
true
"
insufficientScore
"
:
true
},
},
"
errCode
"
:
0
"
errCode
"
:
0
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录