import type { EnvironmentVariable, EnvironmentVariableValue, LLMCompletionParams, LLMEnvironmentVariableValue, } from '@/app/components/workflow/types' import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { toast } from '@langgenius/dify-ui/toast' import { RiCloseLine } from '@remixicon/react' import * as React from 'react' import { useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { v4 as uuid4 } from 'uuid' import { Infotip } from '@/app/components/base/infotip' import Input from '@/app/components/base/input' import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' import { useWorkflowStore } from '@/app/components/workflow/store' import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' type ModalPropsType = { env?: EnvironmentVariable onClose: () => void onSave: (env: EnvironmentVariable) => void } const isLLMEnvironmentVariableValue = (value: unknown): value is LLMEnvironmentVariableValue => { if (typeof value !== 'object' || value === null) return false return ( 'provider' in value && typeof value.provider === 'string' && 'name' in value && typeof value.name === 'string' && 'mode' in value && typeof value.mode === 'string' ) } const VariableModal = ({ env, onClose, onSave }: ModalPropsType) => { const { t } = useTranslation() const workflowStore = useWorkflowStore() const [type, setType] = React.useState('string') const [name, setName] = React.useState('') const [value, setValue] = React.useState() const [description, setDescription] = React.useState('') const { activeTextGenerationModelList } = useTextGenerationCurrentProviderAndModelAndModelList() const originalLLMMode = env?.value_type === 'llm' && isLLMEnvironmentVariableValue(env.value) ? env.value.mode : undefined const selectableModelList = useMemo(() => { if (!originalLLMMode) return activeTextGenerationModelList return activeTextGenerationModelList .map((provider) => ({ ...provider, models: provider.models.filter((model) => model.model_properties.mode === originalLLMMode), })) .filter((provider) => provider.models.length > 0) }, [activeTextGenerationModelList, originalLLMMode]) const isTypeChangeDisabled = (nextType: EnvironmentVariable['value_type']) => !!env && (env.value_type === 'llm') !== (nextType === 'llm') const checkVariableName = (value: string) => { const { isValid, errorMessageKey } = checkKeys([value], false) if (!isValid) { toast.error( t(($) => $[`varKeyError.${errorMessageKey}`], { ns: 'appDebug', key: t(($) => $['env.modal.name'], { ns: 'workflow' }), }), ) return false } return true } const handleVarNameChange = (e: React.ChangeEvent) => { replaceSpaceWithUnderscoreInVarNameInput(e.target) if (!!e.target.value && !checkVariableName(e.target.value)) return setName(e.target.value || '') } const handleTypeChange = (nextType: EnvironmentVariable['value_type']) => { if (isTypeChangeDisabled(nextType)) return setType(nextType) if (nextType === 'llm') { if (!isLLMEnvironmentVariableValue(value)) setValue(undefined) return } if (isLLMEnvironmentVariableValue(value)) { setValue('') return } if (nextType === 'number') { if (value === undefined || value === '' || Number.isNaN(Number(value))) setValue('') return } if (typeof value === 'number') setValue(String(value)) } const handleModelSelect = ({ provider, modelId }: { provider: string; modelId: string }) => { const targetProvider = activeTextGenerationModelList.find( (providerItem) => providerItem.provider === provider, ) const targetModel = targetProvider?.models.find((modelItem) => modelItem.model === modelId) const mode = targetModel?.model_properties.mode if (typeof mode !== 'string') return if (originalLLMMode && mode !== originalLLMMode) { toast.error(t(($) => $['modelProvider.selector.incompatibleTip'], { ns: 'common' })) return } const completionParams = isLLMEnvironmentVariableValue(value) && value.provider === provider && value.name === modelId ? (value.completion_params ?? {}) : {} setValue({ provider, name: modelId, mode, completion_params: completionParams }) } const handleCompletionParamsChange = (completionParams: LLMCompletionParams) => { if (!isLLMEnvironmentVariableValue(value)) return setValue({ ...value, completion_params: completionParams }) } const handleSave = () => { if (!checkVariableName(name)) return if ( value === undefined || value === '' || (type === 'llm' && !isLLMEnvironmentVariableValue(value)) ) return toast.error(t(($) => $['env.modal.valueRequired'], { ns: 'workflow' })) // Add check for duplicate name when editing const envList = workflowStore.getState().environmentVariables if (env && env.name !== name && envList.some((e) => e.name === name)) return toast.error( t(($) => $['varKeyError.keyAlreadyExists'], { ns: 'appDebug', key: t(($) => $['env.modal.name'], { ns: 'workflow' }), }), ) // Original check for create new variable if (!env && envList.some((e) => e.name === name)) return toast.error( t(($) => $['varKeyError.keyAlreadyExists'], { ns: 'appDebug', key: t(($) => $['env.modal.name'], { ns: 'workflow' }), }), ) onSave({ id: env ? env.id : uuid4(), value_type: type, name, value: type === 'number' ? Number(value) : value, description, }) onClose() } useEffect(() => { if (env) { setType(env.value_type) setName(env.name) const envSecrets = workflowStore.getState().envSecrets setValue(env.value_type === 'secret' ? envSecrets[env.id] : env.value) setDescription(env.description) } }, [env, workflowStore]) return (
{!env ? t(($) => $['env.modal.title'], { ns: 'workflow' }) : t(($) => $['env.modal.editTitle'], { ns: 'workflow' })}
{/* type */}
{t(($) => $['env.modal.type'], { ns: 'workflow' })}
$['env.modal.type'], { ns: 'workflow' })} className="grid grid-cols-4 gap-2" >
$['env.modal.secretTip'], { ns: 'workflow' })} className="absolute top-1/2 right-1 size-3.5 -translate-y-1/2" popupClassName="w-[240px]" > {t(($) => $['env.modal.secretTip'], { ns: 'workflow' })}
{/* name */}
{t(($) => $['env.modal.name'], { ns: 'workflow' })}
$['env.modal.namePlaceholder'], { ns: 'workflow' }) || ''} value={name} onChange={handleVarNameChange} onBlur={(e) => checkVariableName(e.target.value)} type="text" />
{/* value */}
{type === 'llm' ? t(($) => $['modelProvider.model'], { ns: 'common' }) : t(($) => $['env.modal.value'], { ns: 'workflow' })}
{type === 'llm' ? ( ) : type !== 'number' ? (