Commit ecce182a by archer

code

parent 509ca92f
...@@ -89,6 +89,8 @@ export const streamFetch = ({ ...@@ -89,6 +89,8 @@ export const streamFetch = ({
}); });
read(); read();
} catch (err: any) { } catch (err: any) {
console.log(111111111111111111);
if (err?.message === 'The user aborted a request.') { if (err?.message === 'The user aborted a request.') {
return resolve({ return resolve({
responseText, responseText,
...@@ -102,7 +104,7 @@ export const streamFetch = ({ ...@@ -102,7 +104,7 @@ export const streamFetch = ({
}; };
read(); read();
} catch (err: any) { } catch (err: any) {
console.log(err); console.log(err, 'fetch error');
reject(getErrText(err, '请求异常')); reject(getErrText(err, '请求异常'));
} }
......
...@@ -464,7 +464,7 @@ const ChatBox = ( ...@@ -464,7 +464,7 @@ const ChatBox = (
source={item.value} source={item.value}
isChatting={index === chatHistory.length - 1 && isChatting} isChatting={index === chatHistory.length - 1 && isChatting}
/> />
{(item[quoteLenKey] || item[rawSearchKey]?.length) && ( {(!!item[quoteLenKey] || !!item[rawSearchKey]?.length) && (
<Button <Button
size={'xs'} size={'xs'}
variant={'base'} variant={'base'}
......
<?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="1689602517026" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5455" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M727.008 487.232l194.016-184.32a99.2 99.2 0 0 0 0-140.288l-48.416-48.416a99.2 99.2 0 0 0-138.464-1.76L544.64 292.384l-184.064-196.64-1.504-1.568a64.832 64.832 0 0 0-91.712-0.384L129.184 231.968a64.8 64.8 0 0 0-1.12 90.144l181.344 193.728-171.456 162.88a99.264 99.264 0 0 0-28.256 49.28l-28.992 123.744a65.632 65.632 0 0 0 82.4 77.92l119.296-35.136a99.744 99.744 0 0 0 40.32-23.232l169.056-160.608 203.616 217.536 1.504 1.568a64.832 64.832 0 0 0 91.712 0.384l138.176-138.176a64.8 64.8 0 0 0 1.12-90.144l-200.896-214.624zM319.424 786.176l-90.112-90.112a31.488 31.488 0 0 0-9.792-6.496L667.104 264.352l94.272 94.272c1.408 1.408 3.168 2.08 4.768 3.168L319.424 786.176zM778.208 158.784a35.2 35.2 0 0 1 49.12 0.64l48.416 48.416c13.76 13.76 13.76 36.032-0.64 50.4l-64.448 61.216c-1.28-2.08-2.24-4.288-4.064-6.112l-93.12-93.12 64.736-61.44zM288.512 399.904c8-0.128 16-3.168 22.112-9.28l48-48a31.968 31.968 0 1 0-45.248-45.248l-48 48a31.68 31.68 0 0 0-8.928 20.256L174.816 278.4c-0.512-0.512-0.512-1.024-0.352-1.152L312.64 139.04c0.128-0.128 0.672-0.128 1.248 0.416l184.384 196.992-142.432 135.328-67.328-71.872zM145.024 868.288a1.6 1.6 0 0 1-2.016-1.92l28.992-123.744c0.992-4.16 2.944-7.968 5.312-11.488a31.808 31.808 0 0 0 6.752 10.144l88.288 88.288a35.072 35.072 0 0 1-8 3.552l-119.328 35.168z m598.336 16.672c-0.128 0.128-0.672 0.128-1.248-0.416l-125.6-134.176a31.232 31.232 0 0 0 14.08-7.712l48-48a31.968 31.968 0 1 0-45.248-45.248l-48 48a31.68 31.68 0 0 0-7.296 11.904l-39.904-42.656 142.432-135.328 200.576 214.304c0.48 0.512 0.48 1.024 0.352 1.152l-138.144 138.176z" p-id="5456"></path></svg>
\ No newline at end of file
...@@ -62,7 +62,8 @@ const map = { ...@@ -62,7 +62,8 @@ const map = {
csvImport: require('./icons/file/csv.svg').default, csvImport: require('./icons/file/csv.svg').default,
qaImport: require('./icons/file/qaImport.svg').default, qaImport: require('./icons/file/qaImport.svg').default,
uploadFile: require('./icons/file/uploadFile.svg').default, uploadFile: require('./icons/file/uploadFile.svg').default,
closeLight: require('./icons/light/close.svg').default closeLight: require('./icons/light/close.svg').default,
customTitle: require('./icons/light/customTitle.svg').default
}; };
export type IconName = keyof typeof map; export type IconName = keyof typeof map;
......
...@@ -21,7 +21,7 @@ const NavbarPhone = ({ unread }: { unread: number }) => { ...@@ -21,7 +21,7 @@ const NavbarPhone = ({ unread }: { unread: number }) => {
label: '应用', label: '应用',
icon: 'tabbarModel', icon: 'tabbarModel',
link: `/app/list`, link: `/app/list`,
activeLink: ['/app/list'], activeLink: ['/app/list', '/app/detail'],
unread: 0 unread: 0
}, },
{ {
...@@ -64,7 +64,7 @@ const NavbarPhone = ({ unread }: { unread: number }) => { ...@@ -64,7 +64,7 @@ const NavbarPhone = ({ unread }: { unread: number }) => {
pt={1} pt={1}
px={3} px={3}
transform={'scale(0.9)'} transform={'scale(0.9)'}
{...(item.activeLink.includes(router.asPath) {...(item.activeLink.includes(router.pathname)
? { ? {
color: '#7089f1' color: '#7089f1'
} }
...@@ -72,7 +72,7 @@ const NavbarPhone = ({ unread }: { unread: number }) => { ...@@ -72,7 +72,7 @@ const NavbarPhone = ({ unread }: { unread: number }) => {
color: 'myGray.500' color: 'myGray.500'
})} })}
_after={ _after={
item.activeLink.includes(router.asPath) item.activeLink.includes(router.pathname)
? { ? {
content: '""', content: '""',
position: 'absolute', position: 'absolute',
......
...@@ -23,1377 +23,3 @@ export enum VariableInputEnum { ...@@ -23,1377 +23,3 @@ export enum VariableInputEnum {
input = 'input', input = 'input',
select = 'select' select = 'select'
} }
// 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: '/app/modules/init/userChatInput',
inputs: [
{
key: 'userChatInput',
type: 'systemInput',
label: '用户问题',
connected: false
}
],
outputs: [
{
key: 'userChatInput',
label: '用户问题',
type: 'source',
targets: [
{
moduleId: '7pacf0',
key: 'userChatInput'
}
]
}
],
position: {
x: 477.9074315528994,
y: 1604.2106242223683
},
moduleId: '7z5g5h'
},
{
logo: '/imgs/module/AI.png',
name: 'AI 对话',
intro: 'AI 大模型对话',
flowType: 'chatNode',
type: 'http',
url: '/app/modules/chat/gpt',
inputs: [
{
key: 'model',
type: 'custom',
label: '对话模型',
value: 'gpt-3.5-turbo-16k',
list: [
{
label: 'FastAI-4k',
value: 'gpt-3.5-turbo'
},
{
label: 'FastAI-16k',
value: 'gpt-3.5-turbo-16k'
},
{
label: 'FastAI-Plus',
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: 8000,
min: 0,
max: 16000,
step: 50,
markList: [
{
label: '0',
value: 0
},
{
label: '16000',
value: 16000
}
],
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: 981.9682828103937,
y: 890.014595014464
},
moduleId: '7pacf0'
},
{
logo: '/imgs/module/history.png',
name: '聊天记录',
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
type: 'initInput',
flowType: 'historyNode',
url: '/app/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: '7pacf0',
key: 'history'
}
]
}
],
position: {
x: 452.5466249541586,
y: 1276.3930310334215
},
moduleId: 'xj0c9p'
}
]
},
{
id: 'simpleKbChat',
avatar: '/imgs/module/db.png',
name: '基础知识库',
intro: '每次提问时进行一次知识库搜索,将搜索结果注入 LLM 模型进行参考回答',
modules: [
{
logo: '/imgs/module/userChatInput.png',
name: '用户问题',
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
type: 'initInput',
flowType: 'questionInput',
url: '/app/modules/init/userChatInput',
inputs: [
{
key: 'userChatInput',
type: 'systemInput',
label: '用户问题',
connected: false
}
],
outputs: [
{
key: 'userChatInput',
label: '用户问题',
type: 'source',
targets: [
{
moduleId: 'q9v14m',
key: 'userChatInput'
},
{
moduleId: 'qbf8td',
key: 'userChatInput'
}
]
}
],
position: {
x: -210.24817109253843,
y: 665.7922967022607
},
moduleId: 'v0nc1s'
},
{
logo: '/imgs/module/history.png',
name: '聊天记录',
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
type: 'initInput',
flowType: 'historyNode',
url: '/app/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: 'qbf8td',
key: 'history'
}
]
}
],
position: {
x: 211.58250540918442,
y: 611.8700401034965
},
moduleId: 'k9y3jm'
},
{
logo: '/imgs/module/AI.png',
name: 'AI 对话',
intro: 'AI 大模型对话',
flowType: 'chatNode',
type: 'http',
url: '/app/modules/chat/gpt',
inputs: [
{
key: 'model',
type: 'custom',
label: '对话模型',
value: 'gpt-3.5-turbo-16k',
list: [
{
label: 'FastAI-4k',
value: 'gpt-3.5-turbo'
},
{
label: 'FastAI-16k',
value: 'gpt-3.5-turbo-16k'
},
{
label: 'FastAI-Plus',
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: 8000,
min: 0,
max: 16000,
step: 50,
markList: [
{
label: '0',
value: 0
},
{
label: '16000',
value: 16000
}
],
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: '知识库是关于 Laf 的内容,参考知识库回答我的问题。',
connected: false
},
{
key: 'switch',
type: 'target',
label: '触发器',
connected: true
},
{
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: 830.725790038998,
y: 201.0790739617387
},
moduleId: 'qbf8td'
},
{
logo: '/imgs/module/db.png',
name: '知识库搜索',
intro: '去知识库中搜索对应的答案。可作为 AI 对话引用参考。',
flowType: 'kbSearchNode',
type: 'http',
url: '/app/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: rawSearchKey,
label: '源搜索数据',
type: 'hidden',
response: true,
targets: []
},
{
key: 'isEmpty',
label: '搜索结果为空',
type: 'source',
targets: [
{
moduleId: 'w8av9y',
key: 'switch'
}
]
},
{
key: 'quotePrompt',
label: '引用内容',
description: '搜索结果为空时不返回',
type: 'source',
targets: [
{
moduleId: 'qbf8td',
key: 'quotePrompt'
}
]
}
],
position: {
x: 101.2612930583856,
y: -31.342317423453437
},
moduleId: 'q9v14m'
},
{
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: 827.8570503787319,
y: -63.837994077710675
},
moduleId: 'w8av9y'
}
]
},
{
id: 'chatGuide',
avatar: '/imgs/module/userGuide.png',
name: '对话前引导',
intro: '可以在每次对话开始前提示用户填写一些内容,作为本次对话的永久内容',
modules: [
{
logo: '/imgs/module/userChatInput.png',
name: '用户问题',
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
type: 'initInput',
flowType: 'questionInput',
url: '/app/modules/init/userChatInput',
inputs: [
{
key: 'userChatInput',
type: 'systemInput',
label: '用户问题',
connected: false
}
],
outputs: [
{
key: 'userChatInput',
label: '用户问题',
type: 'source',
targets: [
{
moduleId: '7pacf0',
key: 'userChatInput'
}
]
}
],
position: {
x: 485.8457451202796,
y: 1601.0352987954163
},
moduleId: '7z5g5h'
},
{
logo: '/imgs/module/AI.png',
name: 'AI 对话',
intro: 'OpenAI GPT 大模型对话。',
flowType: 'chatNode',
type: 'http',
url: '/app/modules/chat/gpt',
inputs: [
{
key: 'model',
type: 'custom',
label: '对话模型',
value: 'gpt-3.5-turbo-16k',
list: [
{
label: 'FastAI-4k',
value: 'gpt-3.5-turbo'
},
{
label: 'FastAI-16k',
value: 'gpt-3.5-turbo-16k'
},
{
label: 'FastAI-Plus',
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: 8000,
min: 0,
max: 16000,
step: 50,
markList: [
{
label: '0',
value: 0
},
{
label: '16000',
value: 16000
}
],
connected: false
},
{
key: 'systemPrompt',
type: 'textarea',
label: '系统提示词',
description:
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。',
placeholder:
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。',
value: '将我发送的任何内容,直接翻译成{{test}}',
connected: false
},
{
key: 'limitPrompt',
type: 'textarea',
label: '限定词',
description:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
placeholder:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
value: '将我发送的任何内容,直接翻译成{{language}}',
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: 981.9682828103937,
y: 890.014595014464
},
moduleId: '7pacf0'
},
{
logo: '/imgs/module/history.png',
name: '聊天记录',
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
type: 'initInput',
flowType: 'historyNode',
url: '/app/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: '7pacf0',
key: 'history'
}
]
}
],
position: {
x: 446.2698477029736,
y: 1281.1006139718102
},
moduleId: 'xj0c9p'
},
{
logo: '/imgs/module/userGuide.png',
name: '开场引导',
intro:
'可以在每个新对话开始前,给用户发送一段开场白,或要求用户填写一些内容作为本轮对话的变量。',
type: 'userGuide',
flowType: 'userGuide',
inputs: [
{
key: 'welcomeText',
type: 'input',
label: '开场白',
value: '你好,我是翻译助手,可以帮你翻译任何语言,请告诉我,你需要翻译成什么语言?',
connected: false
},
{
key: 'variables',
type: 'systemInput',
label: '变量输入',
value: [
{
id: 'z3bs2f',
key: 'language',
label: '目标语言',
type: 'input',
required: true,
maxLen: 50,
enums: [
{
value: ''
}
]
},
{
id: 'lg4p31',
key: 'test',
label: '单选测试',
type: 'select',
required: false,
maxLen: 50,
enums: [
{
value: '英语'
},
{
value: '法语'
}
]
}
],
connected: false
}
],
outputs: [],
position: {
x: 421.82048705763134,
y: 879.3868698959807
},
moduleId: '7blchb'
}
]
},
{
id: 'CQ',
avatar: '/imgs/module/cq.png',
name: '问题分类 + 知识库',
intro: '先对用户的问题进行分类,再根据不同类型问题,执行不同的操作',
modules: [
{
logo: '/imgs/module/userChatInput.png',
name: '用户问题',
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
type: 'initInput',
flowType: 'questionInput',
url: '/app/modules/init/userChatInput',
inputs: [
{
key: 'userChatInput',
type: 'systemInput',
label: '用户问题',
connected: false
}
],
outputs: [
{
key: 'userChatInput',
label: '用户问题',
type: 'source',
targets: [
{
moduleId: '3n49vn',
key: 'userChatInput'
},
{
moduleId: 's7qnhf',
key: 'userChatInput'
},
{
moduleId: '15c9bv',
key: 'userChatInput'
}
]
}
],
position: {
x: -216.08819066976912,
y: 585.9302721518841
},
moduleId: 'xzj0oo'
},
{
logo: '/imgs/module/history.png',
name: '聊天记录',
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
type: 'initInput',
flowType: 'historyNode',
url: '/app/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: 1146.0216647621794,
y: 236.92269104756855
},
moduleId: 'hh6of9'
},
{
logo: '/imgs/module/AI.png',
name: 'AI 对话',
intro: 'OpenAI GPT 大模型对话。',
flowType: 'chatNode',
type: 'http',
url: '/app/modules/chat/gpt',
inputs: [
{
key: 'model',
type: 'select',
label: '对话模型',
value: 'gpt-3.5-turbo',
list: [
{
label: 'FastAI-16k',
value: 'gpt-3.5-turbo-16k'
},
{
label: 'FastAI-4k',
value: 'gpt-3.5-turbo'
},
{
label: 'FastAI-Plus-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 助手,可以回答 Laf 相关问题。',
connected: false
},
{
key: 'limitPrompt',
type: 'textarea',
label: '限定词',
description:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
placeholder:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
value: '知识库是 Laf 的内容,参考知识库回答问题。',
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: 1494.4843114348841,
y: -13.57201521210618
},
moduleId: '3n49vn'
},
{
logo: '/imgs/module/db.png',
name: '知识库搜索',
intro: '去知识库中搜索对应的答案。可作为 AI 对话引用参考。',
flowType: 'kbSearchNode',
type: 'http',
url: '/app/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: rawSearchKey,
label: '源搜索数据',
type: 'hidden',
response: true,
targets: []
},
{
key: 'isEmpty',
label: '搜索结果为空',
type: 'source',
targets: [
{
moduleId: 'phwr0u',
key: 'switch'
}
]
},
{
key: 'quotePrompt',
label: '引用内容',
description: '搜索结果为空时不返回',
type: 'source',
targets: [
{
moduleId: '3n49vn',
key: 'quotePrompt'
}
]
}
],
position: {
x: 690.1930900957847,
y: 102.10119978743109
},
moduleId: 's7qnhf'
},
{
logo: '/imgs/module/history.png',
name: '聊天记录',
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
type: 'initInput',
flowType: 'historyNode',
url: '/app/modules/init/history',
inputs: [
{
key: 'maxContext',
type: 'numberInput',
label: '最长记录数',
value: 2,
min: 0,
max: 50,
connected: false
},
{
key: 'history',
type: 'hidden',
label: '聊天记录',
connected: false
}
],
outputs: [
{
key: 'history',
label: '聊天记录',
type: 'source',
targets: [
{
moduleId: '15c9bv',
key: 'history'
}
]
}
],
position: {
x: -274.2362185453961,
y: 152.19755525696058
},
moduleId: 'qiwrjt'
},
{
logo: '/imgs/module/reply.png',
name: '指定回复',
intro: '该模块可以直接回复一段指定的内容。常用于引导、提示。',
type: 'answer',
flowType: 'answerNode',
inputs: [
{
key: 'switch',
type: 'target',
label: '触发器',
connected: true
},
{
key: 'answerText',
value: '你好,我是 Laf 助手,有什么可以帮助你的么?',
type: 'input',
label: '回复的内容',
connected: false
}
],
outputs: [],
position: {
x: 686.1260929408212,
y: -142.16731465682332
},
moduleId: 'l4e36k'
},
{
logo: '/imgs/module/reply.png',
name: '指定回复',
intro: '该模块可以直接回复一段指定的内容。常用于引导、提示。',
type: 'answer',
flowType: 'answerNode',
inputs: [
{
key: 'switch',
type: 'target',
label: '触发器',
connected: true
},
{
key: 'answerText',
value: '对不起,我无法回答你的问题,请问有什么关于 Laf 的问题么?',
type: 'input',
label: '回复的内容',
connected: false
}
],
outputs: [],
position: {
x: 1469.3636235179692,
y: 937.5555811306511
},
moduleId: 'phwr0u'
},
{
logo: '/imgs/module/cq.png',
name: '问题分类',
intro: '可以判断用户问题属于哪方面问题,从而执行不同的操作。',
type: 'http',
url: '/app/modules/agent/classifyQuestion',
flowType: 'classifyQuestion',
inputs: [
{
key: 'systemPrompt',
type: 'textarea',
label: '系统提示词',
description:
'你可以添加一些特定内容的介绍,从而更好的识别用户的问题类型。这个内容通常是给模型介绍一个它不知道的内容。',
placeholder: '例如: \n1. Laf 是一个云函数开发平台……\n2. Sealos 是一个集群操作系统',
value:
' laf 是什么\nlaf 是云开发平台,可以快速的开发应用\nlaf 是一个开源的 BaaS 开发平台(Backend as a Service)\nlaf 是一个开箱即用的 serverless 开发平台\nlaf 是一个集「函数计算」、「数据库」、「对象存储」等于一身的一站式开发平台\nlaf 可以是开源版的腾讯云开发、开源版的 Google Firebase、开源版的 UniCloud\nlaf 让每个开发团队都可以随时拥有一个自己的云开发平台!',
connected: false
},
{
key: 'history',
type: 'target',
label: '聊天记录',
connected: true
},
{
key: 'userChatInput',
type: 'target',
label: '用户问题',
connected: true
},
{
key: 'agents',
type: 'custom',
label: '',
value: [
{
value: '打招呼、问候等',
key: 'fasw'
},
{
value: '关于 laf 云函数的问题',
key: 'fqsw'
},
{
value: '其他问题',
key: 'q73b'
}
],
connected: false
}
],
outputs: [
{
key: 'fasw',
label: '',
type: 'hidden',
targets: [
{
moduleId: 'l4e36k',
key: 'switch'
}
]
},
{
key: 'fqsw',
label: '',
type: 'hidden',
targets: [
{
moduleId: 's7qnhf',
key: 'switch'
}
]
},
{
key: 'q73b',
label: '',
type: 'hidden',
targets: [
{
moduleId: 'phwr0u',
key: 'switch'
}
]
}
],
position: {
x: 154.9724540917009,
y: -37.48714632270105
},
moduleId: '15c9bv'
}
]
}
];
import { AppModuleItemTypeEnum, SystemInputEnum, SpecificInputEnum } from '../app'; import { AppModuleItemTypeEnum, SystemInputEnum, SpecificInputEnum } from '../app';
import { FlowModuleTypeEnum, FlowInputItemTypeEnum, FlowOutputItemTypeEnum } from './index'; import { FlowModuleTypeEnum, FlowInputItemTypeEnum, FlowOutputItemTypeEnum } from './index';
import type { AppModuleTemplateItemType } from '@/types/app'; import type { AppItemType, AppModuleTemplateItemType } from '@/types/app';
import { chatModelList } from '@/store/static'; import { chatModelList } from '@/store/static';
import { import {
Input_Template_History, Input_Template_History,
...@@ -116,14 +116,14 @@ export const ChatModule: AppModuleTemplateItemType = { ...@@ -116,14 +116,14 @@ export const ChatModule: AppModuleTemplateItemType = {
}, },
{ {
key: 'maxToken', key: 'maxToken',
type: FlowInputItemTypeEnum.slider, type: FlowInputItemTypeEnum.custom,
label: '回复上限', label: '回复上限',
value: defaultModel ? defaultModel.contextMaxToken / 2 : 2000, value: defaultModel ? defaultModel.contextMaxToken / 2 : 2000,
min: 0, min: 100,
max: defaultModel?.contextMaxToken || 4000, max: defaultModel?.contextMaxToken || 4000,
step: 50, step: 50,
markList: [ markList: [
{ label: '0', value: 0 }, { label: '100', value: 100 },
{ {
label: `${defaultModel?.contextMaxToken || 4000}`, label: `${defaultModel?.contextMaxToken || 4000}`,
value: defaultModel?.contextMaxToken || 4000 value: defaultModel?.contextMaxToken || 4000
...@@ -194,7 +194,7 @@ export const KBSearchModule: AppModuleTemplateItemType = { ...@@ -194,7 +194,7 @@ export const KBSearchModule: AppModuleTemplateItemType = {
max: 1, max: 1,
step: 0.01, step: 0.01,
markList: [ markList: [
{ label: '0', value: 0 }, { label: '100', value: 100 },
{ label: '1', value: 1 } { label: '1', value: 1 }
] ]
}, },
...@@ -202,6 +202,7 @@ export const KBSearchModule: AppModuleTemplateItemType = { ...@@ -202,6 +202,7 @@ export const KBSearchModule: AppModuleTemplateItemType = {
key: 'limit', key: 'limit',
type: FlowInputItemTypeEnum.slider, type: FlowInputItemTypeEnum.slider,
label: '单次搜索上限', label: '单次搜索上限',
description: '最多取 n 条记录作为本次问题引用',
value: 5, value: 5,
min: 1, min: 1,
max: 20, max: 20,
...@@ -356,3 +357,1282 @@ export const ModuleTemplates = [ ...@@ -356,3 +357,1282 @@ export const ModuleTemplates = [
list: [ClassifyQuestionModule] list: [ClassifyQuestionModule]
} }
]; ];
// template
export const appTemplates: (AppItemType & { avatar: string; intro: string })[] = [
{
id: 'simpleChat',
avatar: '/imgs/module/AI.png',
name: '简单的对话',
intro: '一个极其简单的 AI 对话应用',
modules: [
{
...UserInputModule,
inputs: [
{
key: 'userChatInput',
type: 'systemInput',
label: '用户问题',
connected: false
}
],
outputs: [
{
key: 'userChatInput',
label: '用户问题',
type: 'source',
targets: [
{
moduleId: '7pacf0',
key: 'userChatInput'
}
]
}
],
position: {
x: 477.9074315528994,
y: 1604.2106242223683
},
moduleId: '7z5g5h'
},
{
...ChatModule,
inputs: [
{
key: 'model',
type: 'custom',
label: '对话模型',
value: 'gpt-3.5-turbo-16k',
list: [
{
label: 'FastAI-4k',
value: 'gpt-3.5-turbo'
},
{
label: 'FastAI-16k',
value: 'gpt-3.5-turbo-16k'
},
{
label: 'FastAI-Plus',
value: 'gpt-4'
}
],
connected: false
},
{
key: 'temperature',
type: 'custom',
label: '温度',
value: 0,
min: 0,
max: 10,
step: 1,
markList: [
{
label: '严谨',
value: 0
},
{
label: '发散',
value: 10
}
],
connected: false
},
{
key: 'maxToken',
type: 'custom',
label: '回复上限',
value: 8000,
min: 100,
max: 16000,
step: 50,
markList: [
{
label: '0',
value: 0
},
{
label: '16000',
value: 16000
}
],
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: 981.9682828103937,
y: 890.014595014464
},
moduleId: '7pacf0'
},
{
...HistoryModule,
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: '7pacf0',
key: 'history'
}
]
}
],
position: {
x: 452.5466249541586,
y: 1276.3930310334215
},
moduleId: 'xj0c9p'
}
]
},
{
id: 'simpleKbChat',
avatar: '/imgs/module/db.png',
name: '基础知识库',
intro: '每次提问时进行一次知识库搜索,将搜索结果注入 LLM 模型进行参考回答',
modules: [
{
...UserInputModule,
inputs: [
{
key: 'userChatInput',
type: 'systemInput',
label: '用户问题',
connected: false
}
],
outputs: [
{
key: 'userChatInput',
label: '用户问题',
type: 'source',
targets: [
{
moduleId: 'q9v14m',
key: 'userChatInput'
},
{
moduleId: 'qbf8td',
key: 'userChatInput'
}
]
}
],
position: {
x: -196.84632684738483,
y: 797.3401378431948
},
moduleId: 'v0nc1s'
},
{
...HistoryModule,
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: 'qbf8td',
key: 'history'
}
]
}
],
position: {
x: 211.58250540918442,
y: 611.8700401034965
},
moduleId: 'k9y3jm'
},
{
...ChatModule,
inputs: [
{
key: 'model',
type: 'custom',
label: '对话模型',
value: 'gpt-3.5-turbo-16k',
list: [
{
label: 'FastAI-4k',
value: 'gpt-3.5-turbo'
},
{
label: 'FastAI-16k',
value: 'gpt-3.5-turbo-16k'
},
{
label: 'FastAI-Plus',
value: 'gpt-4'
}
],
connected: false
},
{
key: 'temperature',
type: 'custom',
label: '温度',
value: 0,
min: 0,
max: 10,
step: 1,
markList: [
{
label: '严谨',
value: 0
},
{
label: '发散',
value: 10
}
],
connected: false
},
{
key: 'maxToken',
type: 'custom',
label: '回复上限',
value: 8000,
min: 100,
max: 16000,
step: 50,
markList: [
{
label: '0',
value: 0
},
{
label: '16000',
value: 16000
}
],
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: '知识库是关于 Laf 的内容,参考知识库回答我的问题。',
connected: false
},
{
key: 'switch',
type: 'target',
label: '触发器',
connected: true
},
{
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: 745.484449528062,
y: 259.9361900288137
},
moduleId: 'qbf8td'
},
{
...KBSearchModule,
inputs: [
{
key: 'kb_ids',
type: 'custom',
label: '关联的知识库',
value: [],
list: [],
connected: false
},
{
key: 'similarity',
type: 'custom',
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: 'custom',
label: '单次搜索上限',
description: '最多取 n 条记录作为本次问题引用',
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: rawSearchKey,
label: '源搜索数据',
type: 'hidden',
response: true,
targets: []
},
{
key: 'isEmpty',
label: '搜索结果为空',
type: 'source',
targets: [
{
moduleId: 'w8av9y',
key: 'switch'
}
]
},
{
key: 'quotePrompt',
label: '引用内容',
description: '搜索结果为空时不返回',
type: 'source',
targets: [
{
moduleId: 'qbf8td',
key: 'quotePrompt'
}
]
}
],
position: {
x: 101.2612930583856,
y: -31.342317423453437
},
moduleId: 'q9v14m'
},
{
...AnswerModule,
inputs: [
{
key: 'switch',
type: 'target',
label: '触发器',
connected: true
},
{
key: 'answerText',
value: '对不起,我没有找到你的问题',
type: 'input',
label: '回复的内容',
connected: false
}
],
outputs: [],
position: {
x: 673.6108151684664,
y: -84.13355134221933
},
moduleId: 'w8av9y'
}
]
},
{
id: 'chatGuide',
avatar: '/imgs/module/userGuide.png',
name: '对话前引导',
intro: '可以在每次对话开始前提示用户填写一些内容,作为本次对话的永久内容',
modules: [
{
...UserInputModule,
inputs: [
{
key: 'userChatInput',
type: 'systemInput',
label: '用户问题',
connected: false
}
],
outputs: [
{
key: 'userChatInput',
label: '用户问题',
type: 'source',
targets: [
{
moduleId: '7pacf0',
key: 'userChatInput'
}
]
}
],
position: {
x: 485.8457451202796,
y: 1601.0352987954163
},
moduleId: '7z5g5h'
},
{
...ChatModule,
inputs: [
{
key: 'model',
type: 'custom',
label: '对话模型',
value: 'gpt-3.5-turbo-16k',
list: [
{
label: 'FastAI-4k',
value: 'gpt-3.5-turbo'
},
{
label: 'FastAI-16k',
value: 'gpt-3.5-turbo-16k'
},
{
label: 'FastAI-Plus',
value: 'gpt-4'
}
],
connected: false
},
{
key: 'temperature',
type: 'custom',
label: '温度',
value: 0,
min: 0,
max: 10,
step: 1,
markList: [
{
label: '严谨',
value: 0
},
{
label: '发散',
value: 10
}
],
connected: false
},
{
key: 'maxToken',
type: 'custom',
label: '回复上限',
value: 8000,
min: 100,
max: 16000,
step: 50,
markList: [
{
label: '0',
value: 0
},
{
label: '16000',
value: 16000
}
],
connected: false
},
{
key: 'systemPrompt',
type: 'textarea',
label: '系统提示词',
description:
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。',
placeholder:
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。',
value: '将我发送的任何内容,直接翻译成{{test}}',
connected: false
},
{
key: 'limitPrompt',
type: 'textarea',
label: '限定词',
description:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
placeholder:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
value: '将我发送的任何内容,直接翻译成{{language}}',
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: 981.9682828103937,
y: 890.014595014464
},
moduleId: '7pacf0'
},
{
...HistoryModule,
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: '7pacf0',
key: 'history'
}
]
}
],
position: {
x: 446.2698477029736,
y: 1281.1006139718102
},
moduleId: 'xj0c9p'
},
{
...VariableInputModule,
inputs: [
{
key: 'welcomeText',
type: 'input',
label: '开场白',
value: '你好,我是翻译助手,可以帮你翻译任何语言,请告诉我,你需要翻译成什么语言?',
connected: false
},
{
key: 'variables',
type: 'systemInput',
label: '变量输入',
value: [
{
id: 'z3bs2f',
key: 'language',
label: '目标语言',
type: 'input',
required: true,
maxLen: 50,
enums: [
{
value: ''
}
]
},
{
id: 'lg4p31',
key: 'test',
label: '单选测试',
type: 'select',
required: false,
maxLen: 50,
enums: [
{
value: '英语'
},
{
value: '法语'
}
]
}
],
connected: false
}
],
outputs: [],
position: {
x: 421.82048705763134,
y: 879.3868698959807
},
moduleId: '7blchb'
}
]
},
{
id: 'CQ',
avatar: '/imgs/module/cq.png',
name: '问题分类 + 知识库',
intro: '先对用户的问题进行分类,再根据不同类型问题,执行不同的操作',
modules: [
{
...UserInputModule,
inputs: [
{
key: 'userChatInput',
type: 'systemInput',
label: '用户问题',
connected: false
}
],
outputs: [
{
key: 'userChatInput',
label: '用户问题',
type: 'source',
targets: [
{
moduleId: '3n49vn',
key: 'userChatInput'
},
{
moduleId: 's7qnhf',
key: 'userChatInput'
},
{
moduleId: '15c9bv',
key: 'userChatInput'
}
]
}
],
position: {
x: -216.08819066976912,
y: 585.9302721518841
},
moduleId: 'xzj0oo'
},
{
...HistoryModule,
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: 1146.0216647621794,
y: 236.92269104756855
},
moduleId: 'hh6of9'
},
{
...ChatModule,
inputs: [
{
key: 'model',
type: 'select',
label: '对话模型',
value: 'gpt-3.5-turbo',
list: [
{
label: 'FastAI-16k',
value: 'gpt-3.5-turbo-16k'
},
{
label: 'FastAI-4k',
value: 'gpt-3.5-turbo'
},
{
label: 'FastAI-Plus-8k',
value: 'gpt-4'
}
],
connected: false
},
{
key: 'temperature',
type: 'custom',
label: '温度',
value: 0,
min: 0,
max: 10,
step: 1,
markList: [
{
label: '严谨',
value: 0
},
{
label: '发散',
value: 10
}
],
connected: false
},
{
key: 'maxToken',
type: 'custom',
label: '回复上限',
value: 3000,
min: 100,
max: 4000,
step: 50,
markList: [
{
label: '0',
value: 0
},
{
label: '4000',
value: 4000
}
],
connected: false
},
{
key: 'systemPrompt',
type: 'textarea',
label: '系统提示词',
description:
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。',
placeholder:
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。',
value: '你是 Laf 助手,可以回答 Laf 相关问题。',
connected: false
},
{
key: 'limitPrompt',
type: 'textarea',
label: '限定词',
description:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
placeholder:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
value: '知识库是 Laf 的内容,参考知识库回答问题。',
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: 1494.4843114348841,
y: -13.57201521210618
},
moduleId: '3n49vn'
},
{
...KBSearchModule,
inputs: [
{
key: 'kb_ids',
type: 'custom',
label: '关联的知识库',
value: [],
list: [],
connected: false
},
{
key: 'similarity',
type: 'custom',
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: 'custom',
label: '单次搜索上限',
description: '最多取 n 条记录作为本次问题引用',
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: rawSearchKey,
label: '源搜索数据',
type: 'hidden',
response: true,
targets: []
},
{
key: 'isEmpty',
label: '搜索结果为空',
type: 'source',
targets: [
{
moduleId: 'phwr0u',
key: 'switch'
}
]
},
{
key: 'quotePrompt',
label: '引用内容',
description: '搜索结果为空时不返回',
type: 'source',
targets: [
{
moduleId: '3n49vn',
key: 'quotePrompt'
}
]
}
],
position: {
x: 690.1930900957847,
y: 102.10119978743109
},
moduleId: 's7qnhf'
},
{
...HistoryModule,
inputs: [
{
key: 'maxContext',
type: 'numberInput',
label: '最长记录数',
value: 2,
min: 0,
max: 50,
connected: false
},
{
key: 'history',
type: 'hidden',
label: '聊天记录',
connected: false
}
],
outputs: [
{
key: 'history',
label: '聊天记录',
type: 'source',
targets: [
{
moduleId: '15c9bv',
key: 'history'
}
]
}
],
position: {
x: -274.2362185453961,
y: 152.19755525696058
},
moduleId: 'qiwrjt'
},
{
...AnswerModule,
inputs: [
{
key: 'switch',
type: 'target',
label: '触发器',
connected: true
},
{
key: 'answerText',
value: '你好,我是 Laf 助手,有什么可以帮助你的么?',
type: 'input',
label: '回复的内容',
connected: false
}
],
outputs: [],
position: {
x: 686.1260929408212,
y: -142.16731465682332
},
moduleId: 'l4e36k'
},
{
...AnswerModule,
inputs: [
{
key: 'switch',
type: 'target',
label: '触发器',
connected: true
},
{
key: 'answerText',
value: '对不起,我无法回答你的问题,请问有什么关于 Laf 的问题么?',
type: 'input',
label: '回复的内容',
connected: false
}
],
outputs: [],
position: {
x: 1469.3636235179692,
y: 937.5555811306511
},
moduleId: 'phwr0u'
},
{
...ClassifyQuestionModule,
inputs: [
{
key: 'systemPrompt',
type: 'textarea',
label: '系统提示词',
description:
'你可以添加一些特定内容的介绍,从而更好的识别用户的问题类型。这个内容通常是给模型介绍一个它不知道的内容。',
placeholder: '例如: \n1. Laf 是一个云函数开发平台……\n2. Sealos 是一个集群操作系统',
value:
' laf 是什么\nlaf 是云开发平台,可以快速的开发应用\nlaf 是一个开源的 BaaS 开发平台(Backend as a Service)\nlaf 是一个开箱即用的 serverless 开发平台\nlaf 是一个集「函数计算」、「数据库」、「对象存储」等于一身的一站式开发平台\nlaf 可以是开源版的腾讯云开发、开源版的 Google Firebase、开源版的 UniCloud\nlaf 让每个开发团队都可以随时拥有一个自己的云开发平台!',
connected: false
},
{
key: 'history',
type: 'target',
label: '聊天记录',
connected: true
},
{
key: 'userChatInput',
type: 'target',
label: '用户问题',
connected: true
},
{
key: 'agents',
type: 'custom',
label: '',
value: [
{
value: '打招呼、问候等',
key: 'fasw'
},
{
value: '关于 laf 云函数的问题',
key: 'fqsw'
},
{
value: '其他问题',
key: 'q73b'
}
],
connected: false
}
],
outputs: [
{
key: 'fasw',
label: '',
type: 'hidden',
targets: [
{
moduleId: 'l4e36k',
key: 'switch'
}
]
},
{
key: 'fqsw',
label: '',
type: 'hidden',
targets: [
{
moduleId: 's7qnhf',
key: 'switch'
}
]
},
{
key: 'q73b',
label: '',
type: 'hidden',
targets: [
{
moduleId: 'phwr0u',
key: 'switch'
}
]
}
],
position: {
x: 154.9724540917009,
y: -37.48714632270105
},
moduleId: '15c9bv'
}
]
}
];
...@@ -3,7 +3,6 @@ import type { NextApiRequest, NextApiResponse } from 'next'; ...@@ -3,7 +3,6 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response'; import { jsonRes } from '@/service/response';
import { authUser } from '@/service/utils/auth'; import { authUser } from '@/service/utils/auth';
import { connectToDatabase, App } from '@/service/mongo'; import { connectToDatabase, App } from '@/service/mongo';
import { appTemplates } from '@/constants/app';
import { rawSearchKey } from '@/constants/chat'; import { rawSearchKey } from '@/constants/chat';
const chatTemplate = ({ const chatTemplate = ({
...@@ -85,7 +84,7 @@ const chatTemplate = ({ ...@@ -85,7 +84,7 @@ const chatTemplate = ({
}, },
{ {
key: 'temperature', key: 'temperature',
type: 'slider', type: 'custom',
label: '温度', label: '温度',
value: temperature, value: temperature,
min: 0, min: 0,
...@@ -105,10 +104,10 @@ const chatTemplate = ({ ...@@ -105,10 +104,10 @@ const chatTemplate = ({
}, },
{ {
key: 'maxToken', key: 'maxToken',
type: 'slider', type: 'custom',
label: '回复上限', label: '回复上限',
value: maxToken, value: maxToken,
min: 0, min: 100,
max: 16000, max: 16000,
step: 50, step: 50,
markList: [ markList: [
...@@ -328,8 +327,8 @@ const kbTemplate = ({ ...@@ -328,8 +327,8 @@ const kbTemplate = ({
} }
], ],
position: { position: {
x: 211.58250540918442, x: -196.84632684738483,
y: 611.8700401034965 y: 797.3401378431948
}, },
moduleId: 'k9y3jm' moduleId: 'k9y3jm'
}, },
...@@ -364,7 +363,7 @@ const kbTemplate = ({ ...@@ -364,7 +363,7 @@ const kbTemplate = ({
}, },
{ {
key: 'temperature', key: 'temperature',
type: 'slider', type: 'custom',
label: '温度', label: '温度',
value: temperature, value: temperature,
min: 0, min: 0,
...@@ -384,10 +383,10 @@ const kbTemplate = ({ ...@@ -384,10 +383,10 @@ const kbTemplate = ({
}, },
{ {
key: 'maxToken', key: 'maxToken',
type: 'slider', type: 'custom',
label: '回复上限', label: '回复上限',
value: maxToken, value: maxToken,
min: 0, min: 100,
max: 16000, max: 16000,
step: 50, step: 50,
markList: [ markList: [
...@@ -459,8 +458,8 @@ const kbTemplate = ({ ...@@ -459,8 +458,8 @@ const kbTemplate = ({
} }
], ],
position: { position: {
x: 830.725790038998, x: 745.484449528062,
y: 201.0790739617387 y: 259.9361900288137
}, },
moduleId: 'qbf8td' moduleId: 'qbf8td'
}, },
...@@ -482,7 +481,7 @@ const kbTemplate = ({ ...@@ -482,7 +481,7 @@ const kbTemplate = ({
}, },
{ {
key: 'similarity', key: 'similarity',
type: 'slider', type: 'custom',
label: '相似度', label: '相似度',
value: searchSimilarity, value: searchSimilarity,
min: 0, min: 0,
...@@ -502,8 +501,9 @@ const kbTemplate = ({ ...@@ -502,8 +501,9 @@ const kbTemplate = ({
}, },
{ {
key: 'limit', key: 'limit',
type: 'slider', type: 'custom',
label: '单次搜索上限', label: '单次搜索上限',
description: '最多取 n 条记录作为本次问题引用',
value: searchLimit, value: searchLimit,
min: 1, min: 1,
max: 20, max: 20,
...@@ -575,7 +575,7 @@ const kbTemplate = ({ ...@@ -575,7 +575,7 @@ const kbTemplate = ({
}, },
moduleId: 'q9v14m' moduleId: 'q9v14m'
}, },
searchEmptyText ...(searchEmptyText
? [ ? [
{ {
logo: '/imgs/module/reply.png', logo: '/imgs/module/reply.png',
...@@ -600,13 +600,13 @@ const kbTemplate = ({ ...@@ -600,13 +600,13 @@ const kbTemplate = ({
], ],
outputs: [], outputs: [],
position: { position: {
x: 827.8570503787319, x: 673.6108151684664,
y: -63.837994077710675 y: -84.13355134221933
}, },
moduleId: 'w8av9y' moduleId: 'w8av9y'
} }
] ]
: [] : [])
]; ];
}; };
...@@ -619,13 +619,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ...@@ -619,13 +619,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const apps = await App.find( const apps = await App.find(
{ {
chat: { $ne: null }, chat: { $ne: null },
modules: { $ne: null } modules: { $exists: false }
// userId: '63f9a14228d2a688d8dc9e1b'
}, },
'_id chat' '_id chat'
).limit(2); );
const result = await Promise.all( await Promise.all(
apps.map(async (app) => { apps.map(async (app) => {
if (!app.chat) return app;
const modules = (() => { const modules = (() => {
if (app.chat.relatedKbs.length === 0) { if (app.chat.relatedKbs.length === 0) {
return chatTemplate({ return chatTemplate({
...@@ -650,17 +652,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ...@@ -650,17 +652,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
} }
})(); })();
await App.findByIdAndUpdate(app.id, {
modules
});
return modules; return modules;
}) })
); );
console.log(apps);
jsonRes(res, { jsonRes(res, {
data: { data: apps.length
apps,
result
}
}); });
} catch (error) { } catch (error) {
jsonRes(res, { jsonRes(res, {
......
...@@ -30,7 +30,7 @@ export type Props = { ...@@ -30,7 +30,7 @@ export type Props = {
export type Response = { [SpecificInputEnum.answerText]: string; totalTokens: number }; export type Response = { [SpecificInputEnum.answerText]: string; totalTokens: number };
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
let { model, temperature = 0, stream } = req.body as Props; let { model, stream } = req.body as Props;
try { try {
await authUser({ req, authRoot: true }); await authUser({ req, authRoot: true });
...@@ -54,7 +54,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ...@@ -54,7 +54,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
} }
} catch (err) { } catch (err) {
if (stream) { if (stream) {
res.status(500);
sseErrRes(res, err); sseErrRes(res, err);
res.end(); res.end();
} else { } else {
......
...@@ -9,6 +9,8 @@ import RenderOutput from './render/RenderOutput'; ...@@ -9,6 +9,8 @@ import RenderOutput from './render/RenderOutput';
import { FlowOutputItemTypeEnum } from '@/constants/flow'; import { FlowOutputItemTypeEnum } from '@/constants/flow';
import MySelect from '@/components/Select'; import MySelect from '@/components/Select';
import { chatModelList } from '@/store/static'; import { chatModelList } from '@/store/static';
import MySlider from '@/components/Slider';
import { Box } from '@chakra-ui/react';
const NodeChat = ({ const NodeChat = ({
data: { moduleId, inputs, outputs, onChangeNode, ...props } data: { moduleId, inputs, outputs, onChangeNode, ...props }
...@@ -47,7 +49,7 @@ const NodeChat = ({ ...@@ -47,7 +49,7 @@ const NodeChat = ({
key: 'maxToken', key: 'maxToken',
valueKey: 'markList', valueKey: 'markList',
value: [ value: [
{ label: '0', value: 0 }, { label: '100', value: 100 },
{ label: `${model.contextMaxToken}`, value: model.contextMaxToken } { label: `${model.contextMaxToken}`, value: model.contextMaxToken }
] ]
}); });
...@@ -65,7 +67,35 @@ const NodeChat = ({ ...@@ -65,7 +67,35 @@ const NodeChat = ({
}); });
}} }}
/> />
) ),
maxToken: (inputItem) => {
const model = inputs.find((item) => item.key === 'model')?.value;
const modelData = chatModelList.find((item) => item.model === model);
const maxToken = modelData ? modelData.contextMaxToken : 4000;
const markList = [
{ label: '100', value: 100 },
{ label: `${maxToken}`, value: maxToken }
];
return (
<Box pt={5} pb={4} px={2}>
<MySlider
markList={markList}
width={'100%'}
min={inputItem.min || 100}
max={maxToken}
step={inputItem.step || 1}
value={inputItem.value}
onChange={(e) => {
onChangeNode({
moduleId,
key: inputItem.key,
value: e
});
}}
/>
</Box>
);
}
}} }}
/> />
</Container> </Container>
......
...@@ -84,6 +84,7 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { ...@@ -84,6 +84,7 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
const { x, y, zoom } = useViewport(); const { x, y, zoom } = useViewport();
const [nodes, setNodes, onNodesChange] = useNodesState<FlowModuleItemType>([]); const [nodes, setNodes, onNodesChange] = useNodesState<FlowModuleItemType>([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const [loaded, setLoaded] = useState(false);
const { const {
isOpen: isOpenTemplate, isOpen: isOpenTemplate,
onOpen: onOpenTemplate, onOpen: onOpenTemplate,
...@@ -255,6 +256,9 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { ...@@ -255,6 +256,9 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
}) })
) )
); );
setLoaded(true);
onFixView();
}, },
[onDelConnect, setEdges, setNodes, onChangeNode, onDelNode] [onDelConnect, setEdges, setNodes, onChangeNode, onDelNode]
); );
...@@ -377,14 +381,15 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { ...@@ -377,14 +381,15 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
isOpenTemplate ? onCloseTemplate() : onOpenTemplate(); isOpenTemplate ? onCloseTemplate() : onOpenTemplate();
}} }}
/> />
<ReactFlow <ReactFlow
ref={reactFlowWrapper} ref={reactFlowWrapper}
className={styles.panel} className={styles.panel}
fitView
nodes={nodes} nodes={nodes}
edges={edges} edges={edges}
minZoom={0.4} minZoom={0.4}
maxZoom={1.5} maxZoom={1.5}
fitView
defaultEdgeOptions={edgeOptions} defaultEdgeOptions={edgeOptions}
connectionLineStyle={connectionLineStyle} connectionLineStyle={connectionLineStyle}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}
......
...@@ -21,9 +21,8 @@ import { getErrText } from '@/utils/tools'; ...@@ -21,9 +21,8 @@ import { getErrText } from '@/utils/tools';
import { useToast } from '@/hooks/useToast'; import { useToast } from '@/hooks/useToast';
import { postCreateApp } from '@/api/app'; import { postCreateApp } from '@/api/app';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { appTemplates } from '@/constants/app'; import { appTemplates } from '@/constants/flow/ModuleTemplate';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import MyIcon from '@/components/Icon';
type FormType = { type FormType = {
avatar: string; avatar: string;
......
...@@ -30,7 +30,8 @@ const ChatHistorySlider = ({ ...@@ -30,7 +30,8 @@ const ChatHistorySlider = ({
activeHistoryId, activeHistoryId,
onChangeChat, onChangeChat,
onDelHistory, onDelHistory,
onSetHistoryTop onSetHistoryTop,
onUpdateTitle
}: { }: {
appId?: string; appId?: string;
appName: string; appName: string;
...@@ -40,6 +41,7 @@ const ChatHistorySlider = ({ ...@@ -40,6 +41,7 @@ const ChatHistorySlider = ({
onChangeChat: (historyId?: string) => void; onChangeChat: (historyId?: string) => void;
onDelHistory: (historyId: string) => void; onDelHistory: (historyId: string) => void;
onSetHistoryTop?: (e: { historyId: string; top: boolean }) => void; onSetHistoryTop?: (e: { historyId: string; top: boolean }) => void;
onUpdateTitle?: (e: { historyId: string; title: string }) => void;
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
const router = useRouter(); const router = useRouter();
...@@ -159,6 +161,20 @@ const ChatHistorySlider = ({ ...@@ -159,6 +161,20 @@ const ChatHistorySlider = ({
{item.top ? '取消置顶' : '置顶'} {item.top ? '取消置顶' : '置顶'}
</MenuItem> </MenuItem>
)} )}
{/* {onUpdateTitle && (
<MenuItem
onClick={(e) => {
e.stopPropagation();
onUpdateTitle({
historyId: item.id,
title: '是是是'
});
}}
>
<MyIcon mr={2} name={'customTitle'} w={'16px'}></MyIcon>
自定义标题
</MenuItem>
)} */}
<MenuItem <MenuItem
_hover={{ color: 'red.500' }} _hover={{ color: 'red.500' }}
onClick={(e) => { onClick={(e) => {
......
...@@ -24,7 +24,7 @@ const ToolMenu = ({ history }: { history: ChatItemType[] }) => { ...@@ -24,7 +24,7 @@ const ToolMenu = ({ history }: { history: ChatItemType[] }) => {
}, },
{ icon: 'pdf', label: 'PDF导出', onClick: () => onExportChat({ type: 'pdf', history }) } { icon: 'pdf', label: 'PDF导出', onClick: () => onExportChat({ type: 'pdf', history }) }
]); ]);
return ( return history.length > 0 ? (
<Menu autoSelect={false} isLazy> <Menu autoSelect={false} isLazy>
<MenuButton <MenuButton
_hover={{ bg: 'myWhite.600 ' }} _hover={{ bg: 'myWhite.600 ' }}
...@@ -45,7 +45,7 @@ const ToolMenu = ({ history }: { history: ChatItemType[] }) => { ...@@ -45,7 +45,7 @@ const ToolMenu = ({ history }: { history: ChatItemType[] }) => {
))} ))}
</MenuList> </MenuList>
</Menu> </Menu>
); ) : null;
}; };
export default ToolMenu; export default ToolMenu;
...@@ -260,6 +260,20 @@ const Chat = () => { ...@@ -260,6 +260,20 @@ const Chat = () => {
}); });
} catch (error) {} } catch (error) {}
}} }}
onUpdateTitle={async (e) => {
try {
await putChatHistory({
historyId: e.historyId,
customTitle: e.title
});
const historyItem = history.find((item) => item._id === e.historyId);
if (!historyItem) return;
updateHistory({
...historyItem,
title: e.title
});
} catch (error) {}
}}
/> />
)} )}
{/* chat container */} {/* chat container */}
......
...@@ -96,7 +96,7 @@ export const moduleFetch = ({ url, data, res }: Props) => ...@@ -96,7 +96,7 @@ export const moduleFetch = ({ url, data, res }: Props) =>
data: JSON.stringify(data) data: JSON.stringify(data)
}); });
} else if (item.event === sseResponseEventEnum.error) { } else if (item.event === sseResponseEventEnum.error) {
return reject(getErrText(data, '流响应错误')); return reject(data);
} }
}); });
read(); read();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment