extract modules

This commit is contained in:
archer
2023-07-31 18:17:27 +08:00
parent 2f28f57d78
commit 58153306c5
17 changed files with 377 additions and 78 deletions

View File

@@ -25,6 +25,7 @@ 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 = {
@@ -337,7 +338,8 @@ export async function dispatchModules({
[FlowModuleTypeEnum.answerNode]: dispatchAnswer,
[FlowModuleTypeEnum.chatNode]: dispatchChatCompletion,
[FlowModuleTypeEnum.kbSearchNode]: dispatchKBSearch,
[FlowModuleTypeEnum.classifyQuestion]: dispatchClassifyQuestion
[FlowModuleTypeEnum.classifyQuestion]: dispatchClassifyQuestion,
[FlowModuleTypeEnum.contentExtract]: dispatchContentExtract
};
if (callbackMap[module.flowType]) {
return callbackMap[module.flowType](props);

View File

@@ -10,7 +10,7 @@ import type { ClassifyQuestionAgentItemType } from '@/types/app';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 4);
import MyIcon from '@/components/Icon';
import { FlowOutputItemTypeEnum, FlowValueTypeEnum } from '@/constants/flow';
import { FlowOutputItemTypeEnum, FlowValueTypeEnum, SpecialInputKeyEnum } from '@/constants/flow';
import SourceHandle from '../render/SourceHandle';
const NodeCQNode = ({
@@ -25,7 +25,7 @@ const NodeCQNode = ({
onChangeNode={onChangeNode}
flowInputList={inputs}
CustomComponent={{
agents: ({
[SpecialInputKeyEnum.agents]: ({
key: agentKey,
value: agents = []
}: {

View File

@@ -0,0 +1,146 @@
import React, { useState } from 'react';
import { Box, Button, Table, Thead, Tbody, Tr, Th, Td, TableContainer } from '@chakra-ui/react';
import { NodeProps } from 'reactflow';
import { FlowModuleItemType } from '@/types/flow';
import { useTranslation } from 'next-i18next';
import NodeCard from '../modules/NodeCard';
import Container from '../modules/Container';
import { AddIcon } from '@chakra-ui/icons';
import RenderInput from '../render/RenderInput';
import Divider from '../modules/Divider';
import { ContextExtractAgentItemType } from '@/types/app';
import RenderOutput from '../render/RenderOutput';
import MyIcon from '@/components/Icon';
import ExtractFieldModal from '../modules/ExtractFieldModal';
import { ContextExtractEnum } from '@/constants/flow/flowField';
const NodeExtract = ({
data: { inputs, outputs, moduleId, onChangeNode, ...props }
}: NodeProps<FlowModuleItemType>) => {
const { t } = useTranslation();
const [editExtractFiled, setEditExtractField] = useState<ContextExtractAgentItemType>();
return (
<NodeCard minW={'380px'} moduleId={moduleId} {...props}>
<Divider text="Input" />
<Container>
<RenderInput
moduleId={moduleId}
onChangeNode={onChangeNode}
flowInputList={inputs}
CustomComponent={{
[ContextExtractEnum.extractKeys]: ({
key,
value: extractKeys = []
}: {
key: string;
value?: ContextExtractAgentItemType[];
}) => (
<Box>
<TableContainer>
<Table>
<Thead>
<Tr>
<Th> key</Th>
<Th></Th>
<Th></Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{extractKeys.map((item, index) => (
<Tr key={index}>
<Td>{item.key}</Td>
<Td whiteSpace={'pre-line'} wordBreak={'break-all'}>
{item.desc}{' '}
</Td>
<Td>{item.required ? '✔' : ''}</Td>
<Td whiteSpace={'nowrap'}>
<MyIcon
mr={3}
name={'settingLight'}
w={'16px'}
cursor={'pointer'}
onClick={() => {
setEditExtractField(item);
}}
/>
<MyIcon
name={'delete'}
w={'16px'}
cursor={'pointer'}
onClick={() => {
onChangeNode({
moduleId,
type: 'inputs',
key: ContextExtractEnum.extractKeys,
value: extractKeys.filter((extract) => item.key !== extract.key)
});
}}
/>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
<Box mt={2} textAlign={'right'}>
<Button
variant={'base'}
leftIcon={<AddIcon fontSize={'10px'} />}
onClick={() =>
setEditExtractField({
desc: '',
key: '',
required: true
})
}
>
</Button>
</Box>
</Box>
)
}}
/>
</Container>
<Divider text="Output" />
<Container>
<RenderOutput flowOutputList={outputs} />
</Container>
{!!editExtractFiled && (
<ExtractFieldModal
defaultField={editExtractFiled}
onClose={() => setEditExtractField(undefined)}
onSubmit={(data) => {
const extracts: ContextExtractAgentItemType[] =
inputs.find((item) => item.key === ContextExtractEnum.extractKeys)?.value || [];
const exists = extracts.find((item) => item.key === editExtractFiled.key);
if (exists) {
onChangeNode({
moduleId,
type: 'inputs',
key: ContextExtractEnum.extractKeys,
value: extracts.map((item) => (item.key === editExtractFiled.key ? data : item))
});
} else {
onChangeNode({
moduleId,
type: 'inputs',
key: ContextExtractEnum.extractKeys,
value: extracts.concat(data)
});
}
setEditExtractField(undefined);
}}
/>
)}
</NodeCard>
);
};
export default NodeExtract;

View File

@@ -1,8 +1,10 @@
import React from 'react';
import { Box, useTheme } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
const Divider = ({ text }: { text: 'Body' | 'Input' | 'Output' | string }) => {
const Divider = ({ text }: { text: 'Input' | 'Output' | string }) => {
const theme = useTheme();
const { t } = useTranslation();
return (
<Box
textAlign={'center'}
@@ -12,7 +14,7 @@ const Divider = ({ text }: { text: 'Body' | 'Input' | 'Output' | string }) => {
borderBottom={theme.borders.base}
fontSize={'lg'}
>
{text}
{t(`common.${text}`)}
</Box>
);
};

View File

@@ -0,0 +1,74 @@
import React, { useState } from 'react';
import {
Box,
Button,
ModalHeader,
ModalFooter,
ModalBody,
Flex,
Switch,
Input,
FormControl
} from '@chakra-ui/react';
import type { ContextExtractAgentItemType } 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';
const ExtractFieldModal = ({
defaultField = {
desc: '',
key: '',
required: true
},
onClose,
onSubmit
}: {
defaultField?: ContextExtractAgentItemType;
onClose: () => void;
onSubmit: (data: ContextExtractAgentItemType) => void;
}) => {
const { register, handleSubmit } = useForm<ContextExtractAgentItemType>({
defaultValues: defaultField
});
return (
<MyModal isOpen={true} onClose={onClose}>
<ModalHeader display={'flex'} alignItems={'center'}>
<Avatar src={'/imgs/module/extract.png'} mr={2} w={'20px'} objectFit={'cover'} />
</ModalHeader>
<ModalBody>
<Flex alignItems={'center'}>
<Box w={'70px'}></Box>
<Switch {...register('required')} />
</Flex>
<Flex mt={5} alignItems={'center'}>
<Box w={'80px'}></Box>
<Input
placeholder="姓名/年龄/sql语句……"
{...register('desc', { required: '字段描述不能为空' })}
/>
</Flex>
<Flex mt={5} alignItems={'center'}>
<Box w={'80px'}> key</Box>
<Input
placeholder="name/age/sql"
{...register('key', { required: '字段 key 不能为空' })}
/>
</Flex>
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={3} onClick={onClose}>
</Button>
<Button onClick={handleSubmit(onSubmit)}></Button>
</ModalFooter>
</MyModal>
);
};
export default React.memo(ExtractFieldModal);

View File

@@ -29,7 +29,14 @@ const NodeCard = ({
const theme = useTheme();
return (
<Box minW={minW} bg={'white'} border={theme.borders.md} borderRadius={'md'} boxShadow={'sm'}>
<Box
minW={minW}
maxW={'430px'}
bg={'white'}
border={theme.borders.md}
borderRadius={'md'}
boxShadow={'sm'}
>
<Flex className="custom-drag-handle" px={4} py={3} alignItems={'center'}>
<Avatar src={logo} borderRadius={'md'} objectFit={'contain'} w={'30px'} h={'30px'} />
<Box ml={3} fontSize={'lg'} color={'myGray.600'}>
@@ -39,7 +46,7 @@ const NodeCard = ({
<MyTooltip label={description} forceShow>
<QuestionOutlineIcon
display={['none', 'inline']}
transform={'translateY(-1px)'}
transform={'translateY(1px)'}
ml={1}
/>
</MyTooltip>

View File

@@ -68,6 +68,9 @@ const NodeVariable = dynamic(() => import('./components/Nodes/NodeVariable'), {
const NodeUserGuide = dynamic(() => import('./components/Nodes/NodeUserGuide'), {
ssr: false
});
const NodeExtract = dynamic(() => import('./components/Nodes/NodeExtract'), {
ssr: false
});
import 'reactflow/dist/style.css';
import styles from './index.module.scss';
@@ -83,7 +86,8 @@ const nodeTypes = {
[FlowModuleTypeEnum.kbSearchNode]: NodeKbSearch,
[FlowModuleTypeEnum.tfSwitchNode]: NodeTFSwitch,
[FlowModuleTypeEnum.answerNode]: NodeAnswer,
[FlowModuleTypeEnum.classifyQuestion]: NodeCQNode
[FlowModuleTypeEnum.classifyQuestion]: NodeCQNode,
[FlowModuleTypeEnum.contentExtract]: NodeExtract
// [FlowModuleTypeEnum.empty]: EmptyModule
};
const edgeTypes = {