mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-29 09:44:47 +00:00
4.8.12 dev (#2928)
* perf: optimize global variables (#2863) * feat: add global variable types * add global variables to debug * fix select dnd * unify InputTypeConfig params * feat: http node url support variables (#2891) * feat: http node url support variables * change to prompt editor * fix: global variables (#2892) * fix global variables * fix type * perf: global variables * perf: workflow delete node error (#2905) * update lock * update 4812 doc * feat: add node course url config (#2897) * feat: add node course url config * change plugin course url * change default doc url * change url store * delete unused code * fix: global variable (#2915) * fix: global variable * add comment * fix: interactive check * locj * perf: debug switch to global tab when click run & global var default reset (#2925) * fix: tool course url * fix: global var default value & wrap variable form (#2926) * fix: add dataset tags not update render (#2927) * feat: tool will save histories * perf: global variables code * perf: FE_DOMAIN config --------- Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
@@ -2,17 +2,7 @@ import React, { useCallback, useMemo, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ModalFooter,
|
||||
ModalBody,
|
||||
NumberInput,
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberDecrementStepper,
|
||||
Flex,
|
||||
Switch,
|
||||
Input,
|
||||
FormControl,
|
||||
Table,
|
||||
Thead,
|
||||
Tbody,
|
||||
@@ -20,7 +10,7 @@ import {
|
||||
Th,
|
||||
Td,
|
||||
TableContainer,
|
||||
useDisclosure
|
||||
Stack
|
||||
} from '@chakra-ui/react';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import {
|
||||
@@ -31,38 +21,34 @@ import {
|
||||
import type { VariableItemType } from '@fastgpt/global/core/app/type.d';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useFieldArray } from 'react-hook-form';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import MyRadio from '@/components/common/MyRadio';
|
||||
import { formatEditorVariablePickerIcon } from '@fastgpt/global/core/workflow/utils';
|
||||
import ChatFunctionTip from './Tip';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import { FlowValueTypeMap } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import InputTypeConfig from '@/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig';
|
||||
|
||||
export const defaultVariable: VariableItemType = {
|
||||
id: nanoid(),
|
||||
key: 'key',
|
||||
label: 'label',
|
||||
key: '',
|
||||
label: '',
|
||||
type: VariableInputEnum.input,
|
||||
description: '',
|
||||
required: true,
|
||||
maxLen: 50,
|
||||
enums: [{ value: '' }],
|
||||
valueType: WorkflowIOValueTypeEnum.string
|
||||
};
|
||||
export const addVariable = () => {
|
||||
const newVariable = { ...defaultVariable, key: '', id: '' };
|
||||
return newVariable;
|
||||
|
||||
type InputItemType = VariableItemType & {
|
||||
list: { label: string; value: string }[];
|
||||
};
|
||||
const valueTypeMap = {
|
||||
[VariableInputEnum.input]: WorkflowIOValueTypeEnum.string,
|
||||
[VariableInputEnum.select]: WorkflowIOValueTypeEnum.string,
|
||||
[VariableInputEnum.textarea]: WorkflowIOValueTypeEnum.string,
|
||||
[VariableInputEnum.custom]: WorkflowIOValueTypeEnum.any
|
||||
|
||||
export const addVariable = () => {
|
||||
const newVariable = { ...defaultVariable, key: '', id: '', list: [{ value: '', label: '' }] };
|
||||
return newVariable;
|
||||
};
|
||||
|
||||
const VariableEdit = ({
|
||||
@@ -74,46 +60,37 @@ const VariableEdit = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
|
||||
const VariableTypeList = useMemo(
|
||||
const form = useForm<VariableItemType>();
|
||||
const { setValue, reset, watch, getValues } = form;
|
||||
const value = getValues();
|
||||
const type = watch('type');
|
||||
const valueType = watch('valueType');
|
||||
const max = watch('max');
|
||||
const min = watch('min');
|
||||
const defaultValue = watch('defaultValue');
|
||||
|
||||
const inputTypeList = useMemo(
|
||||
() =>
|
||||
Object.entries(variableMap).map(([key, value]) => ({
|
||||
title: t(value.title as any),
|
||||
icon: value.icon,
|
||||
value: key
|
||||
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) : ''
|
||||
})),
|
||||
[t]
|
||||
);
|
||||
|
||||
const { isOpen: isOpenEdit, onOpen: onOpenEdit, onClose: onCloseEdit } = useDisclosure();
|
||||
const {
|
||||
setValue,
|
||||
reset: resetEdit,
|
||||
register: registerEdit,
|
||||
getValues: getValuesEdit,
|
||||
setValue: setValuesEdit,
|
||||
control: editVariableController,
|
||||
handleSubmit: handleSubmitEdit,
|
||||
watch
|
||||
} = useForm<{ variable: VariableItemType }>();
|
||||
|
||||
const variableType = watch('variable.type');
|
||||
const valueType = watch('variable.valueType');
|
||||
|
||||
const {
|
||||
fields: selectEnums,
|
||||
append: appendEnums,
|
||||
remove: removeEnums
|
||||
} = useFieldArray({
|
||||
control: editVariableController,
|
||||
name: 'variable.enums'
|
||||
});
|
||||
const defaultValueType = useMemo(() => {
|
||||
const item = inputTypeList.find((item) => item.value === type);
|
||||
return item?.defaultValueType;
|
||||
}, [inputTypeList, type]);
|
||||
|
||||
const formatVariables = useMemo(() => {
|
||||
const results = formatEditorVariablePickerIcon(variables);
|
||||
return results.map((item) => {
|
||||
const variable = variables.find((variable) => variable.key === item.key);
|
||||
return results.map<VariableItemType & { icon?: string }>((item) => {
|
||||
const variable = variables.find((variable) => variable.key === item.key)!;
|
||||
return {
|
||||
...variable,
|
||||
icon: item.icon
|
||||
@@ -121,45 +98,12 @@ const VariableEdit = ({
|
||||
});
|
||||
}, [variables]);
|
||||
|
||||
const valueTypeSelectList = useMemo(
|
||||
() =>
|
||||
Object.values(FlowValueTypeMap)
|
||||
.map((item) => ({
|
||||
label: t(item.label as any),
|
||||
value: item.value
|
||||
}))
|
||||
.filter(
|
||||
(item) =>
|
||||
![
|
||||
WorkflowIOValueTypeEnum.arrayAny,
|
||||
WorkflowIOValueTypeEnum.selectApp,
|
||||
WorkflowIOValueTypeEnum.selectDataset,
|
||||
WorkflowIOValueTypeEnum.dynamic
|
||||
].includes(item.value)
|
||||
),
|
||||
[t]
|
||||
);
|
||||
const showValueTypeSelect = variableType === VariableInputEnum.custom;
|
||||
const onSubmitSuccess = useCallback(
|
||||
(data: InputItemType, action: 'confirm' | 'continue') => {
|
||||
data.label = data?.label?.trim();
|
||||
|
||||
const onSubmit = useCallback(
|
||||
({ variable }: { variable: VariableItemType }) => {
|
||||
variable.key = variable.key.trim();
|
||||
|
||||
// check select
|
||||
if (variable.type === VariableInputEnum.select) {
|
||||
const enums = variable.enums.filter((item) => item.value);
|
||||
if (enums.length === 0) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('common:core.module.variable.variable option is required')
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check repeat key
|
||||
const existingVariable = variables.find(
|
||||
(item) => item.key === variable.key && item.id !== variable.id
|
||||
(item) => item.label === data.label && item.id !== data.id
|
||||
);
|
||||
if (existingVariable) {
|
||||
toast({
|
||||
@@ -169,32 +113,59 @@ const VariableEdit = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// set valuetype based on variable.type
|
||||
variable.valueType =
|
||||
variable.type === VariableInputEnum.custom
|
||||
? variable.valueType
|
||||
: valueTypeMap[variable.type];
|
||||
data.key = data.label;
|
||||
data.enums = data.list;
|
||||
|
||||
// set default required value based on variableType
|
||||
if (variable.type === VariableInputEnum.custom) {
|
||||
variable.required = false;
|
||||
if (data.type === VariableInputEnum.custom) {
|
||||
data.required = false;
|
||||
}
|
||||
|
||||
if (data.type === VariableInputEnum.numberInput) {
|
||||
data.valueType = WorkflowIOValueTypeEnum.number;
|
||||
}
|
||||
|
||||
const onChangeVariable = [...variables];
|
||||
// update
|
||||
if (variable.id) {
|
||||
const index = variables.findIndex((item) => item.id === variable.id);
|
||||
onChangeVariable[index] = variable;
|
||||
if (data.id) {
|
||||
const index = variables.findIndex((item) => item.id === data.id);
|
||||
onChangeVariable[index] = data;
|
||||
} else {
|
||||
onChangeVariable.push({
|
||||
...variable,
|
||||
...data,
|
||||
id: nanoid()
|
||||
});
|
||||
}
|
||||
onChange(onChangeVariable);
|
||||
onCloseEdit();
|
||||
|
||||
if (action === 'confirm') {
|
||||
onChange(onChangeVariable);
|
||||
reset({});
|
||||
} else if (action === 'continue') {
|
||||
onChange(onChangeVariable);
|
||||
toast({
|
||||
status: 'success',
|
||||
title: t('common:common.Add Success')
|
||||
});
|
||||
reset({
|
||||
...addVariable(),
|
||||
defaultValue: ''
|
||||
});
|
||||
}
|
||||
},
|
||||
[onChange, onCloseEdit, t, toast, variables]
|
||||
[variables, toast, t, onChange, reset]
|
||||
);
|
||||
|
||||
const onSubmitError = useCallback(
|
||||
(e: Object) => {
|
||||
for (const item of Object.values(e)) {
|
||||
if (item.message) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: item.message
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[toast]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -212,8 +183,7 @@ const VariableEdit = ({
|
||||
size={'sm'}
|
||||
mr={'-5px'}
|
||||
onClick={() => {
|
||||
resetEdit({ variable: addVariable() });
|
||||
onOpenEdit();
|
||||
reset(addVariable());
|
||||
}}
|
||||
>
|
||||
{t('common:common.Add New')}
|
||||
@@ -232,7 +202,7 @@ const VariableEdit = ({
|
||||
w={'18px !important'}
|
||||
p={0}
|
||||
/>
|
||||
<Th fontSize={'mini'}>{t('common:core.module.variable.variable name')}</Th>
|
||||
<Th fontSize={'mini'}>{t('workflow:Variable_name')}</Th>
|
||||
<Th fontSize={'mini'}>{t('common:core.module.variable.key')}</Th>
|
||||
<Th fontSize={'mini'}>{t('common:common.Require Input')}</Th>
|
||||
<Th fontSize={'mini'} borderRadius={'none !important'}></Th>
|
||||
@@ -241,8 +211,8 @@ const VariableEdit = ({
|
||||
<Tbody>
|
||||
{formatVariables.map((item) => (
|
||||
<Tr key={item.id}>
|
||||
<Td textAlign={'center'} p={0} pl={3}>
|
||||
<MyIcon name={item.icon as any} w={'14px'} color={'myGray.500'} />
|
||||
<Td p={0} pl={3}>
|
||||
<MyIcon name={item.icon as any} w={'16px'} color={'myGray.500'} />
|
||||
</Td>
|
||||
<Td>{item.label}</Td>
|
||||
<Td>{item.key}</Td>
|
||||
@@ -254,8 +224,11 @@ const VariableEdit = ({
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
resetEdit({ variable: item });
|
||||
onOpenEdit();
|
||||
const formattedItem = {
|
||||
...item,
|
||||
list: item.enums || []
|
||||
};
|
||||
reset(formattedItem);
|
||||
}}
|
||||
/>
|
||||
<MyIcon
|
||||
@@ -274,160 +247,100 @@ const VariableEdit = ({
|
||||
</TableContainer>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Edit modal */}
|
||||
<MyModal
|
||||
iconSrc="core/app/simpleMode/variable"
|
||||
title={t('common:core.module.Variable Setting')}
|
||||
isOpen={isOpenEdit}
|
||||
onClose={onCloseEdit}
|
||||
maxW={['90vw', '500px']}
|
||||
>
|
||||
<ModalBody>
|
||||
{variableType !== VariableInputEnum.custom && (
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel w={'70px'}>{t('common:common.Require Input')}</FormLabel>
|
||||
<Switch {...registerEdit('variable.required')} />
|
||||
</Flex>
|
||||
)}
|
||||
<Flex mt={5} alignItems={'center'}>
|
||||
<FormLabel w={'80px'}>{t('common:core.module.variable.variable name')}</FormLabel>
|
||||
<Input
|
||||
{...registerEdit('variable.label', {
|
||||
required: t('common:core.module.variable.variable name is required')
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex mt={5} alignItems={'center'}>
|
||||
<FormLabel w={'80px'}>{t('common:core.module.variable.key')}</FormLabel>
|
||||
<Input
|
||||
{...registerEdit('variable.key', {
|
||||
required: t('common:core.module.variable.key is required')
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex mt={5} alignItems={'center'}>
|
||||
<FormLabel w={'80px'}>{t('workflow:value_type')}</FormLabel>
|
||||
{showValueTypeSelect ? (
|
||||
<Box flex={1}>
|
||||
<MySelect<WorkflowIOValueTypeEnum>
|
||||
list={valueTypeSelectList.filter(
|
||||
(item) => item.value !== WorkflowIOValueTypeEnum.arrayAny
|
||||
)}
|
||||
value={valueType}
|
||||
onchange={(e) => {
|
||||
setValue('variable.valueType', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Box fontSize={'14px'}>{valueTypeMap[variableType]}</Box>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
<FormLabel mt={5} mb={2}>
|
||||
{t('common:core.workflow.Variable.Variable type')}
|
||||
</FormLabel>
|
||||
<MyRadio
|
||||
gridGap={4}
|
||||
gridTemplateColumns={'repeat(2,1fr)'}
|
||||
value={variableType}
|
||||
list={VariableTypeList}
|
||||
color={'myGray.600'}
|
||||
hiddenCircle
|
||||
onChange={(e) => {
|
||||
setValuesEdit('variable.type', e as any);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* desc */}
|
||||
{variableMap[variableType]?.desc && (
|
||||
<Box mt={2} fontSize={'sm'} color={'myGray.500'} whiteSpace={'pre-wrap'}>
|
||||
{t(variableMap[variableType].desc as any)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{variableType === VariableInputEnum.input && (
|
||||
<>
|
||||
<FormLabel mt={5} mb={2}>
|
||||
{t('common:core.module.variable.text max length')}
|
||||
{!!Object.keys(value).length && (
|
||||
<MyModal
|
||||
iconSrc="core/app/simpleMode/variable"
|
||||
title={t('common:core.module.Variable Setting')}
|
||||
isOpen={true}
|
||||
onClose={() => reset({})}
|
||||
maxW={['90vw', '928px']}
|
||||
w={'100%'}
|
||||
isCentered
|
||||
>
|
||||
<Flex h={'560px'}>
|
||||
<Stack gap={4} p={8}>
|
||||
<FormLabel color={'myGray.600'} fontWeight={'medium'}>
|
||||
{t('workflow:Variable.Variable type')}
|
||||
</FormLabel>
|
||||
<Box>
|
||||
<NumberInput max={500} min={1} step={1} position={'relative'}>
|
||||
<NumberInputField
|
||||
{...registerEdit('variable.maxLen', {
|
||||
min: 1,
|
||||
max: 500,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
max={500}
|
||||
/>
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
<NumberDecrementStepper />
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
||||
{variableType === VariableInputEnum.select && (
|
||||
<>
|
||||
<Box mt={5} mb={2}>
|
||||
{t('common:core.module.variable.variable options')}
|
||||
</Box>
|
||||
<Box>
|
||||
{selectEnums.map((item, i) => (
|
||||
<Flex key={item.id} mb={2} alignItems={'center'}>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...registerEdit(`variable.enums.${i}.value`, {
|
||||
required: t(
|
||||
'common:core.module.variable.variable option is value is required'
|
||||
)
|
||||
})}
|
||||
/>
|
||||
</FormControl>
|
||||
{selectEnums.length > 1 && (
|
||||
<Flex flexDirection={'column'} gap={4}></Flex>
|
||||
<Box display={'grid'} gridTemplateColumns={'repeat(2, 1fr)'} gap={4}>
|
||||
{inputTypeList.map((item) => {
|
||||
const isSelected = type === item.value;
|
||||
return (
|
||||
<Box
|
||||
display={'flex'}
|
||||
key={item.label}
|
||||
border={isSelected ? '1px solid #3370FF' : '1px solid #DFE2EA'}
|
||||
p={3}
|
||||
rounded={'6px'}
|
||||
fontWeight={'medium'}
|
||||
fontSize={'14px'}
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
boxShadow={isSelected ? '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)' : 'none'}
|
||||
_hover={{
|
||||
'& > svg': {
|
||||
color: 'primary.600'
|
||||
},
|
||||
'& > span': {
|
||||
color: 'myGray.900'
|
||||
},
|
||||
border: '1px solid #3370FF',
|
||||
boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)'
|
||||
}}
|
||||
onClick={() => {
|
||||
const defaultValIsNumber = !isNaN(Number(value.defaultValue));
|
||||
// 如果切换到 numberInput,不是数字,则清空
|
||||
if (
|
||||
item.value === VariableInputEnum.select ||
|
||||
(item.value === VariableInputEnum.numberInput && !defaultValIsNumber)
|
||||
) {
|
||||
setValue('defaultValue', '');
|
||||
}
|
||||
setValue('type', item.value);
|
||||
}}
|
||||
>
|
||||
<MyIcon
|
||||
ml={3}
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
p={2}
|
||||
borderRadius={'md'}
|
||||
_hover={{ bg: 'red.100' }}
|
||||
onClick={() => removeEnums(i)}
|
||||
name={item.icon as any}
|
||||
w={'20px'}
|
||||
mr={1.5}
|
||||
color={isSelected ? 'primary.600' : 'myGray.400'}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
))}
|
||||
<Box
|
||||
as="span"
|
||||
color={isSelected ? 'myGray.900' : 'inherit'}
|
||||
pr={4}
|
||||
whiteSpace="nowrap"
|
||||
>
|
||||
{item.label}
|
||||
</Box>
|
||||
{item.description && (
|
||||
<QuestionTip label={item.description as string} ml={1} />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
<Button
|
||||
variant={'solid'}
|
||||
w={'100%'}
|
||||
textAlign={'left'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
bg={'myGray.100 !important'}
|
||||
onClick={() => appendEnums({ value: '' })}
|
||||
>
|
||||
{t('common:core.module.variable add option')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onCloseEdit}>
|
||||
{t('common:common.Close')}
|
||||
</Button>
|
||||
<Button onClick={handleSubmitEdit(onSubmit)}>
|
||||
{getValuesEdit('variable.id')
|
||||
? t('common:common.Confirm Update')
|
||||
: t('common:common.Add New')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
</Stack>
|
||||
<InputTypeConfig
|
||||
form={form}
|
||||
type={'variable'}
|
||||
isEdit={!!value.key}
|
||||
inputType={type}
|
||||
valueType={valueType}
|
||||
defaultValue={defaultValue}
|
||||
defaultValueType={defaultValueType}
|
||||
max={max}
|
||||
min={min}
|
||||
onClose={() => reset({})}
|
||||
onSubmitSuccess={onSubmitSuccess}
|
||||
onSubmitError={onSubmitError}
|
||||
/>
|
||||
</Flex>
|
||||
</MyModal>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@@ -81,16 +81,11 @@ const ChatInput = ({
|
||||
const canSendMessage = havInput && !hasFileUploading;
|
||||
|
||||
// Upload files
|
||||
useRequest2(
|
||||
async () => {
|
||||
uploadFiles();
|
||||
},
|
||||
{
|
||||
manual: false,
|
||||
errorToast: t('common:upload_file_error'),
|
||||
refreshDeps: [fileList, outLinkAuthData, chatId]
|
||||
}
|
||||
);
|
||||
useRequest2(uploadFiles, {
|
||||
manual: false,
|
||||
errorToast: t('common:upload_file_error'),
|
||||
refreshDeps: [fileList, outLinkAuthData, chatId]
|
||||
});
|
||||
|
||||
/* on send */
|
||||
const handleSend = useCallback(
|
||||
|
@@ -1,7 +1,18 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import { Controller, UseFormReturn } from 'react-hook-form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Box, Button, Card, FormControl, Input, Textarea } from '@chakra-ui/react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Input,
|
||||
NumberDecrementStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberInput,
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
Textarea
|
||||
} from '@chakra-ui/react';
|
||||
import ChatAvatar from './ChatAvatar';
|
||||
import { MessageCardStyle } from '../constants';
|
||||
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
@@ -10,6 +21,113 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { ChatBoxInputFormType } from '../type.d';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
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';
|
||||
|
||||
export const VariableInputItem = ({
|
||||
item,
|
||||
variablesForm
|
||||
}: {
|
||||
item: VariableItemType;
|
||||
variablesForm: UseFormReturn<any>;
|
||||
}) => {
|
||||
const { register, control, setValue } = variablesForm;
|
||||
|
||||
return (
|
||||
<Box key={item.id} mb={4} pl={1}>
|
||||
<Box
|
||||
as={'label'}
|
||||
display={'flex'}
|
||||
position={'relative'}
|
||||
mb={1}
|
||||
alignItems={'center'}
|
||||
w={'full'}
|
||||
>
|
||||
{item.label}
|
||||
{item.required && (
|
||||
<Box
|
||||
position={'absolute'}
|
||||
top={'-2px'}
|
||||
left={'-8px'}
|
||||
color={'red.500'}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
*
|
||||
</Box>
|
||||
)}
|
||||
{item.description && <QuestionTip ml={1} label={item.description} />}
|
||||
</Box>
|
||||
{item.type === VariableInputEnum.input && (
|
||||
<Input
|
||||
maxLength={item.maxLength || 4000}
|
||||
bg={'myGray.50'}
|
||||
{...register(item.key, {
|
||||
required: item.required
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
{item.type === VariableInputEnum.textarea && (
|
||||
<Textarea
|
||||
{...register(item.key, {
|
||||
required: item.required
|
||||
})}
|
||||
rows={5}
|
||||
bg={'myGray.50'}
|
||||
maxLength={item.maxLength || 4000}
|
||||
/>
|
||||
)}
|
||||
{item.type === VariableInputEnum.select && (
|
||||
<Controller
|
||||
key={item.key}
|
||||
control={control}
|
||||
name={item.key}
|
||||
rules={{ required: item.required }}
|
||||
render={({ field: { ref, value } }) => {
|
||||
return (
|
||||
<MySelect
|
||||
ref={ref}
|
||||
width={'100%'}
|
||||
list={(item.enums || []).map((item: { value: any }) => ({
|
||||
label: item.value,
|
||||
value: item.value
|
||||
}))}
|
||||
value={value}
|
||||
onchange={(e) => setValue(item.key, e)}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{item.type === VariableInputEnum.numberInput && (
|
||||
<Controller
|
||||
key={item.key}
|
||||
control={control}
|
||||
name={item.key}
|
||||
rules={{ required: item.required, min: item.min, max: item.max }}
|
||||
render={({ field: { ref, value, onChange } }) => (
|
||||
<NumberInput
|
||||
step={1}
|
||||
min={item.min}
|
||||
max={item.max}
|
||||
bg={'white'}
|
||||
rounded={'md'}
|
||||
clampValueOnBlur={false}
|
||||
value={value}
|
||||
onChange={(valueString) => onChange(Number(valueString))}
|
||||
>
|
||||
<NumberInputField ref={ref} bg={'white'} />
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
<NumberDecrementStepper />
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const VariableInput = ({
|
||||
chatForm,
|
||||
@@ -21,13 +139,22 @@ const VariableInput = ({
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { appAvatar, variableList, variablesForm } = useContextSelector(ChatBoxContext, (v) => v);
|
||||
const { register, setValue, handleSubmit: handleSubmitChat, control } = variablesForm;
|
||||
const { reset, handleSubmit: handleSubmitChat } = variablesForm;
|
||||
|
||||
const defaultValues = useMemo(() => {
|
||||
return variableList.reduce((acc: Record<string, any>, item) => {
|
||||
acc[item.key] = item.defaultValue;
|
||||
return acc;
|
||||
}, {});
|
||||
}, [variableList]);
|
||||
|
||||
useDeepCompareEffect(() => {
|
||||
reset(defaultValues);
|
||||
}, [defaultValues]);
|
||||
|
||||
return (
|
||||
<Box py={3}>
|
||||
{/* avatar */}
|
||||
<ChatAvatar src={appAvatar} type={'AI'} />
|
||||
{/* message */}
|
||||
<Box textAlign={'left'}>
|
||||
<Card
|
||||
order={2}
|
||||
@@ -38,74 +165,21 @@ const VariableInput = ({
|
||||
boxShadow={'0 0 8px rgba(0,0,0,0.15)'}
|
||||
>
|
||||
{variableList.map((item) => (
|
||||
<Box key={item.id} mb={4}>
|
||||
<Box as={'label'} display={'inline-block'} position={'relative'} mb={1}>
|
||||
{item.label}
|
||||
{item.required && (
|
||||
<Box
|
||||
position={'absolute'}
|
||||
top={'-2px'}
|
||||
right={'-10px'}
|
||||
color={'red.500'}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
*
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
{item.type === VariableInputEnum.input && (
|
||||
<Input
|
||||
bg={'myWhite.400'}
|
||||
{...register(item.key, {
|
||||
required: item.required
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
{item.type === VariableInputEnum.textarea && (
|
||||
<Textarea
|
||||
bg={'myWhite.400'}
|
||||
{...register(item.key, {
|
||||
required: item.required
|
||||
})}
|
||||
rows={5}
|
||||
maxLength={4000}
|
||||
/>
|
||||
)}
|
||||
{item.type === VariableInputEnum.select && (
|
||||
<Controller
|
||||
key={item.key}
|
||||
control={control}
|
||||
name={item.key}
|
||||
rules={{ required: item.required }}
|
||||
render={({ field: { ref, value } }) => {
|
||||
return (
|
||||
<MySelect
|
||||
ref={ref}
|
||||
width={'100%'}
|
||||
list={(item.enums || []).map((item) => ({
|
||||
label: item.value,
|
||||
value: item.value
|
||||
}))}
|
||||
value={value}
|
||||
onchange={(e) => setValue(item.key, e)}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<VariableInputItem key={item.id} item={item} variablesForm={variablesForm} />
|
||||
))}
|
||||
{!chatStarted && (
|
||||
<Button
|
||||
leftIcon={<MyIcon name={'core/chat/chatFill'} w={'16px'} />}
|
||||
size={'sm'}
|
||||
maxW={'100px'}
|
||||
onClick={handleSubmitChat(() => {
|
||||
chatForm.setValue('chatStarted', true);
|
||||
})}
|
||||
>
|
||||
{t('common:core.chat.Start Chat')}
|
||||
</Button>
|
||||
<Box>
|
||||
<Button
|
||||
leftIcon={<MyIcon name={'core/chat/chatFill'} w={'16px'} />}
|
||||
size={'sm'}
|
||||
maxW={'100px'}
|
||||
onClick={handleSubmitChat(() => {
|
||||
chatForm.setValue('chatStarted', true);
|
||||
})}
|
||||
>
|
||||
{t('common:core.chat.Start Chat')}
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</Card>
|
||||
</Box>
|
||||
|
@@ -181,7 +181,7 @@ export const useFileUpload = (props: UseFileUploadOptions) => {
|
||||
});
|
||||
|
||||
// Update file url
|
||||
copyFile.url = `${location.origin}${previewUrl}`;
|
||||
copyFile.url = previewUrl;
|
||||
updateFiles(fileIndex, copyFile);
|
||||
} catch (error) {
|
||||
errorFileIndex.push(fileList.findIndex((item) => item.id === file.id)!);
|
||||
|
@@ -495,7 +495,8 @@ const ChatBox = (
|
||||
// 这里,无论是否为交互模式,最后都是 Human 的消息。
|
||||
const messages = chats2GPTMessages({
|
||||
messages: newChatList.slice(0, -1),
|
||||
reserveId: true
|
||||
reserveId: true,
|
||||
reserveTool: true
|
||||
});
|
||||
|
||||
const {
|
||||
|
@@ -57,9 +57,9 @@ export const checkIsInteractiveByHistories = (chatHistories: ChatSiteItemType[])
|
||||
) {
|
||||
const params = lastMessageValue.interactive.params;
|
||||
// 如果用户选择了,则不认为是交互模式(可能是上一轮以交互结尾,发起的新的一轮对话)
|
||||
if ('userSelectOptions' in params && 'userSelectedVal' in params) {
|
||||
if ('userSelectOptions' in params) {
|
||||
return !params.userSelectedVal;
|
||||
} else if ('inputForm' in params && 'submitted' in params) {
|
||||
} else if ('inputForm' in params) {
|
||||
return !params.submitted;
|
||||
}
|
||||
}
|
||||
|
@@ -228,7 +228,8 @@ const PluginRunContextProvider = ({
|
||||
value: []
|
||||
}
|
||||
],
|
||||
reserveId: true
|
||||
reserveId: true,
|
||||
reserveTool: true
|
||||
});
|
||||
|
||||
try {
|
||||
|
Reference in New Issue
Block a user