mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-24 22:03:54 +00:00
feat: add tool params node & tool params add array type (#2824)
* add tool params node * add tool params enum * node response * tool add array type params * fix tool params * fix * fix * fix
This commit is contained in:
@@ -355,6 +355,12 @@ export const WholeResponseContent = ({
|
||||
|
||||
{/* form input */}
|
||||
<Row label={t('workflow:form_input_result')} value={activeModule?.formInputResult} />
|
||||
|
||||
{/* tool params */}
|
||||
<Row
|
||||
label={t('workflow:tool_params.tool_params_result')}
|
||||
value={activeModule?.toolParamsResult}
|
||||
/>
|
||||
</Box>
|
||||
) : null;
|
||||
};
|
||||
|
@@ -106,8 +106,12 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
|
||||
if (item.flowNodeType === FlowNodeTypeEnum.lafModule && !feConfigs.lafEnv) {
|
||||
return false;
|
||||
}
|
||||
// tool stop
|
||||
if (!hasToolNode && item.flowNodeType === FlowNodeTypeEnum.stopTool) {
|
||||
// tool stop or tool params
|
||||
if (
|
||||
!hasToolNode &&
|
||||
(item.flowNodeType === FlowNodeTypeEnum.stopTool ||
|
||||
item.flowNodeType === FlowNodeTypeEnum.toolParams)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@@ -48,6 +48,7 @@ const nodeTypes: Record<FlowNodeTypeEnum, any> = {
|
||||
[FlowNodeTypeEnum.stopTool]: (data: NodeProps<FlowNodeItemType>) => (
|
||||
<NodeSimple {...data} minW={'100px'} maxW={'300px'} />
|
||||
),
|
||||
[FlowNodeTypeEnum.toolParams]: dynamic(() => import('./nodes/NodeToolParams')),
|
||||
[FlowNodeTypeEnum.lafModule]: dynamic(() => import('./nodes/NodeLaf')),
|
||||
[FlowNodeTypeEnum.ifElseNode]: dynamic(() => import('./nodes/NodeIfElse')),
|
||||
[FlowNodeTypeEnum.variableUpdate]: dynamic(() => import('./nodes/NodeVariableUpdate')),
|
||||
|
@@ -0,0 +1,172 @@
|
||||
import { fnValueTypeSelect } from '@/web/core/workflow/constants/dataType';
|
||||
import { Box, Button, Flex, Input, ModalBody, ModalFooter, Textarea } from '@chakra-ui/react';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { defaultEditFormData } from '../render/RenderToolInput/EditFieldModal';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
|
||||
const ToolParamsEditModal = ({
|
||||
defaultValue = defaultEditFormData,
|
||||
nodeId,
|
||||
onClose
|
||||
}: {
|
||||
defaultValue: FlowNodeInputItemType;
|
||||
nodeId: string;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const { register, setValue, handleSubmit, watch } = useForm<FlowNodeInputItemType>({
|
||||
defaultValues: defaultValue
|
||||
});
|
||||
const valueType = watch('valueType');
|
||||
|
||||
const { runAsync: onClickSubmit } = useRequest2(
|
||||
async (e: FlowNodeInputItemType) => {
|
||||
e.key = e.key.trim();
|
||||
|
||||
const inputConfig: FlowNodeInputItemType = {
|
||||
...e,
|
||||
description: e.toolDescription,
|
||||
label: e.key
|
||||
};
|
||||
if (defaultValue.key) {
|
||||
// edit
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'replaceInput',
|
||||
key: defaultValue.key,
|
||||
value: inputConfig
|
||||
});
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'replaceOutput',
|
||||
key: defaultValue.key,
|
||||
value: {
|
||||
...e,
|
||||
id: e.key,
|
||||
label: e.key,
|
||||
type: FlowNodeOutputTypeEnum.static
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// create
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'addInput',
|
||||
value: {
|
||||
...e,
|
||||
label: e.key
|
||||
}
|
||||
});
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'addOutput',
|
||||
value: {
|
||||
...e,
|
||||
id: e.key,
|
||||
label: e.key,
|
||||
type: FlowNodeOutputTypeEnum.static
|
||||
}
|
||||
});
|
||||
}
|
||||
onClose();
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const onClickSubmitError = useCallback(
|
||||
(e: Object) => {
|
||||
for (const item of Object.values(e)) {
|
||||
if (item.message) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: item.message
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[toast]
|
||||
);
|
||||
|
||||
return (
|
||||
<MyModal isOpen iconSrc="modal/edit" title={t('workflow:tool_field')} onClose={onClose}>
|
||||
<ModalBody>
|
||||
<Flex alignItems={'center'} mb={5}>
|
||||
<FormLabel flex={'0 0 80px'}>{t('common:core.module.Data Type')}</FormLabel>
|
||||
<Box flex={'1 0 0'}>
|
||||
<MySelect
|
||||
list={fnValueTypeSelect}
|
||||
value={valueType}
|
||||
onchange={(e: any) => {
|
||||
setValue('valueType', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mb={5}>
|
||||
<FormLabel flex={'0 0 80px'}>{t('workflow:tool_params.params_name')}</FormLabel>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
{...register('key', {
|
||||
required: true,
|
||||
pattern: {
|
||||
value: /^[a-zA-Z]+[0-9]*$/,
|
||||
message: t('common:info.felid_message')
|
||||
}
|
||||
})}
|
||||
placeholder={t('workflow:tool_params.params_name_placeholder')}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mb={5}>
|
||||
<FormLabel flex={'0 0 80px'}>{t('workflow:tool_params.params_description')}</FormLabel>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
{...register('toolDescription', {
|
||||
required: true
|
||||
})}
|
||||
placeholder={t('workflow:tool_params.params_description_placeholder')}
|
||||
/>
|
||||
</Flex>
|
||||
<Box>
|
||||
<Flex alignItems={'center'} mb={2}>
|
||||
<FormLabel>{t('workflow:tool_params.enum_values')}</FormLabel>
|
||||
<QuestionTip label={t('workflow:tool_params.enum_values_tip')} />
|
||||
</Flex>
|
||||
<Textarea
|
||||
bg={'myGray.50'}
|
||||
{...register('enum')}
|
||||
placeholder={t('workflow:tool_params.enum_placeholder')}
|
||||
/>
|
||||
</Box>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={2} onClick={onClose}>
|
||||
{t('common:common.Close')}
|
||||
</Button>
|
||||
<Button onClick={handleSubmit((data) => onClickSubmit(data), onClickSubmitError)}>
|
||||
{t('common:common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ToolParamsEditModal);
|
@@ -0,0 +1,117 @@
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import NodeCard from '../render/NodeCard';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import Container from '../../components/Container';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
FormLabel,
|
||||
Table,
|
||||
TableContainer,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr
|
||||
} from '@chakra-ui/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { defaultEditFormData } from '../render/RenderToolInput/EditFieldModal';
|
||||
import ToolParamsEditModal from './ToolParamsEditModal';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
|
||||
const NodeToolParams = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
const [editField, setEditField] = useState<FlowNodeInputItemType>();
|
||||
const { nodeId, inputs } = data;
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
<NodeCard selected={selected} {...data}>
|
||||
<Container>
|
||||
<Flex alignItems={'center'} justifyContent={'space-between'} mb={1.5}>
|
||||
<FormLabel>{t('workflow:custom_tool_input')}</FormLabel>
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
iconSpacing={1}
|
||||
size={'sm'}
|
||||
onClick={() => setEditField(defaultEditFormData)}
|
||||
>
|
||||
{t('common:add_new_param')}
|
||||
</Button>
|
||||
{!!editField && (
|
||||
<ToolParamsEditModal
|
||||
defaultValue={editField}
|
||||
nodeId={nodeId}
|
||||
onClose={() => setEditField(undefined)}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Box borderRadius={'md'} overflow={'hidden'} border={'base'}>
|
||||
<TableContainer>
|
||||
<Table bg={'white'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{t('workflow:tool_params.params_name')}</Th>
|
||||
<Th>{t('workflow:tool_params.params_description')}</Th>
|
||||
<Th>{t('common:common.Operation')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{inputs.map((item, index) => (
|
||||
<Tr
|
||||
key={index}
|
||||
position={'relative'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
wordBreak={'break-all'}
|
||||
>
|
||||
<Td>{item.key}</Td>
|
||||
<Td>{item.toolDescription}</Td>
|
||||
<Td whiteSpace={'nowrap'}>
|
||||
<MyIcon
|
||||
mr={3}
|
||||
name={'common/settingLight'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => setEditField(item)}
|
||||
/>
|
||||
<MyIcon
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'delInput',
|
||||
key: item.key
|
||||
});
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'delOutput',
|
||||
key: item.key
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
</Container>
|
||||
</NodeCard>
|
||||
);
|
||||
}, [selected, data, t, editField, inputs, onChangeNode, nodeId]);
|
||||
|
||||
return Render;
|
||||
};
|
||||
|
||||
export default React.memo(NodeToolParams);
|
@@ -103,8 +103,9 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
(item) => item.renderType === updateItem.renderType
|
||||
);
|
||||
|
||||
const nodeIds = nodeList.map((node) => node.nodeId);
|
||||
const handleUpdate = (newValue: ReferenceValueProps | string) => {
|
||||
if (isReferenceValue(newValue)) {
|
||||
if (isReferenceValue(newValue, nodeIds)) {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) =>
|
||||
i === index ? { ...update, value: newValue as ReferenceValueProps } : update
|
||||
|
@@ -85,7 +85,7 @@ const EditFieldModal = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<MyModal isOpen iconSrc="modal/edit" title={t('common:tool_field')} onClose={onClose}>
|
||||
<MyModal isOpen iconSrc="modal/edit" title={t('workflow:tool_field')} onClose={onClose}>
|
||||
<ModalBody>
|
||||
<Flex alignItems={'center'} mb={5}>
|
||||
<Box flex={'0 0 80px'}>{t('common:common.Require Input')}</Box>
|
||||
|
@@ -12,5 +12,17 @@ export const fnValueTypeSelect = [
|
||||
{
|
||||
label: WorkflowIOValueTypeEnum.boolean,
|
||||
value: WorkflowIOValueTypeEnum.boolean
|
||||
},
|
||||
{
|
||||
label: WorkflowIOValueTypeEnum.arrayString,
|
||||
value: WorkflowIOValueTypeEnum.arrayString
|
||||
},
|
||||
{
|
||||
label: WorkflowIOValueTypeEnum.arrayNumber,
|
||||
value: WorkflowIOValueTypeEnum.arrayNumber
|
||||
},
|
||||
{
|
||||
label: WorkflowIOValueTypeEnum.arrayBoolean,
|
||||
value: WorkflowIOValueTypeEnum.arrayBoolean
|
||||
}
|
||||
];
|
||||
|
Reference in New Issue
Block a user