import type { ModelParameterRule } from '../declarations' import type { Node, NodeOutPutVar } from '@/app/components/workflow/types' import { cn } from '@langgenius/dify-ui/cn' import { Field, FieldItem, FieldLabel } from '@langgenius/dify-ui/field' import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset' import { Radio, RadioGroup } from '@langgenius/dify-ui/radio' import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectLabel, SelectTrigger, SelectValue, } from '@langgenius/dify-ui/select' import { Slider } from '@langgenius/dify-ui/slider' import { Switch } from '@langgenius/dify-ui/switch' import { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { Infotip } from '@/app/components/base/infotip' import PromptEditor from '@/app/components/base/prompt-editor' import TagInput from '@/app/components/base/tag-input' import { BlockEnum } from '@/app/components/workflow/types' import { useLanguage } from '../hooks' import { isNullOrUndefined } from '../utils' export type ParameterValue = number | string | string[] | boolean | undefined type ParameterItemProps = { parameterRule: ModelParameterRule value?: ParameterValue onChange?: (value: ParameterValue) => void onSwitch?: (checked: boolean, assignValue: ParameterValue) => void isInWorkflow?: boolean nodesOutputVars?: NodeOutPutVar[] availableNodes?: Node[] } function ParameterItem({ parameterRule, value, onChange, onSwitch, isInWorkflow, nodesOutputVars, availableNodes = [], }: ParameterItemProps) { const { t } = useTranslation() const language = useLanguage() const [localValue, setLocalValue] = useState(value) const numberInputRef = useRef(null) const workflowNodesMap = useMemo(() => { if (!isInWorkflow || !availableNodes.length) return undefined return availableNodes.reduce>>( (acc, node) => { acc[node.id] = { title: node.data.title, type: node.data.type, } if (node.data.type === BlockEnum.Start) { acc.sys = { title: t(($) => $['blocks.start'], { ns: 'workflow' }), type: BlockEnum.Start, } } return acc }, {}, ) }, [availableNodes, isInWorkflow, t]) const getDefaultValue = () => { let defaultValue: ParameterValue if (parameterRule.type === 'int' || parameterRule.type === 'float') defaultValue = isNullOrUndefined(parameterRule.default) ? parameterRule.min || 0 : parameterRule.default else if (parameterRule.type === 'string' || parameterRule.type === 'text') defaultValue = parameterRule.default || '' else if (parameterRule.type === 'boolean') defaultValue = !isNullOrUndefined(parameterRule.default) ? parameterRule.default : false else if (parameterRule.type === 'tag') defaultValue = !isNullOrUndefined(parameterRule.default) ? parameterRule.default : [] return defaultValue } const renderValue = value ?? localValue ?? getDefaultValue() const sliderLabel = parameterRule.label[language] || parameterRule.label.en_US const handleInputChange = (newValue: ParameterValue) => { setLocalValue(newValue) if ( onChange && (parameterRule.name === 'stop' || !isNullOrUndefined(value) || parameterRule.required) ) onChange(newValue) } const handleNumberInputChange = (e: React.ChangeEvent) => { let num = +e.target.value if (!isNullOrUndefined(parameterRule.max) && num > parameterRule.max!) { num = parameterRule.max as number numberInputRef.current!.value = `${num}` } if (!isNullOrUndefined(parameterRule.min) && num < parameterRule.min!) num = parameterRule.min as number handleInputChange(num) } const handleNumberInputBlur = () => { if (numberInputRef.current) numberInputRef.current.value = renderValue as string } const handleSlideChange = (num: number) => { if (!isNullOrUndefined(parameterRule.max) && num > parameterRule.max!) { handleInputChange(parameterRule.max) numberInputRef.current!.value = `${parameterRule.max}` return } if (!isNullOrUndefined(parameterRule.min) && num < parameterRule.min!) { handleInputChange(parameterRule.min) numberInputRef.current!.value = `${parameterRule.min}` return } handleInputChange(num) numberInputRef.current!.value = `${num}` } const handleRadioChange = (v: boolean) => { handleInputChange(v) } const handleStringInputChange = ( e: React.ChangeEvent, ) => { handleInputChange(e.target.value) } const handleTagChange = (newSequences: string[]) => { handleInputChange(newSequences) } const handleSwitch = (checked: boolean) => { if (onSwitch) { const assignValue: ParameterValue = localValue ?? getDefaultValue() onSwitch(checked, assignValue) } } useEffect(() => { if ((parameterRule.type === 'int' || parameterRule.type === 'float') && numberInputRef.current) numberInputRef.current.value = `${renderValue}` }, [value, parameterRule.type, renderValue]) const renderInput = () => { const numberInputWithSlide = (parameterRule.type === 'int' || parameterRule.type === 'float') && !isNullOrUndefined(parameterRule.min) && !isNullOrUndefined(parameterRule.max) if (parameterRule.type === 'int') { let step = 100 if (parameterRule.max) { if (parameterRule.max < 100) step = 1 else if (parameterRule.max < 1000) step = 10 } if (!numberInputWithSlide) { return ( ) } return (
{sliderLabel}
) } if (parameterRule.type === 'float') { if (!numberInputWithSlide) { return ( ) } return (
{sliderLabel}
) } if (parameterRule.type === 'boolean') { const booleanValue = typeof renderValue === 'boolean' ? renderValue : undefined const translatedLabel = parameterRule.label[language] || parameterRule.label.en_US return (
className="w-[150px] gap-3" value={booleanValue} onValueChange={handleRadioChange} /> } > {translatedLabel} value={true} /> True value={false} /> False
) } if (parameterRule.type === 'string' && !parameterRule.options?.length) { if (isInWorkflow && nodesOutputVars) { return (
{ handleInputChange(text) }} workflowVariableBlock={{ show: true, variables: nodesOutputVars, workflowNodesMap, }} editable />
) } return ( ) } if (parameterRule.type === 'text') { if (isInWorkflow && nodesOutputVars) { return (
{ handleInputChange(text) }} workflowVariableBlock={{ show: true, variables: nodesOutputVars, workflowNodesMap, }} editable />
) } return (