perf: all plugin variables type support referense & replace input and textarea with prompt editor (#2950)

* support reference as plugin variables

* replace input and textarea with prompt editor

* adjust height & optimize textarea and input

* input select
This commit is contained in:
heheer
2024-10-22 11:21:28 +08:00
committed by GitHub
parent 779ff29ed5
commit 3f34c33d4c
17 changed files with 258 additions and 141 deletions

View File

@@ -36,7 +36,7 @@ export const defaultVariable: VariableItemType = {
id: nanoid(),
key: '',
label: '',
type: VariableInputEnum.input,
type: VariableInputEnum.textInput,
description: '',
required: true,
valueType: WorkflowIOValueTypeEnum.string
@@ -72,13 +72,18 @@ const VariableEdit = ({
const inputTypeList = useMemo(
() =>
Object.values(variableMap).map((item) => ({
icon: item.icon,
label: t(item.label as any),
value: item.value,
defaultValueType: item.defaultValueType,
description: item.description ? t(item.description as any) : ''
})),
Object.values(variableMap)
.filter(
(item) =>
item.value !== VariableInputEnum.input && item.value !== VariableInputEnum.textarea
)
.map((item) => ({
icon: item.icon,
label: t(item.label as any),
value: item.value,
defaultValueType: item.defaultValueType,
description: item.description ? t(item.description as any) : ''
})),
[t]
);

View File

@@ -24,6 +24,7 @@ import { ChatBoxContext } from '../Provider';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { useDeepCompareEffect } from 'ahooks';
import { VariableItemType } from '@fastgpt/global/core/app/type';
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
export const VariableInputItem = ({
item,
@@ -77,6 +78,16 @@ export const VariableInputItem = ({
maxLength={item.maxLength || 4000}
/>
)}
{item.type === VariableInputEnum.textInput && (
<PromptEditor
value={item.defaultValue}
onChange={(e) => setValue(item.key, e)}
bg={'myGray.50'}
minH={50}
maxH={150}
showOpenModal={false}
/>
)}
{item.type === VariableInputEnum.select && (
<Controller
key={item.key}

View File

@@ -38,6 +38,7 @@ import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/consta
import { useTranslation } from 'react-i18next';
import { Controller, useForm } from 'react-hook-form';
import MySelect from '@fastgpt/web/components/common/MySelect';
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
type props = {
value: UserChatItemValueItemType | AIChatItemValueItemType;
@@ -240,6 +241,15 @@ const RenderUserFormInteractive = React.memo(function RenderFormInput({
maxLength={input.maxLength || 4000}
/>
)}
{input.type === FlowNodeInputTypeEnum.textInput && (
<PromptEditor
value={input.value}
onChange={(e) => setValue(input.label, e)}
minH={40}
maxH={100}
showOpenModal={false}
/>
)}
{input.type === FlowNodeInputTypeEnum.numberInput && (
<NumberInput
step={1}

View File

@@ -13,7 +13,6 @@ import {
Box,
Button,
Flex,
Textarea,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
@@ -34,6 +33,7 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { AppContext } from '../../../context';
import { VariableInputItem } from '@/components/core/chat/ChatContainer/ChatBox/components/VariableInput';
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
const MyRightDrawer = dynamic(
() => import('@fastgpt/web/components/common/MyDrawer/MyRightDrawer')
@@ -270,10 +270,14 @@ export const useDebug = () => {
const RenderInput = (() => {
if (input.valueType === WorkflowIOValueTypeEnum.string) {
return (
<Textarea
{...register(`nodeVariables.${input.key}`, {
required
})}
<PromptEditor
value={getValues(`nodeVariables.${input.key}`)}
onChange={(e) => {
setValue(`nodeVariables.${input.key}`, e);
}}
minH={50}
maxH={150}
showOpenModal={false}
placeholder={t(input.placeholder || ('' as any))}
bg={'myGray.50'}
/>

View File

@@ -11,7 +11,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import InputTypeConfig from '../NodePluginIO/InputTypeConfig';
export const defaultFormInput: UserInputFormItemType = {
type: FlowNodeInputTypeEnum.input,
type: FlowNodeInputTypeEnum.textInput,
key: '',
label: '',
description: '',
@@ -54,14 +54,8 @@ const InputFormEditModal = ({
const inputTypeList = [
{
icon: 'core/workflow/inputType/input',
label: t('common:core.workflow.inputType.input'),
value: FlowNodeInputTypeEnum.input,
defaultValueType: WorkflowIOValueTypeEnum.string
},
{
icon: 'core/workflow/inputType/textarea',
label: t('common:core.workflow.inputType.textarea'),
value: FlowNodeInputTypeEnum.textarea,
label: t('common:core.workflow.inputType.textInput'),
value: FlowNodeInputTypeEnum.textInput,
defaultValueType: WorkflowIOValueTypeEnum.string
},
{

View File

@@ -54,14 +54,8 @@ const FieldEditModal = ({
},
{
icon: 'core/workflow/inputType/input',
label: t('common:core.workflow.inputType.input'),
value: FlowNodeInputTypeEnum.input,
defaultValueType: WorkflowIOValueTypeEnum.string
},
{
icon: 'core/workflow/inputType/textarea',
label: t('common:core.workflow.inputType.textarea'),
value: FlowNodeInputTypeEnum.textarea,
label: t('common:core.workflow.inputType.textInput'),
value: FlowNodeInputTypeEnum.textInput,
defaultValueType: WorkflowIOValueTypeEnum.string
},
{
@@ -138,11 +132,24 @@ const FieldEditModal = ({
});
const { getValues, setValue, watch, reset } = form;
const inputType = watch('renderTypeList.0') || FlowNodeInputTypeEnum.reference;
const renderTypeList = watch('renderTypeList');
const inputType = renderTypeList[0] || FlowNodeInputTypeEnum.reference;
const valueType = watch('valueType');
const [isToolInput, { toggle: setIsToolInput }] = useBoolean(!!getValues('toolDescription'));
const isRefrence = renderTypeList.includes(FlowNodeInputTypeEnum.reference);
const setIsRefrence = () => {
if (isRefrence) {
setValue(
'renderTypeList',
renderTypeList.filter((item) => item !== FlowNodeInputTypeEnum.reference)
);
} else {
setValue('renderTypeList', [...getValues('renderTypeList'), FlowNodeInputTypeEnum.reference]);
}
};
const maxLength = watch('maxLength');
const max = watch('max');
const min = watch('min');
@@ -328,6 +335,8 @@ const FieldEditModal = ({
defaultValue={defaultInputValue}
isToolInput={isToolInput}
setIsToolInput={setIsToolInput}
isRefrence={isRefrence}
setIsRefrence={setIsRefrence}
valueType={valueType}
defaultValueType={defaultValueType}
onSubmitSuccess={onSubmitSuccess}

View File

@@ -34,6 +34,7 @@ import { useFieldArray, UseFormReturn } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import DndDrag, { Draggable } from '@fastgpt/web/components/common/DndDrag';
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
type ListValueType = { id: string; value: string; label: string }[];
@@ -50,6 +51,8 @@ const InputTypeConfig = ({
defaultValue,
isToolInput,
setIsToolInput,
isRefrence,
setIsRefrence,
valueType,
defaultValueType,
onSubmitSuccess,
@@ -72,6 +75,8 @@ const InputTypeConfig = ({
// Plugin-specific fields
isToolInput?: boolean;
setIsToolInput?: () => void;
isRefrence?: boolean;
setIsRefrence?: () => void;
valueType?: WorkflowIOValueTypeEnum;
defaultValueType?: WorkflowIOValueTypeEnum;
@@ -132,7 +137,7 @@ const InputTypeConfig = ({
}, [inputType]);
const showMaxLenInput = useMemo(() => {
const list = [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.textarea];
const list = [FlowNodeInputTypeEnum.textInput];
return list.includes(inputType as FlowNodeInputTypeEnum);
}, [inputType]);
@@ -143,8 +148,7 @@ const InputTypeConfig = ({
const showDefaultValue = useMemo(() => {
const list = [
FlowNodeInputTypeEnum.input,
FlowNodeInputTypeEnum.textarea,
FlowNodeInputTypeEnum.textInput,
FlowNodeInputTypeEnum.JSONEditor,
FlowNodeInputTypeEnum.numberInput,
FlowNodeInputTypeEnum.switch,
@@ -158,7 +162,7 @@ const InputTypeConfig = ({
<Stack flex={1} borderLeft={'1px solid #F0F1F6'} justifyContent={'space-between'}>
<Flex flexDirection={'column'} p={8} pb={2} gap={4} flex={'1 0 0'} overflow={'auto'}>
<Flex alignItems={'center'}>
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{typeLabels.name[type] || typeLabels.name.formInput}
</FormLabel>
<Input
@@ -170,7 +174,7 @@ const InputTypeConfig = ({
/>
</Flex>
<Flex alignItems={'flex-start'}>
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{typeLabels.description[type] || typeLabels.description.plugin}
</FormLabel>
<Textarea
@@ -184,7 +188,7 @@ const InputTypeConfig = ({
{/* value type */}
{type !== 'formInput' && (
<Flex alignItems={'center'}>
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{t('common:core.module.Data Type')}
</FormLabel>
{showValueTypeSelect ? (
@@ -208,18 +212,33 @@ const InputTypeConfig = ({
)}
{showRequired && (
<Flex alignItems={'center'} minH={'40px'}>
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{t('workflow:field_required')}
</FormLabel>
<Switch {...register('required')} />
</Flex>
)}
{/* reference */}
{inputType === FlowNodeInputTypeEnum.reference && (
{inputType !== FlowNodeInputTypeEnum.reference && setIsRefrence && (
<>
<Flex alignItems={'center'} minH={'40px'}>
<FormLabel flex={'1'} fontWeight={'medium'}>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{t('workflow:field_used_as_reference')}
</FormLabel>
<Switch
isChecked={isRefrence}
onChange={(e) => {
setIsRefrence();
}}
/>
</Flex>
</>
)}
{/* reference */}
{(inputType === FlowNodeInputTypeEnum.reference || isRefrence) && (
<>
<Flex alignItems={'center'} minH={'40px'}>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{t('workflow:field_used_as_tool_input')}
</FormLabel>
<Switch
@@ -234,7 +253,7 @@ const InputTypeConfig = ({
{showMaxLenInput && (
<Flex alignItems={'center'}>
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{t('common:core.module.Max Length')}
</FormLabel>
<MyNumberInput
@@ -254,7 +273,7 @@ const InputTypeConfig = ({
{showMinMaxInput && (
<>
<Flex alignItems={'center'}>
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{t('common:core.module.Max Value')}
</FormLabel>
<MyNumberInput
@@ -268,7 +287,7 @@ const InputTypeConfig = ({
/>
</Flex>
<Flex alignItems={'center'}>
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{t('common:core.module.Min Value')}
</FormLabel>
<MyNumberInput
@@ -286,70 +305,77 @@ const InputTypeConfig = ({
{showDefaultValue && (
<Flex alignItems={'center'} minH={'40px'}>
<FormLabel
flex={inputType === FlowNodeInputTypeEnum.switch ? 1 : '0 0 100px'}
fontWeight={'medium'}
>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{t('common:core.module.Default Value')}
</FormLabel>
{inputType === FlowNodeInputTypeEnum.numberInput && (
<NumberInput flex={1} step={1} min={min} max={max} position={'relative'}>
<NumberInputField
{...register('defaultValue', {
min: min,
max: max
})}
<Flex alignItems={'start'} flex={1} h={10}>
{inputType === FlowNodeInputTypeEnum.numberInput && (
<NumberInput flex={1} step={1} min={min} max={max} position={'relative'}>
<NumberInputField
{...register('defaultValue', {
min: min,
max: max
})}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
)}
{inputType === FlowNodeInputTypeEnum.textInput && (
<PromptEditor
value={defaultValue}
onChange={(e) => {
setValue('defaultValue', e);
}}
minH={40}
maxH={200}
showOpenModal={false}
bg={'myGray.50'}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
)}
{inputType === FlowNodeInputTypeEnum.input && (
<Input bg={'myGray.50'} maxLength={maxLength} {...register('defaultValue')} />
)}
{inputType === FlowNodeInputTypeEnum.textarea && (
<Textarea bg={'myGray.50'} maxLength={maxLength} {...register('defaultValue')} />
)}
{inputType === FlowNodeInputTypeEnum.JSONEditor && (
<JsonEditor
bg={'myGray.50'}
resize
w={'full'}
onChange={(e) => {
setValue('defaultValue', e);
}}
defaultValue={defaultValue}
/>
)}
{inputType === FlowNodeInputTypeEnum.switch && <Switch {...register('defaultValue')} />}
{inputType === FlowNodeInputTypeEnum.select && (
<MySelect<string>
list={[defaultListValue, ...listValue]
.filter((item) => item.label !== '')
.map((item) => ({
label: item.label,
value: item.value
}))}
value={
defaultValue && listValue.map((item) => item.value).includes(defaultValue)
? defaultValue
: ''
}
onchange={(e) => {
setValue('defaultValue', e);
}}
w={'200px'}
/>
)}
)}
{inputType === FlowNodeInputTypeEnum.JSONEditor && (
<JsonEditor
bg={'myGray.50'}
resize
w={'full'}
onChange={(e) => {
setValue('defaultValue', e);
}}
defaultValue={defaultValue}
/>
)}
{inputType === FlowNodeInputTypeEnum.switch && (
<Switch {...register('defaultValue')} />
)}
{inputType === FlowNodeInputTypeEnum.select && (
<MySelect<string>
list={[defaultListValue, ...listValue]
.filter((item) => item.label !== '')
.map((item) => ({
label: item.label,
value: item.value
}))}
value={
defaultValue && listValue.map((item) => item.value).includes(defaultValue)
? defaultValue
: ''
}
onchange={(e) => {
setValue('defaultValue', e);
}}
w={'200px'}
/>
)}
</Flex>
</Flex>
)}
{inputType === FlowNodeInputTypeEnum.addInputParam && (
<>
<Flex alignItems={'center'}>
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{t('common:core.module.Input Type')}
</FormLabel>
<Box fontSize={'14px'}>{t('workflow:only_the_reference_type_is_supported')}</Box>
@@ -432,7 +458,7 @@ const InputTypeConfig = ({
transform={snapshot.isDragging ? `scale(0.5)` : ''}
transformOrigin={'top left'}
>
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{`${t('common:core.module.variable.variable options')} ${i + 1}`}
</FormLabel>
<FormControl>

View File

@@ -16,10 +16,6 @@ const RenderList: {
types: [FlowNodeInputTypeEnum.reference],
Component: dynamic(() => import('./templates/Reference'))
},
{
types: [FlowNodeInputTypeEnum.input],
Component: dynamic(() => import('./templates/TextInput'))
},
{
types: [FlowNodeInputTypeEnum.select],
Component: dynamic(() => import('./templates/Select'))
@@ -33,8 +29,8 @@ const RenderList: {
Component: dynamic(() => import('./templates/Switch'))
},
{
types: [FlowNodeInputTypeEnum.textarea],
Component: dynamic(() => import('./templates/Textarea'))
types: [FlowNodeInputTypeEnum.textInput],
Component: dynamic(() => import('./templates/TextInput'))
},
{
types: [FlowNodeInputTypeEnum.selectApp],
@@ -67,6 +63,14 @@ const RenderList: {
{
types: [FlowNodeInputTypeEnum.settingDatasetQuotePrompt],
Component: dynamic(() => import('./templates/SettingQuotePrompt'))
},
{
types: [FlowNodeInputTypeEnum.input],
Component: dynamic(() => import('./templates/TextInput'))
},
{
types: [FlowNodeInputTypeEnum.textarea],
Component: dynamic(() => import('./templates/Textarea'))
}
];

View File

@@ -1,37 +1,65 @@
import React, { useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import type { RenderInputProps } from '../type';
import { Input } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context';
import { useTranslation } from 'next-i18next';
import { useCreation } from 'ahooks';
import { AppContext } from '@/pages/app/detail/components/context';
import { getEditorVariables } from '../../../../../utils';
const TextInput = ({ item, nodeId }: RenderInputProps) => {
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const TextInputRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
const { t } = useTranslation();
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const edges = useContextSelector(WorkflowContext, (v) => v.edges);
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const { appDetail } = useContextSelector(AppContext, (v) => v);
// get variable
const variables = useCreation(() => {
return getEditorVariables({
nodeId,
nodeList,
edges,
appDetail,
t
});
}, [nodeId, nodeList, edges, appDetail, t]);
const onChange = useCallback(
(e: string) => {
onChangeNode({
nodeId,
type: 'updateInput',
key: item.key,
value: {
...item,
value: e
}
});
},
[item, nodeId, onChangeNode]
);
const Render = useMemo(() => {
return (
<Input
placeholder={t(item.placeholder as any) ?? t(item.description as any)}
defaultValue={item.value}
bg={'white'}
px={3}
borderRadius={'sm'}
onBlur={(e) => {
onChangeNode({
nodeId,
type: 'updateInput',
key: item.key,
value: {
...item,
value: e.target.value
}
});
}}
<PromptEditor
variableLabels={variables}
variables={variables}
title={t(item.label as any)}
maxLength={item.maxLength}
minH={40}
maxH={120}
placeholder={t((item.placeholder as any) || '')}
value={item.value}
onChange={onChange}
isFlow={true}
/>
);
}, [item, nodeId, onChangeNode, t]);
}, [item.label, item.maxLength, item.placeholder, item.value, onChange, t, variables]);
return Render;
};
export default React.memo(TextInput);
export default React.memo(TextInputRender);