feat: Add default value support for all workflow start node variable types (#24129)
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -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}
|
||||
/>
|
||||
<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> = {}
|
||||
const initialInputs = { ...inputs }
|
||||
if (startVariables) {
|
||||
startVariables.forEach((variable) => {
|
||||
if (variable.default)
|
||||
initInputs[variable.variable] = variable.default
|
||||
initialInputs[variable.variable] = variable.default
|
||||
})
|
||||
}
|
||||
return initInputs
|
||||
}, [startVariables])
|
||||
|
||||
useEffect(() => {
|
||||
setInputs({
|
||||
...initialInputs,
|
||||
...inputs,
|
||||
})
|
||||
}, [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 {
|
||||
|
Reference in New Issue
Block a user