diff --git a/client/src/components/Icon/icons/light/chat.svg b/client/src/components/Icon/icons/light/chat.svg
new file mode 100644
index 000000000..c2561573d
--- /dev/null
+++ b/client/src/components/Icon/icons/light/chat.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/client/src/components/Icon/index.tsx b/client/src/components/Icon/index.tsx
index 62d4b9c40..895fe49cf 100644
--- a/client/src/components/Icon/index.tsx
+++ b/client/src/components/Icon/index.tsx
@@ -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;
diff --git a/client/src/components/Layout/navbar.tsx b/client/src/components/Layout/navbar.tsx
index f36764e4f..f22d1f24a 100644
--- a/client/src/components/Layout/navbar.tsx
+++ b/client/src/components/Layout/navbar.tsx
@@ -23,7 +23,7 @@ const Navbar = ({ unread }: { unread: number }) => {
{
label: '聊天',
icon: 'chat',
- link: `/chat?modelId=${lastChatModelId}&chatId=${lastChatId}`,
+ link: `/chat?appId=${lastChatModelId}&chatId=${lastChatId}`,
activeLink: ['/chat']
},
{
diff --git a/client/src/components/Layout/navbarPhone.tsx b/client/src/components/Layout/navbarPhone.tsx
index 33b3c2076..fd41f3179 100644
--- a/client/src/components/Layout/navbarPhone.tsx
+++ b/client/src/components/Layout/navbarPhone.tsx
@@ -13,7 +13,7 @@ const NavbarPhone = ({ unread }: { unread: number }) => {
{
label: '聊天',
icon: 'tabbarChat',
- link: `/chat?modelId=${lastChatModelId}&chatId=${lastChatId}`,
+ link: `/chat?appId=${lastChatModelId}&chatId=${lastChatId}`,
activeLink: ['/chat'],
unread: 0
},
diff --git a/client/src/constants/app.ts b/client/src/constants/app.ts
index aed39f7bf..e37e60ffb 100644
--- a/client/src/constants/app.ts
+++ b/client/src/constants/app.ts
@@ -1,6 +1,4 @@
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,
- inputs: [
- {
- key: SystemInputEnum.switch,
- type: FlowInputItemTypeEnum.target,
- label: '触发器',
- connected: true
- },
- {
- key: SpecificInputEnum.answerText,
- value: '',
- type: FlowInputItemTypeEnum.input,
- 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,
- url: '/openapi/modules/chat/gpt',
- inputs: [
+// template
+export const appTemplates: (AppItemType & { avatar: string; intro: string })[] = [
+ {
+ id: 'simpleChat',
+ avatar: '/imgs/module/AI.png',
+ name: '简单的对话',
+ intro: '一个极其简单的 AI 对话应用',
+ modules: [
{
- key: 'model',
- type: FlowInputItemTypeEnum.select,
- label: '对话模型',
- value: chatModelList[0].value,
- list: chatModelList
+ 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'
},
{
- key: 'temperature',
- type: FlowInputItemTypeEnum.slider,
- label: '温度',
- value: 0,
- min: 0,
- max: 10,
- step: 1,
- markList: [
- { label: '严谨', value: 0 },
- { label: '发散', value: 10 }
- ]
+ 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'
},
{
- key: 'maxToken',
- type: FlowInputItemTypeEnum.slider,
- label: '回复上限',
- value: 3000,
- min: 0,
- max: 4000,
- step: 50,
- markList: [
- { label: '0', value: 0 },
- { label: '4000', value: 4000 }
- ]
- },
- {
- key: 'systemPrompt',
- type: FlowInputItemTypeEnum.textarea,
- label: '系统提示词',
- description:
- '模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。',
- placeholder:
- '模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。',
- value: systemPrompt
- },
- {
- key: 'limitPrompt',
- type: FlowInputItemTypeEnum.textarea,
- label: '限定词',
- description:
- '限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
- placeholder:
- '限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
- value: limitPrompt
- },
- {
- key: SystemInputEnum.switch,
- type: FlowInputItemTypeEnum.target,
- label: '触发器',
- connected: true
- },
- {
- key: 'quotePrompt',
- type: FlowInputItemTypeEnum.target,
- label: '引用内容(字符串)',
- connected: true
- },
- {
- key: SystemInputEnum.history,
- type: FlowInputItemTypeEnum.numberInput,
- label: '最长上下文',
- description: '为 0 时,代表不需要上下文。',
- value: history,
- min: 0,
- max: 50
- },
- {
- key: SystemInputEnum.userChatInput,
- type: FlowInputItemTypeEnum.none,
- label: '用户输入(系统自动填写)',
- description: ''
- }
- ],
- outputs: [
- {
- key: 'answer',
- label: '模型回复',
- type: FlowOutputItemTypeEnum.answer,
- targets: []
+ 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: '',
+ 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: 'userChatInput',
+ type: 'target',
+ label: '用户问题',
+ connected: true
+ }
+ ],
+ outputs: [
+ {
+ key: 'answerText',
+ label: '模型回复',
+ description: '直接响应,无需配置',
+ type: 'hidden',
+ targets: []
+ }
+ ],
+ position: {
+ x: 965.5863241865428,
+ y: -29.569293606933797
+ },
+ moduleId: '3n49vn'
}
]
- };
-};
-
-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 }
-// }
-// ]
-// };
+ },
+ {
+ 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: '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: 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: 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'
+ }
+ ]
+ },
+ {
+ 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',
diff --git a/client/src/pages/api/openapi/modules/kb/search.ts b/client/src/pages/api/openapi/modules/kb/search.ts
index 21ed9cf5f..f9c934a7e 100644
--- a/client/src/pages/api/openapi/modules/kb/search.ts
+++ b/client/src/pages/api/openapi/modules/kb/search.ts
@@ -72,6 +72,12 @@ export async function kbSearch({
maxToken = 2500,
userChatInput
}: Props): Promise {
+ if (kb_ids.length === 0)
+ return {
+ isEmpty: true,
+ rawSearch: [],
+ quotePrompt: undefined
+ };
// get vector
const promptVector = await openaiEmbedding_system({
input: [userChatInput]
diff --git a/client/src/pages/app/detail/components/Settings.tsx b/client/src/pages/app/detail/components/Settings.tsx
index 2f7ba4969..e3c0ba1f1 100644
--- a/client/src/pages/app/detail/components/Settings.tsx
+++ b/client/src/pages/app/detail/components/Settings.tsx
@@ -238,7 +238,7 @@ const Settings = ({ modelId }: { modelId: string }) => {
router.prefetch('/chat');
await saveUpdateModel();
} catch (error) {}
- router.push(`/chat?modelId=${modelId}`);
+ router.push(`/chat?appId=${modelId}`);
}}
>
对话
diff --git a/client/src/pages/app/detail/components/edit/components/TemplateList.tsx b/client/src/pages/app/detail/components/edit/components/TemplateList.tsx
index 11dd2ce8b..fb817ad70 100644
--- a/client/src/pages/app/detail/components/edit/components/TemplateList.tsx
+++ b/client/src/pages/app/detail/components/edit/components/TemplateList.tsx
@@ -31,8 +31,8 @@ const ModuleStoreList = ({
position={'fixed'}
top={0}
left={0}
- right={0}
bottom={0}
+ w={'360px'}
>
{
- // if (e.clientX < 400) return;
+ if (e.clientX < 360) return;
onAddNode({
template: item,
position: { x: e.clientX, y: e.clientY }
diff --git a/client/src/pages/app/detail/components/edit/index.tsx b/client/src/pages/app/detail/components/edit/index.tsx
index b8a1b2897..1c62b1ca4 100644
--- a/client/src/pages/app/detail/components/edit/index.tsx
+++ b/client/src/pages/app/detail/components/edit/index.tsx
@@ -187,6 +187,7 @@ const AppEdit = ({ app, onBack }: Props) => {
targets: [] as FlowOutputTargetItemType[]
}))
}));
+ console.log(modules);
// update inputs and outputs
modules.forEach((module) => {
diff --git a/client/src/pages/app/detail/index.tsx b/client/src/pages/app/detail/index.tsx
index 5009a78e5..4142c1f37 100644
--- a/client/src/pages/app/detail/index.tsx
+++ b/client/src/pages/app/detail/index.tsx
@@ -83,7 +83,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
{/* pc tab */}
-
+
{appDetail.name}
@@ -97,7 +97,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
activeId={currentTab}
onChange={(e: any) => {
if (e === 'startChat') {
- router.push(`/chat?modelId=${appId}`);
+ router.push(`/chat?appId=${appId}`);
} else {
setCurrentTab(e);
}
@@ -118,7 +118,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
activeId={currentTab}
onChange={(e: any) => {
if (e === 'startChat') {
- router.push(`/chat?modelId=${appId}`);
+ router.push(`/chat?appId=${appId}`);
} else {
setCurrentTab(e);
}
diff --git a/client/src/pages/app/list/component/CreateModal.tsx b/client/src/pages/app/list/component/CreateModal.tsx
index 3570569d5..11402ebd4 100644
--- a/client/src/pages/app/list/component/CreateModal.tsx
+++ b/client/src/pages/app/list/component/CreateModal.tsx
@@ -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: templates.find((item) => item.id === data.templateId)?.modules || []
+ modules: appTemplates.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}
>
- {templates.map((item) => (
+ {appTemplates.map((item) => (
void; onSuccess: (
}}
>
-
+
{item.name}
diff --git a/client/src/pages/app/list/index.tsx b/client/src/pages/app/list/index.tsx
index 69964c6e7..ef637352c 100644
--- a/client/src/pages/app/list/index.tsx
+++ b/client/src/pages/app/list/index.tsx
@@ -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}`)}
>
-
+
{app.name}
}
variant={'base'}
@@ -129,6 +134,25 @@ const MyApps = () => {
>
{app.intro || '这个应用还没写介绍~'}
+ }
+ variant={'base'}
+ borderRadius={'md'}
+ aria-label={'delete'}
+ display={['', 'none']}
+ _hover={{
+ bg: 'myGray.100'
+ }}
+ onClick={(e) => {
+ e.stopPropagation();
+ router.push(`/chat?appId=${app._id}`);
+ }}
+ />
))}
diff --git a/client/src/pages/appStore/components/list.tsx b/client/src/pages/appStore/components/list.tsx
index ee42104bc..4aaca49a8 100644
--- a/client/src/pages/appStore/components/list.tsx
+++ b/client/src/pages/appStore/components/list.tsx
@@ -77,7 +77,7 @@ const ShareModelList = ({
size={'sm'}
variant={'base'}
w={['60px', '70px']}
- onClick={() => router.push(`/chat?modelId=${model._id}`)}
+ onClick={() => router.push(`/chat?appId=${model._id}`)}
>
体验
diff --git a/client/src/pages/chat/components/History.tsx b/client/src/pages/chat/components/History.tsx
index 1b38eb3a0..a95011b65 100644
--- a/client/src/pages/chat/components/History.tsx
+++ b/client/src/pages/chat/components/History.tsx
@@ -125,7 +125,7 @@ const PcSliderBar = ({
w={'100%'}
h={'100%'}
leftIcon={}
- onClick={() => router.replace(`/chat?modelId=${modelId}`)}
+ onClick={() => router.replace(`/chat?appId=${modelId}`)}
>
新对话
@@ -176,9 +176,9 @@ const PcSliderBar = ({
onClick={() => {
if (item._id === chatId) return;
if (isPc) {
- router.replace(`/chat?modelId=${item.modelId}&chatId=${item._id}`);
+ router.replace(`/chat?appId=${item.modelId}&chatId=${item._id}`);
} else {
- router.push(`/chat?modelId=${item.modelId}&chatId=${item._id}`);
+ router.push(`/chat?appId=${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?modelId=${modelId}`);
+ router.replace(`/chat?appId=${modelId}`);
}
} catch (error) {
console.log(error);
diff --git a/client/src/pages/chat/components/ModelList.tsx b/client/src/pages/chat/components/ModelList.tsx
index 43c1a8195..6ec39ed18 100644
--- a/client/src/pages/chat/components/ModelList.tsx
+++ b/client/src/pages/chat/components/ModelList.tsx
@@ -30,7 +30,7 @@ const ModelList = ({ models, modelId }: { models: AppListItemType[]; modelId: st
}
: {})}
onClick={() => {
- router.replace(`/chat?modelId=${item._id}`);
+ router.replace(`/chat?appId=${item._id}`);
}}
>
diff --git a/client/src/pages/chat/components/PhoneSliderBar.tsx b/client/src/pages/chat/components/PhoneSliderBar.tsx
index a73c5bf64..cdec7b912 100644
--- a/client/src/pages/chat/components/PhoneSliderBar.tsx
+++ b/client/src/pages/chat/components/PhoneSliderBar.tsx
@@ -95,7 +95,7 @@ const PhoneSliderBar = ({
color={'white'}
leftIcon={}
onClick={() => {
- router.replace(`/chat?modelId=${modelId}`);
+ router.replace(`/chat?appId=${modelId}`);
onClose();
}}
>
@@ -128,7 +128,7 @@ const PhoneSliderBar = ({
: {})}
onClick={async () => {
if (item._id === modelId) return;
- router.replace(`/chat?modelId=${item._id}`);
+ router.replace(`/chat?appId=${item._id}`);
onClose();
}}
>
@@ -159,7 +159,7 @@ const PhoneSliderBar = ({
: {})}
onClick={() => {
if (item._id === chatId) return;
- router.replace(`/chat?modelId=${item.modelId}&chatId=${item._id}`);
+ router.replace(`/chat?appId=${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?modelId=${modelId}`);
+ router.replace(`/chat?appId=${modelId}`);
}
}}
/>
diff --git a/client/src/pages/chat/index.tsx b/client/src/pages/chat/index.tsx
index fea76bcb3..bc6982775 100644
--- a/client/src/pages/chat/index.tsx
+++ b/client/src/pages/chat/index.tsx
@@ -65,7 +65,7 @@ const textareaMinH = '22px';
const Chat = () => {
const router = useRouter();
- const { modelId = '', chatId = '' } = router.query as { modelId: string; chatId: string };
+ const { appId = '', chatId = '' } = router.query as { appId: string; chatId: string };
const theme = useTheme();
const ChatBox = useRef(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=${modelId}&chatId=${newChatId}`);
+ router.replace(`/chat?appId=${appId}&chatId=${newChatId}`);
}
abortSignal.signal.aborted && (await delay(500));
@@ -243,7 +243,7 @@ const Chat = () => {
},
[
chatId,
- modelId,
+ appId,
setChatData,
generatingMessage,
setForbidLoadChatData,
@@ -474,17 +474,17 @@ const Chat = () => {
// 获取对话信息
const loadChatInfo = useCallback(
async ({
- modelId,
+ appId,
chatId,
loading = false
}: {
- modelId: string;
+ appId: string;
chatId: string;
loading?: boolean;
}) => {
try {
loading && setIsLoading(true);
- const res = await getInitChatSiteInfo(modelId, chatId);
+ const res = await getInitChatSiteInfo(appId, chatId);
setChatData({
...res,
@@ -502,9 +502,9 @@ const Chat = () => {
}
// 空 modelId 请求, 重定向到新的 model 聊天
- if (res.modelId !== modelId) {
+ if (res.modelId !== appId) {
setForbidLoadChatData(true);
- router.replace(`/chat?modelId=${res.modelId}`);
+ router.replace(`/chat?appId=${res.modelId}`);
}
} catch (e: any) {
// reset all chat tore
@@ -529,15 +529,15 @@ const Chat = () => {
]
);
// 初始化聊天框
- useQuery(['init', modelId, chatId], () => {
+ useQuery(['init', appId, chatId], () => {
// pc: redirect to latest model chat
- if (!modelId && lastChatModelId) {
- router.replace(`/chat?modelId=${lastChatModelId}&chatId=${lastChatId}`);
+ if (!appId && lastChatModelId) {
+ router.replace(`/chat?appId=${lastChatModelId}&chatId=${lastChatId}`);
return null;
}
// store id
- modelId && setLastChatModelId(modelId);
+ appId && setLastChatModelId(appId);
setLastChatId(chatId);
if (forbidLoadChatData) {
@@ -546,7 +546,7 @@ const Chat = () => {
}
return loadChatInfo({
- modelId,
+ appId,
chatId,
loading: true
});
@@ -559,7 +559,7 @@ const Chat = () => {
isLeavePage.current = true;
controller.current?.abort();
};
- }, [modelId, chatId]);
+ }, [appId, chatId]);
// context menu component
const RenderContextMenu = useCallback(
@@ -611,14 +611,14 @@ const Chat = () => {
backgroundColor={useColorModeValue('#fdfdfd', '')}
>
{/* pc always show history. */}
- {(isPc || !modelId) && (
+ {(isPc || !appId) && (
)}
{/* 聊天内容 */}
- {modelId && (
+ {appId && (
{
/>
-
+