Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>tags/1.8.0
| @@ -21,6 +21,10 @@ import Checkbox from '@/app/components/base/checkbox' | |||
| import { DEFAULT_FILE_UPLOAD_SETTING } from '@/app/components/workflow/constants' | |||
| import { DEFAULT_VALUE_MAX_LEN } from '@/config' | |||
| import { SimpleSelect } from '@/app/components/base/select' | |||
| import Textarea from '@/app/components/base/textarea' | |||
| import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader' | |||
| import { TransferMethod } from '@/types/app' | |||
| import type { FileEntity } from '@/app/components/base/file-uploader/types' | |||
| const TEXT_MAX_LENGTH = 256 | |||
| @@ -82,6 +86,8 @@ const ConfigModal: FC<IConfigModalProps> = ({ | |||
| return () => { | |||
| const newPayload = produce(tempPayload, (draft) => { | |||
| draft.type = type | |||
| // Clear default value when switching types | |||
| draft.default = undefined | |||
| if ([InputVarType.singleFile, InputVarType.multiFiles].includes(type)) { | |||
| (Object.keys(DEFAULT_FILE_UPLOAD_SETTING)).forEach((key) => { | |||
| if (key !== 'max_length') | |||
| @@ -234,6 +240,41 @@ const ConfigModal: FC<IConfigModalProps> = ({ | |||
| </Field> | |||
| )} | |||
| {/* Default value for text input */} | |||
| {type === InputVarType.textInput && ( | |||
| <Field title={t('appDebug.variableConfig.defaultValue')}> | |||
| <Input | |||
| value={tempPayload.default || ''} | |||
| onChange={e => handlePayloadChange('default')(e.target.value || undefined)} | |||
| placeholder={t('appDebug.variableConfig.inputPlaceholder')!} | |||
| /> | |||
| </Field> | |||
| )} | |||
| {/* Default value for paragraph */} | |||
| {type === InputVarType.paragraph && ( | |||
| <Field title={t('appDebug.variableConfig.defaultValue')}> | |||
| <Textarea | |||
| value={tempPayload.default || ''} | |||
| onChange={e => handlePayloadChange('default')(e.target.value || undefined)} | |||
| placeholder={t('appDebug.variableConfig.inputPlaceholder')!} | |||
| /> | |||
| </Field> | |||
| )} | |||
| {/* Default value for number input */} | |||
| {type === InputVarType.number && ( | |||
| <Field title={t('appDebug.variableConfig.defaultValue')}> | |||
| <Input | |||
| type="number" | |||
| value={tempPayload.default || ''} | |||
| onChange={e => handlePayloadChange('default')(e.target.value || undefined)} | |||
| placeholder={t('appDebug.variableConfig.inputPlaceholder')!} | |||
| /> | |||
| </Field> | |||
| )} | |||
| {type === InputVarType.select && ( | |||
| <> | |||
| <Field title={t('appDebug.variableConfig.options')}> | |||
| @@ -263,11 +304,30 @@ const ConfigModal: FC<IConfigModalProps> = ({ | |||
| )} | |||
| {[InputVarType.singleFile, InputVarType.multiFiles].includes(type) && ( | |||
| <FileUploadSetting | |||
| payload={tempPayload as UploadFileSetting} | |||
| onChange={(p: UploadFileSetting) => setTempPayload(p as InputVar)} | |||
| isMultiple={type === InputVarType.multiFiles} | |||
| /> | |||
| <> | |||
| <FileUploadSetting | |||
| payload={tempPayload as UploadFileSetting} | |||
| onChange={(p: UploadFileSetting) => setTempPayload(p as InputVar)} | |||
| isMultiple={type === InputVarType.multiFiles} | |||
| /> | |||
| <Field title={t('appDebug.variableConfig.defaultValue')}> | |||
| <FileUploaderInAttachmentWrapper | |||
| value={(type === InputVarType.singleFile ? (tempPayload.default ? [tempPayload.default] : []) : (tempPayload.default || [])) as unknown as FileEntity[]} | |||
| onChange={(files) => { | |||
| if (type === InputVarType.singleFile) | |||
| handlePayloadChange('default')(files?.[0] || undefined) | |||
| else | |||
| handlePayloadChange('default')(files || undefined) | |||
| }} | |||
| fileConfig={{ | |||
| allowed_file_types: tempPayload.allowed_file_types || [SupportUploadFileTypes.document], | |||
| allowed_file_extensions: tempPayload.allowed_file_extensions || [], | |||
| allowed_file_upload_methods: tempPayload.allowed_file_upload_methods || [TransferMethod.remote_url], | |||
| number_limits: type === InputVarType.singleFile ? 1 : tempPayload.max_length || 5, | |||
| }} | |||
| /> | |||
| </Field> | |||
| </> | |||
| )} | |||
| <div className='!mt-5 flex h-6 items-center space-x-2'> | |||
| @@ -210,7 +210,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { | |||
| return { | |||
| ...item.paragraph, | |||
| default: value || item.default, | |||
| default: value || item.default || item.paragraph.default, | |||
| type: 'paragraph', | |||
| } | |||
| } | |||
| @@ -218,7 +218,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { | |||
| const convertedNumber = Number(initInputs[item.number.variable]) ?? undefined | |||
| return { | |||
| ...item.number, | |||
| default: convertedNumber || item.default, | |||
| default: convertedNumber || item.default || item.number.default, | |||
| type: 'number', | |||
| } | |||
| } | |||
| @@ -251,7 +251,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { | |||
| return { | |||
| ...item['text-input'], | |||
| default: value || item.default, | |||
| default: value || item.default || item['text-input'].default, | |||
| type: 'text-input', | |||
| } | |||
| }) | |||
| @@ -183,7 +183,7 @@ export const useEmbeddedChatbot = () => { | |||
| return { | |||
| ...item.paragraph, | |||
| default: value || item.default, | |||
| default: value || item.default || item.paragraph.default, | |||
| type: 'paragraph', | |||
| } | |||
| } | |||
| @@ -191,7 +191,7 @@ export const useEmbeddedChatbot = () => { | |||
| const convertedNumber = Number(initInputs[item.number.variable]) ?? undefined | |||
| return { | |||
| ...item.number, | |||
| default: convertedNumber || item.default, | |||
| default: convertedNumber || item.default || item.number.default, | |||
| type: 'number', | |||
| } | |||
| } | |||
| @@ -224,7 +224,7 @@ export const useEmbeddedChatbot = () => { | |||
| return { | |||
| ...item['text-input'], | |||
| default: value || item.default, | |||
| default: value || item.default || item['text-input'].default, | |||
| type: 'text-input', | |||
| } | |||
| }) | |||
| @@ -1,5 +1,5 @@ | |||
| import type { ChangeEvent, FC, FormEvent } from 'react' | |||
| import { useEffect } from 'react' | |||
| import { useEffect, useState } from 'react' | |||
| import React, { useCallback } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import { | |||
| @@ -41,6 +41,7 @@ const RunOnce: FC<IRunOnceProps> = ({ | |||
| const { t } = useTranslation() | |||
| const media = useBreakpoints() | |||
| const isPC = media === MediaType.pc | |||
| const [isInitialized, setIsInitialized] = useState(false) | |||
| const onClear = () => { | |||
| const newInputs: Record<string, any> = {} | |||
| @@ -64,16 +65,24 @@ const RunOnce: FC<IRunOnceProps> = ({ | |||
| }, [onInputsChange, inputsRef]) | |||
| useEffect(() => { | |||
| if (isInitialized) return | |||
| const newInputs: Record<string, any> = {} | |||
| promptConfig.prompt_variables.forEach((item) => { | |||
| if (item.type === 'select') | |||
| newInputs[item.key] = item.default | |||
| else if (item.type === 'string' || item.type === 'paragraph') | |||
| newInputs[item.key] = '' | |||
| newInputs[item.key] = item.default || '' | |||
| else if (item.type === 'number') | |||
| newInputs[item.key] = item.default | |||
| else if (item.type === 'file') | |||
| newInputs[item.key] = item.default | |||
| else if (item.type === 'file-list') | |||
| newInputs[item.key] = item.default || [] | |||
| else | |||
| newInputs[item.key] = undefined | |||
| }) | |||
| onInputsChange(newInputs) | |||
| setIsInitialized(true) | |||
| }, [promptConfig.prompt_variables, onInputsChange]) | |||
| return ( | |||
| @@ -81,7 +90,7 @@ const RunOnce: FC<IRunOnceProps> = ({ | |||
| <section> | |||
| {/* input form */} | |||
| <form onSubmit={onSubmit}> | |||
| {(inputs === null || inputs === undefined || Object.keys(inputs).length === 0) ? null | |||
| {(inputs === null || inputs === undefined || Object.keys(inputs).length === 0) || !isInitialized ? null | |||
| : promptConfig.prompt_variables.map(item => ( | |||
| <div className='mt-4 w-full' key={item.key}> | |||
| <label className='system-md-semibold flex h-6 items-center text-text-secondary'>{item.name}</label> | |||
| @@ -122,6 +131,7 @@ const RunOnce: FC<IRunOnceProps> = ({ | |||
| )} | |||
| {item.type === 'file' && ( | |||
| <FileUploaderInAttachmentWrapper | |||
| value={inputs[item.key] ? [inputs[item.key]] : []} | |||
| onChange={(files) => { handleInputsChange({ ...inputsRef.current, [item.key]: getProcessedFiles(files)[0] }) }} | |||
| fileConfig={{ | |||
| ...item.config, | |||
| @@ -131,6 +141,7 @@ const RunOnce: FC<IRunOnceProps> = ({ | |||
| )} | |||
| {item.type === 'file-list' && ( | |||
| <FileUploaderInAttachmentWrapper | |||
| value={inputs[item.key]} | |||
| onChange={(files) => { handleInputsChange({ ...inputsRef.current, [item.key]: getProcessedFiles(files) }) }} | |||
| fileConfig={{ | |||
| ...item.config, | |||
| @@ -1,7 +1,6 @@ | |||
| import { | |||
| memo, | |||
| useCallback, | |||
| useEffect, | |||
| useMemo, | |||
| } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| @@ -33,7 +32,7 @@ type Props = { | |||
| const InputsPanel = ({ onRun }: Props) => { | |||
| const { t } = useTranslation() | |||
| const workflowStore = useWorkflowStore() | |||
| const { inputs, setInputs } = useStore(s => ({ | |||
| const { inputs } = useStore(s => ({ | |||
| inputs: s.inputs, | |||
| setInputs: s.setInputs, | |||
| })) | |||
| @@ -48,23 +47,13 @@ const InputsPanel = ({ onRun }: Props) => { | |||
| const startVariables = startNode?.data.variables | |||
| const { checkInputsForm } = useCheckInputsForms() | |||
| const initialInputs = useMemo(() => { | |||
| const initInputs: Record<string, any> = {} | |||
| if (startVariables) { | |||
| startVariables.forEach((variable) => { | |||
| if (variable.default) | |||
| initInputs[variable.variable] = variable.default | |||
| }) | |||
| } | |||
| return initInputs | |||
| }, [startVariables]) | |||
| useEffect(() => { | |||
| setInputs({ | |||
| ...initialInputs, | |||
| ...inputs, | |||
| const initialInputs = { ...inputs } | |||
| if (startVariables) { | |||
| startVariables.forEach((variable) => { | |||
| if (variable.default) | |||
| initialInputs[variable.variable] = variable.default | |||
| }) | |||
| }, [initialInputs]) | |||
| } | |||
| const variables = useMemo(() => { | |||
| const data = startVariables || [] | |||
| @@ -102,11 +91,11 @@ const InputsPanel = ({ onRun }: Props) => { | |||
| } | |||
| const doRun = useCallback(() => { | |||
| if (!checkInputsForm(inputs, variables as any)) | |||
| if (!checkInputsForm(initialInputs, variables as any)) | |||
| return | |||
| onRun() | |||
| handleRun({ inputs: getProcessedInputs(inputs, variables as any), files }) | |||
| }, [files, handleRun, inputs, onRun, variables, checkInputsForm]) | |||
| handleRun({ inputs: getProcessedInputs(initialInputs, variables as any), files }) | |||
| }, [files, handleRun, initialInputs, onRun, variables, checkInputsForm]) | |||
| const canRun = useMemo(() => { | |||
| if (files?.some(item => (item.transfer_method as any) === TransferMethod.local_file && !item.upload_file_id)) | |||
| @@ -128,7 +117,7 @@ const InputsPanel = ({ onRun }: Props) => { | |||
| autoFocus={index === 0} | |||
| className='!block' | |||
| payload={variable} | |||
| value={inputs[variable.variable]} | |||
| value={initialInputs[variable.variable]} | |||
| onChange={v => handleValueChange(variable.variable, v)} | |||
| /> | |||
| </div> | |||
| @@ -41,6 +41,7 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | | |||
| options: [], | |||
| is_context_var, | |||
| hide: content.hide, | |||
| default: content.default, | |||
| }) | |||
| } | |||
| else if (type === 'number') { | |||
| @@ -51,6 +52,7 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | | |||
| type, | |||
| options: [], | |||
| hide: content.hide, | |||
| default: content.default, | |||
| }) | |||
| } | |||
| else if (type === 'select') { | |||
| @@ -78,6 +80,7 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | | |||
| number_limits: 1, | |||
| }, | |||
| hide: content.hide, | |||
| default: content.default, | |||
| }) | |||
| } | |||
| else if (type === 'file-list') { | |||
| @@ -93,6 +96,7 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | | |||
| number_limits: content.max_length, | |||
| }, | |||
| hide: content.hide, | |||
| default: content.default, | |||
| }) | |||
| } | |||
| else { | |||