merge main

This commit is contained in:
zxhlyh
2025-05-16 10:09:24 +08:00
63 changed files with 120 additions and 659 deletions

View File

@ -12,6 +12,7 @@ import { DataSourceType } from '@/models/datasets'
import FileIcon from '@/app/components/base/file-icon'
import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
import { Globe06 } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import Drawer from '@/app/components/base/drawer'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Badge from '@/app/components/base/badge'
@ -47,56 +48,66 @@ const Item: FC<ItemProps> = ({
const [isDeleting, setIsDeleting] = useState(false)
return (
<div className={cn('group relative mb-1 flex w-full items-center rounded-lg border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg py-2 pl-2.5 pr-3 shadow-xs last-of-type:mb-0 hover:bg-components-panel-on-panel-item-bg-hover hover:shadow-sm', isDeleting && 'border-state-destructive-border hover:bg-state-destructive-hover')}>
{
config.data_source_type === DataSourceType.FILE && (
<div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#E0EAFF] bg-[#F5F8FF]'>
<Folder className='h-4 w-4 text-[#444CE7]' />
</div>
)
}
{
config.data_source_type === DataSourceType.NOTION && (
<div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#EAECF5]'>
<FileIcon type='notion' className='h-4 w-4' />
</div>
)
}
{
config.data_source_type === DataSourceType.WEB && (
<div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-blue-100 bg-[#F5FAFF]'>
<Globe06 className='h-4 w-4 text-blue-600' />
</div>
)
}
<div className='grow'>
<div className='flex h-[18px] items-center'>
<div className='grow truncate text-[13px] font-medium text-text-secondary' title={config.name}>{config.name}</div>
{config.provider === 'external'
? <Badge text={t('dataset.externalTag') as string} />
: <Badge
text={formatIndexingTechniqueAndMethod(config.indexing_technique, config.retrieval_model_dict?.search_method)}
/>}
</div>
</div >
<div className='absolute bottom-0 right-0 top-0 hidden w-[124px] items-center justify-end rounded-lg bg-gradient-to-r from-white/50 to-white to-50% pr-2 group-hover:flex'>
<div className={cn(
'group relative mb-1 flex h-10 w-full cursor-pointer items-center justify-between rounded-lg border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg px-2 last-of-type:mb-0 hover:bg-components-panel-on-panel-item-bg-hover',
isDeleting && 'border-state-destructive-border hover:bg-state-destructive-hover',
)}>
<div className='flex w-0 grow items-center space-x-1.5'>
{
editable && <div
className='mr-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-black/5'
onClick={() => setShowSettingsModal(true)}
>
<RiEditLine className='h-4 w-4 text-text-tertiary' />
</div>
config.data_source_type === DataSourceType.FILE && (
<div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#E0EAFF] bg-[#F5F8FF]'>
<Folder className='h-4 w-4 text-[#444CE7]' />
</div>
)
}
<div
className='flex h-6 w-6 cursor-pointer items-center justify-center text-text-tertiary hover:text-text-destructive'
{
config.data_source_type === DataSourceType.NOTION && (
<div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#EAECF5]'>
<FileIcon type='notion' className='h-4 w-4' />
</div>
)
}
{
config.data_source_type === DataSourceType.WEB && (
<div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-blue-100 bg-[#F5FAFF]'>
<Globe06 className='h-4 w-4 text-blue-600' />
</div>
)
}
<div className='system-sm-medium w-0 grow truncate text-text-secondary' title={config.name}>{config.name}</div>
</div>
<div className='ml-2 hidden shrink-0 items-center space-x-1 group-hover:flex'>
{
editable && <ActionButton
onClick={(e) => {
e.stopPropagation()
setShowSettingsModal(true)
}}
>
<RiEditLine className='h-4 w-4 shrink-0 text-text-tertiary' />
</ActionButton>
}
<ActionButton
onClick={() => onRemove(config.id)}
onMouseOver={() => setIsDeleting(true)}
state={isDeleting ? ActionButtonState.Destructive : ActionButtonState.Default}
onMouseEnter={() => setIsDeleting(true)}
onMouseLeave={() => setIsDeleting(false)}
>
<RiDeleteBinLine className='h-4 w-4' />
</div>
<RiDeleteBinLine className={cn('h-4 w-4 shrink-0 text-text-tertiary', isDeleting && 'text-text-destructive')} />
</ActionButton>
</div>
{
config.indexing_technique && <Badge
className='shrink-0 group-hover:hidden'
text={formatIndexingTechniqueAndMethod(config.indexing_technique, config.retrieval_model_dict?.search_method)}
/>
}
{
config.provider === 'external' && <Badge
className='shrink-0 group-hover:hidden'
text={t('dataset.externalTag') as string}
/>
}
<Drawer isOpen={showSettingsModal} onClose={() => setShowSettingsModal(false)} footer={null} mask={isMobile} panelClassName='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
<SettingsModal
currentDataset={config}

View File

@ -47,14 +47,14 @@ const Empty = () => {
))}
</div>
{/* mask */}
<div className='absolute z-20 h-full w-full bg-gradient-to-b from-background-gradient-mask-transparent to-white' />
<div className='absolute z-20 h-full w-full bg-gradient-to-b from-components-panel-bg-transparent to-components-panel-bg' />
<div className='relative z-30 flex h-full items-center justify-center'>
<div className='flex flex-col items-center gap-y-3'>
<div className='relative -z-10 flex h-[52px] w-[52px] items-center justify-center rounded-xl
border-[1px] border-dashed border-divider-deep bg-components-card-bg shadow-xl shadow-shadow-shadow-5'>
<Group className='h-5 w-5 text-text-tertiary' />
<Line className='absolute -right-[1px] top-1/2 -translate-y-1/2' />
<Line className='absolute -left-[1px] top-1/2 -translate-y-1/2' />
<Line className='absolute right-[-1px] top-1/2 -translate-y-1/2' />
<Line className='absolute left-[-1px] top-1/2 -translate-y-1/2' />
<Line className='absolute left-1/2 top-0 -translate-x-1/2 -translate-y-1/2 rotate-90' />
<Line className='absolute left-1/2 top-full -translate-x-1/2 -translate-y-1/2 rotate-90' />
</div>

View File

@ -140,16 +140,6 @@ const WorkflowChecklist = ({
</div>
)
}
{
node.varErrorMessage?.map((errorMessage: string) => (
<div className='rounded-b-lg bg-gray-25 px-3 py-2'>
<div className='flex text-xs leading-[18px] text-gray-500'>
<AlertTriangle className='mr-2 mt-[3px] h-3 w-3 text-[#F79009]' />
{errorMessage}
</div>
</div>
))
}
</div>
</div>
))

View File

@ -15,7 +15,6 @@ import { useStore } from '../store'
import {
getToolCheckParams,
getValidTreeNodes,
transformStartNodeVariables,
} from '../utils'
import {
CUSTOM_NODE,
@ -46,9 +45,6 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
const { data: strategyProviders } = useStrategyProviders()
const datasetsDetail = useDatasetsDetailStore(s => s.datasetsDetail)
const chatVarList = useStore(s => s.conversationVariables)
const environmentVariables = useStore(s => s.environmentVariables)
const getCheckData = useCallback((data: CommonNodeType<{}>) => {
let checkData = data
if (data.type === BlockEnum.KnowledgeRetrieval) {
@ -68,10 +64,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
const needWarningNodes = useMemo(() => {
const list = []
const { validNodes } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges, true)
const allVariablesMap = transformStartNodeVariables(chatVarList, environmentVariables)
const errMessageMap = new Map()
const { validNodes } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges)
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
@ -117,32 +110,8 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
toolIcon,
unConnected: !validNodes.find(n => n.id === node.id),
errorMessage,
varErrorMessage: [],
})
}
errMessageMap.set(node.id, list[list.length - 1])
if (nodesExtraData[node.data.type as BlockEnum].checkVarValid) {
const { errorMessage: varErrorMessages } = nodesExtraData[node.data.type as BlockEnum].checkVarValid(node.data, { ...allVariablesMap, ...node._parentOutputVarMap }, t)
if (varErrorMessages?.length) {
const errMessage = errMessageMap.get(node.id)
if (errMessage) {
errMessage.varErrorMessage = varErrorMessages
}
else {
list.push({
id: node.id,
type: node.data.type,
title: node.data.title,
toolIcon,
unConnected: !validNodes.find(n => n.id === node.id),
errorMessage: '',
varErrorMessage: varErrorMessages,
})
errMessageMap.set(node.id, list[list.length - 1])
}
}
}
}
}
@ -164,13 +133,8 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
})
}
for (let i = 0; i < validNodes.length; i++) {
const node = validNodes[i]
delete node._parentOutputVarMap
}
return list
}, [nodes, edges, isChatMode, buildInTools, customTools, workflowTools, language, nodesExtraData, t, strategyProviders, getCheckData, chatVarList, environmentVariables])
}, [nodes, edges, isChatMode, buildInTools, customTools, workflowTools, language, nodesExtraData, t, strategyProviders, getCheckData])
return needWarningNodes
}
@ -189,9 +153,6 @@ export const useChecklistBeforePublish = () => {
const updateDatasetsDetail = useDatasetsDetailStore(s => s.updateDatasetsDetail)
const updateTime = useRef(0)
const chatVarList = useStore(s => s.conversationVariables)
const environmentVariables = useStore(s => s.environmentVariables)
const getCheckData = useCallback((data: CommonNodeType<{}>, datasets: DataSet[]) => {
let checkData = data
if (data.type === BlockEnum.KnowledgeRetrieval) {
@ -222,15 +183,12 @@ export const useChecklistBeforePublish = () => {
const {
validNodes,
maxDepth,
} = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges, true)
} = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges)
if (maxDepth > MAX_TREE_DEPTH) {
notify({ type: 'error', message: t('workflow.common.maxTreeDepth', { depth: MAX_TREE_DEPTH }) })
return false
}
const allVariablesMap = transformStartNodeVariables(chatVarList, environmentVariables)
// Before publish, we need to fetch datasets detail, in case of the settings of datasets have been changed
const knowledgeRetrievalNodes = nodes.filter(node => node.data.type === BlockEnum.KnowledgeRetrieval)
const allDatasetIds = knowledgeRetrievalNodes.reduce<string[]>((acc, node) => {
@ -281,14 +239,6 @@ export const useChecklistBeforePublish = () => {
notify({ type: 'error', message: `[${node.data.title}] ${t('workflow.common.needConnectTip')}` })
return false
}
if (nodesExtraData[node.data.type as BlockEnum].checkVarValid) {
const { errorMessage: varErrorMessage } = nodesExtraData[node.data.type as BlockEnum].checkVarValid(node.data, { ...allVariablesMap, ...node._parentOutputVarMap }, t)
if (varErrorMessage?.length) {
notify({ type: 'error', message: `[${node.data.title}] ${varErrorMessage[0]}` })
return false
}
}
}
if (isChatMode && !nodes.find(node => node.data.type === BlockEnum.Answer)) {
@ -302,7 +252,7 @@ export const useChecklistBeforePublish = () => {
}
return true
}, [store, isChatMode, notify, t, buildInTools, customTools, workflowTools, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData, chatVarList, environmentVariables])
}, [store, isChatMode, notify, t, buildInTools, customTools, workflowTools, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData])
return {
handleCheckBeforePublish,

View File

@ -164,7 +164,7 @@ const findExceptVarInObject = (obj: any, filterVar: (payload: Var, selector: Val
return res
}
export const formatItem = (
const formatItem = (
item: any,
isChatMode: boolean,
filterVar: (payload: Var, selector: ValueSelector) => boolean,

View File

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types'
import { getNotExistVariablesByText } from '../../utils/workflow'
import type { NodeDefault } from '../../types'
import type { AnswerNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types'
@ -25,19 +24,6 @@ const nodeDefault: NodeDefault<AnswerNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: AnswerNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const answer_warnings = getNotExistVariablesByText(payload.answer || '', varMap)
if (answer_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.answer.answer')} ${t('workflow.common.referenceVar')}${answer_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: [...answer_warnings],
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { NodeDefault } from '../../types'
import { type AssignerNodeType, WriteMode } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types'
@ -46,24 +45,6 @@ const nodeDefault: NodeDefault<AssignerNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: AssignerNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr: string[] = []
const items = payload.items ?? []
const variables_warnings = getNotExistVariablesByArray(items.map(item => item.variable_selector ?? []) ?? [], varMap)
if (variables_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.assigner.assignedVariable')} ${t('workflow.common.referenceVar')}${variables_warnings.join('、')}${t('workflow.common.noExist')}`)
const value_warnings = getNotExistVariablesByArray(items.map(item => item.value ?? []) ?? [], varMap)
if (value_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.assigner.setVariable')} ${t('workflow.common.referenceVar')}${value_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: [...variables_warnings, ...value_warnings],
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { NodeDefault } from '../../types'
import { CodeLanguage, type CodeNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types'
@ -35,20 +34,7 @@ const nodeDefault: NodeDefault<CodeNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: CodeNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const variables_selector = payload.variables.map(v => v.value_selector)
const variables_selector_warnings = getNotExistVariablesByArray(variables_selector, varMap)
if (variables_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.code.inputVars')} ${t('workflow.common.referenceVar')}${variables_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: variables_selector_warnings,
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { NodeDefault } from '../../types'
import type { DocExtractorNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types'
@ -30,19 +29,6 @@ const nodeDefault: NodeDefault<DocExtractorNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: DocExtractorNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr: string[] = []
const variables_warnings = getNotExistVariablesByArray([payload.variable_selector], varMap)
if (variables_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.docExtractor.inputVar')} ${t('workflow.common.referenceVar')}${variables_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: variables_warnings,
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types'
import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow'
import type { NodeDefault } from '../../types'
import { AuthorizationType, BodyType, Method } from './types'
import type { BodyPayload, HttpNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
@ -45,8 +44,8 @@ const nodeDefault: NodeDefault<HttpNodeType> = {
errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('workflow.nodes.http.api') })
if (!errorMessages
&& payload.body.type === BodyType.binary
&& ((!(payload.body.data as BodyPayload)[0]?.file) || (payload.body.data as BodyPayload)[0]?.file?.length === 0)
&& payload.body.type === BodyType.binary
&& ((!(payload.body.data as BodyPayload)[0]?.file) || (payload.body.data as BodyPayload)[0]?.file?.length === 0)
)
errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('workflow.nodes.http.binaryFileVariable') })
@ -55,53 +54,6 @@ const nodeDefault: NodeDefault<HttpNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: HttpNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr: string[] = []
const url_warnings = getNotExistVariablesByText(payload.url, varMap)
if (url_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.http.api')} ${t('workflow.common.referenceVar')}${url_warnings.join('、')}${t('workflow.common.noExist')}`)
const headers_warnings = getNotExistVariablesByText(payload.headers, varMap)
if (headers_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.http.headers')} ${t('workflow.common.referenceVar')}${headers_warnings.join('、')}${t('workflow.common.noExist')}`)
const params_warnings = getNotExistVariablesByText(payload.params, varMap)
if (params_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.http.params')} ${t('workflow.common.referenceVar')}${params_warnings.join('、')}${t('workflow.common.noExist')}`)
const body_warnings: string[] = []
if ([BodyType.binary].includes(payload.body.type)) {
const body_data = payload.body.data as BodyPayload
body_data.forEach((item) => {
const key_warnings = getNotExistVariablesByText(item.key || '', varMap)
if (key_warnings.length)
body_warnings.push(...key_warnings)
const warnings = getNotExistVariablesByArray([item.file || []], varMap)
if (warnings.length)
body_warnings.push(...warnings)
})
}
else {
const body_data = payload.body.data as BodyPayload
body_data.forEach((item) => {
const key_warnings = getNotExistVariablesByText(item.key || '', varMap)
if (key_warnings.length)
body_warnings.push(...key_warnings)
const value_warnings = getNotExistVariablesByText(item.value || '', varMap)
if (value_warnings.length)
body_warnings.push(...value_warnings)
})
}
if (body_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.http.body')} ${t('workflow.common.referenceVar')}${body_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: [...url_warnings, ...headers_warnings, ...params_warnings, ...body_warnings],
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,6 +1,4 @@
import type { Var } from '../../types'
import type { NodeDefault } from '../../types'
import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow'
import { type IfElseNodeType, LogicalOperator } from './types'
import { isEmptyRelatedOperator } from './utils'
import { genNodeMetaData } from '@/app/components/workflow/utils'
@ -76,41 +74,6 @@ const nodeDefault: NodeDefault<IfElseNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: IfElseNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const condition_variable_selector_warnings: string[] = []
const condition_value_warnings: string[] = []
payload.cases.forEach((caseItem) => {
caseItem.conditions.forEach((condition) => {
if (!condition.variable_selector)
return
const selector_warnings = getNotExistVariablesByArray([condition.variable_selector], varMap)
if (selector_warnings.length)
condition_variable_selector_warnings.push(...selector_warnings)
const value_warnings = Array.isArray(condition.value) ? getNotExistVariablesByArray([condition.value], varMap) : getNotExistVariablesByText(condition.value, varMap)
if (value_warnings.length)
condition_value_warnings.push(...value_warnings)
condition.sub_variable_condition?.conditions.forEach((subCondition) => {
const sub_variable_value_warnings = Array.isArray(subCondition.value) ? getNotExistVariablesByArray([subCondition.value], varMap) : getNotExistVariablesByText(subCondition.value, varMap)
if (sub_variable_value_warnings.length)
condition_value_warnings.push(...sub_variable_value_warnings)
})
})
})
if (condition_variable_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.ifElse.condition')} ${t('workflow.common.referenceVar')}${condition_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
if (condition_value_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.ifElse.enterValue')} ${t('workflow.common.referenceVar')}${condition_value_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: condition_variable_selector_warnings,
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,6 +1,5 @@
import { BlockEnum, ErrorHandleMode } from '../../types'
import type { NodeDefault, Var } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { NodeDefault } from '../../types'
import type { IterationNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types'
@ -49,19 +48,6 @@ const nodeDefault: NodeDefault<IterationNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: IterationNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr: string[] = []
const iterator_selector_warnings = getNotExistVariablesByArray([payload.iterator_selector], varMap)
if (iterator_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.iteration.input')} ${t('workflow.common.referenceVar')}${iterator_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: iterator_selector_warnings,
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -55,7 +55,7 @@ const DatasetItem: FC<Props> = ({
}, [onRemove])
return (
<div className={`group/dataset-item flex h-10 cursor-pointer items-center justify-between rounded-xl
<div className={`group/dataset-item flex h-10 cursor-pointer items-center justify-between rounded-lg
border-[0.5px] border-components-panel-border-subtle px-2
${isDeleteHovered
? 'border-state-destructive-border bg-state-destructive-hover'

View File

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { NodeDefault } from '../../types'
import type { KnowledgeRetrievalNodeType } from './types'
import { checkoutRerankModelConfigedInRetrievalSettings } from './utils'
import { DATASET_DEFAULT } from '@/config'
@ -48,19 +47,6 @@ const nodeDefault: NodeDefault<KnowledgeRetrievalNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: KnowledgeRetrievalNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const query_variable_selector_warnings = getNotExistVariablesByArray([payload.query_variable_selector], varMap)
if (query_variable_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.knowledgeRetrieval.queryVariable')} ${t('workflow.common.referenceVar')}${query_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: [...query_variable_selector_warnings],
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,6 +1,5 @@
import { BlockEnum, VarType } from '../../types'
import type { NodeDefault, Var } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { NodeDefault } from '../../types'
import { comparisonOperatorNotRequireValue } from '../if-else/utils'
import { type ListFilterNodeType, OrderBy } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
@ -58,18 +57,6 @@ const nodeDefault: NodeDefault<ListFilterNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: ListFilterNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const variable_warnings = getNotExistVariablesByArray([payload.variable], varMap)
if (variable_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.listFilter.inputVar')} ${t('workflow.common.referenceVar')}${variable_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: variable_warnings,
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,7 +1,5 @@
import type { Var } from '../../types'
import { BlockEnum, EditionType, VarType } from '../../types'
import { BlockEnum, EditionType } from '../../types'
import { type NodeDefault, type PromptItem, PromptRole } from '../../types'
import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow'
import type { LLMNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
@ -83,37 +81,6 @@ const nodeDefault: NodeDefault<LLMNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: LLMNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const prompt_templates_warnings: string[] = []
if (payload.context?.enabled && payload.context?.variable_selector?.length) {
const context_variable_selector_warnings = getNotExistVariablesByArray([payload.context.variable_selector], varMap)
if (context_variable_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.llm.context')} ${t('workflow.common.referenceVar')}${context_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
}
const prompt_templates = Array.isArray(payload.prompt_template) ? payload.prompt_template : [payload.prompt_template] as PromptItem[]
prompt_templates.forEach((v) => {
prompt_templates_warnings.push(...getNotExistVariablesByText(v.text, { context: { variable: 'context', type: VarType.string }, ...varMap }))
})
if (prompt_templates_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.llm.prompt')} ${t('workflow.common.referenceVar')}${prompt_templates_warnings.join('、')}${t('workflow.common.noExist')}`)
const memory_query_prompt_template_warnings = getNotExistVariablesByText(payload.memory?.query_prompt_template || '', varMap)
if (memory_query_prompt_template_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.common.memories.title')}USER ${t('workflow.common.referenceVar')}${memory_query_prompt_template_warnings.join('、')}${t('workflow.common.noExist')}`)
if (payload.vision?.enabled && payload.vision?.configs?.variable_selector?.length) {
const vision_variable_selector_warnings = getNotExistVariablesByArray([payload.vision.configs.variable_selector], varMap)
if (vision_variable_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.llm.vision')} ${t('workflow.common.referenceVar')}${vision_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
}
return {
isValid: true,
warning_vars: [...prompt_templates_warnings, ...memory_query_prompt_template_warnings],
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types'
import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow'
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'
@ -62,30 +61,6 @@ const nodeDefault: NodeDefault<ParameterExtractorNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: ParameterExtractorNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr: string[] = []
const variables_warnings = getNotExistVariablesByArray([payload.query], varMap)
if (variables_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.parameterExtractor.inputVar')} ${t('workflow.common.referenceVar')}${variables_warnings.join('、')}${t('workflow.common.noExist')}`)
let vision_variable_warnings: string[] = []
if (payload.vision?.configs?.variable_selector?.length) {
vision_variable_warnings = getNotExistVariablesByArray([payload.vision.configs.variable_selector], varMap)
if (vision_variable_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.llm.vision')} ${t('workflow.common.referenceVar')}${vision_variable_warnings.join('、')}${t('workflow.common.noExist')}`)
}
const instruction_warnings = getNotExistVariablesByText(payload.instruction, varMap)
if (instruction_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.parameterExtractor.instruction')} ${t('workflow.common.referenceVar')}${instruction_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: [...variables_warnings, ...vision_variable_warnings, ...instruction_warnings],
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types'
import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow'
import type { NodeDefault } from '../../types'
import type { QuestionClassifierNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types'
@ -69,30 +68,6 @@ const nodeDefault: NodeDefault<QuestionClassifierNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: QuestionClassifierNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const query_variable_selector_warnings = getNotExistVariablesByArray([payload.query_variable_selector], varMap)
if (query_variable_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.questionClassifiers.inputVars')} ${t('workflow.common.referenceVar')}${query_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
let vision_variable_selector_warnings: string[] = []
if (payload.vision?.configs?.variable_selector?.length) {
vision_variable_selector_warnings = getNotExistVariablesByArray([payload.vision?.configs?.variable_selector], varMap)
if (vision_variable_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.llm.vision')} ${t('workflow.common.referenceVar')}${vision_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
}
const instruction_warnings: string[] = getNotExistVariablesByText(payload.instruction, varMap)
if (instruction_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.questionClassifiers.advancedSetting')}-${t('workflow.nodes.questionClassifiers.instruction')} ${t('workflow.common.referenceVar')}${instruction_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: [...query_variable_selector_warnings, ...vision_variable_selector_warnings, ...instruction_warnings],
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { NodeDefault } from '../../types'
import type { TemplateTransformNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types'
@ -33,19 +32,6 @@ const nodeDefault: NodeDefault<TemplateTransformNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: TemplateTransformNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const variables_selector = payload.variables.map(v => v.value_selector)
const variables_selector_warnings = getNotExistVariablesByArray(variables_selector, varMap)
if (variables_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.templateTransform.inputVars')} ${t('workflow.common.referenceVar')}${variables_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,9 +1,8 @@
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types'
import type { NodeDefault, Var } from '../../types'
import type { NodeDefault } from '../../types'
import type { ToolNodeType } from './types'
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow'
const i18nPrefix = 'workflow.errorMsg'
@ -60,35 +59,6 @@ const nodeDefault: NodeDefault<ToolNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: ToolNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const tool_parametersMap = payload.tool_parameters
const tool_parameters_array = Object.values(tool_parametersMap)
const tool_parameters_warnings: string[] = []
tool_parameters_array?.forEach((item) => {
if (!item.value)
return
if (Array.isArray(item.value)) {
const warnings = getNotExistVariablesByArray([item.value], varMap)
if (warnings.length)
tool_parameters_warnings.push(...warnings)
return
}
if (typeof item.value === 'string') {
const warnings = getNotExistVariablesByText(item.value, varMap)
if (warnings.length)
tool_parameters_warnings.push(...warnings)
}
})
if (tool_parameters_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.tool.inputVars')} ${t('workflow.common.referenceVar')}${tool_parameters_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: tool_parameters_warnings,
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -1,6 +1,4 @@
import type { Var } from '../../types'
import { type NodeDefault, VarType } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { VariableAssignerNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types'
@ -53,18 +51,6 @@ const nodeDefault: NodeDefault<VariableAssignerNodeType> = {
errorMessage: errorMessages,
}
},
checkVarValid(payload: VariableAssignerNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr: string[] = []
const variables_warnings = getNotExistVariablesByArray(payload.variables ?? [], varMap)
if (variables_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.variableAssigner.title')} ${t('workflow.common.referenceVar')}${variables_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: variables_warnings,
errorMessage: errorMessageArr,
}
},
}
export default nodeDefault

View File

@ -303,7 +303,6 @@ export type NodeDefault<T = {}> = {
}
defaultValue: Partial<T>
checkValid: (payload: T, t: any, moreDataForCheckValid?: any) => { isValid: boolean; errorMessage?: string }
checkVarValid?: (payload: T, varMap: Record<string, Var>, t: any,) => { isValid: boolean; errorMessage?: string[] }
}
export type OnSelectBlock = (type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => void

View File

@ -5,18 +5,6 @@ import type {
} from '@/app/components/workflow/types'
import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants'
jest.mock('ky', () => ({
__esModule: true,
default: {
create: jest.fn(),
},
}))
jest.mock('lodash-es/groupBy', () => ({
__esModule: true,
default: jest.fn(),
}))
describe('preprocessNodesAndEdges', () => {
it('process nodes without iteration node or loop node should return origin nodes and edges.', () => {
const nodes = [

View File

@ -10,20 +10,14 @@ import {
uniqBy,
} from 'lodash-es'
import type {
ConversationVariable,
Edge,
EnvironmentVariable,
Node,
Var,
} from '../types'
import {
BlockEnum,
} from '../types'
import type { IterationNodeType } from '../nodes/iteration/types'
import type { LoopNodeType } from '../nodes/loop/types'
import { VAR_REGEX_TEXT } from '@/config'
import { formatItem } from '../nodes/_base/components/variable/utils'
import type { StructuredOutput } from '../nodes/llm/types'
export const canRunBySingle = (nodeType: BlockEnum) => {
return nodeType === BlockEnum.LLM
@ -92,17 +86,7 @@ export const getNodesConnectedSourceOrTargetHandleIdsMap = (changes: ConnectedSo
return nodesConnectedSourceOrTargetHandleIdsMap
}
function getParentOutputVarMap(item: Var, path: string, varMap: Record<string, Var>) {
if (!item.children || (Array.isArray(item.children) && !item.children.length) || ((item.children as StructuredOutput).schema))
return
(item.children as Var[]).forEach((child) => {
const newPath = `${path}.${child.variable}`
varMap[newPath] = child
getParentOutputVarMap(child, newPath, varMap)
})
}
export const getValidTreeNodes = (nodes: Node[], edges: Edge[], isCollectVar?: boolean) => {
export const getValidTreeNodes = (nodes: Node[], edges: Edge[]) => {
const startNode = nodes.find(node => node.data.type === BlockEnum.Start)
if (!startNode) {
@ -125,19 +109,6 @@ export const getValidTreeNodes = (nodes: Node[], edges: Edge[], isCollectVar?: b
outgoers.forEach((outgoer) => {
list.push(outgoer)
if (isCollectVar) {
const nodeObj = formatItem(root, false, () => true)
const varMap = {} as Record<string, Var>
nodeObj.vars.forEach((item) => {
if (item.variable.startsWith('sys.'))
return
const newPath = `${nodeObj.nodeId}.${item.variable}`
varMap[newPath] = item
getParentOutputVarMap(item, newPath, varMap)
})
outgoer._parentOutputVarMap = { ...(root._parentOutputVarMap ?? {}), ...varMap }
}
if (outgoer.data.type === BlockEnum.Iteration)
list.push(...nodes.filter(node => node.parentId === outgoer.id))
if (outgoer.data.type === BlockEnum.Loop)
@ -356,48 +327,3 @@ export const getParallelInfo = (nodes: Node[], edges: Edge[], parentNodeId?: str
export const hasErrorHandleNode = (nodeType?: BlockEnum) => {
return nodeType === BlockEnum.LLM || nodeType === BlockEnum.Tool || nodeType === BlockEnum.HttpRequest || nodeType === BlockEnum.Code
}
export const transformStartNodeVariables = (chatVarList: ConversationVariable[], environmentVariables: EnvironmentVariable[]) => {
const variablesMap: Record<string, ConversationVariable | EnvironmentVariable> = {}
chatVarList.forEach((variable) => {
variablesMap[`conversation.${variable.name}`] = variable
})
environmentVariables.forEach((variable) => {
variablesMap[`env.${variable.name}`] = variable
})
return variablesMap
}
export const getNotExistVariablesByText = (text: string, varMap: Record<string, Var>) => {
const var_warnings: string[] = []
text?.replace(VAR_REGEX_TEXT, (str, id_name) => {
if (id_name.startsWith('sys.'))
return str
if (varMap[id_name])
return str
const arr = id_name.split('.')
arr.shift()
var_warnings.push(arr.join('.'))
return str
})
return var_warnings
}
export const getNotExistVariablesByArray = (array: string[][], varMap: Record<string, Var>) => {
if (!array.length)
return []
const var_warnings: string[] = []
array.forEach((item) => {
if (!item.length)
return
if (['sys'].includes(item[0]))
return
const var_warning = varMap[item.join('.')]
if (var_warning)
return
const arr = [...item]
arr.shift()
var_warnings.push(arr.join('.'))
})
return var_warnings
}