From 952da2a06eefe0281ec1ad0233341bfc55e3371e Mon Sep 17 00:00:00 2001 From: archer <545436317@qq.com> Date: Thu, 3 Aug 2023 11:09:57 +0800 Subject: [PATCH] feat: http modules --- client/public/imgs/module/http.png | Bin 0 -> 1566 bytes client/public/locales/en/common.json | 36 ++-- client/public/locales/zh/common.json | 36 ++-- .../src/components/Icon/icons/circle/add.svg | 1 + client/src/components/Icon/icons/withdraw.svg | 1 - client/src/components/Icon/index.tsx | 4 +- client/src/constants/flow/ModuleTemplate.ts | 32 +++- client/src/constants/flow/flowField.ts | 7 + client/src/constants/flow/index.ts | 3 +- client/src/pages/api/admin/initv4.ts | 4 +- .../AdEdit/components/Nodes/NodeCQNode.tsx | 23 ++- .../AdEdit/components/Nodes/NodeChat.tsx | 49 +++-- .../AdEdit/components/Nodes/NodeExtract.tsx | 17 +- .../AdEdit/components/Nodes/NodeHistory.tsx | 6 +- .../AdEdit/components/Nodes/NodeHttp.tsx | 80 ++++++++ .../AdEdit/components/Nodes/NodeKbSearch.tsx | 11 +- .../components/Nodes/NodeQuestionInput.tsx | 2 +- .../AdEdit/components/Nodes/NodeUserGuide.tsx | 39 ++-- .../AdEdit/components/Nodes/NodeVariable.tsx | 7 +- .../components/modules/SetInputFieldModal.tsx | 116 +++++++++++ .../modules/SetOutputFieldModal.tsx | 105 ++++++++++ .../AdEdit/components/render/RenderInput.tsx | 180 ++++++++++++++---- .../AdEdit/components/render/RenderOutput.tsx | 148 ++++++++++++-- .../AdEdit/components/render/TargetHandle.tsx | 2 +- .../app/detail/components/AdEdit/index.tsx | 56 ++++-- .../detail/components/Import/FileSelect.tsx | 2 +- .../moduleDispatch/tools/httpRequest.ts | 31 +++ client/src/types/app.d.ts | 8 +- client/src/types/flow.d.ts | 5 +- client/src/utils/adapt.ts | 9 +- client/src/utils/app.ts | 7 +- 31 files changed, 851 insertions(+), 176 deletions(-) create mode 100644 client/public/imgs/module/http.png create mode 100644 client/src/components/Icon/icons/circle/add.svg delete mode 100644 client/src/components/Icon/icons/withdraw.svg create mode 100644 client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHttp.tsx create mode 100644 client/src/pages/app/detail/components/AdEdit/components/modules/SetInputFieldModal.tsx create mode 100644 client/src/pages/app/detail/components/AdEdit/components/modules/SetOutputFieldModal.tsx create mode 100644 client/src/service/moduleDispatch/tools/httpRequest.ts diff --git a/client/public/imgs/module/http.png b/client/public/imgs/module/http.png new file mode 100644 index 0000000000000000000000000000000000000000..dca870cc0c538fd713de499c6c174638b69e0080 GIT binary patch literal 1566 zcmV+(2I2XMP)Px)*hxe|RCr$PoZVI2JP?MZ?Eq+ibOGFiR04G2yM(*#f=wk^ZUS9|6cFfOl{1!X z>u2;Y+48bJ=R3)lwY<`^<5x4IktkWw)m?qulg;64-(^N`Mb|G0A#dV8LMVg3|0Rdb z9{z3ndItZ$_(d1*fu{rA)yE~-9KH*%05$g76Uv^+VY6Ejdb6M<}b!3$7$YJxm zeLdT`hTK1AbGRjp-g)59X5r0%c3AP7us$bT8~6a|@ceJJ<)jJF-Szt~)QTy4-2T4! zk^aJ}EqkwpzcHr7aP3a|F+=*dIA{yxs;{(odrVoO?6H=2vM6X2a$zfWN(yC9hILo^ z{>4C}khxaQ<_YvJB!{g|z7=3E0=fmrvZbRFabeiAgYItrJrPFLAY^7s#^eNrU9exX zf)=wrysFbxa4;dWztjJ|e*dFsb}`p)Hqas%0XI&DT-BwD1urxk`Z&3Ng(PGa&|&~! zEz?EWZu|SjFkm9kfLTjS1zQxhR>gocnSh7bk@uZn7!p|cd%~9r35biC-3BJ;dKZ|-#y0sk})ZIL3>s^ z0P3C`Hb2(}(Fb|>yp()6L0j|0gv`Da6bh#FeTaM{c}!N&7RVq8tCN5n_(s|wQKwPR zXyS*E{r2VjTTZ9f*tV2T8=%p|&q{pX8b73wK1iGp70_rDm-yfsgI?;H-Ss<@zPp#t z*eWO$w3#OSlycz>T&5d>oq`cGV$G|H;%ZvE;o_NJ=3PvlK%3!aQ@P@rAbrmc4A`8D z1U+aZC^=a(Lca$N*XD3%DY~@6=AiX_aiXv2vy5Z<+)=)U!X7{?vuevp`PP0cH0D6u zVTw59!DU#zk11XHa*ykC{WBjx-?Lrq1KaA=s8GZnlmrV|L8rz|5vfegzf@d7-*dIL zZqAyN1$Y6|^K#lo1Zqg16dqL9=k3e6H=`$Iwq(gUKF|?q2g~x;=((LhyWgd#fDMt5 zu|0rJo(Og6fI8-}-`oY7Dv~rw5^s_`Onq5w(nqa2KeMe|B-7Vk3zzMy;j%@hA6%sh zI_S7xQ2mj7I%{1;&Pm+Fw$-#$i5HB7T7*WAa;lfSiDv+4v4%qdRBkW^9& zumk1e2rPP7mKyjJ+oZ2Y+(SNE*Xf|7|1p4$ac6yrFU|8%_D*upx=shm#|g9@z3ybV zma@DRIVaF5pv@BnqQdc**XlFeYqe+*e(IBAkXC;NaIs@VCqpSAqMwsdZ2MPiN7sJ8 z9yOaKVS3p?LSkj@kaGKv>_XdPwRey^sIu2CcK~Z?kL-VWv{jRuN9{y}=4$~`qo3HG zN=Qz*>ZA4Ty2JK}YKMo^82H@JG-_mq#J{T}XaeS>*;ZsojmMY)^sabYvR&SyzPT-F z>F=`~jWMl)`#Oj&IPylr!sb4ApR}(JEq`t;4bv7NycR9_*ycGK!-xEsG+13Jmi=lO zu9P3LZE7{5>R`~)!|_wLR5C%ORIS5A`ls%l#0FrQ!=gY7*!Vd_E2LJu%Z>$uXwajS z@Y51)b;8BR(DVZXVvJ \ No newline at end of file diff --git a/client/src/components/Icon/icons/withdraw.svg b/client/src/components/Icon/icons/withdraw.svg deleted file mode 100644 index c7cc52da5..000000000 --- a/client/src/components/Icon/icons/withdraw.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/src/components/Icon/index.tsx b/client/src/components/Icon/index.tsx index ed6a745e0..1c12299dd 100644 --- a/client/src/components/Icon/index.tsx +++ b/client/src/components/Icon/index.tsx @@ -8,7 +8,6 @@ const map = { copy: require('./icons/copy.svg').default, chatSend: require('./icons/chatSend.svg').default, delete: require('./icons/delete.svg').default, - withdraw: require('./icons/withdraw.svg').default, stop: require('./icons/stop.svg').default, collectionLight: require('./icons/collectionLight.svg').default, collectionSolid: require('./icons/collectionSolid.svg').default, @@ -72,7 +71,8 @@ const map = { language_en: require('./icons/language/en.svg').default, language_zh: require('./icons/language/zh.svg').default, outlink_share: require('./icons/outlink/share.svg').default, - outlink_iframe: require('./icons/outlink/iframe.svg').default + outlink_iframe: require('./icons/outlink/iframe.svg').default, + addCircle: require('./icons/circle/add.svg').default }; export type IconName = keyof typeof map; diff --git a/client/src/constants/flow/ModuleTemplate.ts b/client/src/constants/flow/ModuleTemplate.ts index f2a3c868d..3bbe94a4b 100644 --- a/client/src/constants/flow/ModuleTemplate.ts +++ b/client/src/constants/flow/ModuleTemplate.ts @@ -15,7 +15,7 @@ import { Input_Template_TFSwitch, Input_Template_UserChatInput } from './inputTemplate'; -import { ContextExtractEnum } from './flowField'; +import { ContextExtractEnum, HttpPropsEnum } from './flowField'; export const ChatModelSystemTip = '模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}'; @@ -448,6 +448,34 @@ export const ContextExtractModule: FlowModuleTemplateType = { } ] }; +export const HttpModule: FlowModuleTemplateType = { + logo: '/imgs/module/http.png', + name: 'HTTP模块', + intro: '可以发出一个 HTTP POST 请求,实现更为复杂的操作(联网搜索、数据库查询等)', + description: '可以发出一个 HTTP POST 请求,实现更为复杂的操作(联网搜索、数据库查询等)', + flowType: FlowModuleTypeEnum.httpRequest, + inputs: [ + { + key: HttpPropsEnum.url, + value: '', + type: FlowInputItemTypeEnum.input, + label: '请求地址', + description: '请求目标地址', + placeholder: 'https://api.fastgpt.run/getInventory', + required: true + }, + Input_Template_TFSwitch + ], + outputs: [ + { + key: HttpPropsEnum.finish, + label: '请求结束', + valueType: FlowValueTypeEnum.boolean, + type: FlowOutputItemTypeEnum.source, + targets: [] + } + ] +}; export const EmptyModule: FlowModuleTemplateType = { logo: '/imgs/module/cq.png', name: '该模块已被移除', @@ -477,7 +505,7 @@ export const ModuleTemplates = [ }, { label: 'Agent', - list: [ClassifyQuestionModule, ContextExtractModule] + list: [ClassifyQuestionModule, ContextExtractModule, HttpModule] } ]; export const ModuleTemplatesFlat = ModuleTemplates.map((templates) => templates.list)?.flat(); diff --git a/client/src/constants/flow/flowField.ts b/client/src/constants/flow/flowField.ts index 0f7be6037..1cd0885d5 100644 --- a/client/src/constants/flow/flowField.ts +++ b/client/src/constants/flow/flowField.ts @@ -6,3 +6,10 @@ export enum ContextExtractEnum { failed = 'failed', fields = 'fields' } + +export enum HttpPropsEnum { + url = 'url', + finish = 'finish', + body = 'body', + response = 'response' +} diff --git a/client/src/constants/flow/index.ts b/client/src/constants/flow/index.ts index dc7196b19..e9945ee90 100644 --- a/client/src/constants/flow/index.ts +++ b/client/src/constants/flow/index.ts @@ -31,7 +31,8 @@ export enum FlowModuleTypeEnum { tfSwitchNode = 'tfSwitchNode', answerNode = 'answerNode', classifyQuestion = 'classifyQuestion', - contentExtract = 'contentExtract' + contentExtract = 'contentExtract', + httpRequest = 'httpRequest' } export enum SpecialInputKeyEnum { diff --git a/client/src/pages/api/admin/initv4.ts b/client/src/pages/api/admin/initv4.ts index 213a00d6c..e180ae581 100644 --- a/client/src/pages/api/admin/initv4.ts +++ b/client/src/pages/api/admin/initv4.ts @@ -3,9 +3,9 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; import { authUser } from '@/service/utils/auth'; import { connectToDatabase, App } from '@/service/mongo'; -import { AppModuleInputItemType } from '@/types/app'; import { FlowModuleTypeEnum, SpecialInputKeyEnum } from '@/constants/flow'; import { TaskResponseKeyEnum } from '@/constants/chat'; +import { FlowInputItemType } from '@/types/flow'; const chatModelInput = ({ model, @@ -21,7 +21,7 @@ const chatModelInput = ({ systemPrompt: string; limitPrompt: string; kbList: { kbId: string }[]; -}): AppModuleInputItemType[] => [ +}): FlowInputItemType[] => [ { key: 'model', value: model, diff --git a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeCQNode.tsx b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeCQNode.tsx index 1720f95e2..425aabd17 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeCQNode.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeCQNode.tsx @@ -27,7 +27,8 @@ const NodeCQNode = ({ CustomComponent={{ [SpecialInputKeyEnum.agents]: ({ key: agentKey, - value: agents = [] + value: agents = [], + ...props }: { key: string; value?: ClassifyQuestionAgentItemType[]; @@ -50,7 +51,11 @@ const NodeCQNode = ({ moduleId, type: 'inputs', key: agentKey, - value: newInputValue + value: { + ...props, + key: agentKey, + value: newInputValue + } }); onChangeNode({ moduleId, @@ -77,8 +82,13 @@ const NodeCQNode = ({ ); onChangeNode({ moduleId, + type: 'inputs', key: agentKey, - value: newVal + value: { + ...props, + key: agentKey, + value: newVal + } }); }} /> @@ -97,11 +107,16 @@ const NodeCQNode = ({ type: FlowOutputItemTypeEnum.hidden, targets: [] }); + onChangeNode({ moduleId, type: 'inputs', key: agentKey, - value: newInputValue + value: { + ...props, + key: agentKey, + value: newInputValue + } }); onChangeNode({ moduleId, diff --git a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeChat.tsx b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeChat.tsx index 5c7c1c32f..e8e6386bd 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeChat.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeChat.tsx @@ -48,33 +48,32 @@ const NodeChat = ({ onchange={(e) => { onChangeNode({ moduleId, + type: 'inputs', key: inputItem.key, - value: e + value: { + ...inputItem, + value: e + } }); + // update max tokens - const model = chatModelList.find((item) => item.model === e); + const model = + chatModelList.find((item) => item.model === e) || chatModelList[0]; if (!model) return; onChangeNode({ moduleId, + type: 'inputs', key: 'maxToken', - valueKey: 'markList', - value: [ - { label: '100', value: 100 }, - { label: `${model.contextMaxToken}`, value: model.contextMaxToken } - ] - }); - onChangeNode({ - moduleId, - key: 'maxToken', - valueKey: 'max', - value: model.contextMaxToken - }); - onChangeNode({ - moduleId, - key: 'maxToken', - valueKey: 'value', - value: model.contextMaxToken / 2 + value: { + ...inputs.find((input) => input.key === 'maxToken'), + markList: [ + { label: '100', value: 100 }, + { label: `${model.contextMaxToken}`, value: model.contextMaxToken } + ], + max: model.contextMaxToken, + value: model.contextMaxToken / 2 + } }); }} /> @@ -100,8 +99,12 @@ const NodeChat = ({ onChange={(e) => { onChangeNode({ moduleId, + type: 'inputs', key: inputItem.key, - value: e + value: { + ...inputItem, + value: e + } }); }} /> @@ -115,7 +118,11 @@ const NodeChat = ({ <> - + )} diff --git a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeExtract.tsx b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeExtract.tsx index 4eaf07273..b06cc512b 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeExtract.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeExtract.tsx @@ -31,10 +31,9 @@ const NodeExtract = ({ flowInputList={inputs} CustomComponent={{ [ContextExtractEnum.extractKeys]: ({ - key, - value: extractKeys = [] + value: extractKeys = [], + ...props }: { - key: string; value?: ContextExtractAgentItemType[]; }) => ( @@ -97,7 +96,10 @@ const NodeExtract = ({ moduleId, type: 'inputs', key: ContextExtractEnum.extractKeys, - value: newInputValue + value: { + ...props, + value: newInputValue + } }); onChangeNode({ moduleId, @@ -121,7 +123,7 @@ const NodeExtract = ({ - + {!!editExtractFiled && ( @@ -160,7 +162,10 @@ const NodeExtract = ({ moduleId, type: 'inputs', key: ContextExtractEnum.extractKeys, - value: newInputs + value: { + ...inputs.find((input) => input.key === ContextExtractEnum.extractKeys), + value: newInputs + } }); onChangeNode({ moduleId, diff --git a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHistory.tsx b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHistory.tsx index 0836a55c3..cf50c0825 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHistory.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHistory.tsx @@ -18,7 +18,11 @@ const NodeHistory = ({ - + ); diff --git a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHttp.tsx b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHttp.tsx new file mode 100644 index 000000000..656f4144a --- /dev/null +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHttp.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { NodeProps } from 'reactflow'; +import NodeCard from '../modules/NodeCard'; +import { FlowModuleItemType } from '@/types/flow'; +import Divider from '../modules/Divider'; +import Container from '../modules/Container'; +import RenderInput from '../render/RenderInput'; +import { Box, Button } from '@chakra-ui/react'; +import { SmallAddIcon } from '@chakra-ui/icons'; +import RenderOutput from '../render/RenderOutput'; + +import { FlowInputItemTypeEnum, FlowOutputItemTypeEnum, FlowValueTypeEnum } from '@/constants/flow'; +import { customAlphabet } from 'nanoid'; +const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6); + +const NodeHttp = ({ + data: { moduleId, inputs, outputs, onChangeNode, ...props } +}: NodeProps) => { + return ( + + + + + + + + + + + + + + ); +}; +export default React.memo(NodeHttp); diff --git a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeKbSearch.tsx b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeKbSearch.tsx index bb621d55a..80eed4df5 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeKbSearch.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeKbSearch.tsx @@ -81,14 +81,19 @@ const NodeKbSearch = ({ onChangeNode={onChangeNode} flowInputList={inputs} CustomComponent={{ - kbList: ({ key, value }) => ( + kbList: ({ key, value, ...props }) => ( { onChangeNode({ moduleId, key, - value: e + type: 'inputs', + value: { + ...props, + key, + value: e + } }); }} /> @@ -98,7 +103,7 @@ const NodeKbSearch = ({ - + ); diff --git a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeQuestionInput.tsx b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeQuestionInput.tsx index bed59d523..f428e382a 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeQuestionInput.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeQuestionInput.tsx @@ -9,7 +9,7 @@ import { FlowValueTypeEnum } from '@/constants/flow'; import SourceHandle from '../render/SourceHandle'; const QuestionInputNode = ({ - data: { inputs, outputs, onChangeNode, ...props } + data: { inputs, outputs, ...props } }: NodeProps) => { return ( diff --git a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeUserGuide.tsx b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeUserGuide.tsx index 81d408d61..2b0313805 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeUserGuide.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeUserGuide.tsx @@ -14,7 +14,7 @@ const NodeUserGuide = ({ data: { inputs, outputs, onChangeNode, ...props } }: NodeProps) => { const welcomeText = useMemo( - () => inputs.find((item) => item.key === SystemInputEnum.welcomeText)?.value, + () => inputs.find((item) => item.key === SystemInputEnum.welcomeText), [inputs] ); @@ -30,22 +30,27 @@ const NodeUserGuide = ({ -