Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
赵月辉
/
fastgpt-migrated
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
aef42cef
authored
Jul 08, 2023
by
archer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
perf: attribute
parent
23642af6
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
1118 additions
and
291 deletions
+1118
-291
client/src/components/Icon/icons/light/chat.svg
+2
-0
client/src/components/Icon/index.tsx
+2
-1
client/src/components/Layout/navbar.tsx
+1
-1
client/src/components/Layout/navbarPhone.tsx
+1
-1
client/src/constants/app.ts
+1037
-211
client/src/pages/api/openapi/modules/kb/search.ts
+6
-0
client/src/pages/app/detail/components/Settings.tsx
+1
-1
client/src/pages/app/detail/components/edit/components/TemplateList.tsx
+2
-2
client/src/pages/app/detail/components/edit/index.tsx
+1
-0
client/src/pages/app/detail/index.tsx
+3
-3
client/src/pages/app/list/component/CreateModal.tsx
+6
-37
client/src/pages/app/list/index.tsx
+26
-2
client/src/pages/appStore/components/list.tsx
+1
-1
client/src/pages/chat/components/History.tsx
+4
-4
client/src/pages/chat/components/ModelList.tsx
+1
-1
client/src/pages/chat/components/PhoneSliderBar.tsx
+4
-4
client/src/pages/chat/index.tsx
+20
-22
No files found.
client/src/components/Icon/icons/light/chat.svg
0 → 100644
View file @
aef42cef
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg
t=
"1688783528309"
class=
"icon"
viewBox=
"0 0 1024 1024"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
p-id=
"5845"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
><path
d=
"M931.53 960H512C264.97 960 64 759.03 64 512S264.97 64 512 64s448 200.97 448 448v419.53c0 15.69-12.77 28.47-28.47 28.47zM512 116.71c-217.96 0-395.29 177.33-395.29 395.29 0 217.97 177.33 395.3 395.29 395.3h395.29V512c0-217.97-177.33-395.29-395.29-395.29z"
fill=
"#333333"
p-id=
"5846"
></path><path
d=
"M730.13 439.04H293.87c-14.55 0-26.35-11.8-26.35-26.35s11.8-26.35 26.35-26.35h436.26c14.55 0 26.35 11.8 26.35 26.35s-11.8 26.35-26.35 26.35zM730.13 637.67H293.87c-14.55 0-26.35-11.8-26.35-26.35s11.8-26.35 26.35-26.35h436.26c14.55 0 26.35 11.8 26.35 26.35s-11.8 26.35-26.35 26.35z"
p-id=
"5847"
></path></svg>
\ No newline at end of file
client/src/components/Icon/index.tsx
View file @
aef42cef
...
...
@@ -36,7 +36,8 @@ const map = {
date
:
require
(
'./icons/date.svg'
).
default
,
apikey
:
require
(
'./icons/apikey.svg'
).
default
,
save
:
require
(
'./icons/save.svg'
).
default
,
minus
:
require
(
'./icons/minus.svg'
).
default
minus
:
require
(
'./icons/minus.svg'
).
default
,
chatLight
:
require
(
'./icons/light/chat.svg'
).
default
};
export
type
IconName
=
keyof
typeof
map
;
...
...
client/src/components/Layout/navbar.tsx
View file @
aef42cef
...
...
@@ -23,7 +23,7 @@ const Navbar = ({ unread }: { unread: number }) => {
{
label
:
'聊天'
,
icon
:
'chat'
,
link
:
`/chat?
model
Id=
${
lastChatModelId
}
&chatId=
${
lastChatId
}
`
,
link
:
`/chat?
app
Id=
${
lastChatModelId
}
&chatId=
${
lastChatId
}
`
,
activeLink
:
[
'/chat'
]
},
{
...
...
client/src/components/Layout/navbarPhone.tsx
View file @
aef42cef
...
...
@@ -13,7 +13,7 @@ const NavbarPhone = ({ unread }: { unread: number }) => {
{
label
:
'聊天'
,
icon
:
'tabbarChat'
,
link
:
`/chat?
model
Id=
${
lastChatModelId
}
&chatId=
${
lastChatId
}
`
,
link
:
`/chat?
app
Id=
${
lastChatModelId
}
&chatId=
${
lastChatId
}
`
,
activeLink
:
[
'/chat'
],
unread
:
0
},
...
...
client/src/constants/app.ts
View file @
aef42cef
import
type
{
AppItemType
}
from
'@/types/app'
;
import
{
FlowInputItemTypeEnum
,
FlowModuleTypeEnum
,
FlowOutputItemTypeEnum
}
from
'./flow'
;
import
{
chatModelList
}
from
'./data'
;
/* app */
export
enum
AppModuleItemTypeEnum
{
...
...
@@ -18,282 +16,1110 @@ export enum SpecificInputEnum {
'answerText'
=
'answerText'
// answer module text key
}
export
const
answerModule
=
({
id
}:
{
id
:
string
})
=>
({
moduleId
:
id
,
type
:
AppModuleItemTypeEnum
.
answer
,
flowType
:
FlowModuleTypeEnum
.
answerNode
,
// template
export
const
appTemplates
:
(
AppItemType
&
{
avatar
:
string
;
intro
:
string
})[]
=
[
{
id
:
'simpleChat'
,
avatar
:
'/imgs/module/AI.png'
,
name
:
'简单的对话'
,
intro
:
'一个极其简单的 AI 对话应用'
,
modules
:
[
{
logo
:
'/imgs/module/userChatInput.png'
,
name
:
'用户问题'
,
intro
:
'用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。'
,
type
:
'initInput'
,
flowType
:
'questionInput'
,
url
:
'/openapi/modules/init/userChatInput'
,
inputs
:
[
{
key
:
'userChatInput'
,
type
:
'systemInput'
,
label
:
'用户问题'
,
connected
:
false
}
],
outputs
:
[
{
key
:
'userChatInput'
,
label
:
'用户问题'
,
type
:
'source'
,
targets
:
[
{
moduleId
:
'3n49vn'
,
key
:
'userChatInput'
}
]
}
],
position
:
{
x
:
481.4684021933373
,
y
:
741.252592445572
},
moduleId
:
'xzj0oo'
},
{
logo
:
'/imgs/module/history.png'
,
name
:
'聊天记录'
,
intro
:
'用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。'
,
type
:
'initInput'
,
flowType
:
'historyNode'
,
url
:
'/openapi/modules/init/history'
,
inputs
:
[
{
key
:
'maxContext'
,
type
:
'numberInput'
,
label
:
'最长记录数'
,
value
:
4
,
min
:
0
,
max
:
50
,
connected
:
false
},
{
key
:
'history'
,
type
:
'hidden'
,
label
:
'聊天记录'
,
connected
:
false
}
],
outputs
:
[
{
key
:
'history'
,
label
:
'聊天记录'
,
type
:
'source'
,
targets
:
[
{
moduleId
:
'3n49vn'
,
key
:
'history'
}
]
}
],
position
:
{
x
:
405.6002299937601
,
y
:
374.16606887857023
},
moduleId
:
'hh6of9'
},
{
logo
:
'/imgs/module/AI.png'
,
name
:
'AI 对话'
,
intro
:
'OpenAI GPT 大模型对话。'
,
flowType
:
'chatNode'
,
type
:
'http'
,
url
:
'/openapi/modules/chat/gpt'
,
inputs
:
[
{
key
:
SystemInputEnum
.
switch
,
type
:
FlowInputItemTypeEnum
.
target
,
key
:
'model'
,
type
:
'select'
,
label
:
'对话模型'
,
value
:
'gpt-3.5-turbo-16k'
,
list
:
[
{
label
:
'Gpt35-16k'
,
value
:
'gpt-3.5-turbo-16k'
},
{
label
:
'Gpt35-4k'
,
value
:
'gpt-3.5-turbo'
},
{
label
:
'Gpt4-8k'
,
value
:
'gpt-4'
}
],
connected
:
false
},
{
key
:
'temperature'
,
type
:
'slider'
,
label
:
'温度'
,
value
:
0
,
min
:
0
,
max
:
10
,
step
:
1
,
markList
:
[
{
label
:
'严谨'
,
value
:
0
},
{
label
:
'发散'
,
value
:
10
}
],
connected
:
false
},
{
key
:
'maxToken'
,
type
:
'slider'
,
label
:
'回复上限'
,
value
:
3000
,
min
:
0
,
max
:
4000
,
step
:
50
,
markList
:
[
{
label
:
'0'
,
value
:
0
},
{
label
:
'4000'
,
value
:
4000
}
],
connected
:
false
},
{
key
:
'systemPrompt'
,
type
:
'textarea'
,
label
:
'系统提示词'
,
description
:
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。'
,
placeholder
:
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。'
,
value
:
''
,
connected
:
false
},
{
key
:
'limitPrompt'
,
type
:
'textarea'
,
label
:
'限定词'
,
description
:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"'
,
placeholder
:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"'
,
value
:
''
,
connected
:
false
},
{
key
:
'switch'
,
type
:
'target'
,
label
:
'触发器'
,
connected
:
false
},
{
key
:
'quotePrompt'
,
type
:
'target'
,
label
:
'引用内容'
,
connected
:
false
},
{
key
:
'history'
,
type
:
'target'
,
label
:
'聊天记录'
,
connected
:
true
},
{
key
:
SpecificInputEnum
.
answerText
,
value
:
''
,
type
:
FlowInputItemTypeEnum
.
input
,
label
:
'响应内容'
,
key
:
'userChatInput'
,
type
:
'target'
,
label
:
'用户问题'
,
connected
:
true
}
],
outputs
:
[]
});
export
const
chatModule
=
({
id
,
systemPrompt
=
''
,
limitPrompt
=
''
,
history
=
10
}:
{
id
:
string
;
systemPrompt
?:
string
;
limitPrompt
?:
string
;
history
?:
number
;
})
=>
{
return
{
moduleId
:
id
,
flowType
:
FlowModuleTypeEnum
.
chatNode
,
type
:
AppModuleItemTypeEnum
.
http
,
outputs
:
[
{
key
:
'answerText'
,
label
:
'模型回复'
,
description
:
'直接响应,无需配置'
,
type
:
'hidden'
,
targets
:
[]
}
],
position
:
{
x
:
965.5863241865428
,
y
:
-
29.569293606933797
},
moduleId
:
'3n49vn'
}
]
},
{
id
:
'simpleKbChat'
,
avatar
:
'/imgs/module/db.png'
,
name
:
'基础知识库'
,
intro
:
'每次提问时进行一次知识库搜索,将搜索结果注入 LLM 模型进行参考回答'
,
modules
:
[
{
logo
:
'/imgs/module/userChatInput.png'
,
name
:
'用户问题'
,
intro
:
'用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。'
,
type
:
'initInput'
,
flowType
:
'questionInput'
,
url
:
'/openapi/modules/init/userChatInput'
,
inputs
:
[
{
key
:
'userChatInput'
,
type
:
'systemInput'
,
label
:
'用户问题'
,
connected
:
false
}
],
outputs
:
[
{
key
:
'userChatInput'
,
label
:
'用户问题'
,
type
:
'source'
,
targets
:
[
{
moduleId
:
'3n49vn'
,
key
:
'userChatInput'
},
{
moduleId
:
'zid0fj'
,
key
:
'userChatInput'
}
]
}
],
position
:
{
x
:
447.0165784462213
,
y
:
748.7421193471189
},
moduleId
:
'xzj0oo'
},
{
logo
:
'/imgs/module/history.png'
,
name
:
'聊天记录'
,
intro
:
'用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。'
,
type
:
'initInput'
,
flowType
:
'historyNode'
,
url
:
'/openapi/modules/init/history'
,
inputs
:
[
{
key
:
'maxContext'
,
type
:
'numberInput'
,
label
:
'最长记录数'
,
value
:
4
,
min
:
0
,
max
:
50
,
connected
:
false
},
{
key
:
'history'
,
type
:
'hidden'
,
label
:
'聊天记录'
,
connected
:
false
}
],
outputs
:
[
{
key
:
'history'
,
label
:
'聊天记录'
,
type
:
'source'
,
targets
:
[
{
moduleId
:
'3n49vn'
,
key
:
'history'
}
]
}
],
position
:
{
x
:
1182.3679138395933
,
y
:
882.21575235563
},
moduleId
:
'hh6of9'
},
{
logo
:
'/imgs/module/AI.png'
,
name
:
'AI 对话'
,
intro
:
'OpenAI GPT 大模型对话。'
,
flowType
:
'chatNode'
,
type
:
'http'
,
url
:
'/openapi/modules/chat/gpt'
,
inputs
:
[
{
key
:
'model'
,
type
:
FlowInputItemTypeEnum
.
select
,
type
:
'select'
,
label
:
'对话模型'
,
value
:
chatModelList
[
0
].
value
,
list
:
chatModelList
value
:
'gpt-3.5-turbo-16k'
,
list
:
[
{
label
:
'Gpt35-16k'
,
value
:
'gpt-3.5-turbo-16k'
},
{
label
:
'Gpt35-4k'
,
value
:
'gpt-3.5-turbo'
},
{
label
:
'Gpt4-8k'
,
value
:
'gpt-4'
}
],
connected
:
false
},
{
key
:
'temperature'
,
type
:
FlowInputItemTypeEnum
.
slider
,
type
:
'slider'
,
label
:
'温度'
,
value
:
0
,
min
:
0
,
max
:
10
,
step
:
1
,
markList
:
[
{
label
:
'严谨'
,
value
:
0
},
{
label
:
'发散'
,
value
:
10
}
]
{
label
:
'严谨'
,
value
:
0
},
{
label
:
'发散'
,
value
:
10
}
],
connected
:
false
},
{
key
:
'maxToken'
,
type
:
FlowInputItemTypeEnum
.
slider
,
type
:
'slider'
,
label
:
'回复上限'
,
value
:
3000
,
min
:
0
,
max
:
4000
,
step
:
50
,
markList
:
[
{
label
:
'0'
,
value
:
0
},
{
label
:
'4000'
,
value
:
4000
}
]
{
label
:
'0'
,
value
:
0
},
{
label
:
'4000'
,
value
:
4000
}
],
connected
:
false
},
{
key
:
'systemPrompt'
,
type
:
FlowInputItemTypeEnum
.
textarea
,
type
:
'textarea'
,
label
:
'系统提示词'
,
description
:
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。'
,
placeholder
:
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。'
,
value
:
systemPrompt
value
:
''
,
connected
:
false
},
{
key
:
'limitPrompt'
,
type
:
FlowInputItemTypeEnum
.
textarea
,
type
:
'textarea'
,
label
:
'限定词'
,
description
:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"'
,
placeholder
:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"'
,
value
:
limitPrompt
value
:
''
,
connected
:
false
},
{
key
:
SystemInputEnum
.
switch
,
type
:
FlowInputItemTypeEnum
.
target
,
key
:
'switch'
,
type
:
'target'
,
label
:
'触发器'
,
connected
:
tru
e
connected
:
fals
e
},
{
key
:
'quotePrompt'
,
type
:
FlowInputItemTypeEnum
.
target
,
label
:
'引用内容(字符串)
'
,
type
:
'target'
,
label
:
'引用内容
'
,
connected
:
true
},
{
key
:
SystemInputEnum
.
history
,
type
:
FlowInputItemTypeEnum
.
numberInput
,
label
:
'最长上下文'
,
description
:
'为 0 时,代表不需要上下文。'
,
value
:
history
,
min
:
0
,
max
:
50
key
:
'history'
,
type
:
'target'
,
label
:
'聊天记录'
,
connected
:
true
},
{
key
:
SystemInputEnum
.
userChatInput
,
type
:
FlowInputItemTypeEnum
.
none
,
label
:
'用户输入(系统自动填写)
'
,
description
:
''
key
:
'userChatInput'
,
type
:
'target'
,
label
:
'用户问题
'
,
connected
:
true
}
],
outputs
:
[
{
key
:
'answer
'
,
key
:
'answerText
'
,
label
:
'模型回复'
,
type
:
FlowOutputItemTypeEnum
.
answer
,
description
:
'直接响应,无需配置'
,
type
:
'hidden'
,
targets
:
[]
}
],
position
:
{
x
:
1611.18354309989
,
y
:
-
56.531590452502826
},
moduleId
:
'3n49vn'
},
{
logo
:
'/imgs/module/db.png'
,
name
:
'知识库搜索'
,
intro
:
'去知识库中搜索对应的答案。可作为 AI 对话引用参考。'
,
flowType
:
'kbSearchNode'
,
type
:
'http'
,
url
:
'/openapi/modules/kb/search'
,
inputs
:
[
{
key
:
'kb_ids'
,
type
:
'custom'
,
label
:
'关联的知识库'
,
value
:
[],
list
:
[],
connected
:
false
},
{
key
:
'similarity'
,
type
:
'slider'
,
label
:
'相似度'
,
value
:
0.8
,
min
:
0
,
max
:
1
,
step
:
0.01
,
markList
:
[
{
label
:
'0'
,
value
:
0
},
{
label
:
'1'
,
value
:
1
}
],
connected
:
false
},
{
key
:
'limit'
,
type
:
'slider'
,
label
:
'单次搜索上限'
,
value
:
5
,
min
:
1
,
max
:
20
,
step
:
1
,
markList
:
[
{
label
:
'1'
,
value
:
1
},
{
label
:
'20'
,
value
:
20
}
],
connected
:
false
},
{
key
:
'switch'
,
type
:
'target'
,
label
:
'触发器'
,
connected
:
false
},
{
key
:
'userChatInput'
,
type
:
'target'
,
label
:
'用户问题'
,
connected
:
true
}
],
outputs
:
[
{
key
:
'rawSearch'
,
label
:
'源搜索数据'
,
type
:
'hidden'
,
response
:
true
,
targets
:
[]
},
{
key
:
'isEmpty'
,
label
:
'搜索结果为空'
,
type
:
'source'
,
targets
:
[
{
moduleId
:
'gbnzif'
,
key
:
'switch'
}
]
};
};
export
const
chatAppDemo
:
AppItemType
=
{
id
:
'chat'
,
name
:
''
,
// 标记字段
modules
:
[
chatModule
({
id
:
'chat'
})]
};
// export const kbChatAppDemo: AppItemType = {
// id: 'kbchat',
// name: 'kbchat',
// // 标记字段
// modules: [
// {
// moduleId: 'kbsearch',
// flowType: FlowModuleTypeEnum.kbSearchNode,
// type: AppModuleItemTypeEnum.http,
// url: '/openapi/modules/kb/search',
// position: { x: -500, y: 0 },
// inputs: [
// {
// key: 'kb_ids',
// type: FlowInputItemTypeEnum.custom,
// label: '关联的知识库',
// value: ['646627f4f7b896cfd8910e38'],
// list: []
// },
// {
// key: 'similarity',
// type: FlowInputItemTypeEnum.slider,
// label: '相似度',
// value: 0.8,
// min: 0,
// max: 1,
// step: 0.01,
// markList: [
// { label: '0', value: 0 },
// { label: '1', value: 1 }
// ]
// },
// {
// key: 'limit',
// type: FlowInputItemTypeEnum.slider,
// label: '单次搜索上限',
// value: 5,
// min: 1,
// max: 20,
// step: 1,
// markList: [
// { label: '1', value: 1 },
// { label: '20', value: 20 }
// ]
// },
// {
// key: SystemInputEnum.history,
// type: FlowInputItemTypeEnum.hidden,
// label: '引用复用数量',
// value: 1
// },
// {
// key: SystemInputEnum.userChatInput,
// type: FlowInputItemTypeEnum.none,
// label: '用户输入(系统自动填写)',
// description: ''
// }
// ],
// outputs: [
// {
// key: 'rawSearch',
// label: '源搜索数据',
// type: FlowOutputItemTypeEnum.none,
// response: true,
// targets: []
// },
// {
// key: 'isEmpty',
// label: '无搜索结果',
// type: FlowOutputItemTypeEnum.source,
// targets: [
// {
// moduleId: 'tfswitch',
// key: SystemInputEnum.switch
// }
// ]
// },
// {
// key: 'quotePrompt',
// label: '引用内容(字符串)',
// type: FlowOutputItemTypeEnum.source,
// targets: [
// {
// moduleId: 'chat',
// key: 'quotePrompt'
// }
// ]
// }
// ]
// },
// {
// moduleId: 'tfswitch',
// type: AppModuleItemTypeEnum.switch,
// flowType: FlowModuleTypeEnum.tfSwitchNode,
// position: { x: 0, y: 510 },
// inputs: [
// {
// key: SystemInputEnum.switch,
// type: FlowInputItemTypeEnum.target,
// label: '触发器',
// connected: true
// }
// ],
// outputs: [
// {
// key: 'true',
// label: '无搜索数据',
// type: FlowOutputItemTypeEnum.source,
// targets: [
// {
// moduleId: 'answer',
// key: SystemInputEnum.switch
// }
// ]
// },
// {
// key: 'false',
// label: '有搜索数据',
// type: FlowOutputItemTypeEnum.source,
// targets: [
// {
// moduleId: 'chat',
// key: SystemInputEnum.switch
// }
// ]
// }
// ]
// },
// {
// ...chatModule({ id: 'chat', limitPrompt: '参考知识库内容进行回答', history: 5 }),
// position: { x: 300, y: 240 }
// },
// {
// ...answerModule({ id: 'answer' }),
// position: { x: 300, y: 0 }
// }
// ]
// };
},
{
key
:
'quotePrompt'
,
label
:
'引用内容'
,
description
:
'搜索结果为空时不返回'
,
type
:
'source'
,
targets
:
[
{
moduleId
:
'3n49vn'
,
key
:
'quotePrompt'
}
]
}
],
position
:
{
x
:
718.7528704477357
,
y
:
112.64438442321625
},
moduleId
:
'zid0fj'
},
{
logo
:
'/imgs/module/reply.png'
,
name
:
'指定回复'
,
intro
:
'该模块可以直接回复一段指定的内容。常用于引导、提示。'
,
type
:
'answer'
,
flowType
:
'answerNode'
,
inputs
:
[
{
key
:
'switch'
,
type
:
'target'
,
label
:
'触发器'
,
connected
:
true
},
{
key
:
'answerText'
,
value
:
'对不起,你的问题不在知识库中。'
,
type
:
'input'
,
label
:
'回复的内容'
,
connected
:
false
}
],
outputs
:
[],
position
:
{
x
:
1171.1202953011716
,
y
:
213.00404490394536
},
moduleId
:
'gbnzif'
}
]
},
{
id
:
'chatGuide'
,
avatar
:
'/imgs/module/db.png'
,
name
:
'问答前引导'
,
intro
:
'可以在每次对话开始前提示用户填写一些内容,作为本次对话的永久内容'
,
modules
:
[]
},
{
id
:
'CQ'
,
avatar
:
'/imgs/module/cq.png'
,
name
:
'意图识别 + 知识库'
,
intro
:
'先对用户的问题进行分类,再根据不同类型问题,执行不同的操作'
,
modules
:
[
{
logo
:
'/imgs/module/userChatInput.png'
,
name
:
'用户问题'
,
intro
:
'用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。'
,
type
:
'initInput'
,
flowType
:
'questionInput'
,
url
:
'/openapi/modules/init/userChatInput'
,
inputs
:
[
{
key
:
'userChatInput'
,
type
:
'systemInput'
,
label
:
'用户问题'
,
connected
:
false
}
],
outputs
:
[
{
key
:
'userChatInput'
,
label
:
'用户问题'
,
type
:
'source'
,
targets
:
[
{
moduleId
:
'3n49vn'
,
key
:
'userChatInput'
},
{
moduleId
:
'zid0fj'
,
key
:
'userChatInput'
},
{
moduleId
:
'gm15of'
,
key
:
'userChatInput'
}
]
}
],
position
:
{
x
:
-
33.86673792997432
,
y
:
874.685676808633
},
moduleId
:
'xzj0oo'
},
{
logo
:
'/imgs/module/history.png'
,
name
:
'聊天记录'
,
intro
:
'用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。'
,
type
:
'initInput'
,
flowType
:
'historyNode'
,
url
:
'/openapi/modules/init/history'
,
inputs
:
[
{
key
:
'maxContext'
,
type
:
'numberInput'
,
label
:
'最长记录数'
,
value
:
4
,
min
:
0
,
max
:
50
,
connected
:
false
},
{
key
:
'history'
,
type
:
'hidden'
,
label
:
'聊天记录'
,
connected
:
false
}
],
outputs
:
[
{
key
:
'history'
,
label
:
'聊天记录'
,
type
:
'source'
,
targets
:
[
{
moduleId
:
'3n49vn'
,
key
:
'history'
}
]
}
],
position
:
{
x
:
1388.8842960266352
,
y
:
854.1553026226809
},
moduleId
:
'hh6of9'
},
{
logo
:
'/imgs/module/AI.png'
,
name
:
'AI 对话'
,
intro
:
'OpenAI GPT 大模型对话。'
,
flowType
:
'chatNode'
,
type
:
'http'
,
url
:
'/openapi/modules/chat/gpt'
,
inputs
:
[
{
key
:
'model'
,
type
:
'select'
,
label
:
'对话模型'
,
value
:
'gpt-3.5-turbo-16k'
,
list
:
[
{
label
:
'Gpt35-16k'
,
value
:
'gpt-3.5-turbo-16k'
},
{
label
:
'Gpt35-4k'
,
value
:
'gpt-3.5-turbo'
},
{
label
:
'Gpt4-8k'
,
value
:
'gpt-4'
}
],
connected
:
false
},
{
key
:
'temperature'
,
type
:
'slider'
,
label
:
'温度'
,
value
:
0
,
min
:
0
,
max
:
10
,
step
:
1
,
markList
:
[
{
label
:
'严谨'
,
value
:
0
},
{
label
:
'发散'
,
value
:
10
}
],
connected
:
false
},
{
key
:
'maxToken'
,
type
:
'slider'
,
label
:
'回复上限'
,
value
:
3000
,
min
:
0
,
max
:
4000
,
step
:
50
,
markList
:
[
{
label
:
'0'
,
value
:
0
},
{
label
:
'4000'
,
value
:
4000
}
],
connected
:
false
},
{
key
:
'systemPrompt'
,
type
:
'textarea'
,
label
:
'系统提示词'
,
description
:
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。'
,
placeholder
:
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。'
,
value
:
'知识库是关于 Laf 的介绍,根据知识库内容回答问题。'
,
connected
:
false
},
{
key
:
'limitPrompt'
,
type
:
'textarea'
,
label
:
'限定词'
,
description
:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"'
,
placeholder
:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"'
,
value
:
''
,
connected
:
false
},
{
key
:
'switch'
,
type
:
'target'
,
label
:
'触发器'
,
connected
:
false
},
{
key
:
'quotePrompt'
,
type
:
'target'
,
label
:
'引用内容'
,
connected
:
true
},
{
key
:
'history'
,
type
:
'target'
,
label
:
'聊天记录'
,
connected
:
true
},
{
key
:
'userChatInput'
,
type
:
'target'
,
label
:
'用户问题'
,
connected
:
true
}
],
outputs
:
[
{
key
:
'answerText'
,
label
:
'模型回复'
,
description
:
'直接响应,无需配置'
,
type
:
'hidden'
,
targets
:
[]
}
],
position
:
{
x
:
1827.0428559231655
,
y
:
446.8058354748067
},
moduleId
:
'3n49vn'
},
{
logo
:
'/imgs/module/db.png'
,
name
:
'知识库搜索'
,
intro
:
'去知识库中搜索对应的答案。可作为 AI 对话引用参考。'
,
flowType
:
'kbSearchNode'
,
type
:
'http'
,
url
:
'/openapi/modules/kb/search'
,
inputs
:
[
{
key
:
'kb_ids'
,
type
:
'custom'
,
label
:
'关联的知识库'
,
value
:
[],
list
:
[],
connected
:
false
},
{
key
:
'similarity'
,
type
:
'slider'
,
label
:
'相似度'
,
value
:
0.8
,
min
:
0
,
max
:
1
,
step
:
0.01
,
markList
:
[
{
label
:
'0'
,
value
:
0
},
{
label
:
'1'
,
value
:
1
}
],
connected
:
false
},
{
key
:
'limit'
,
type
:
'slider'
,
label
:
'单次搜索上限'
,
value
:
5
,
min
:
1
,
max
:
20
,
step
:
1
,
markList
:
[
{
label
:
'1'
,
value
:
1
},
{
label
:
'20'
,
value
:
20
}
],
connected
:
false
},
{
key
:
'switch'
,
type
:
'target'
,
label
:
'触发器'
,
connected
:
true
},
{
key
:
'userChatInput'
,
type
:
'target'
,
label
:
'用户问题'
,
connected
:
true
}
],
outputs
:
[
{
key
:
'rawSearch'
,
label
:
'源搜索数据'
,
type
:
'hidden'
,
response
:
true
,
targets
:
[]
},
{
key
:
'isEmpty'
,
label
:
'搜索结果为空'
,
type
:
'source'
,
targets
:
[
{
moduleId
:
'gbnzif'
,
key
:
'switch'
}
]
},
{
key
:
'quotePrompt'
,
label
:
'引用内容'
,
description
:
'搜索结果为空时不返回'
,
type
:
'source'
,
targets
:
[
{
moduleId
:
'3n49vn'
,
key
:
'quotePrompt'
}
]
}
],
position
:
{
x
:
850.3203039824494
,
y
:
919.7043887997417
},
moduleId
:
'zid0fj'
},
{
logo
:
'/imgs/module/reply.png'
,
name
:
'指定回复'
,
intro
:
'该模块可以直接回复一段指定的内容。常用于引导、提示。'
,
type
:
'answer'
,
flowType
:
'answerNode'
,
inputs
:
[
{
key
:
'switch'
,
type
:
'target'
,
label
:
'触发器'
,
connected
:
true
},
{
key
:
'answerText'
,
value
:
'对不起,我找不到你的问题。'
,
type
:
'input'
,
label
:
'回复的内容'
,
connected
:
false
}
],
outputs
:
[],
position
:
{
x
:
1392.0649222586217
,
y
:
553.0130337399224
},
moduleId
:
'gbnzif'
},
{
logo
:
'/imgs/module/cq.png'
,
name
:
'意图识别'
,
intro
:
'可以判断用户问题属于哪方面问题,从而执行不同的操作。'
,
type
:
'http'
,
url
:
'/openapi/modules/agent/classifyQuestion'
,
flowType
:
'classifyQuestionNode'
,
inputs
:
[
{
key
:
'systemPrompt'
,
type
:
'textarea'
,
label
:
'系统提示词'
,
description
:
'你可以添加一些特定内容的介绍,从而更好的识别用户的问题类型。这个内容通常是给模型介绍一个它不知道的内容。'
,
placeholder
:
'例如: \n1. Laf 是一个云函数开发平台……\n2. Sealos 是一个集群操作系统'
,
value
:
'Laf 一个云函数开发平台,提供了基于 Node 的 serveless 的快速开发和部署。是一个集「函数计算」、「数据库」、「对象存储」等于一身的一站式开发平台。支持云函数、云数据库、在线编程 IDE、触发器、云存储和静态网站托管等功能。'
,
connected
:
false
},
{
key
:
'history'
,
type
:
'target'
,
label
:
'聊天记录'
,
connected
:
false
},
{
key
:
'userChatInput'
,
type
:
'target'
,
label
:
'用户问题'
,
connected
:
true
},
{
key
:
'agents'
,
type
:
'custom'
,
label
:
''
,
value
:
[
{
value
:
'打招呼、问候、身份询问等问题'
,
key
:
'a'
},
{
value
:
'商务类、联系方式问题'
,
key
:
'b'
},
{
value
:
'其他问题'
,
key
:
'ek3f'
},
{
value
:
'关于 Laf 云函数问题'
,
key
:
'psau'
}
],
connected
:
false
}
],
outputs
:
[
{
key
:
'a'
,
label
:
''
,
type
:
'hidden'
,
targets
:
[
{
moduleId
:
'6jnrp5'
,
key
:
'switch'
}
]
},
{
key
:
'b'
,
label
:
''
,
type
:
'hidden'
,
targets
:
[
{
moduleId
:
'g13ipe'
,
key
:
'switch'
}
]
},
{
key
:
'ek3f'
,
label
:
''
,
type
:
'hidden'
,
targets
:
[
{
moduleId
:
'gbnzif'
,
key
:
'switch'
}
]
},
{
key
:
'psau'
,
label
:
''
,
type
:
'hidden'
,
targets
:
[
{
moduleId
:
'zid0fj'
,
key
:
'switch'
}
]
}
],
position
:
{
x
:
366.0894497581114
,
y
:
250.81741383805945
},
moduleId
:
'gm15of'
},
{
logo
:
'/imgs/module/reply.png'
,
name
:
'指定回复'
,
intro
:
'该模块可以直接回复一段指定的内容。常用于引导、提示。'
,
type
:
'answer'
,
flowType
:
'answerNode'
,
inputs
:
[
{
key
:
'switch'
,
type
:
'target'
,
label
:
'触发器'
,
connected
:
true
},
{
key
:
'answerText'
,
value
:
'你好,我是 Laf 助手,可以回答你 Laf 相关问题。'
,
type
:
'input'
,
label
:
'回复的内容'
,
connected
:
false
}
],
outputs
:
[],
position
:
{
x
:
855.9439119466947
,
y
:
15.463108315267931
},
moduleId
:
'6jnrp5'
},
{
logo
:
'/imgs/module/reply.png'
,
name
:
'指定回复'
,
intro
:
'该模块可以直接回复一段指定的内容。常用于引导、提示。'
,
type
:
'answer'
,
flowType
:
'answerNode'
,
inputs
:
[
{
key
:
'switch'
,
type
:
'target'
,
label
:
'触发器'
,
connected
:
true
},
{
key
:
'answerText'
,
value
:
'联系方式:xxxxx'
,
type
:
'input'
,
label
:
'回复的内容'
,
connected
:
false
}
],
outputs
:
[],
position
:
{
x
:
854.0492662385566
,
y
:
320.5010673254856
},
moduleId
:
'g13ipe'
}
]
}
];
// export const classifyQuestionDemo: AppItemType = {
// id: 'classifyQuestionDemo',
...
...
client/src/pages/api/openapi/modules/kb/search.ts
View file @
aef42cef
...
...
@@ -72,6 +72,12 @@ export async function kbSearch({
maxToken
=
2500
,
userChatInput
}:
Props
):
Promise
<
Response
>
{
if
(
kb_ids
.
length
===
0
)
return
{
isEmpty
:
true
,
rawSearch
:
[],
quotePrompt
:
undefined
};
// get vector
const
promptVector
=
await
openaiEmbedding_system
({
input
:
[
userChatInput
]
...
...
client/src/pages/app/detail/components/Settings.tsx
View file @
aef42cef
...
...
@@ -238,7 +238,7 @@ const Settings = ({ modelId }: { modelId: string }) => {
router
.
prefetch
(
'/chat'
);
await
saveUpdateModel
();
}
catch
(
error
)
{}
router
.
push
(
`/chat?
model
Id=${modelId}`
);
router
.
push
(
`/chat?
app
Id=${modelId}`
);
}
}
>
对话
...
...
client/src/pages/app/detail/components/edit/components/TemplateList.tsx
View file @
aef42cef
...
...
@@ -31,8 +31,8 @@ const ModuleStoreList = ({
position=
{
'fixed'
}
top=
{
0
}
left=
{
0
}
right=
{
0
}
bottom=
{
0
}
w=
{
'360px'
}
></
Box
>
<
Flex
zIndex=
{
3
}
...
...
@@ -66,7 +66,7 @@ const ModuleStoreList = ({
borderRadius=
{
'md'
}
draggable
onDragEnd=
{
(
e
)
=>
{
// if (e.clientX < 40
0) return;
if
(
e
.
clientX
<
36
0
)
return
;
onAddNode
({
template
:
item
,
position
:
{
x
:
e
.
clientX
,
y
:
e
.
clientY
}
...
...
client/src/pages/app/detail/components/edit/index.tsx
View file @
aef42cef
...
...
@@ -187,6 +187,7 @@ const AppEdit = ({ app, onBack }: Props) => {
targets
:
[]
as
FlowOutputTargetItemType
[]
}))
}));
console
.
log
(
modules
);
// update inputs and outputs
modules
.
forEach
((
module
)
=>
{
...
...
client/src/pages/app/detail/index.tsx
View file @
aef42cef
...
...
@@ -83,7 +83,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
<
Box
display=
{
[
'block'
,
'flex'
]
}
h=
{
'100%'
}
>
{
/* pc tab */
}
<
Box
display=
{
[
'none'
,
'block'
]
}
p=
{
4
}
w=
{
'200px'
}
borderRight=
{
theme
.
borders
.
base
}
>
<
Flex
mb=
{
4
}
>
<
Flex
mb=
{
4
}
alignItems=
{
'center'
}
>
<
Avatar
src=
{
appDetail
.
avatar
}
w=
{
'34px'
}
borderRadius=
{
'lg'
}
/>
<
Box
ml=
{
2
}
fontWeight=
{
'bold'
}
>
{
appDetail
.
name
}
...
...
@@ -97,7 +97,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
activeId=
{
currentTab
}
onChange=
{
(
e
:
any
)
=>
{
if
(
e
===
'startChat'
)
{
router
.
push
(
`/chat?
model
Id=${appId}`
);
router
.
push
(
`/chat?
app
Id=${appId}`
);
}
else
{
setCurrentTab
(
e
);
}
...
...
@@ -118,7 +118,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
activeId=
{
currentTab
}
onChange=
{
(
e
:
any
)
=>
{
if
(
e
===
'startChat'
)
{
router
.
push
(
`/chat?
model
Id=${appId}`
);
router
.
push
(
`/chat?
app
Id=${appId}`
);
}
else
{
setCurrentTab
(
e
);
}
...
...
client/src/pages/app/list/component/CreateModal.tsx
View file @
aef42cef
...
...
@@ -21,47 +21,16 @@ import { getErrText } from '@/utils/tools';
import
{
useToast
}
from
'@/hooks/useToast'
;
import
{
postCreateApp
}
from
'@/api/app'
;
import
{
useRouter
}
from
'next/router'
;
import
{
chatAppDemo
}
from
'@/constants/app'
;
import
{
appTemplates
}
from
'@/constants/app'
;
import
Avatar
from
'@/components/Avatar'
;
import
MyIcon
from
'@/components/Icon'
;
type
FormType
=
{
avatar
:
string
;
name
:
string
;
templateId
:
number
;
templateId
:
string
;
};
const
templates
=
[
{
id
:
0
,
icon
:
'settings'
,
name
:
'简单的对话'
,
intro
:
'一个极其简单的 AI 对话应用'
,
modules
:
chatAppDemo
.
modules
},
{
id
:
1
,
icon
:
'settings'
,
name
:
'基础知识库'
,
intro
:
'每次提问时进行一次知识库搜索,将搜索结果注入 LLM 模型进行参考回答'
,
modules
:
chatAppDemo
.
modules
},
{
id
:
2
,
icon
:
'settings'
,
name
:
'问答前引导'
,
intro
:
'可以在每次对话开始前提示用户填写一些内容,作为本次对话的永久内容'
,
modules
:
chatAppDemo
.
modules
},
{
id
:
3
,
icon
:
'settings'
,
name
:
'意图识别 + 知识库'
,
intro
:
'先对用户的问题进行分类,再根据不同类型问题,执行不同的操作'
,
modules
:
chatAppDemo
.
modules
}
];
const
CreateModal
=
({
onClose
,
onSuccess
}:
{
onClose
:
()
=>
void
;
onSuccess
:
()
=>
void
})
=>
{
const
[
refresh
,
setRefresh
]
=
useState
(
false
);
const
[
creating
,
setCreating
]
=
useState
(
false
);
...
...
@@ -72,7 +41,7 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
defaultValues
:
{
avatar
:
'/icon/logo.png'
,
name
:
''
,
templateId
:
0
templateId
:
appTemplates
[
0
].
id
}
});
...
...
@@ -110,7 +79,7 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
const
id
=
await
postCreateApp
({
avatar
:
data
.
avatar
,
name
:
data
.
name
,
modules
:
t
emplates
.
find
((
item
)
=>
item
.
id
===
data
.
templateId
)?.
modules
||
[]
modules
:
appT
emplates
.
find
((
item
)
=>
item
.
id
===
data
.
templateId
)?.
modules
||
[]
});
toast
({
title
:
'创建成功'
,
...
...
@@ -163,7 +132,7 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
gridTemplateColumns=
{
[
'repeat(1,1fr)'
,
'repeat(2,1fr)'
]
}
gridGap=
{
4
}
>
{
t
emplates
.
map
((
item
)
=>
(
{
appT
emplates
.
map
((
item
)
=>
(
<
Card
key=
{
item
.
id
}
border=
{
theme
.
borders
.
base
}
...
...
@@ -186,7 +155,7 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
}
}
>
<
Flex
alignItems=
{
'center'
}
>
<
MyIcon
name=
{
'apikey'
}
w=
{
'16
px'
}
/>
<
Avatar
src=
{
item
.
avatar
}
borderRadius=
{
'md'
}
w=
{
'20
px'
}
/>
<
Box
ml=
{
3
}
fontWeight=
{
'bold'
}
>
{
item
.
name
}
</
Box
>
...
...
client/src/pages/app/list/index.tsx
View file @
aef42cef
...
...
@@ -89,22 +89,27 @@ const MyApps = () => {
border=
{
theme
.
borders
.
md
}
boxShadow=
{
'none'
}
userSelect=
{
'none'
}
position=
{
'relative'
}
_hover=
{
{
boxShadow
:
'1px 1px 10px rgba(0,0,0,0.2)'
,
borderColor
:
'transparent'
,
'& .delete'
:
{
display
:
'block'
},
'& .chat'
:
{
display
:
'block'
}
}
}
onClick=
{
()
=>
router
.
push
(
`/app/detail?appId=${app._id}`
)
}
>
<
Flex
alignItems=
{
'center'
}
h=
{
'38px'
}
position=
{
'relative'
}
>
<
Flex
alignItems=
{
'center'
}
h=
{
'38px'
}
>
<
Avatar
src=
{
app
.
avatar
}
borderRadius=
{
'md'
}
w=
{
'28px'
}
/>
<
Box
ml=
{
3
}
>
{
app
.
name
}
</
Box
>
<
IconButton
className=
"delete"
position=
{
'absolute'
}
right=
{
0
}
top=
{
4
}
right=
{
4
}
size=
{
'sm'
}
icon=
{
<
MyIcon
name=
{
'delete'
}
w=
{
'14px'
}
/>
}
variant=
{
'base'
}
...
...
@@ -129,6 +134,25 @@ const MyApps = () => {
>
{
app
.
intro
||
'这个应用还没写介绍~'
}
</
Box
>
<
IconButton
className=
"chat"
position=
{
'absolute'
}
right=
{
4
}
bottom=
{
4
}
size=
{
'sm'
}
icon=
{
<
MyIcon
name=
{
'chatLight'
}
w=
{
'14px'
}
/>
}
variant=
{
'base'
}
borderRadius=
{
'md'
}
aria
-
label=
{
'delete'
}
display=
{
[
''
,
'none'
]
}
_hover=
{
{
bg
:
'myGray.100'
}
}
onClick=
{
(
e
)
=>
{
e
.
stopPropagation
();
router
.
push
(
`/chat?appId=${app._id}`
);
}
}
/>
</
Card
>
))
}
</
Grid
>
...
...
client/src/pages/appStore/components/list.tsx
View file @
aef42cef
...
...
@@ -77,7 +77,7 @@ const ShareModelList = ({
size=
{
'sm'
}
variant=
{
'base'
}
w=
{
[
'60px'
,
'70px'
]
}
onClick=
{
()
=>
router
.
push
(
`/chat?
model
Id=${model._id}`
)
}
onClick=
{
()
=>
router
.
push
(
`/chat?
app
Id=${model._id}`
)
}
>
体验
</
Button
>
...
...
client/src/pages/chat/components/History.tsx
View file @
aef42cef
...
...
@@ -125,7 +125,7 @@ const PcSliderBar = ({
w=
{
'100%'
}
h=
{
'100%'
}
leftIcon=
{
<
AddIcon
/>
}
onClick=
{
()
=>
router
.
replace
(
`/chat?
model
Id=${modelId}`
)
}
onClick=
{
()
=>
router
.
replace
(
`/chat?
app
Id=${modelId}`
)
}
>
新对话
</
Button
>
...
...
@@ -176,9 +176,9 @@ const PcSliderBar = ({
onClick=
{
()
=>
{
if
(
item
.
_id
===
chatId
)
return
;
if
(
isPc
)
{
router
.
replace
(
`/chat?
model
Id=${item.modelId}&chatId=${item._id}`
);
router
.
replace
(
`/chat?
app
Id=${item.modelId}&chatId=${item._id}`
);
}
else
{
router
.
push
(
`/chat?
model
Id=${item.modelId}&chatId=${item._id}`
);
router
.
push
(
`/chat?
app
Id=${item.modelId}&chatId=${item._id}`
);
}
}
}
onContextMenu=
{
(
e
)
=>
onclickContextMenu
(
e
,
item
)
}
...
...
@@ -251,7 +251,7 @@ const PcSliderBar = ({
try
{
await
onclickDelHistory
(
contextMenuData
.
history
.
_id
);
if
(
contextMenuData
.
history
.
_id
===
chatId
)
{
router
.
replace
(
`/chat?
model
Id=${modelId}`
);
router
.
replace
(
`/chat?
app
Id=${modelId}`
);
}
}
catch
(
error
)
{
console
.
log
(
error
);
...
...
client/src/pages/chat/components/ModelList.tsx
View file @
aef42cef
...
...
@@ -30,7 +30,7 @@ const ModelList = ({ models, modelId }: { models: AppListItemType[]; modelId: st
}
:
{})}
onClick=
{
()
=>
{
router
.
replace
(
`/chat?
model
Id=${item._id}`
);
router
.
replace
(
`/chat?
app
Id=${item._id}`
);
}
}
>
<
Avatar
src=
{
item
.
avatar
}
w=
{
'34px'
}
h=
{
'34px'
}
/>
...
...
client/src/pages/chat/components/PhoneSliderBar.tsx
View file @
aef42cef
...
...
@@ -95,7 +95,7 @@ const PhoneSliderBar = ({
color=
{
'white'
}
leftIcon=
{
<
AddIcon
/>
}
onClick=
{
()
=>
{
router
.
replace
(
`/chat?
model
Id=${modelId}`
);
router
.
replace
(
`/chat?
app
Id=${modelId}`
);
onClose
();
}
}
>
...
...
@@ -128,7 +128,7 @@ const PhoneSliderBar = ({
:
{})}
onClick=
{
async
()
=>
{
if
(
item
.
_id
===
modelId
)
return
;
router
.
replace
(
`/chat?
model
Id=${item._id}`
);
router
.
replace
(
`/chat?
app
Id=${item._id}`
);
onClose
();
}
}
>
...
...
@@ -159,7 +159,7 @@ const PhoneSliderBar = ({
:
{})}
onClick=
{
()
=>
{
if
(
item
.
_id
===
chatId
)
return
;
router
.
replace
(
`/chat?
model
Id=${item.modelId}&chatId=${item._id}`
);
router
.
replace
(
`/chat?
app
Id=${item.modelId}&chatId=${item._id}`
);
onClose
();
}
}
>
...
...
@@ -177,7 +177,7 @@ const PhoneSliderBar = ({
await
delChatHistoryById
(
item
.
_id
);
loadHistory
({
pageNum
:
1
,
init
:
true
});
if
(
item
.
_id
===
chatId
)
{
router
.
replace
(
`/chat?
model
Id=${modelId}`
);
router
.
replace
(
`/chat?
app
Id=${modelId}`
);
}
}
}
/>
...
...
client/src/pages/chat/index.tsx
View file @
aef42cef
...
...
@@ -65,7 +65,7 @@ const textareaMinH = '22px';
const
Chat
=
()
=>
{
const
router
=
useRouter
();
const
{
modelId
=
''
,
chatId
=
''
}
=
router
.
query
as
{
model
Id
:
string
;
chatId
:
string
};
const
{
appId
=
''
,
chatId
=
''
}
=
router
.
query
as
{
app
Id
:
string
;
chatId
:
string
};
const
theme
=
useTheme
();
const
ChatBox
=
useRef
<
HTMLDivElement
>
(
null
);
...
...
@@ -179,7 +179,7 @@ const Chat = () => {
data
:
{
messages
,
chatId
,
appId
:
modelId
,
appId
,
model
:
''
},
onMessage
:
(
text
:
string
)
=>
{
...
...
@@ -206,7 +206,7 @@ const Chat = () => {
// save chat
if
(
newChatId
)
{
setForbidLoadChatData
(
true
);
router
.
replace
(
`/chat?
modelId=
${
model
Id
}
&chatId=
${
newChatId
}
`
);
router
.
replace
(
`/chat?
appId=
${
app
Id
}
&chatId=
${
newChatId
}
`
);
}
abortSignal
.
signal
.
aborted
&&
(
await
delay
(
500
));
...
...
@@ -243,7 +243,7 @@ const Chat = () => {
},
[
chatId
,
model
Id
,
app
Id
,
setChatData
,
generatingMessage
,
setForbidLoadChatData
,
...
...
@@ -474,17 +474,17 @@ const Chat = () => {
// 获取对话信息
const loadChatInfo = useCallback(
async ({
model
Id,
app
Id,
chatId,
loading = false
}: {
model
Id: string;
app
Id: string;
chatId: string;
loading?: boolean;
}) => {
try {
loading && setIsLoading(true);
const res = await getInitChatSiteInfo(
model
Id, chatId);
const res = await getInitChatSiteInfo(
app
Id, chatId);
setChatData({
...res,
...
...
@@ -502,9 +502,9 @@ const Chat = () => {
}
// 空 modelId 请求, 重定向到新的 model 聊天
if (res.modelId !==
model
Id) {
if (res.modelId !==
app
Id) {
setForbidLoadChatData(true);
router.replace(`/chat?
model
Id=${res.modelId}`);
router.replace(`/chat?
app
Id=${res.modelId}`);
}
} catch (e: any) {
// reset all chat tore
...
...
@@ -529,15 +529,15 @@ const Chat = () => {
]
);
// 初始化聊天框
useQuery(['init',
model
Id, chatId], () => {
useQuery(['init',
app
Id, chatId], () => {
// pc: redirect to latest model chat
if (!
model
Id && lastChatModelId) {
router.replace(`/chat?
model
Id=${lastChatModelId}&chatId=${lastChatId}`);
if (!
app
Id && lastChatModelId) {
router.replace(`/chat?
app
Id=${lastChatModelId}&chatId=${lastChatId}`);
return null;
}
// store id
modelId && setLastChatModelId(model
Id);
appId && setLastChatModelId(app
Id);
setLastChatId(chatId);
if (forbidLoadChatData) {
...
...
@@ -546,7 +546,7 @@ const Chat = () => {
}
return loadChatInfo({
model
Id,
app
Id,
chatId,
loading: true
});
...
...
@@ -559,7 +559,7 @@ const Chat = () => {
isLeavePage.current = true;
controller.current?.abort();
};
}, [
model
Id, chatId]);
}, [
app
Id, chatId]);
// context menu component
const RenderContextMenu = useCallback(
...
...
@@ -611,14 +611,14 @@ const Chat = () => {
backgroundColor={useColorModeValue('#fdfdfd', '')}
>
{/* pc always show history. */}
{(isPc || !
model
Id) && (
{(isPc || !
app
Id) && (
<SideBar>
<History onclickDelHistory={onclickDelHistory} onclickExportChat={onclickExportChat} />
</SideBar>
)}
{/* 聊天内容 */}
{
model
Id && (
{
app
Id && (
<Flex
position={'relative'}
h={[0, '100%']}
...
...
@@ -667,15 +667,13 @@ const Chat = () => {
/>
</MenuButton>
<MenuList minW={`90px !important`}>
<MenuItem onClick={() => router.replace(`/chat?modelId=${modelId}`)}>
新对话
</MenuItem>
<MenuItem onClick={() => router.replace(`/chat?appId=${appId}`)}>新对话</MenuItem>
<MenuItem
onClick={async () => {
try {
setIsLoading(true);
await onclickDelHistory(chatData.chatId);
router.replace(`/chat?
modelId=${model
Id}`);
router.replace(`/chat?
appId=${app
Id}`);
} catch (err) {
console.log(err);
}
...
...
@@ -901,7 +899,7 @@ const Chat = () => {
<Drawer isOpen={isOpenSlider} placement="
left
"
size
=
{
'xs'
}
onClose
=
{
onCloseSlider
}
>
<
DrawerOverlay
backgroundColor=
{
'rgba(255,255,255,0.5)'
}
/>
<
DrawerContent
maxW=
{
'70%'
}
>
<
PhoneSliderBar
chatId=
{
chatId
}
modelId=
{
model
Id
}
onClose=
{
onCloseSlider
}
/>
<
PhoneSliderBar
chatId=
{
chatId
}
modelId=
{
app
Id
}
onClose=
{
onCloseSlider
}
/>
</
DrawerContent
>
<
/
Drawer
>
)}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment