feat: add DYNAMIC_SELECT parameter type for dynamic options in parameter entities (#21425)

This commit is contained in:
Yeuoly
2025-06-26 17:44:14 +08:00
committed by GitHub
parent ae00ba44db
commit cea6522122
15 changed files with 293 additions and 16 deletions

View File

@@ -13,6 +13,8 @@ type Props = {
readonly: boolean
value: string
onChange: (value: string | number, varKindType: VarKindType, varInfo?: Var) => void
onOpenChange?: (open: boolean) => void
isLoading?: boolean
}
const DEFAULT_SCHEMA = {} as CredentialFormSchema
@@ -22,6 +24,8 @@ const ConstantField: FC<Props> = ({
readonly,
value,
onChange,
onOpenChange,
isLoading,
}) => {
const language = useLanguage()
const placeholder = (schema as CredentialFormSchemaSelect).placeholder
@@ -36,7 +40,7 @@ const ConstantField: FC<Props> = ({
return (
<>
{schema.type === FormTypeEnum.select && (
{(schema.type === FormTypeEnum.select || schema.type === FormTypeEnum.dynamicSelect) && (
<SimpleSelect
wrapperClassName='w-full !h-8'
className='flex items-center'
@@ -45,6 +49,8 @@ const ConstantField: FC<Props> = ({
items={(schema as CredentialFormSchemaSelect).options.map(option => ({ value: option.value, name: option.label[language] || option.label.en_US }))}
onSelect={item => handleSelectChange(item.value)}
placeholder={placeholder?.[language] || placeholder?.en_US}
onOpenChange={onOpenChange}
isLoading={isLoading}
/>
)}
{schema.type === FormTypeEnum.textNumber && (

View File

@@ -6,6 +6,7 @@ import {
RiArrowDownSLine,
RiCloseLine,
RiErrorWarningFill,
RiLoader4Line,
RiMoreLine,
} from '@remixicon/react'
import produce from 'immer'
@@ -16,8 +17,9 @@ import VarReferencePopup from './var-reference-popup'
import { getNodeInfoById, isConversationVar, isENV, isSystemVar, varTypeToStructType } from './utils'
import ConstantField from './constant-field'
import cn from '@/utils/classnames'
import type { Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { Node, NodeOutPutVar, ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types'
import type { CredentialFormSchemaSelect } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { type CredentialFormSchema, type FormOption, FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { BlockEnum } from '@/app/components/workflow/types'
import { VarBlockIcon } from '@/app/components/workflow/block-icon'
import { Line3 } from '@/app/components/base/icons/src/public/common'
@@ -40,6 +42,8 @@ import Tooltip from '@/app/components/base/tooltip'
import { isExceptionVariable } from '@/app/components/workflow/utils'
import VarFullPathPanel from './var-full-path-panel'
import { noop } from 'lodash-es'
import { useFetchDynamicOptions } from '@/service/use-plugins'
import type { Tool } from '@/app/components/tools/types'
const TRIGGER_DEFAULT_WIDTH = 227
@@ -68,6 +72,8 @@ type Props = {
minWidth?: number
popupFor?: 'assigned' | 'toAssigned'
zIndex?: number
currentTool?: Tool
currentProvider?: ToolWithProvider
}
const DEFAULT_VALUE_SELECTOR: Props['value'] = []
@@ -97,6 +103,8 @@ const VarReferencePicker: FC<Props> = ({
minWidth,
popupFor,
zIndex,
currentTool,
currentProvider,
}) => {
const { t } = useTranslation()
const store = useStoreApi()
@@ -316,6 +324,42 @@ const VarReferencePicker: FC<Props> = ({
return null
}, [isValidVar, isShowAPart, hasValue, t, outputVarNode?.title, outputVarNode?.type, value, type])
const [dynamicOptions, setDynamicOptions] = useState<FormOption[] | null>(null)
const [isLoading, setIsLoading] = useState(false)
const { mutateAsync: fetchDynamicOptions } = useFetchDynamicOptions(
currentProvider?.plugin_id || '', currentProvider?.name || '', currentTool?.name || '', (schema as CredentialFormSchemaSelect)?.variable || '',
'tool',
)
const handleFetchDynamicOptions = async () => {
if (schema?.type !== FormTypeEnum.dynamicSelect || !currentTool || !currentProvider)
return
setIsLoading(true)
try {
const data = await fetchDynamicOptions()
setDynamicOptions(data?.options || [])
}
finally {
setIsLoading(false)
}
}
useEffect(() => {
handleFetchDynamicOptions()
}, [currentTool, currentProvider, schema])
const schemaWithDynamicSelect = useMemo(() => {
if (schema?.type !== FormTypeEnum.dynamicSelect)
return schema
// rewrite schema.options with dynamicOptions
if (dynamicOptions) {
return {
...schema,
options: dynamicOptions,
}
}
return schema
}, [dynamicOptions])
return (
<div className={cn(className, !readonly && 'cursor-pointer')}>
<PortalToFollowElem
@@ -366,8 +410,9 @@ const VarReferencePicker: FC<Props> = ({
<ConstantField
value={value as string}
onChange={onChange as ((value: string | number, varKindType: VarKindType, varInfo?: Var) => void)}
schema={schema as CredentialFormSchema}
schema={schemaWithDynamicSelect as CredentialFormSchema}
readonly={readonly}
isLoading={isLoading}
/>
)
: (
@@ -412,6 +457,7 @@ const VarReferencePicker: FC<Props> = ({
)}
<div className='flex items-center text-text-accent'>
{!hasValue && <Variable02 className='h-3.5 w-3.5' />}
{isLoading && <RiLoader4Line className='h-3.5 w-3.5 animate-spin text-text-secondary' />}
{isEnv && <Env className='h-3.5 w-3.5 text-util-colors-violet-violet-600' />}
{isChatVar && <BubbleX className='h-3.5 w-3.5 text-util-colors-teal-teal-700' />}
<div className={cn('ml-0.5 truncate text-xs font-medium', isEnv && '!text-text-secondary', isChatVar && 'text-util-colors-teal-teal-700', isException && 'text-text-warning')} title={varName} style={{
@@ -424,7 +470,16 @@ const VarReferencePicker: FC<Props> = ({
{!isValidVar && <RiErrorWarningFill className='ml-0.5 h-3 w-3 text-text-destructive' />}
</>
)
: <div className={`overflow-hidden ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'} system-sm-regular text-ellipsis`}>{placeholder ?? t('workflow.common.setVarValuePlaceholder')}</div>}
: <div className={`overflow-hidden ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'} system-sm-regular text-ellipsis`}>
{isLoading ? (
<div className='flex items-center'>
<RiLoader4Line className='mr-1 h-3.5 w-3.5 animate-spin text-text-secondary' />
<span>{placeholder ?? t('workflow.common.setVarValuePlaceholder')}</span>
</div>
) : (
placeholder ?? t('workflow.common.setVarValuePlaceholder')
)}
</div>}
</div>
</Tooltip>
</div>

View File

@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'
import type { ToolVarInputs } from '../types'
import { VarType as VarKindType } from '../types'
import cn from '@/utils/classnames'
import type { ValueSelector, Var } from '@/app/components/workflow/types'
import type { ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types'
import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
@@ -17,6 +17,7 @@ import { VarType } from '@/app/components/workflow/types'
import AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector'
import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector'
import { noop } from 'lodash-es'
import type { Tool } from '@/app/components/tools/types'
type Props = {
readOnly: boolean
@@ -27,6 +28,8 @@ type Props = {
onOpen?: (index: number) => void
isSupportConstantValue?: boolean
filterVar?: (payload: Var, valueSelector: ValueSelector) => boolean
currentTool?: Tool
currentProvider?: ToolWithProvider
}
const InputVarList: FC<Props> = ({
@@ -38,6 +41,8 @@ const InputVarList: FC<Props> = ({
onOpen = noop,
isSupportConstantValue,
filterVar,
currentTool,
currentProvider,
}) => {
const language = useLanguage()
const { t } = useTranslation()
@@ -58,6 +63,8 @@ const InputVarList: FC<Props> = ({
return 'ModelSelector'
else if (type === FormTypeEnum.toolSelector)
return 'ToolSelector'
else if (type === FormTypeEnum.dynamicSelect || type === FormTypeEnum.select)
return 'Select'
else
return 'String'
}
@@ -149,6 +156,7 @@ const InputVarList: FC<Props> = ({
const handleOpen = useCallback((index: number) => {
return () => onOpen(index)
}, [onOpen])
return (
<div className='space-y-3'>
{
@@ -163,7 +171,8 @@ const InputVarList: FC<Props> = ({
} = schema
const varInput = value[variable]
const isNumber = type === FormTypeEnum.textNumber
const isSelect = type === FormTypeEnum.select
const isDynamicSelect = type === FormTypeEnum.dynamicSelect
const isSelect = type === FormTypeEnum.select || type === FormTypeEnum.dynamicSelect
const isFile = type === FormTypeEnum.file || type === FormTypeEnum.files
const isAppSelector = type === FormTypeEnum.appSelector
const isModelSelector = type === FormTypeEnum.modelSelector
@@ -198,11 +207,13 @@ const InputVarList: FC<Props> = ({
value={varInput?.type === VarKindType.constant ? (varInput?.value ?? '') : (varInput?.value ?? [])}
onChange={handleNotMixedTypeChange(variable)}
onOpen={handleOpen(index)}
defaultVarKindType={varInput?.type || (isNumber ? VarKindType.constant : VarKindType.variable)}
defaultVarKindType={varInput?.type || ((isNumber || isDynamicSelect) ? VarKindType.constant : VarKindType.variable)}
isSupportConstantValue={isSupportConstantValue}
filterVar={isNumber ? filterVar : undefined}
availableVars={isSelect ? availableVars : undefined}
schema={schema}
currentTool={currentTool}
currentProvider={currentProvider}
/>
)}
{isFile && (

View File

@@ -42,6 +42,7 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({
isLoading,
outputSchema,
hasObjectOutput,
currTool,
} = useConfig(id, data)
if (isLoading) {
@@ -80,6 +81,8 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({
filterVar={filterVar}
isSupportConstantValue
onOpen={handleOnVarOpen}
currentProvider={currCollection}
currentTool={currTool}
/>
</Field>
)}