From ffd4e194bf26f5e2af58c640770e920139751932 Mon Sep 17 00:00:00 2001 From: archer <545436317@qq.com> Date: Thu, 3 Aug 2023 15:43:06 +0800 Subject: [PATCH] feat: http request --- client/src/constants/flow/flowField.ts | 5 +- .../pages/api/openapi/v1/chat/completions.ts | 8 +- .../AdEdit/components/Nodes/NodeExtract.tsx | 2 +- .../AdEdit/components/Nodes/NodeHttp.tsx | 1 - .../components/modules/SetInputFieldModal.tsx | 3 +- .../AdEdit/components/render/RenderInput.tsx | 19 ++--- .../AdEdit/components/render/RenderOutput.tsx | 80 ++++++------------- .../AdEdit/components/render/TargetHandle.tsx | 4 +- .../app/detail/components/AdEdit/index.tsx | 69 +++++++++------- .../service/moduleDispatch/agent/extract.ts | 2 +- client/src/service/moduleDispatch/index.ts | 2 + .../src/service/moduleDispatch/tools/http.ts | 51 ++++++++++++ .../moduleDispatch/tools/httpRequest.ts | 31 ------- 13 files changed, 138 insertions(+), 139 deletions(-) create mode 100644 client/src/service/moduleDispatch/tools/http.ts delete mode 100644 client/src/service/moduleDispatch/tools/httpRequest.ts diff --git a/client/src/constants/flow/flowField.ts b/client/src/constants/flow/flowField.ts index 1cd0885d5..3b3e3dcaf 100644 --- a/client/src/constants/flow/flowField.ts +++ b/client/src/constants/flow/flowField.ts @@ -9,7 +9,6 @@ export enum ContextExtractEnum { export enum HttpPropsEnum { url = 'url', - finish = 'finish', - body = 'body', - response = 'response' + failed = 'failed', + finish = 'finish' } diff --git a/client/src/pages/api/openapi/v1/chat/completions.ts b/client/src/pages/api/openapi/v1/chat/completions.ts index 2143cf97c..1766365bd 100644 --- a/client/src/pages/api/openapi/v1/chat/completions.ts +++ b/client/src/pages/api/openapi/v1/chat/completions.ts @@ -10,7 +10,9 @@ import { dispatchChatCompletion, dispatchKBSearch, dispatchAnswer, - dispatchClassifyQuestion + dispatchClassifyQuestion, + dispatchContentExtract, + dispatchHttpRequest } from '@/service/moduleDispatch'; import type { CreateChatCompletionRequest } from 'openai'; import { gptMessage2ChatType, textAdaptGptResponse } from '@/utils/adapt'; @@ -25,7 +27,6 @@ import { pushTaskBill } from '@/service/events/pushBill'; import { BillSourceEnum } from '@/constants/user'; import { ChatHistoryItemResType } from '@/types/chat'; import { UserModelSchema } from '@/types/mongoSchema'; -import { dispatchContentExtract } from '@/service/moduleDispatch/agent/extract'; export type MessageItemType = ChatCompletionRequestMessage & { _id?: string }; type FastGptWebChatProps = { @@ -339,7 +340,8 @@ export async function dispatchModules({ [FlowModuleTypeEnum.chatNode]: dispatchChatCompletion, [FlowModuleTypeEnum.kbSearchNode]: dispatchKBSearch, [FlowModuleTypeEnum.classifyQuestion]: dispatchClassifyQuestion, - [FlowModuleTypeEnum.contentExtract]: dispatchContentExtract + [FlowModuleTypeEnum.contentExtract]: dispatchContentExtract, + [FlowModuleTypeEnum.httpRequest]: dispatchHttpRequest }; if (callbackMap[module.flowType]) { return callbackMap[module.flowType](props); 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 b06cc512b..998c1e090 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 @@ -66,7 +66,7 @@ const NodeExtract = ({ {extractKeys.map((item, index) => ( {item.key} - + {item.desc} {item.required ? '✔' : ''} 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 index 656f4144a..0c443eae4 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHttp.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHttp.tsx @@ -32,7 +32,6 @@ const NodeHttp = ({ key, value: { key, - value: '', valueType: FlowValueTypeEnum.string, type: FlowInputItemTypeEnum.target, label: 'New Param', diff --git a/client/src/pages/app/detail/components/AdEdit/components/modules/SetInputFieldModal.tsx b/client/src/pages/app/detail/components/AdEdit/components/modules/SetInputFieldModal.tsx index dd46ea5e4..e78fe2a3e 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/modules/SetInputFieldModal.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/modules/SetInputFieldModal.tsx @@ -10,14 +10,13 @@ import { Input, FormControl } from '@chakra-ui/react'; -import type { ContextExtractAgentItemType, HttpFieldItemType } from '@/types/app'; import { useForm } from 'react-hook-form'; import { customAlphabet } from 'nanoid'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6); import MyModal from '@/components/MyModal'; import Avatar from '@/components/Avatar'; import MyTooltip from '@/components/MyTooltip'; -import { FlowInputItemTypeEnum, FlowValueTypeEnum, FlowValueTypeStyle } from '@/constants/flow'; +import { FlowInputItemTypeEnum, FlowValueTypeEnum } from '@/constants/flow'; import { useTranslation } from 'react-i18next'; import MySelect from '@/components/Select'; import { FlowInputItemType } from '@/types/flow'; diff --git a/client/src/pages/app/detail/components/AdEdit/components/render/RenderInput.tsx b/client/src/pages/app/detail/components/AdEdit/components/render/RenderInput.tsx index 04b72a8f4..596fb82b0 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/render/RenderInput.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/render/RenderInput.tsx @@ -35,7 +35,7 @@ export const Label = ({ const [editField, setEditField] = useState(); return ( - + {label} {description && ( @@ -83,9 +83,6 @@ export const Label = ({ ml={2} _hover={{ color: 'red.500' }} onClick={() => { - { - console.log(moduleId, inputKey, valueType); - } onChangeNode({ moduleId, type: 'delInput', @@ -113,15 +110,15 @@ export const Label = ({ // diff key. del and add onChangeNode({ moduleId, - type: 'delInput', - key: editField.key, - value: '' + type: 'addInput', + key: data.key, + value: data }); onChangeNode({ moduleId, - type: 'addInput', + type: 'delInput', key: editField.key, - value: data + value: '' }); } setEditField(undefined); @@ -132,7 +129,7 @@ export const Label = ({ ); }; -const RenderBody = ({ +const RenderInput = ({ flowInputList, moduleId, CustomComponent = {}, @@ -270,4 +267,4 @@ const RenderBody = ({ ); }; -export default React.memo(RenderBody); +export default React.memo(RenderInput); diff --git a/client/src/pages/app/detail/components/AdEdit/components/render/RenderOutput.tsx b/client/src/pages/app/detail/components/AdEdit/components/render/RenderOutput.tsx index 0769bff81..1644f6b4b 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/render/RenderOutput.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/render/RenderOutput.tsx @@ -1,9 +1,8 @@ -import React, { useCallback, useState } from 'react'; +import React, { useState } from 'react'; import type { FlowModuleItemType, FlowOutputItemType } from '@/types/flow'; import { Box, Flex } from '@chakra-ui/react'; import { FlowOutputItemTypeEnum } from '@/constants/flow'; import { QuestionOutlineIcon } from '@chakra-ui/icons'; -import { Handle, Position } from 'reactflow'; import MyTooltip from '@/components/MyTooltip'; import SourceHandle from './SourceHandle'; import MyIcon from '@/components/Icon'; @@ -13,24 +12,26 @@ const SetOutputFieldModal = dynamic(() => import('../modules/SetOutputFieldModal const Label = ({ moduleId, outputKey, - delOutputByKey, - updateOutput, + outputs, onChangeNode, - addUpdateOutput, ...item }: FlowOutputItemType & { outputKey: string; moduleId: string; - delOutputByKey: (key: string) => void; - updateOutput: (key: string, val: FlowOutputItemType) => void; - addUpdateOutput: (val: FlowOutputItemType) => void; + outputs: FlowOutputItemType[]; onChangeNode: FlowModuleItemType['onChangeNode']; }) => { const { label, description, edit } = item; const [editField, setEditField] = useState(); return ( - + {edit && ( <> delOutputByKey(outputKey)} + onClick={() => { + onChangeNode({ + moduleId, + type: 'outputs', + key: '', + value: outputs.filter((output) => output.key !== outputKey) + }); + }} /> )} @@ -69,13 +77,13 @@ const Label = ({ defaultField={editField} onClose={() => setEditField(undefined)} onSubmit={(data) => { - console.log(data); // same key - if (editField.key === data.key) { - updateOutput(data.key, data); - } else { - delOutputByKey(editField.key); - addUpdateOutput(data); - } + onChangeNode({ + moduleId, + type: 'outputs', + key: '', + value: outputs.map((output) => (output.key === outputKey ? data : output)) + }); + setEditField(undefined); }} /> @@ -93,40 +101,6 @@ const RenderOutput = ({ flowOutputList: FlowOutputItemType[]; onChangeNode: FlowModuleItemType['onChangeNode']; }) => { - const delOutputByKey = useCallback( - (key: string) => { - onChangeNode({ - moduleId, - type: 'outputs', - key: '', - value: flowOutputList.filter((output) => output.key !== key) - }); - }, - [moduleId, flowOutputList, onChangeNode] - ); - const updateOutput = useCallback( - (key: string, val: FlowOutputItemType) => { - onChangeNode({ - moduleId, - type: 'outputs', - key: '', - value: flowOutputList.map((output) => (output.key === key ? val : output)) - }); - }, - [flowOutputList, moduleId, onChangeNode] - ); - const addUpdateOutput = useCallback( - (val: FlowOutputItemType) => { - onChangeNode({ - moduleId, - type: 'outputs', - key: '', - value: flowOutputList.concat(val) - }); - }, - [flowOutputList, moduleId, onChangeNode] - ); - return ( <> {flowOutputList.map( @@ -137,9 +111,7 @@ const RenderOutput = ({ moduleId={moduleId} onChangeNode={onChangeNode} outputKey={item.key} - delOutputByKey={delOutputByKey} - addUpdateOutput={addUpdateOutput} - updateOutput={updateOutput} + outputs={flowOutputList} {...item} /> diff --git a/client/src/pages/app/detail/components/AdEdit/components/render/TargetHandle.tsx b/client/src/pages/app/detail/components/AdEdit/components/render/TargetHandle.tsx index 8eb0198f2..4c710a50f 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/render/TargetHandle.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/render/TargetHandle.tsx @@ -21,6 +21,7 @@ const TargetHandle = ({ handleKey, valueType, onConnect, ...props }: Props) => { return ( { }} type="target" id={handleKey} - datatype={valueStyle} position={Position.Left} /> @@ -44,4 +44,4 @@ const TargetHandle = ({ handleKey, valueType, onConnect, ...props }: Props) => { ); }; -export default TargetHandle; +export default React.memo(TargetHandle); diff --git a/client/src/pages/app/detail/components/AdEdit/index.tsx b/client/src/pages/app/detail/components/AdEdit/index.tsx index 7fb8bdba6..608a9fe61 100644 --- a/client/src/pages/app/detail/components/AdEdit/index.tsx +++ b/client/src/pages/app/detail/components/AdEdit/index.tsx @@ -125,6 +125,36 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { }, 100); }, []); + const onDelNode = useCallback( + (nodeId: string) => { + setNodes((state) => state.filter((item) => item.id !== nodeId)); + setEdges((state) => state.filter((edge) => edge.source !== nodeId && edge.target !== nodeId)); + }, + [setEdges, setNodes] + ); + const onDelEdge = useCallback( + ({ + moduleId, + sourceHandle, + targetHandle + }: { + moduleId: string; + sourceHandle?: string; + targetHandle?: string; + }) => { + if (!sourceHandle && !targetHandle) return; + setEdges((state) => + state.filter((edge) => { + if (edge.source === moduleId && edge.sourceHandle === sourceHandle) return false; + if (edge.target === moduleId && edge.targetHandle === targetHandle) return false; + + return true; + }) + ); + }, + [setEdges] + ); + const flow2AppModules = useCallback(() => { const modules: AppModuleItemType[] = nodes.map((item) => ({ moduleId: item.data.moduleId, @@ -203,6 +233,7 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { }; } if (type === 'delInput') { + onDelEdge({ moduleId, targetHandle: key }); return { ...node, data: { @@ -211,7 +242,14 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { } }; } - console.log(value); + + // del output connect + const delOutputs = node.data.outputs.filter( + (item) => !value.find((output: FlowOutputTargetItemType) => output.key === item.key) + ); + delOutputs.forEach((output) => { + onDelEdge({ moduleId, sourceHandle: output.key }); + }); return { ...node, @@ -225,35 +263,6 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { }, [setNodes] ); - const onDelNode = useCallback( - (nodeId: string) => { - setNodes((state) => state.filter((item) => item.id !== nodeId)); - setEdges((state) => state.filter((edge) => edge.source !== nodeId && edge.target !== nodeId)); - }, - [setEdges, setNodes] - ); - const onDelEdge = useCallback( - ({ - moduleId, - sourceHandle, - targetHandle - }: { - moduleId: string; - sourceHandle?: string; - targetHandle?: string; - }) => { - if (!sourceHandle && !targetHandle) return; - setEdges((state) => - state.filter((edge) => { - if (edge.source === moduleId && edge.sourceHandle === sourceHandle) return false; - if (edge.target === moduleId && edge.targetHandle === targetHandle) return false; - - return true; - }) - ); - }, - [setEdges] - ); const onAddNode = useCallback( ({ template, position }: { template: FlowModuleTemplateType; position: XYPosition }) => { diff --git a/client/src/service/moduleDispatch/agent/extract.ts b/client/src/service/moduleDispatch/agent/extract.ts index 7a5ea90eb..b6fcc430a 100644 --- a/client/src/service/moduleDispatch/agent/extract.ts +++ b/client/src/service/moduleDispatch/agent/extract.ts @@ -66,7 +66,7 @@ export async function dispatchContentExtract({ // function body const agentFunction = { name: agentFunName, - description: `${description}\n如果内容不存在,返回空字符串。当前时间是2023/7/31 18:00`, + description: `${description}\n如果内容不存在,返回空字符串。`, parameters: { type: 'object', properties, diff --git a/client/src/service/moduleDispatch/index.ts b/client/src/service/moduleDispatch/index.ts index a84cc7783..2d30518ae 100644 --- a/client/src/service/moduleDispatch/index.ts +++ b/client/src/service/moduleDispatch/index.ts @@ -3,4 +3,6 @@ export * from './init/userChatInput'; export * from './chat/oneapi'; export * from './kb/search'; export * from './tools/answer'; +export * from './tools/http'; export * from './agent/classifyQuestion'; +export * from './agent/extract'; diff --git a/client/src/service/moduleDispatch/tools/http.ts b/client/src/service/moduleDispatch/tools/http.ts new file mode 100644 index 000000000..10c963102 --- /dev/null +++ b/client/src/service/moduleDispatch/tools/http.ts @@ -0,0 +1,51 @@ +import { HttpPropsEnum } from '@/constants/flow/flowField'; +import type { NextApiResponse } from 'next'; + +export type HttpRequestProps = { + res: NextApiResponse; + stream: boolean; + userOpenaiAccount: any; + [HttpPropsEnum.url]: string; + [key: string]: any; +}; +export type HttpResponse = { + [HttpPropsEnum.finish]: boolean; + [HttpPropsEnum.failed]?: boolean; + [key: string]: any; +}; + +export const dispatchHttpRequest = async (props: Record): Promise => { + const { res, stream, userOpenaiAccount, url, ...body } = props as HttpRequestProps; + + try { + const response = await fetchData({ url, body }); + + return { + [HttpPropsEnum.finish]: true, + ...response + }; + } catch (error) { + return { + [HttpPropsEnum.finish]: true, + [HttpPropsEnum.failed]: true + }; + } +}; + +async function fetchData({ + url, + body +}: { + url: string; + body: Record; +}): Promise> { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(body) + }).then((res) => res.json()); + + return response; +} diff --git a/client/src/service/moduleDispatch/tools/httpRequest.ts b/client/src/service/moduleDispatch/tools/httpRequest.ts deleted file mode 100644 index c292b97c7..000000000 --- a/client/src/service/moduleDispatch/tools/httpRequest.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { sseResponseEventEnum, TaskResponseKeyEnum } from '@/constants/chat'; -import { sseResponse } from '@/service/utils/tools'; -import { textAdaptGptResponse } from '@/utils/adapt'; -import type { NextApiResponse } from 'next'; - -export type AnswerProps = { - res: NextApiResponse; - text: string; - stream: boolean; -}; -export type AnswerResponse = { - [TaskResponseKeyEnum.answerText]: string; -}; - -export const dispatchAnswer = (props: Record): AnswerResponse => { - const { res, text = '', stream } = props as AnswerProps; - - if (stream) { - sseResponse({ - res, - event: sseResponseEventEnum.answer, - data: textAdaptGptResponse({ - text: text.replace(/\\n/g, '\n') - }) - }); - } - - return { - [TaskResponseKeyEnum.answerText]: text - }; -};