dify
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import {
|
||||
memo,
|
||||
useCallback,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import BlockSelector from '../../../../block-selector'
|
||||
import type { Param, ParamType } from '../../types'
|
||||
import cn from '@/utils/classnames'
|
||||
import type {
|
||||
PluginDefaultValue,
|
||||
ToolDefaultValue,
|
||||
} from '@/app/components/workflow/block-selector/types'
|
||||
import type { ToolParameter } from '@/app/components/tools/types'
|
||||
import { CollectionType } from '@/app/components/tools/types'
|
||||
import type { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { canFindTool } from '@/utils'
|
||||
import {
|
||||
useAllBuiltInTools,
|
||||
useAllCustomTools,
|
||||
useAllWorkflowTools,
|
||||
} from '@/service/use-tools'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.parameterExtractor'
|
||||
|
||||
type Props = {
|
||||
onImport: (params: Param[]) => void
|
||||
}
|
||||
|
||||
function toParmExactParams(toolParams: ToolParameter[], lan: string): Param[] {
|
||||
return toolParams.map((item) => {
|
||||
return {
|
||||
name: item.name,
|
||||
type: item.type as ParamType,
|
||||
required: item.required,
|
||||
description: item.llm_description,
|
||||
options: item.options?.map(option => option.label[lan] || option.label.en_US) || [],
|
||||
}
|
||||
})
|
||||
}
|
||||
const ImportFromTool: FC<Props> = ({
|
||||
onImport,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const language = useLanguage()
|
||||
|
||||
const { data: buildInTools } = useAllBuiltInTools()
|
||||
const { data: customTools } = useAllCustomTools()
|
||||
const { data: workflowTools } = useAllWorkflowTools()
|
||||
|
||||
const handleSelectTool = useCallback((_type: BlockEnum, toolInfo?: PluginDefaultValue) => {
|
||||
if (!toolInfo || 'datasource_name' in toolInfo || !('tool_name' in toolInfo))
|
||||
return
|
||||
|
||||
const { provider_id, provider_type, tool_name } = toolInfo as ToolDefaultValue
|
||||
const currentTools = (() => {
|
||||
switch (provider_type) {
|
||||
case CollectionType.builtIn:
|
||||
return buildInTools || []
|
||||
case CollectionType.custom:
|
||||
return customTools || []
|
||||
case CollectionType.workflow:
|
||||
return workflowTools || []
|
||||
default:
|
||||
return []
|
||||
}
|
||||
})()
|
||||
const currCollection = currentTools.find(item => canFindTool(item.id, provider_id))
|
||||
const currTool = currCollection?.tools.find(tool => tool.name === tool_name)
|
||||
const toExactParams = (currTool?.parameters || []).filter(item => item.form === 'llm')
|
||||
const formattedParams = toParmExactParams(toExactParams, language)
|
||||
onImport(formattedParams)
|
||||
}, [buildInTools, customTools, language, onImport, workflowTools])
|
||||
|
||||
const renderTrigger = useCallback((open: boolean) => {
|
||||
return (
|
||||
<div>
|
||||
<div className={cn(
|
||||
'flex h-6 cursor-pointer items-center rounded-md px-2 text-xs font-medium text-text-tertiary hover:bg-state-base-hover',
|
||||
open && 'bg-state-base-hover',
|
||||
)}>
|
||||
{t(`${i18nPrefix}.importFromTool`)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, [t])
|
||||
|
||||
return (
|
||||
<BlockSelector
|
||||
placement='bottom-end'
|
||||
offset={{
|
||||
mainAxis: 4,
|
||||
crossAxis: 52,
|
||||
}}
|
||||
trigger={renderTrigger}
|
||||
onSelect={handleSelectTool}
|
||||
noBlocks
|
||||
/>
|
||||
)
|
||||
}
|
||||
export default memo(ImportFromTool)
|
||||
@@ -0,0 +1,59 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiDeleteBinLine,
|
||||
RiEditLine,
|
||||
} from '@remixicon/react'
|
||||
import type { Param } from '../../types'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
const i18nPrefix = 'workflow.nodes.parameterExtractor'
|
||||
|
||||
type Props = {
|
||||
payload: Param
|
||||
onEdit: () => void
|
||||
onDelete: () => void
|
||||
}
|
||||
|
||||
const Item: FC<Props> = ({
|
||||
payload,
|
||||
onEdit,
|
||||
onDelete,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className='group relative rounded-lg bg-components-input-bg-normal px-2.5 py-2 hover:shadow-xs'>
|
||||
<div className='flex justify-between'>
|
||||
<div className='flex items-center'>
|
||||
<Variable02 className='h-3.5 w-3.5 text-text-accent-secondary' />
|
||||
<div className='ml-1 text-[13px] font-medium text-text-primary'>{payload.name}</div>
|
||||
<div className='ml-2 text-xs font-normal capitalize text-text-tertiary'>{payload.type}</div>
|
||||
</div>
|
||||
{payload.required && (
|
||||
<div className='text-xs font-normal uppercase leading-4 text-text-tertiary'>{t(`${i18nPrefix}.addExtractParameterContent.required`)}</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='mt-0.5 text-xs font-normal leading-[18px] text-text-tertiary'>{payload.description}</div>
|
||||
<div
|
||||
className='absolute right-0 top-0 hidden h-full w-[119px] items-center justify-end space-x-1 rounded-lg bg-gradient-to-l from-components-panel-on-panel-item-bg to-background-gradient-mask-transparent pr-1 group-hover:flex'
|
||||
>
|
||||
<div
|
||||
className='cursor-pointer rounded-md p-1 hover:bg-state-base-hover'
|
||||
onClick={onEdit}
|
||||
>
|
||||
<RiEditLine className='h-4 w-4 text-text-tertiary' />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className='group shrink-0 cursor-pointer rounded-md p-1 hover:!bg-state-destructive-hover'
|
||||
onClick={onDelete}
|
||||
>
|
||||
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary group-hover:text-text-destructive' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(Item)
|
||||
@@ -0,0 +1,85 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import type { Param } from '../../types'
|
||||
import ListNoDataPlaceholder from '../../../_base/components/list-no-data-placeholder'
|
||||
import Item from './item'
|
||||
import EditParam from './update'
|
||||
import type { MoreInfo } from '@/app/components/workflow/types'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.parameterExtractor'
|
||||
|
||||
type Props = {
|
||||
readonly: boolean
|
||||
list: Param[]
|
||||
onChange: (list: Param[], moreInfo?: MoreInfo) => void
|
||||
}
|
||||
|
||||
const List: FC<Props> = ({
|
||||
list,
|
||||
onChange,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [isShowEditModal, {
|
||||
setTrue: showEditModal,
|
||||
setFalse: hideEditModal,
|
||||
}] = useBoolean(false)
|
||||
|
||||
const handleItemChange = useCallback((index: number) => {
|
||||
return (payload: Param, moreInfo?: MoreInfo) => {
|
||||
const newList = list.map((item, i) => {
|
||||
if (i === index)
|
||||
return payload
|
||||
|
||||
return item
|
||||
})
|
||||
onChange(newList, moreInfo)
|
||||
hideEditModal()
|
||||
}
|
||||
}, [hideEditModal, list, onChange])
|
||||
|
||||
const [currEditItemIndex, setCurrEditItemIndex] = useState<number>(-1)
|
||||
|
||||
const handleItemEdit = useCallback((index: number) => {
|
||||
return () => {
|
||||
setCurrEditItemIndex(index)
|
||||
showEditModal()
|
||||
}
|
||||
}, [showEditModal])
|
||||
|
||||
const handleItemDelete = useCallback((index: number) => {
|
||||
return () => {
|
||||
const newList = list.filter((_, i) => i !== index)
|
||||
onChange(newList)
|
||||
}
|
||||
}, [list, onChange])
|
||||
|
||||
if (list.length === 0) {
|
||||
return (
|
||||
<ListNoDataPlaceholder >{t(`${i18nPrefix}.extractParametersNotSet`)}</ListNoDataPlaceholder>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className='space-y-1'>
|
||||
{list.map((item, index) => (
|
||||
<Item
|
||||
key={index}
|
||||
payload={item}
|
||||
onDelete={handleItemDelete(index)}
|
||||
onEdit={handleItemEdit(index)}
|
||||
/>
|
||||
))}
|
||||
{isShowEditModal && (
|
||||
<EditParam
|
||||
type='edit'
|
||||
payload={list[currEditItemIndex]}
|
||||
onSave={handleItemChange(currEditItemIndex)}
|
||||
onCancel={hideEditModal}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(List)
|
||||
@@ -0,0 +1,189 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { Param } from '../../types'
|
||||
import { ParamType } from '../../types'
|
||||
import AddButton from '@/app/components/base/button/add-button'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Field from '@/app/components/app/configuration/config-var/config-modal/field'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Textarea from '@/app/components/base/textarea'
|
||||
import Select from '@/app/components/base/select'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import ConfigSelect from '@/app/components/app/configuration/config-var/config-select'
|
||||
import { ChangeType, type MoreInfo } from '@/app/components/workflow/types'
|
||||
import { checkKeys } from '@/utils/var'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.parameterExtractor'
|
||||
const errorI18nPrefix = 'workflow.errorMsg'
|
||||
|
||||
const DEFAULT_PARAM: Param = {
|
||||
name: '',
|
||||
type: ParamType.string,
|
||||
description: '',
|
||||
required: false,
|
||||
}
|
||||
|
||||
type Props = {
|
||||
type: 'add' | 'edit'
|
||||
payload?: Param
|
||||
onSave: (payload: Param, moreInfo?: MoreInfo) => void
|
||||
onCancel?: () => void
|
||||
}
|
||||
|
||||
const TYPES = [ParamType.string, ParamType.number, ParamType.bool, ParamType.arrayString, ParamType.arrayNumber, ParamType.arrayObject, ParamType.arrayBool]
|
||||
|
||||
const AddExtractParameter: FC<Props> = ({
|
||||
type,
|
||||
payload,
|
||||
onSave,
|
||||
onCancel,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const isAdd = type === 'add'
|
||||
const [param, setParam] = useState<Param>(isAdd ? DEFAULT_PARAM : payload as Param)
|
||||
const [renameInfo, setRenameInfo] = useState<MoreInfo | undefined>(undefined)
|
||||
const handleParamChange = useCallback((key: string) => {
|
||||
return (value: any) => {
|
||||
if (key === 'name') {
|
||||
const { isValid, errorKey, errorMessageKey } = checkKeys([value], true)
|
||||
if (!isValid) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
setRenameInfo(key === 'name'
|
||||
? {
|
||||
type: ChangeType.changeVarName,
|
||||
payload: {
|
||||
beforeKey: param.name,
|
||||
afterKey: value,
|
||||
},
|
||||
}
|
||||
: undefined)
|
||||
setParam((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
[key]: value,
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [param.name, t])
|
||||
|
||||
const [isShowModal, {
|
||||
setTrue: doShowModal,
|
||||
setFalse: doHideModal,
|
||||
}] = useBoolean(!isAdd)
|
||||
|
||||
const hideModal = useCallback(() => {
|
||||
doHideModal()
|
||||
onCancel?.()
|
||||
}, [onCancel, doHideModal])
|
||||
|
||||
const showAddModal = useCallback(() => {
|
||||
if (isAdd)
|
||||
setParam(DEFAULT_PARAM)
|
||||
|
||||
doShowModal()
|
||||
}, [isAdd, doShowModal])
|
||||
|
||||
const checkValid = useCallback(() => {
|
||||
let errMessage = ''
|
||||
if (!param.name)
|
||||
errMessage = t(`${errorI18nPrefix}.fieldRequired`, { field: t(`${i18nPrefix}.addExtractParameterContent.name`) })
|
||||
if (!errMessage && param.type === ParamType.select && (!param.options || param.options.length === 0))
|
||||
errMessage = t(`${errorI18nPrefix}.fieldRequired`, { field: t('appDebug.variableConfig.options') })
|
||||
if (!errMessage && !param.description)
|
||||
errMessage = t(`${errorI18nPrefix}.fieldRequired`, { field: t(`${i18nPrefix}.addExtractParameterContent.description`) })
|
||||
|
||||
if (errMessage) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: errMessage,
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, [param, t])
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
if (!checkValid())
|
||||
return
|
||||
|
||||
onSave(param, renameInfo)
|
||||
hideModal()
|
||||
}, [checkValid, onSave, param, hideModal, renameInfo])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isAdd && (
|
||||
<AddButton className='mx-1' onClick={showAddModal} />
|
||||
)}
|
||||
{isShowModal && (
|
||||
<Modal
|
||||
title={t(`${i18nPrefix}.addExtractParameter`)}
|
||||
isShow
|
||||
onClose={hideModal}
|
||||
className='!w-[400px] !max-w-[400px] !p-4'
|
||||
>
|
||||
<div>
|
||||
<div className='space-y-2'>
|
||||
<Field title={t(`${i18nPrefix}.addExtractParameterContent.name`)}>
|
||||
<Input
|
||||
value={param.name}
|
||||
onChange={e => handleParamChange('name')(e.target.value)}
|
||||
placeholder={t(`${i18nPrefix}.addExtractParameterContent.namePlaceholder`)!}
|
||||
/>
|
||||
</Field>
|
||||
<Field title={t(`${i18nPrefix}.addExtractParameterContent.type`)}>
|
||||
<Select
|
||||
defaultValue={param.type}
|
||||
allowSearch={false}
|
||||
// bgClassName='bg-gray-100'
|
||||
onSelect={v => handleParamChange('type')(v.value)}
|
||||
optionClassName='capitalize'
|
||||
items={
|
||||
TYPES.map(type => ({
|
||||
value: type,
|
||||
name: type,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
{param.type === ParamType.select && (
|
||||
<Field title={t('appDebug.variableConfig.options')}>
|
||||
<ConfigSelect options={param.options || []} onChange={handleParamChange('options')} />
|
||||
</Field>
|
||||
)}
|
||||
<Field title={t(`${i18nPrefix}.addExtractParameterContent.description`)}>
|
||||
<Textarea
|
||||
value={param.description}
|
||||
onChange={e => handleParamChange('description')(e.target.value)}
|
||||
placeholder={t(`${i18nPrefix}.addExtractParameterContent.descriptionPlaceholder`)!}
|
||||
/>
|
||||
</Field>
|
||||
<Field title={t(`${i18nPrefix}.addExtractParameterContent.required`)}>
|
||||
<>
|
||||
<div className='mb-1.5 text-xs font-normal leading-[18px] text-text-tertiary'>{t(`${i18nPrefix}.addExtractParameterContent.requiredContent`)}</div>
|
||||
<Switch size='l' defaultValue={param.required} onChange={handleParamChange('required')} />
|
||||
</>
|
||||
</Field>
|
||||
</div>
|
||||
<div className='mt-4 flex justify-end space-x-2'>
|
||||
<Button className='!w-[95px]' onClick={hideModal} >{t('common.operation.cancel')}</Button>
|
||||
<Button className='!w-[95px]' variant='primary' onClick={handleSave} >{isAdd ? t('common.operation.add') : t('common.operation.save')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(AddExtractParameter)
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ReasoningModeType } from '../types'
|
||||
import Field from '../../_base/components/field'
|
||||
import OptionCard from '../../_base/components/option-card'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.parameterExtractor'
|
||||
|
||||
type Props = {
|
||||
type: ReasoningModeType
|
||||
onChange: (type: ReasoningModeType) => void
|
||||
}
|
||||
|
||||
const ReasoningModePicker: FC<Props> = ({
|
||||
type,
|
||||
onChange,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const handleChange = useCallback((type: ReasoningModeType) => {
|
||||
return () => {
|
||||
onChange(type)
|
||||
}
|
||||
}, [onChange])
|
||||
|
||||
return (
|
||||
<Field
|
||||
title={t(`${i18nPrefix}.reasoningMode`)}
|
||||
tooltip={t(`${i18nPrefix}.reasoningModeTip`)!}
|
||||
>
|
||||
<div className='grid grid-cols-2 gap-x-1'>
|
||||
<OptionCard
|
||||
title='Function/Tool Calling'
|
||||
onSelect={handleChange(ReasoningModeType.functionCall)}
|
||||
selected={type === ReasoningModeType.functionCall}
|
||||
/>
|
||||
<OptionCard
|
||||
title='Prompt'
|
||||
selected={type === ReasoningModeType.prompt}
|
||||
onSelect={handleChange(ReasoningModeType.prompt)}
|
||||
/>
|
||||
</div>
|
||||
</Field>
|
||||
|
||||
)
|
||||
}
|
||||
export default React.memo(ReasoningModePicker)
|
||||
@@ -0,0 +1,67 @@
|
||||
import type { NodeDefault } from '../../types'
|
||||
import { type ParameterExtractorNodeType, ReasoningModeType } from './types'
|
||||
import { genNodeMetaData } from '@/app/components/workflow/utils'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
const i18nPrefix = 'workflow'
|
||||
|
||||
const metaData = genNodeMetaData({
|
||||
classification: BlockClassificationEnum.Transform,
|
||||
sort: 6,
|
||||
type: BlockEnum.ParameterExtractor,
|
||||
})
|
||||
const nodeDefault: NodeDefault<ParameterExtractorNodeType> = {
|
||||
metaData,
|
||||
defaultValue: {
|
||||
query: [],
|
||||
model: {
|
||||
provider: '',
|
||||
name: '',
|
||||
mode: AppModeEnum.CHAT,
|
||||
completion_params: {
|
||||
temperature: 0.7,
|
||||
},
|
||||
},
|
||||
reasoning_mode: ReasoningModeType.prompt,
|
||||
vision: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
checkValid(payload: ParameterExtractorNodeType, t: any) {
|
||||
let errorMessages = ''
|
||||
if (!errorMessages && (!payload.query || payload.query.length === 0))
|
||||
errorMessages = t(`${i18nPrefix}.errorMsg.fieldRequired`, { field: t(`${i18nPrefix}.nodes.parameterExtractor.inputVar`) })
|
||||
|
||||
if (!errorMessages && !payload.model.provider)
|
||||
errorMessages = t(`${i18nPrefix}.errorMsg.fieldRequired`, { field: t(`${i18nPrefix}.nodes.parameterExtractor.model`) })
|
||||
|
||||
if (!errorMessages && (!payload.parameters || payload.parameters.length === 0))
|
||||
errorMessages = t(`${i18nPrefix}.errorMsg.fieldRequired`, { field: t(`${i18nPrefix}.nodes.parameterExtractor.extractParameters`) })
|
||||
|
||||
if (!errorMessages) {
|
||||
payload.parameters.forEach((param) => {
|
||||
if (errorMessages)
|
||||
return
|
||||
if (!param.name) {
|
||||
errorMessages = t(`${i18nPrefix}.errorMsg.fieldRequired`, { field: t(`${i18nPrefix}.nodes.parameterExtractor.addExtractParameterContent.namePlaceholder`) })
|
||||
return
|
||||
}
|
||||
if (!param.type) {
|
||||
errorMessages = t(`${i18nPrefix}.errorMsg.fieldRequired`, { field: t(`${i18nPrefix}.nodes.parameterExtractor.addExtractParameterContent.typePlaceholder`) })
|
||||
return
|
||||
}
|
||||
if (!param.description)
|
||||
errorMessages = t(`${i18nPrefix}.errorMsg.fieldRequired`, { field: t(`${i18nPrefix}.nodes.parameterExtractor.addExtractParameterContent.descriptionPlaceholder`) })
|
||||
})
|
||||
}
|
||||
if (!errorMessages && payload.vision?.enabled && !payload.vision.configs?.variable_selector?.length)
|
||||
errorMessages = t(`${i18nPrefix}.errorMsg.fieldRequired`, { field: t(`${i18nPrefix}.errorMsg.fields.visionVariable`) })
|
||||
return {
|
||||
isValid: !errorMessages,
|
||||
errorMessage: errorMessages,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default nodeDefault
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import type { ParameterExtractorNodeType } from './types'
|
||||
import {
|
||||
useTextGenerationCurrentProviderAndModelAndModelList,
|
||||
} from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
import type { NodeProps } from '@/app/components/workflow/types'
|
||||
|
||||
const Node: FC<NodeProps<ParameterExtractorNodeType>> = ({
|
||||
data,
|
||||
}) => {
|
||||
const { provider, name: modelId } = data.model || {}
|
||||
const {
|
||||
textGenerationModelList,
|
||||
} = useTextGenerationCurrentProviderAndModelAndModelList()
|
||||
const hasSetModel = provider && modelId
|
||||
return (
|
||||
<div className='mb-1 px-3 py-1'>
|
||||
{hasSetModel && (
|
||||
<ModelSelector
|
||||
defaultModel={{ provider, model: modelId }}
|
||||
modelList={textGenerationModelList}
|
||||
triggerClassName='!h-6 !rounded-md'
|
||||
readonly
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(Node)
|
||||
@@ -0,0 +1,212 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import MemoryConfig from '../_base/components/memory-config'
|
||||
import VarReferencePicker from '../_base/components/variable/var-reference-picker'
|
||||
import Editor from '../_base/components/prompt/editor'
|
||||
import ConfigVision from '../_base/components/config-vision'
|
||||
import useConfig from './use-config'
|
||||
import type { ParameterExtractorNodeType } from './types'
|
||||
import ExtractParameter from './components/extract-parameter/list'
|
||||
import ImportFromTool from './components/extract-parameter/import-from-tool'
|
||||
import AddExtractParameter from './components/extract-parameter/update'
|
||||
import ReasoningModePicker from './components/reasoning-mode-picker'
|
||||
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
||||
import Split from '@/app/components/workflow/nodes/_base/components/split'
|
||||
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
|
||||
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
|
||||
import type { NodePanelProps } from '@/app/components/workflow/types'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { VarType } from '@/app/components/workflow/types'
|
||||
import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.parameterExtractor'
|
||||
const i18nCommonPrefix = 'workflow.common'
|
||||
|
||||
const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({
|
||||
id,
|
||||
data,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const {
|
||||
readOnly,
|
||||
inputs,
|
||||
handleInputVarChange,
|
||||
filterVar,
|
||||
isChatModel,
|
||||
isChatMode,
|
||||
isCompletionModel,
|
||||
handleModelChanged,
|
||||
handleImportFromTool,
|
||||
handleCompletionParamsChange,
|
||||
addExtractParameter,
|
||||
handleExactParamsChange,
|
||||
handleInstructionChange,
|
||||
hasSetBlockStatus,
|
||||
handleMemoryChange,
|
||||
isSupportFunctionCall,
|
||||
handleReasoningModeChange,
|
||||
availableVars,
|
||||
availableNodesWithParent,
|
||||
isVisionModel,
|
||||
handleVisionResolutionChange,
|
||||
handleVisionResolutionEnabledChange,
|
||||
} = useConfig(id, data)
|
||||
|
||||
const model = inputs.model
|
||||
|
||||
return (
|
||||
<div className='pt-2'>
|
||||
<div className='space-y-4 px-4'>
|
||||
<Field
|
||||
title={t(`${i18nCommonPrefix}.model`)}
|
||||
required
|
||||
>
|
||||
<ModelParameterModal
|
||||
popupClassName='!w-[387px]'
|
||||
isInWorkflow
|
||||
isAdvancedMode={true}
|
||||
provider={model?.provider}
|
||||
completionParams={model?.completion_params}
|
||||
modelId={model?.name}
|
||||
setModel={handleModelChanged}
|
||||
onCompletionParamsChange={handleCompletionParamsChange}
|
||||
hideDebugWithMultipleModel
|
||||
debugWithMultipleModel={false}
|
||||
readonly={readOnly}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
title={t(`${i18nPrefix}.inputVar`)}
|
||||
required
|
||||
>
|
||||
<>
|
||||
<VarReferencePicker
|
||||
readonly={readOnly}
|
||||
nodeId={id}
|
||||
isShowNodeName
|
||||
value={inputs.query || []}
|
||||
onChange={handleInputVarChange}
|
||||
filterVar={filterVar}
|
||||
/>
|
||||
</>
|
||||
</Field>
|
||||
<Split />
|
||||
<ConfigVision
|
||||
nodeId={id}
|
||||
readOnly={readOnly}
|
||||
isVisionModel={isVisionModel}
|
||||
enabled={inputs.vision?.enabled}
|
||||
onEnabledChange={handleVisionResolutionEnabledChange}
|
||||
config={inputs.vision?.configs}
|
||||
onConfigChange={handleVisionResolutionChange}
|
||||
/>
|
||||
<Field
|
||||
title={t(`${i18nPrefix}.extractParameters`)}
|
||||
required
|
||||
operations={
|
||||
!readOnly
|
||||
? (
|
||||
<div className='flex items-center space-x-1'>
|
||||
{!readOnly && (
|
||||
<ImportFromTool onImport={handleImportFromTool} />
|
||||
)}
|
||||
{!readOnly && (<div className='h-3 w-px bg-divider-regular'></div>)}
|
||||
<AddExtractParameter type='add' onSave={addExtractParameter} />
|
||||
</div>
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<ExtractParameter
|
||||
readonly={readOnly}
|
||||
list={inputs.parameters || []}
|
||||
onChange={handleExactParamsChange}
|
||||
/>
|
||||
</Field>
|
||||
<Editor
|
||||
title={
|
||||
<div className='flex items-center space-x-1'>
|
||||
<span className='uppercase'>{t(`${i18nPrefix}.instruction`)}</span>
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='w-[120px]'>
|
||||
{t(`${i18nPrefix}.instructionTip`)}
|
||||
</div>
|
||||
}
|
||||
triggerClassName='w-3.5 h-3.5 ml-0.5'
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
value={inputs.instruction}
|
||||
onChange={handleInstructionChange}
|
||||
readOnly={readOnly}
|
||||
isChatModel={isChatModel}
|
||||
isChatApp={isChatMode}
|
||||
isShowContext={false}
|
||||
hasSetBlockStatus={hasSetBlockStatus}
|
||||
nodesOutputVars={availableVars}
|
||||
availableNodes={availableNodesWithParent}
|
||||
/>
|
||||
</div>
|
||||
<FieldCollapse title={t(`${i18nPrefix}.advancedSetting`)}>
|
||||
<>
|
||||
{/* Memory */}
|
||||
{isChatMode && (
|
||||
<div className='mt-4'>
|
||||
<MemoryConfig
|
||||
readonly={readOnly}
|
||||
config={{ data: inputs.memory }}
|
||||
onChange={handleMemoryChange}
|
||||
canSetRoleName={isCompletionModel}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isSupportFunctionCall && (
|
||||
<div className='mt-2'>
|
||||
<ReasoningModePicker
|
||||
type={inputs.reasoning_mode}
|
||||
onChange={handleReasoningModeChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</FieldCollapse>
|
||||
{inputs.parameters?.length > 0 && (<>
|
||||
<Split />
|
||||
<div>
|
||||
<OutputVars>
|
||||
<>
|
||||
{inputs.parameters.map((param, index) => (
|
||||
<VarItem
|
||||
key={index}
|
||||
name={param.name}
|
||||
type={param.type}
|
||||
description={param.description}
|
||||
/>
|
||||
))}
|
||||
<VarItem
|
||||
name='__is_success'
|
||||
type={VarType.number}
|
||||
description={t(`${i18nPrefix}.outputVars.isSuccess`)}
|
||||
/>
|
||||
<VarItem
|
||||
name='__reason'
|
||||
type={VarType.string}
|
||||
description={t(`${i18nPrefix}.outputVars.errorReason`)}
|
||||
/>
|
||||
<VarItem
|
||||
name='__usage'
|
||||
type='object'
|
||||
description={t(`${i18nPrefix}.outputVars.usage`)}
|
||||
/>
|
||||
</>
|
||||
</OutputVars>
|
||||
</div>
|
||||
</>)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(Panel)
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { CommonNodeType, Memory, ModelConfig, ValueSelector, VisionSetting } from '@/app/components/workflow/types'
|
||||
|
||||
export enum ParamType {
|
||||
string = 'string',
|
||||
number = 'number',
|
||||
bool = 'boolean',
|
||||
select = 'select',
|
||||
arrayString = 'array[string]',
|
||||
arrayNumber = 'array[number]',
|
||||
arrayObject = 'array[object]',
|
||||
arrayBool = 'array[boolean]',
|
||||
}
|
||||
|
||||
export type Param = {
|
||||
name: string
|
||||
type: ParamType
|
||||
options?: string[]
|
||||
description: string
|
||||
required?: boolean
|
||||
}
|
||||
|
||||
export enum ReasoningModeType {
|
||||
prompt = 'prompt',
|
||||
functionCall = 'function_call',
|
||||
}
|
||||
|
||||
export type ParameterExtractorNodeType = CommonNodeType & {
|
||||
model: ModelConfig
|
||||
query: ValueSelector
|
||||
reasoning_mode: ReasoningModeType
|
||||
parameters: Param[]
|
||||
instruction: string
|
||||
memory?: Memory
|
||||
vision: {
|
||||
enabled: boolean
|
||||
configs?: VisionSetting
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { produce } from 'immer'
|
||||
import type { Memory, MoreInfo, ValueSelector, Var } from '../../types'
|
||||
import { ChangeType, VarType } from '../../types'
|
||||
import { useStore } from '../../store'
|
||||
import {
|
||||
useIsChatMode,
|
||||
useNodesReadOnly,
|
||||
useWorkflow,
|
||||
} from '../../hooks'
|
||||
import useConfigVision from '../../hooks/use-config-vision'
|
||||
import type { Param, ParameterExtractorNodeType, ReasoningModeType } from './types'
|
||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel, useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
|
||||
import { checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants'
|
||||
import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list'
|
||||
import { supportFunctionCall } from '@/utils/tool-call'
|
||||
import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
|
||||
const useConfig = (id: string, payload: ParameterExtractorNodeType) => {
|
||||
const {
|
||||
deleteNodeInspectorVars,
|
||||
renameInspectVarName,
|
||||
} = useInspectVarsCrud()
|
||||
const { nodesReadOnly: readOnly } = useNodesReadOnly()
|
||||
const { handleOutVarRenameChange } = useWorkflow()
|
||||
const isChatMode = useIsChatMode()
|
||||
|
||||
const defaultConfig = useStore(s => s.nodesDefaultConfigs)?.[payload.type]
|
||||
|
||||
const [defaultRolePrefix, setDefaultRolePrefix] = useState<{ user: string; assistant: string }>({ user: '', assistant: '' })
|
||||
const { inputs, setInputs: doSetInputs } = useNodeCrud<ParameterExtractorNodeType>(id, payload)
|
||||
const inputRef = useRef(inputs)
|
||||
|
||||
const setInputs = useCallback((newInputs: ParameterExtractorNodeType) => {
|
||||
if (newInputs.memory && !newInputs.memory.role_prefix) {
|
||||
const newPayload = produce(newInputs, (draft) => {
|
||||
draft.memory!.role_prefix = defaultRolePrefix
|
||||
})
|
||||
doSetInputs(newPayload)
|
||||
inputRef.current = newPayload
|
||||
return
|
||||
}
|
||||
doSetInputs(newInputs)
|
||||
inputRef.current = newInputs
|
||||
}, [doSetInputs, defaultRolePrefix])
|
||||
|
||||
const filterVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.string].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const handleInputVarChange = useCallback((newInputVar: ValueSelector | string) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.query = newInputVar as ValueSelector || []
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const handleExactParamsChange = useCallback((newParams: Param[], moreInfo?: MoreInfo) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.parameters = newParams
|
||||
})
|
||||
setInputs(newInputs)
|
||||
|
||||
if (moreInfo && moreInfo?.type === ChangeType.changeVarName && moreInfo.payload) {
|
||||
handleOutVarRenameChange(id, [id, moreInfo.payload.beforeKey], [id, moreInfo.payload.afterKey!])
|
||||
renameInspectVarName(id, moreInfo.payload.beforeKey, moreInfo.payload.afterKey!)
|
||||
}
|
||||
else {
|
||||
deleteNodeInspectorVars(id)
|
||||
}
|
||||
}, [deleteNodeInspectorVars, handleOutVarRenameChange, id, inputs, renameInspectVarName, setInputs])
|
||||
|
||||
const addExtractParameter = useCallback((payload: Param) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
if (!draft.parameters)
|
||||
draft.parameters = []
|
||||
draft.parameters.push(payload)
|
||||
})
|
||||
setInputs(newInputs)
|
||||
deleteNodeInspectorVars(id)
|
||||
}, [deleteNodeInspectorVars, id, inputs, setInputs])
|
||||
|
||||
// model
|
||||
const model = inputs.model || {
|
||||
provider: '',
|
||||
name: '',
|
||||
mode: AppModeEnum.CHAT,
|
||||
completion_params: {
|
||||
temperature: 0.7,
|
||||
},
|
||||
}
|
||||
const modelMode = inputs.model?.mode
|
||||
const isChatModel = modelMode === AppModeEnum.CHAT
|
||||
const isCompletionModel = !isChatModel
|
||||
|
||||
const {
|
||||
isVisionModel,
|
||||
handleVisionResolutionEnabledChange,
|
||||
handleVisionResolutionChange,
|
||||
handleModelChanged: handleVisionConfigAfterModelChanged,
|
||||
} = useConfigVision(model, {
|
||||
payload: inputs.vision,
|
||||
onChange: (newPayload) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.vision = newPayload
|
||||
})
|
||||
setInputs(newInputs)
|
||||
},
|
||||
})
|
||||
|
||||
const appendDefaultPromptConfig = useCallback((draft: ParameterExtractorNodeType, defaultConfig: any, _passInIsChatMode?: boolean) => {
|
||||
const promptTemplates = defaultConfig.prompt_templates
|
||||
if (!isChatModel) {
|
||||
setDefaultRolePrefix({
|
||||
user: promptTemplates.completion_model.conversation_histories_role.user_prefix,
|
||||
assistant: promptTemplates.completion_model.conversation_histories_role.assistant_prefix,
|
||||
})
|
||||
}
|
||||
}, [isChatModel])
|
||||
|
||||
const [modelChanged, setModelChanged] = useState(false)
|
||||
const {
|
||||
currentProvider,
|
||||
currentModel,
|
||||
} = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.textGeneration)
|
||||
|
||||
const handleModelChanged = useCallback((model: { provider: string; modelId: string; mode?: string }) => {
|
||||
const newInputs = produce(inputRef.current, (draft) => {
|
||||
draft.model.provider = model.provider
|
||||
draft.model.name = model.modelId
|
||||
draft.model.mode = model.mode!
|
||||
const isModeChange = model.mode !== inputRef.current.model?.mode
|
||||
if (isModeChange && defaultConfig && Object.keys(defaultConfig).length > 0)
|
||||
appendDefaultPromptConfig(draft, defaultConfig, model.mode === AppModeEnum.CHAT)
|
||||
})
|
||||
setInputs(newInputs)
|
||||
setModelChanged(true)
|
||||
}, [setInputs, defaultConfig, appendDefaultPromptConfig])
|
||||
|
||||
useEffect(() => {
|
||||
if (currentProvider?.provider && currentModel?.model && !model.provider) {
|
||||
handleModelChanged({
|
||||
provider: currentProvider?.provider,
|
||||
modelId: currentModel?.model,
|
||||
mode: currentModel?.model_properties?.mode as string,
|
||||
})
|
||||
}
|
||||
}, [model?.provider, currentProvider, currentModel, handleModelChanged])
|
||||
|
||||
// change to vision model to set vision enabled, else disabled
|
||||
useEffect(() => {
|
||||
if (!modelChanged)
|
||||
return
|
||||
setModelChanged(false)
|
||||
handleVisionConfigAfterModelChanged()
|
||||
}, [isVisionModel, modelChanged])
|
||||
|
||||
const {
|
||||
currentModel: currModel,
|
||||
} = useTextGenerationCurrentProviderAndModelAndModelList(
|
||||
{
|
||||
provider: model.provider,
|
||||
model: model.name,
|
||||
},
|
||||
)
|
||||
|
||||
const isSupportFunctionCall = supportFunctionCall(currModel?.features)
|
||||
|
||||
const filterInputVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.number, VarType.string].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const {
|
||||
availableVars,
|
||||
availableNodesWithParent,
|
||||
} = useAvailableVarList(id, {
|
||||
onlyLeafNodeVar: false,
|
||||
filterVar: filterInputVar,
|
||||
})
|
||||
|
||||
const handleCompletionParamsChange = useCallback((newParams: Record<string, any>) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.model.completion_params = newParams
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const handleInstructionChange = useCallback((newInstruction: string) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.instruction = newInstruction
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const hasSetBlockStatus = {
|
||||
history: false,
|
||||
query: isChatMode ? checkHasQueryBlock(inputs.instruction) : false,
|
||||
context: false,
|
||||
}
|
||||
|
||||
const handleMemoryChange = useCallback((newMemory?: Memory) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.memory = newMemory
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const handleReasoningModeChange = useCallback((newReasoningMode: ReasoningModeType) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.reasoning_mode = newReasoningMode
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const handleImportFromTool = useCallback((params: Param[]) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.parameters = params
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
return {
|
||||
readOnly,
|
||||
handleInputVarChange,
|
||||
filterVar,
|
||||
isChatMode,
|
||||
inputs,
|
||||
isChatModel,
|
||||
isCompletionModel,
|
||||
handleModelChanged,
|
||||
handleCompletionParamsChange,
|
||||
handleImportFromTool,
|
||||
handleExactParamsChange,
|
||||
addExtractParameter,
|
||||
handleInstructionChange,
|
||||
hasSetBlockStatus,
|
||||
availableVars,
|
||||
availableNodesWithParent,
|
||||
isSupportFunctionCall,
|
||||
handleReasoningModeChange,
|
||||
handleMemoryChange,
|
||||
isVisionModel,
|
||||
handleVisionResolutionEnabledChange,
|
||||
handleVisionResolutionChange,
|
||||
}
|
||||
}
|
||||
|
||||
export default useConfig
|
||||
@@ -0,0 +1,154 @@
|
||||
import type { RefObject } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form'
|
||||
import type { InputVar, Var, Variable } from '@/app/components/workflow/types'
|
||||
import { InputVarType, VarType } from '@/app/components/workflow/types'
|
||||
import type { ParameterExtractorNodeType } from './types'
|
||||
import useNodeCrud from '../_base/hooks/use-node-crud'
|
||||
import { useCallback } from 'react'
|
||||
import useConfigVision from '../../hooks/use-config-vision'
|
||||
import { noop } from 'lodash-es'
|
||||
import { findVariableWhenOnLLMVision } from '../utils'
|
||||
import useAvailableVarList from '../_base/hooks/use-available-var-list'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.parameterExtractor'
|
||||
|
||||
type Params = {
|
||||
id: string,
|
||||
payload: ParameterExtractorNodeType,
|
||||
runInputData: Record<string, any>
|
||||
runInputDataRef: RefObject<Record<string, any>>
|
||||
getInputVars: (textList: string[]) => InputVar[]
|
||||
setRunInputData: (data: Record<string, any>) => void
|
||||
toVarInputs: (variables: Variable[]) => InputVar[]
|
||||
}
|
||||
const useSingleRunFormParams = ({
|
||||
id,
|
||||
payload,
|
||||
runInputData,
|
||||
runInputDataRef,
|
||||
getInputVars,
|
||||
setRunInputData,
|
||||
}: Params) => {
|
||||
const { t } = useTranslation()
|
||||
const { inputs } = useNodeCrud<ParameterExtractorNodeType>(id, payload)
|
||||
|
||||
const model = inputs.model
|
||||
|
||||
const {
|
||||
isVisionModel,
|
||||
} = useConfigVision(model, {
|
||||
payload: inputs.vision,
|
||||
onChange: noop,
|
||||
})
|
||||
|
||||
const visionFiles = runInputData['#files#']
|
||||
const setVisionFiles = useCallback((newFiles: any[]) => {
|
||||
setRunInputData?.({
|
||||
...runInputDataRef.current,
|
||||
'#files#': newFiles,
|
||||
})
|
||||
}, [runInputDataRef, setRunInputData])
|
||||
|
||||
const varInputs = getInputVars([inputs.instruction])
|
||||
|
||||
const inputVarValues = (() => {
|
||||
const vars: Record<string, any> = {}
|
||||
Object.keys(runInputData)
|
||||
.filter(key => !['#context#', '#files#'].includes(key))
|
||||
.forEach((key) => {
|
||||
vars[key] = runInputData[key]
|
||||
})
|
||||
return vars
|
||||
})()
|
||||
|
||||
const setInputVarValues = useCallback((newPayload: Record<string, any>) => {
|
||||
const newVars = {
|
||||
...newPayload,
|
||||
'#context#': runInputDataRef.current['#context#'],
|
||||
'#files#': runInputDataRef.current['#files#'],
|
||||
}
|
||||
setRunInputData?.(newVars)
|
||||
}, [runInputDataRef, setRunInputData])
|
||||
|
||||
const filterVisionInputVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.file, VarType.arrayFile].includes(varPayload.type)
|
||||
}, [])
|
||||
const {
|
||||
availableVars: availableVisionVars,
|
||||
} = useAvailableVarList(id, {
|
||||
onlyLeafNodeVar: false,
|
||||
filterVar: filterVisionInputVar,
|
||||
})
|
||||
|
||||
const forms = (() => {
|
||||
const forms: FormProps[] = []
|
||||
|
||||
forms.push(
|
||||
{
|
||||
label: t('workflow.nodes.llm.singleRun.variable')!,
|
||||
inputs: [{
|
||||
label: t(`${i18nPrefix}.inputVar`)!,
|
||||
variable: 'query',
|
||||
type: InputVarType.paragraph,
|
||||
required: true,
|
||||
}, ...varInputs],
|
||||
values: inputVarValues,
|
||||
onChange: setInputVarValues,
|
||||
},
|
||||
)
|
||||
|
||||
if (isVisionModel && payload.vision?.enabled && payload.vision?.configs?.variable_selector) {
|
||||
const currentVariable = findVariableWhenOnLLMVision(payload.vision.configs.variable_selector, availableVisionVars)
|
||||
|
||||
forms.push(
|
||||
{
|
||||
label: t('workflow.nodes.llm.vision')!,
|
||||
inputs: [{
|
||||
label: currentVariable?.variable as any,
|
||||
variable: '#files#',
|
||||
type: currentVariable?.formType as any,
|
||||
required: false,
|
||||
}],
|
||||
values: { '#files#': visionFiles },
|
||||
onChange: keyValue => setVisionFiles((keyValue as any)['#files#']),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return forms
|
||||
})()
|
||||
|
||||
const getDependentVars = () => {
|
||||
const promptVars = varInputs.map((item) => {
|
||||
// Guard against null/undefined variable to prevent app crash
|
||||
if (!item.variable || typeof item.variable !== 'string')
|
||||
return []
|
||||
|
||||
return item.variable.slice(1, -1).split('.')
|
||||
}).filter(arr => arr.length > 0)
|
||||
const vars = [payload.query, ...promptVars]
|
||||
if (isVisionModel && payload.vision?.enabled && payload.vision?.configs?.variable_selector) {
|
||||
const visionVar = payload.vision.configs.variable_selector
|
||||
vars.push(visionVar)
|
||||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
const getDependentVar = (variable: string) => {
|
||||
if(variable === 'query')
|
||||
return payload.query
|
||||
if(variable === '#files#')
|
||||
return payload.vision.configs?.variable_selector
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return {
|
||||
forms,
|
||||
getDependentVars,
|
||||
getDependentVar,
|
||||
}
|
||||
}
|
||||
|
||||
export default useSingleRunFormParams
|
||||
Reference in New Issue
Block a user