refactor: update input variable types to use PipelineInputVarType and simplify form data handling

This commit is contained in:
twwu 2025-05-19 16:26:13 +08:00
parent 8d4ced227e
commit bd1073ff1a
15 changed files with 194 additions and 151 deletions

View File

@ -1,12 +1,11 @@
import React from 'react' import React from 'react'
import { withForm } from '@/app/components/base/form' import { withForm } from '@/app/components/base/form'
import type { FormData } from './types'
import InputField from '@/app/components/base/form/form-scenarios/input-field/field' import InputField from '@/app/components/base/form/form-scenarios/input-field/field'
import { useStore } from '@tanstack/react-form' import { useStore } from '@tanstack/react-form'
import { useHiddenConfigurations } from './hooks' import { useHiddenConfigurations } from './hooks'
type HiddenFieldsProps = { type HiddenFieldsProps = {
initialData?: FormData initialData?: Record<string, any>
} }
const HiddenFields = ({ const HiddenFields = ({
@ -25,7 +24,7 @@ const HiddenFields = ({
return ( return (
<> <>
{hiddenConfigurations.map((config, index) => { {hiddenConfigurations.map((config, index) => {
const FieldComponent = InputField<FormData>({ const FieldComponent = InputField({
initialData, initialData,
config, config,
}) })

View File

@ -1,5 +1,4 @@
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { InputVarType } from '@/app/components/workflow/types'
import { useCallback, useMemo } from 'react' import { useCallback, useMemo } from 'react'
import type { InputFieldConfiguration } from '@/app/components/base/form/form-scenarios/input-field/types' import type { InputFieldConfiguration } from '@/app/components/base/form/form-scenarios/input-field/types'
import { InputFieldType } from '@/app/components/base/form/form-scenarios/input-field/types' import { InputFieldType } from '@/app/components/base/form/form-scenarios/input-field/types'
@ -11,21 +10,22 @@ import { DEFAULT_FILE_UPLOAD_SETTING } from '@/app/components/workflow/constants
import { DEFAULT_VALUE_MAX_LEN } from '@/config' import { DEFAULT_VALUE_MAX_LEN } from '@/config'
import type { FormData } from './types' import type { FormData } from './types'
import { TEXT_MAX_LENGTH } from './schema' import { TEXT_MAX_LENGTH } from './schema'
import { PipelineInputVarType } from '@/models/pipeline'
export const useHiddenFieldNames = (type: InputVarType) => { export const useHiddenFieldNames = (type: PipelineInputVarType) => {
const { t } = useTranslation() const { t } = useTranslation()
const hiddenFieldNames = useMemo(() => { const hiddenFieldNames = useMemo(() => {
let fieldNames = [] let fieldNames = []
switch (type) { switch (type) {
case InputVarType.textInput: case PipelineInputVarType.textInput:
case InputVarType.paragraph: case PipelineInputVarType.paragraph:
fieldNames = [ fieldNames = [
t('appDebug.variableConfig.defaultValue'), t('appDebug.variableConfig.defaultValue'),
t('appDebug.variableConfig.placeholder'), t('appDebug.variableConfig.placeholder'),
t('appDebug.variableConfig.tooltips'), t('appDebug.variableConfig.tooltips'),
] ]
break break
case InputVarType.number: case PipelineInputVarType.number:
fieldNames = [ fieldNames = [
t('appDebug.variableConfig.defaultValue'), t('appDebug.variableConfig.defaultValue'),
t('appDebug.variableConfig.unit'), t('appDebug.variableConfig.unit'),
@ -33,19 +33,19 @@ export const useHiddenFieldNames = (type: InputVarType) => {
t('appDebug.variableConfig.tooltips'), t('appDebug.variableConfig.tooltips'),
] ]
break break
case InputVarType.select: case PipelineInputVarType.select:
fieldNames = [ fieldNames = [
t('appDebug.variableConfig.defaultValue'), t('appDebug.variableConfig.defaultValue'),
t('appDebug.variableConfig.tooltips'), t('appDebug.variableConfig.tooltips'),
] ]
break break
case InputVarType.singleFile: case PipelineInputVarType.singleFile:
fieldNames = [ fieldNames = [
t('appDebug.variableConfig.uploadMethod'), t('appDebug.variableConfig.uploadMethod'),
t('appDebug.variableConfig.tooltips'), t('appDebug.variableConfig.tooltips'),
] ]
break break
case InputVarType.multiFiles: case PipelineInputVarType.multiFiles:
fieldNames = [ fieldNames = [
t('appDebug.variableConfig.uploadMethod'), t('appDebug.variableConfig.uploadMethod'),
t('appDebug.variableConfig.maxNumberOfUploads'), t('appDebug.variableConfig.maxNumberOfUploads'),
@ -71,20 +71,20 @@ export const useConfigurations = (props: {
const { setFieldValue, supportFile } = props const { setFieldValue, supportFile } = props
const handleTypeChange = useCallback((type: string) => { const handleTypeChange = useCallback((type: string) => {
if ([InputVarType.singleFile, InputVarType.multiFiles].includes(type as InputVarType)) { if ([PipelineInputVarType.singleFile, PipelineInputVarType.multiFiles].includes(type as PipelineInputVarType)) {
setFieldValue('allowedFileUploadMethods', DEFAULT_FILE_UPLOAD_SETTING.allowed_file_upload_methods) setFieldValue('allowedFileUploadMethods', DEFAULT_FILE_UPLOAD_SETTING.allowed_file_upload_methods)
setFieldValue('allowedTypesAndExtensions', { setFieldValue('allowedTypesAndExtensions', {
allowedFileTypes: DEFAULT_FILE_UPLOAD_SETTING.allowed_file_types, allowedFileTypes: DEFAULT_FILE_UPLOAD_SETTING.allowed_file_types,
allowedFileExtensions: DEFAULT_FILE_UPLOAD_SETTING.allowed_file_extensions, allowedFileExtensions: DEFAULT_FILE_UPLOAD_SETTING.allowed_file_extensions,
}) })
if (type === InputVarType.multiFiles) if (type === PipelineInputVarType.multiFiles)
setFieldValue('maxLength', DEFAULT_FILE_UPLOAD_SETTING.max_length) setFieldValue('maxLength', DEFAULT_FILE_UPLOAD_SETTING.max_length)
} }
if (type === InputVarType.paragraph) if (type === PipelineInputVarType.paragraph)
setFieldValue('maxLength', DEFAULT_VALUE_MAX_LEN) setFieldValue('maxLength', DEFAULT_VALUE_MAX_LEN)
}, [setFieldValue]) }, [setFieldValue])
const initialConfigurations = useMemo((): InputFieldConfiguration<FormData>[] => { const initialConfigurations = useMemo((): InputFieldConfiguration[] => {
return [{ return [{
type: InputFieldType.inputTypeSelect, type: InputFieldType.inputTypeSelect,
label: t('appDebug.variableConfig.fieldType'), label: t('appDebug.variableConfig.fieldType'),
@ -117,7 +117,7 @@ export const useConfigurations = (props: {
required: true, required: true,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.textInput, value: PipelineInputVarType.textInput,
}], }],
min: 1, min: 1,
max: TEXT_MAX_LENGTH, max: TEXT_MAX_LENGTH,
@ -128,7 +128,7 @@ export const useConfigurations = (props: {
required: true, required: true,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.select, value: PipelineInputVarType.select,
}], }],
}, { }, {
type: InputFieldType.fileTypes, type: InputFieldType.fileTypes,
@ -137,7 +137,7 @@ export const useConfigurations = (props: {
required: true, required: true,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.singleFile, value: PipelineInputVarType.singleFile,
}], }],
}, { }, {
type: InputFieldType.fileTypes, type: InputFieldType.fileTypes,
@ -146,7 +146,7 @@ export const useConfigurations = (props: {
required: true, required: true,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.multiFiles, value: PipelineInputVarType.multiFiles,
}], }],
}, { }, {
type: InputFieldType.checkbox, type: InputFieldType.checkbox,
@ -192,7 +192,7 @@ export const useHiddenConfigurations = (props: {
return [] return []
}, [options, t]) }, [options, t])
const hiddenConfigurations = useMemo((): InputFieldConfiguration<FormData>[] => { const hiddenConfigurations = useMemo((): InputFieldConfiguration[] => {
return [{ return [{
type: InputFieldType.textInput, type: InputFieldType.textInput,
label: t('appDebug.variableConfig.defaultValue'), label: t('appDebug.variableConfig.defaultValue'),
@ -201,7 +201,7 @@ export const useHiddenConfigurations = (props: {
required: false, required: false,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.textInput, value: PipelineInputVarType.textInput,
}], }],
showOptional: true, showOptional: true,
}, { }, {
@ -212,7 +212,7 @@ export const useHiddenConfigurations = (props: {
required: false, required: false,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.paragraph, value: PipelineInputVarType.paragraph,
}], }],
showOptional: true, showOptional: true,
}, { }, {
@ -223,7 +223,7 @@ export const useHiddenConfigurations = (props: {
required: false, required: false,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.number, value: PipelineInputVarType.number,
}], }],
showOptional: true, showOptional: true,
}, { }, {
@ -233,7 +233,7 @@ export const useHiddenConfigurations = (props: {
required: false, required: false,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.select, value: PipelineInputVarType.select,
}], }],
showOptional: true, showOptional: true,
options: defaultSelectOptions, options: defaultSelectOptions,
@ -248,7 +248,7 @@ export const useHiddenConfigurations = (props: {
required: false, required: false,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.textInput, value: PipelineInputVarType.textInput,
}], }],
showOptional: true, showOptional: true,
}, { }, {
@ -259,7 +259,7 @@ export const useHiddenConfigurations = (props: {
required: false, required: false,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.paragraph, value: PipelineInputVarType.paragraph,
}], }],
showOptional: true, showOptional: true,
}, { }, {
@ -270,7 +270,7 @@ export const useHiddenConfigurations = (props: {
required: false, required: false,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.number, value: PipelineInputVarType.number,
}], }],
showOptional: true, showOptional: true,
}, { }, {
@ -281,7 +281,7 @@ export const useHiddenConfigurations = (props: {
required: false, required: false,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.number, value: PipelineInputVarType.number,
}], }],
showOptional: true, showOptional: true,
}, { }, {
@ -291,7 +291,7 @@ export const useHiddenConfigurations = (props: {
required: false, required: false,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.singleFile, value: PipelineInputVarType.singleFile,
}], }],
}, { }, {
type: InputFieldType.uploadMethod, type: InputFieldType.uploadMethod,
@ -300,7 +300,7 @@ export const useHiddenConfigurations = (props: {
required: false, required: false,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.multiFiles, value: PipelineInputVarType.multiFiles,
}], }],
}, { }, {
type: InputFieldType.numberSlider, type: InputFieldType.numberSlider,
@ -309,7 +309,7 @@ export const useHiddenConfigurations = (props: {
required: false, required: false,
showConditions: [{ showConditions: [{
variable: 'type', variable: 'type',
value: InputVarType.multiFiles, value: PipelineInputVarType.multiFiles,
}], }],
description: t('appDebug.variableConfig.maxNumberTip', { description: t('appDebug.variableConfig.maxNumberTip', {
imgLimit: formatFileSize(imgSizeLimit), imgLimit: formatFileSize(imgSizeLimit),

View File

@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next'
import { useCallback, useState } from 'react' import { useCallback, useState } from 'react'
import { ChangeType } from '@/app/components/workflow/types' import { ChangeType } from '@/app/components/workflow/types'
import { useFileUploadConfig } from '@/service/use-common' import { useFileUploadConfig } from '@/service/use-common'
import type { InputFieldFormProps } from './types' import type { FormData, InputFieldFormProps } from './types'
import { useAppForm } from '@/app/components/base/form' import { useAppForm } from '@/app/components/base/form'
import { createInputFieldSchema } from './schema' import { createInputFieldSchema } from './schema'
import Toast from '@/app/components/base/toast' import Toast from '@/app/components/base/toast'
@ -53,7 +53,7 @@ const InputFieldForm = ({
type: ChangeType.changeVarName, type: ChangeType.changeVarName,
payload: { beforeKey: initialData?.variable || '', afterKey: value.variable }, payload: { beforeKey: initialData?.variable || '', afterKey: value.variable },
} }
onSubmit(value, moreInfo) onSubmit(value as FormData, moreInfo)
}, },
}) })

View File

@ -1,12 +1,10 @@
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { withForm } from '@/app/components/base/form' import { withForm } from '@/app/components/base/form'
import type { FormData } from './types'
import InputField from '@/app/components/base/form/form-scenarios/input-field/field' import InputField from '@/app/components/base/form/form-scenarios/input-field/field'
import type { DeepKeys } from '@tanstack/react-form'
import { useConfigurations } from './hooks' import { useConfigurations } from './hooks'
type InitialFieldsProps = { type InitialFieldsProps = {
initialData?: FormData initialData?: Record<string, any>
supportFile: boolean supportFile: boolean
} }
@ -18,7 +16,7 @@ const InitialFields = ({
render: function Render({ render: function Render({
form, form,
}) { }) {
const setFieldValue = useCallback((fieldName: DeepKeys<FormData>, value: any) => { const setFieldValue = useCallback((fieldName: string, value: any) => {
form.setFieldValue(fieldName, value) form.setFieldValue(fieldName, value)
}, [form]) }, [form])
@ -30,7 +28,7 @@ const InitialFields = ({
return ( return (
<> <>
{initialConfigurations.map((config, index) => { {initialConfigurations.map((config, index) => {
const FieldComponent = InputField<FormData>({ const FieldComponent = InputField({
initialData, initialData,
config, config,
}) })

View File

@ -1,8 +1,8 @@
import { InputVarType } from '@/app/components/workflow/types'
import { MAX_VAR_KEY_LENGTH } from '@/config' import { MAX_VAR_KEY_LENGTH } from '@/config'
import type { TFunction } from 'i18next' import type { TFunction } from 'i18next'
import { z } from 'zod' import { z } from 'zod'
import type { SchemaOptions } from './types' import type { SchemaOptions } from './types'
import { PipelineInputVarType } from '@/models/pipeline'
export const TEXT_MAX_LENGTH = 256 export const TEXT_MAX_LENGTH = 256
@ -30,7 +30,7 @@ export const SupportedFileTypes = z.enum([
'custom', 'custom',
]) ])
export const createInputFieldSchema = (type: InputVarType, t: TFunction, options: SchemaOptions) => { export const createInputFieldSchema = (type: PipelineInputVarType, t: TFunction, options: SchemaOptions) => {
const { maxFileUploadLimit } = options const { maxFileUploadLimit } = options
const commonSchema = z.object({ const commonSchema = z.object({
type: InputType, type: InputType,
@ -47,22 +47,22 @@ export const createInputFieldSchema = (type: InputVarType, t: TFunction, options
message: t('appDebug.variableConfig.errorMsg.labelNameRequired'), message: t('appDebug.variableConfig.errorMsg.labelNameRequired'),
}), }),
required: z.boolean(), required: z.boolean(),
hint: z.string().optional(), tooltips: z.string().optional(),
}) })
if (type === InputVarType.textInput || type === InputVarType.paragraph) { if (type === PipelineInputVarType.textInput || type === PipelineInputVarType.paragraph) {
return z.object({ return z.object({
maxLength: z.number().min(1).max(TEXT_MAX_LENGTH), maxLength: z.number().min(1).max(TEXT_MAX_LENGTH),
default: z.string().optional(), default: z.string().optional(),
}).merge(commonSchema).passthrough() }).merge(commonSchema).passthrough()
} }
if (type === InputVarType.number) { if (type === PipelineInputVarType.number) {
return z.object({ return z.object({
default: z.number().optional(), default: z.number().optional(),
unit: z.string().optional(), unit: z.string().optional(),
placeholder: z.string().optional(), placeholder: z.string().optional(),
}).merge(commonSchema).passthrough() }).merge(commonSchema).passthrough()
} }
if (type === InputVarType.select) { if (type === PipelineInputVarType.select) {
return z.object({ return z.object({
options: z.array(z.string()).nonempty({ options: z.array(z.string()).nonempty({
message: t('appDebug.variableConfig.errorMsg.atLeastOneOption'), message: t('appDebug.variableConfig.errorMsg.atLeastOneOption'),
@ -75,7 +75,7 @@ export const createInputFieldSchema = (type: InputVarType, t: TFunction, options
default: z.string().optional(), default: z.string().optional(),
}).merge(commonSchema).passthrough() }).merge(commonSchema).passthrough()
} }
if (type === InputVarType.singleFile) { if (type === PipelineInputVarType.singleFile) {
return z.object({ return z.object({
allowedFileUploadMethods: z.array(TransferMethod), allowedFileUploadMethods: z.array(TransferMethod),
allowedTypesAndExtensions: z.object({ allowedTypesAndExtensions: z.object({
@ -84,7 +84,7 @@ export const createInputFieldSchema = (type: InputVarType, t: TFunction, options
}), }),
}).merge(commonSchema).passthrough() }).merge(commonSchema).passthrough()
} }
if (type === InputVarType.multiFiles) { if (type === PipelineInputVarType.multiFiles) {
return z.object({ return z.object({
allowedFileUploadMethods: z.array(TransferMethod), allowedFileUploadMethods: z.array(TransferMethod),
allowedTypesAndExtensions: z.object({ allowedTypesAndExtensions: z.object({

View File

@ -1,13 +1,12 @@
import React from 'react' import React from 'react'
import { withForm } from '@/app/components/base/form' import { withForm } from '@/app/components/base/form'
import type { FormData } from './types'
import { useStore } from '@tanstack/react-form' import { useStore } from '@tanstack/react-form'
import { useHiddenFieldNames } from './hooks' import { useHiddenFieldNames } from './hooks'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { RiArrowRightSLine } from '@remixicon/react' import { RiArrowRightSLine } from '@remixicon/react'
type ShowAllSettingsProps = { type ShowAllSettingsProps = {
initialData?: FormData initialData?: Record<string, any>
handleShowAllSettings: () => void handleShowAllSettings: () => void
} }

View File

@ -1,14 +1,15 @@
import type { InputVarType, MoreInfo, SupportUploadFileTypes } from '@/app/components/workflow/types' import type { MoreInfo, SupportUploadFileTypes } from '@/app/components/workflow/types'
import type { PipelineInputVarType } from '@/models/pipeline'
import type { TransferMethod } from '@/types/app' import type { TransferMethod } from '@/types/app'
export type FormData = { export type FormData = {
type: InputVarType type: PipelineInputVarType
label: string label: string
variable: string variable: string
maxLength?: number maxLength?: number
default?: string | number default?: string
required: boolean required: boolean
hint?: string tooltips?: string
options?: string[] options?: string[]
placeholder?: string placeholder?: string
unit?: string unit?: string
@ -20,7 +21,7 @@ export type FormData = {
} }
export type InputFieldFormProps = { export type InputFieldFormProps = {
initialData: FormData initialData: Record<string, any>
supportFile?: boolean supportFile?: boolean
onCancel: () => void onCancel: () => void
onSubmit: (value: FormData, moreInfo?: MoreInfo) => void onSubmit: (value: FormData, moreInfo?: MoreInfo) => void

View File

@ -1,10 +1,11 @@
import { RiCloseLine } from '@remixicon/react' import { RiCloseLine } from '@remixicon/react'
import DialogWrapper from '../dialog-wrapper' import DialogWrapper from '../dialog-wrapper'
import type { InputVar } from '@/app/components/workflow/types'
import InputFieldForm from './form' import InputFieldForm from './form'
import { convertToInputFieldFormData } from './utils' import { convertToInputFieldFormData } from './utils'
import { useCallback } from 'react' import { useCallback } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import type { InputVar } from '@/models/pipeline'
import type { FormData } from './form/types'
type InputFieldEditorProps = { type InputFieldEditorProps = {
show: boolean show: boolean
@ -22,8 +23,36 @@ const InputFieldEditor = ({
const { t } = useTranslation() const { t } = useTranslation()
const formData = convertToInputFieldFormData(initialData) const formData = convertToInputFieldFormData(initialData)
const handleSubmit = useCallback((value: InputVar) => { const handleSubmit = useCallback((value: FormData) => {
onSubmit(value) const {
type,
label,
variable,
maxLength,
required,
tooltips,
options,
placeholder,
unit,
default: defaultValue,
allowedFileUploadMethods,
allowedTypesAndExtensions,
} = value
onSubmit({
type,
label,
variable,
max_length: maxLength,
required,
tooltips,
options,
placeholder,
unit,
default: defaultValue,
allowed_file_upload_methods: allowedFileUploadMethods,
allowed_file_types: allowedTypesAndExtensions.allowedFileTypes,
allowed_file_extensions: allowedTypesAndExtensions.allowedFileExtensions,
})
onClose() onClose()
}, [onSubmit, onClose]) }, [onSubmit, onClose])

View File

@ -1,6 +1,12 @@
import type { InputVar } from '@/app/components/workflow/types' import type { InputVar } from '@/models/pipeline'
import type { FormData } from './form/types' import type { FormData } from './form/types'
import { getNewVarInWorkflow } from '@/utils/var' import { VAR_ITEM_TEMPLATE_IN_PIPELINE } from '@/config'
const getNewInputVarInRagPipeline = (): InputVar => {
return {
...VAR_ITEM_TEMPLATE_IN_PIPELINE,
}
}
export const convertToInputFieldFormData = (data?: InputVar): FormData => { export const convertToInputFieldFormData = (data?: InputVar): FormData => {
const { const {
@ -10,23 +16,23 @@ export const convertToInputFieldFormData = (data?: InputVar): FormData => {
max_length, max_length,
'default': defaultValue, 'default': defaultValue,
required, required,
hint, tooltips,
options, options,
placeholder, placeholder,
unit, unit,
allowed_file_upload_methods, allowed_file_upload_methods,
allowed_file_types, allowed_file_types,
allowed_file_extensions, allowed_file_extensions,
} = data || getNewVarInWorkflow('') } = data || getNewInputVarInRagPipeline()
return { return {
type, type,
label: label as string, label,
variable, variable,
maxLength: max_length, maxLength: max_length,
default: defaultValue, default: defaultValue,
required, required,
hint, tooltips,
options, options,
placeholder, placeholder,
unit, unit,

View File

@ -7,11 +7,12 @@ import {
RiDraggable, RiDraggable,
RiEditLine, RiEditLine,
} from '@remixicon/react' } from '@remixicon/react'
import type { InputVar } from '@/app/components/workflow/types'
import { InputField } from '@/app/components/base/icons/src/vender/pipeline' import { InputField } from '@/app/components/base/icons/src/vender/pipeline'
import InputVarTypeIcon from '@/app/components/workflow/nodes/_base/components/input-var-type-icon' import InputVarTypeIcon from '@/app/components/workflow/nodes/_base/components/input-var-type-icon'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import Badge from '@/app/components/base/badge' import Badge from '@/app/components/base/badge'
import type { InputVar } from '@/models/pipeline'
import type { InputVarType } from '@/app/components/workflow/types'
type FieldItemProps = { type FieldItemProps = {
readonly?: boolean readonly?: boolean
@ -33,7 +34,7 @@ const FieldItem = ({
return ( return (
<div <div
ref={ref} // ref={ref}
className={cn( className={cn(
'flex h-8 cursor-pointer items-center justify-between gap-x-1 rounded-lg border border-components-panel-border-subtle bg-components-panel-on-panel-item-bg py-1 pl-2 shadow-xs hover:shadow-sm', 'flex h-8 cursor-pointer items-center justify-between gap-x-1 rounded-lg border border-components-panel-border-subtle bg-components-panel-on-panel-item-bg py-1 pl-2 shadow-xs hover:shadow-sm',
(!isHovering || readonly) ? 'pr-2.5' : !readonly && 'pr-1', (!isHovering || readonly) ? 'pr-2.5' : !readonly && 'pr-1',
@ -69,7 +70,7 @@ const FieldItem = ({
{payload.required && ( {payload.required && (
<Badge>{t('workflow.nodes.start.required')}</Badge> <Badge>{t('workflow.nodes.start.required')}</Badge>
)} )}
<InputVarTypeIcon type={payload.type} className='h-3 w-3 text-text-tertiary' /> <InputVarTypeIcon type={payload.type as unknown as InputVarType} className='h-3 w-3 text-text-tertiary' />
</div> </div>
) )
: (!readonly && ( : (!readonly && (

View File

@ -1,4 +1,3 @@
import type { InputVar } from '@/app/components/workflow/types'
import { RiAddLine } from '@remixicon/react' import { RiAddLine } from '@remixicon/react'
import FieldItem from './field-item' import FieldItem from './field-item'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
@ -6,6 +5,7 @@ import { useCallback, useMemo, useState } from 'react'
import InputFieldEditor from '../editor' import InputFieldEditor from '../editor'
import { ReactSortable } from 'react-sortablejs' import { ReactSortable } from 'react-sortablejs'
import produce from 'immer' import produce from 'immer'
import type { InputVar } from '@/models/pipeline'
type FieldListProps = { type FieldListProps = {
LabelRightContent: React.ReactNode LabelRightContent: React.ReactNode
@ -48,7 +48,7 @@ const FieldList = ({
}, [handleInputFieldsChange, inputFields]) }, [handleInputFieldsChange, inputFields])
const handleAddField = () => { const handleAddField = () => {
setCurrentIndex(-1) setCurrentIndex(-1) // -1 means add new field
setCurrentInputField(undefined) setCurrentInputField(undefined)
setShowInputFieldEditor(true) setShowInputFieldEditor(true)
} }
@ -61,6 +61,10 @@ const FieldList = ({
const handleSubmitChange = useCallback((data: InputVar) => { const handleSubmitChange = useCallback((data: InputVar) => {
const newInputFields = produce(inputFields, (draft) => { const newInputFields = produce(inputFields, (draft) => {
if (currentIndex === -1) {
draft.push(data)
return
}
draft[currentIndex] = data draft[currentIndex] = data
}) })
handleInputFieldsChange(newInputFields) handleInputFieldsChange(newInputFields)
@ -91,7 +95,7 @@ const FieldList = ({
list={optionList} list={optionList}
setList={list => handleListSortChange(list)} setList={list => handleListSortChange(list)}
handle='.handle' handle='.handle'
ghostClass="opacity-50" ghostClass='opacity-50'
animation={150} animation={150}
disabled={readonly} disabled={readonly}
> >

View File

@ -5,7 +5,7 @@ import {
} from 'react' } from 'react'
import { useStore } from '@/app/components/workflow/store' import { useStore } from '@/app/components/workflow/store'
import { RiCloseLine } from '@remixicon/react' import { RiCloseLine } from '@remixicon/react'
import { BlockEnum, type InputVar } from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types'
import DialogWrapper from './dialog-wrapper' import DialogWrapper from './dialog-wrapper'
import FieldList from './field-list' import FieldList from './field-list'
import FooterTip from './footer-tip' import FooterTip from './footer-tip'
@ -15,6 +15,8 @@ import { useNodes } from 'reactflow'
import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import produce from 'immer' import produce from 'immer'
import { useNodesSyncDraft } from '../../hooks'
import type { InputVar, RAGPipelineVariables } from '@/models/pipeline'
type InputFieldDialogProps = { type InputFieldDialogProps = {
readonly?: boolean readonly?: boolean
@ -29,6 +31,7 @@ const InputFieldDialog = ({
const setShowInputFieldDialog = useStore(state => state.setShowInputFieldDialog) const setShowInputFieldDialog = useStore(state => state.setShowInputFieldDialog)
const ragPipelineVariables = useStore(state => state.ragPipelineVariables) const ragPipelineVariables = useStore(state => state.ragPipelineVariables)
const setRagPipelineVariables = useStore(state => state.setRagPipelineVariables) const setRagPipelineVariables = useStore(state => state.setRagPipelineVariables)
const { doSyncWorkflowDraft } = useNodesSyncDraft()
const datasourceTitleMap = useMemo(() => { const datasourceTitleMap = useMemo(() => {
const datasourceNameMap: Record<string, string> = {} const datasourceNameMap: Record<string, string> = {}
@ -44,11 +47,11 @@ const InputFieldDialog = ({
const inputFieldsMap = useMemo(() => { const inputFieldsMap = useMemo(() => {
const inputFieldsMap: Record<string, InputVar[]> = {} const inputFieldsMap: Record<string, InputVar[]> = {}
ragPipelineVariables?.forEach((variable) => { ragPipelineVariables?.forEach((variable) => {
const { nodeId, variables } = variable const { belong_to_node_id: nodeId, ...varConfig } = variable
if (nodeId) if (inputFieldsMap[nodeId])
inputFieldsMap[nodeId] = variables inputFieldsMap[nodeId].push(varConfig)
else else
inputFieldsMap.shared = variables inputFieldsMap[nodeId] = [varConfig]
}) })
return inputFieldsMap return inputFieldsMap
}, [ragPipelineVariables]) }, [ragPipelineVariables])
@ -57,13 +60,23 @@ const InputFieldDialog = ({
return Object.keys(inputFieldsMap).filter(key => key !== 'shared') return Object.keys(inputFieldsMap).filter(key => key !== 'shared')
}, [inputFieldsMap]) }, [inputFieldsMap])
const updateInputFields = useCallback((key: string, value: InputVar[]) => { const updateInputFields = useCallback(async (key: string, value: InputVar[]) => {
const newRagPipelineVariables = produce(ragPipelineVariables!, (draft) => { const NewInputFieldsMap = produce(inputFieldsMap, (draft) => {
const index = draft.findIndex(variable => variable.nodeId === key) draft[key] = value
draft[index].variables = value })
const newRagPipelineVariables: RAGPipelineVariables = []
Object.keys(NewInputFieldsMap).forEach((key) => {
const inputFields = NewInputFieldsMap[key]
inputFields.forEach((inputField) => {
newRagPipelineVariables.push({
...inputField,
belong_to_node_id: key,
})
})
}) })
setRagPipelineVariables?.(newRagPipelineVariables) setRagPipelineVariables?.(newRagPipelineVariables)
}, [ragPipelineVariables, setRagPipelineVariables]) await doSyncWorkflowDraft()
}, [doSyncWorkflowDraft, inputFieldsMap, setRagPipelineVariables])
const closePanel = useCallback(() => { const closePanel = useCallback(() => {
setShowInputFieldDialog?.(false) setShowInputFieldDialog?.(false)
@ -99,6 +112,7 @@ const InputFieldDialog = ({
return null return null
return ( return (
<FieldList <FieldList
key={key}
LabelRightContent={<Datasource title={datasourceTitleMap[key]} />} LabelRightContent={<Datasource title={datasourceTitleMap[key]} />}
inputFields={inputFields} inputFields={inputFields}
readonly={readonly} readonly={readonly}
@ -109,15 +123,13 @@ const InputFieldDialog = ({
}) })
} }
{/* Shared Inputs */} {/* Shared Inputs */}
{inputFieldsMap.shared?.length > 0 && (
<FieldList <FieldList
LabelRightContent={<SharedInputs />} LabelRightContent={<SharedInputs />}
inputFields={inputFieldsMap.shared} inputFields={inputFieldsMap.shared || []}
readonly={readonly} readonly={readonly}
labelClassName='pt-1 pb-2' labelClassName='pt-1 pb-2'
handleInputFieldsChange={updateInputFields.bind(null, '')} handleInputFieldsChange={updateInputFields.bind(null, 'shared')}
/> />
)}
</div> </div>
<FooterTip /> <FooterTip />
</div> </div>

View File

@ -1,6 +1,5 @@
import type { RAGPipelineVariables } from '@/models/pipeline' import type { RAGPipelineVariables } from '@/models/pipeline'
import type { StateCreator } from 'zustand' import type { StateCreator } from 'zustand'
import { InputVarType } from '../../workflow/types'
export type RagPipelineSliceShape = { export type RagPipelineSliceShape = {
pipelineId: string pipelineId: string
@ -19,35 +18,6 @@ export const createRagPipelineSliceSlice: StateCreator<RagPipelineSliceShape> =
setShowInputFieldDialog: showInputFieldDialog => set(() => ({ showInputFieldDialog })), setShowInputFieldDialog: showInputFieldDialog => set(() => ({ showInputFieldDialog })),
nodesDefaultConfigs: {}, nodesDefaultConfigs: {},
setNodesDefaultConfigs: nodesDefaultConfigs => set(() => ({ nodesDefaultConfigs })), setNodesDefaultConfigs: nodesDefaultConfigs => set(() => ({ nodesDefaultConfigs })),
ragPipelineVariables: [{ ragPipelineVariables: [],
// TODO: delete mock data
nodeId: '123',
variables: [{
variable: 'name',
label: 'name',
type: InputVarType.textInput,
required: true,
max_length: 12,
}, {
variable: 'num',
label: 'num',
type: InputVarType.number,
required: true,
}],
}, {
nodeId: '',
variables: [{
variable: 'name',
label: 'name',
type: InputVarType.textInput,
required: true,
max_length: 12,
}, {
variable: 'num',
label: 'num',
type: InputVarType.number,
required: true,
}],
}],
setRagPipelineVariables: (ragPipelineVariables: RAGPipelineVariables) => set(() => ({ ragPipelineVariables })), setRagPipelineVariables: (ragPipelineVariables: RAGPipelineVariables) => set(() => ({ ragPipelineVariables })),
}) })

View File

@ -1,6 +1,7 @@
import { InputVarType } from '@/app/components/workflow/types' import { InputVarType } from '@/app/components/workflow/types'
import { AgentStrategy } from '@/types/app' import { AgentStrategy } from '@/types/app'
import { PromptRole } from '@/models/debug' import { PromptRole } from '@/models/debug'
import { PipelineInputVarType } from '@/models/pipeline'
export let apiPrefix = '' export let apiPrefix = ''
export let webPrefix = '' export let webPrefix = ''
@ -162,6 +163,15 @@ export const VAR_ITEM_TEMPLATE_IN_WORKFLOW = {
options: [], options: [],
} }
export const VAR_ITEM_TEMPLATE_IN_PIPELINE = {
variable: '',
label: '',
type: PipelineInputVarType.textInput,
max_length: DEFAULT_VALUE_MAX_LEN,
required: true,
options: [],
}
export const appDefaultIconBackground = '#D5F5F6' export const appDefaultIconBackground = '#D5F5F6'
export const NEED_REFRESH_APP_LIST_KEY = 'needRefreshAppList' export const NEED_REFRESH_APP_LIST_KEY = 'needRefreshAppList'

View File

@ -1,9 +1,10 @@
import type { Edge, InputVar, InputVarType, Node } from '@/app/components/workflow/types' import type { Edge, Node, SupportUploadFileTypes } from '@/app/components/workflow/types'
import type { DSLImportMode, DSLImportStatus } from './app' import type { DSLImportMode, DSLImportStatus } from './app'
import type { ChunkingMode, DatasetPermission, IconInfo } from './datasets' import type { ChunkingMode, DatasetPermission, IconInfo } from './datasets'
import type { Dependency } from '@/app/components/plugins/types' import type { Dependency } from '@/app/components/plugins/types'
import type { AppIconSelection } from '@/app/components/base/app-icon-picker' import type { AppIconSelection } from '@/app/components/base/app-icon-picker'
import type { Viewport } from 'reactflow' import type { Viewport } from 'reactflow'
import type { TransferMethod } from '@/types/app'
export type PipelineTemplateListParams = { export type PipelineTemplateListParams = {
type: 'built-in' | 'customized' type: 'built-in' | 'customized'
@ -96,24 +97,37 @@ export type PipelineCheckDependenciesResponse = {
leaked_dependencies: Dependency[] leaked_dependencies: Dependency[]
} }
export type Variables = { export enum PipelineInputVarType {
type: InputVarType textInput = 'text-input',
label: string paragraph = 'paragraph',
description: string select = 'select',
variable: string number = 'number',
max_length: number singleFile = 'file',
required: boolean multiFiles = 'file-list',
options?: string[] checkbox = 'checkbox',
default: string | number | boolean
} }
export type RAGPipelineVariable = {
belong_to_node_id: string // indicates belong to which node or 'shared'
type: PipelineInputVarType
label: string
variable: string
max_length?: number
default?: string
placeholder?: string
unit?: string
required: boolean
tooltips?: string
options?: string[]
allowed_file_upload_methods?: TransferMethod[]
allowed_file_types?: SupportUploadFileTypes[]
allowed_file_extensions?: string[]
}
export type InputVar = Omit<RAGPipelineVariable, 'belong_to_node_id'>
export type PipelineProcessingParamsResponse = { export type PipelineProcessingParamsResponse = {
variables: Variables[] variables: RAGPipelineVariable[]
} }
export type RAGPipelineVariable = InputVar export type RAGPipelineVariables = RAGPipelineVariable[]
export type RAGPipelineVariables = Array<{
nodeId: string
variables: RAGPipelineVariable[]
}>