feat: http request

This commit is contained in:
archer
2023-08-03 15:43:06 +08:00
parent 952da2a06e
commit ffd4e194bf
13 changed files with 138 additions and 139 deletions

View File

@@ -9,7 +9,6 @@ export enum ContextExtractEnum {
export enum HttpPropsEnum {
url = 'url',
finish = 'finish',
body = 'body',
response = 'response'
failed = 'failed',
finish = 'finish'
}

View File

@@ -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);

View File

@@ -66,7 +66,7 @@ const NodeExtract = ({
{extractKeys.map((item, index) => (
<Tr key={index} position={'relative'}>
<Td>{item.key}</Td>
<Td whiteSpace={'pre-line'} wordBreak={'break-all'}>
<Td whiteSpace={'pre-wrap'} wordBreak={'break-all'}>
{item.desc}
</Td>
<Td>{item.required ? '✔' : ''}</Td>

View File

@@ -32,7 +32,6 @@ const NodeHttp = ({
key,
value: {
key,
value: '',
valueType: FlowValueTypeEnum.string,
type: FlowInputItemTypeEnum.target,
label: 'New Param',

View File

@@ -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';

View File

@@ -35,7 +35,7 @@ export const Label = ({
const [editField, setEditField] = useState<FlowInputItemType>();
return (
<Flex alignItems={'center'} position={'relative'}>
<Flex className="nodrag" cursor={'default'} alignItems={'center'} position={'relative'}>
<Box position={'relative'}>
{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);

View File

@@ -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<FlowOutputItemType>();
return (
<Flex as={'label'} justifyContent={'right'} alignItems={'center'} position={'relative'}>
<Flex
className="nodrag"
cursor={'default'}
justifyContent={'right'}
alignItems={'center'}
position={'relative'}
>
{edit && (
<>
<MyIcon
@@ -53,7 +54,14 @@ const Label = ({
cursor={'pointer'}
mr={3}
_hover={{ color: 'red.500' }}
onClick={() => 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}
/>
<Box mt={FlowOutputItemTypeEnum.answer ? 0 : 2} className={'nodrag'}>

View File

@@ -21,6 +21,7 @@ const TargetHandle = ({ handleKey, valueType, onConnect, ...props }: Props) => {
return (
<Box
key={handleKey}
position={'absolute'}
top={'50%'}
left={'-16px'}
@@ -36,7 +37,6 @@ const TargetHandle = ({ handleKey, valueType, onConnect, ...props }: Props) => {
}}
type="target"
id={handleKey}
datatype={valueStyle}
position={Position.Left}
/>
</MyTooltip>
@@ -44,4 +44,4 @@ const TargetHandle = ({ handleKey, valueType, onConnect, ...props }: Props) => {
);
};
export default TargetHandle;
export default React.memo(TargetHandle);

View File

@@ -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 }) => {

View File

@@ -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,

View File

@@ -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';

View File

@@ -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<string, any>): Promise<HttpResponse> => {
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<string, any>;
}): Promise<Record<string, any>> {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
}).then((res) => res.json());
return response;
}

View File

@@ -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<string, any>): 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
};
};