feat(workflow): add variable validation for code nodes

- Introduce useNodesAvailableVarList hook to track available variables
- Enhance code node validation to check variable references
- Add debug logging for validation process
pull/21932/head
Mminamiyama 11 months ago
parent a79f37b686
commit f50773b1e9

@ -33,6 +33,7 @@ import type { KnowledgeRetrievalNodeType } from '../nodes/knowledge-retrieval/ty
import type { DataSet } from '@/models/datasets' import type { DataSet } from '@/models/datasets'
import { fetchDatasets } from '@/service/datasets' import { fetchDatasets } from '@/service/datasets'
import { MAX_TREE_DEPTH } from '@/config' import { MAX_TREE_DEPTH } from '@/config'
import useNodesAvailableVarList from './use-nodes-available-var-list'
export const useChecklist = (nodes: Node[], edges: Edge[]) => { export const useChecklist = (nodes: Node[], edges: Edge[]) => {
const { t } = useTranslation() const { t } = useTranslation()
@ -45,6 +46,10 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
const { data: strategyProviders } = useStrategyProviders() const { data: strategyProviders } = useStrategyProviders()
const datasetsDetail = useDatasetsDetailStore(s => s.datasetsDetail) const datasetsDetail = useDatasetsDetailStore(s => s.datasetsDetail)
console.log('==========================nodes: ', nodes)
const map = useNodesAvailableVarList(nodes)
console.log('==========================map: ', map)
const getCheckData = useCallback((data: CommonNodeType<{}>) => { const getCheckData = useCallback((data: CommonNodeType<{}>) => {
let checkData = data let checkData = data
if (data.type === BlockEnum.KnowledgeRetrieval) { if (data.type === BlockEnum.KnowledgeRetrieval) {
@ -84,8 +89,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
if (provider_type === CollectionType.workflow) if (provider_type === CollectionType.workflow)
toolIcon = workflowTools.find(tool => tool.id === node.data.provider_id)?.icon toolIcon = workflowTools.find(tool => tool.id === node.data.provider_id)?.icon
} }
else if (node.data.type === BlockEnum.Agent) {
if (node.data.type === BlockEnum.Agent) {
const data = node.data as AgentNodeType const data = node.data as AgentNodeType
const isReadyForCheckValid = !!strategyProviders const isReadyForCheckValid = !!strategyProviders
const provider = strategyProviders?.find(provider => provider.declaration.identity.name === data.agent_strategy_provider_name) const provider = strategyProviders?.find(provider => provider.declaration.identity.name === data.agent_strategy_provider_name)
@ -97,6 +101,12 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
isReadyForCheckValid, isReadyForCheckValid,
} }
} }
else {
moreDataForCheckValid = {
node,
...map[node.id],
}
}
if (node.type === CUSTOM_NODE) { if (node.type === CUSTOM_NODE) {
const checkData = getCheckData(node.data) const checkData = getCheckData(node.data)

@ -0,0 +1,74 @@
import {
useIsChatMode,
useWorkflow,
useWorkflowVariables,
} from '@/app/components/workflow/hooks'
import type { Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
type Params = {
onlyLeafNodeVar?: boolean
hideEnv?: boolean
hideChatVar?: boolean
filterVar: (payload: Var, selector: ValueSelector) => boolean
passedInAvailableNodes?: Node[]
}
const getNodeInfo = (nodeId: string, nodes: Node[]) => {
const allNodes = nodes
const node = allNodes.find(n => n.id === nodeId)
const isInIteration = !!node?.data.isInIteration
const isInLoop = !!node?.data.isInLoop
const parentNodeId = node?.parentId
const parentNode = allNodes.find(n => n.id === parentNodeId)
return {
node,
isInIteration,
isInLoop,
parentNode,
}
}
// TODO: loop type?
const useNodesAvailableVarList = (nodes: Node[], {
onlyLeafNodeVar,
filterVar,
hideEnv,
hideChatVar,
passedInAvailableNodes,
}: Params = {
onlyLeafNodeVar: false,
filterVar: () => true,
}) => {
const { getTreeLeafNodes, getBeforeNodesInSameBranchIncludeParent } = useWorkflow()
const { getNodeAvailableVars } = useWorkflowVariables()
const isChatMode = useIsChatMode()
const map: { [key: string ]: { availableVars: NodeOutPutVar[], availableNodes: Node[] } } = {}
nodes.forEach((node) => {
const nodeId = node.id
const availableNodes = passedInAvailableNodes || (onlyLeafNodeVar ? getTreeLeafNodes(nodeId) : getBeforeNodesInSameBranchIncludeParent(nodeId))
const {
parentNode: iterationNode,
} = getNodeInfo(nodeId, nodes)
const availableVars = getNodeAvailableVars({
parentNode: iterationNode,
beforeNodes: availableNodes,
isChatMode,
filterVar,
hideEnv,
hideChatVar,
})
const result = {
node,
availableVars,
availableNodes,
}
map[nodeId] = result
})
return map
}
export default useNodesAvailableVarList

@ -1,7 +1,8 @@
import { BlockEnum } from '../../types' import { BlockEnum } from '../../types'
import type { NodeDefault } from '../../types' import type { Node, NodeDefault, NodeOutPutVar } from '../../types'
import { CodeLanguage, type CodeNodeType } from './types' import { CodeLanguage, type CodeNodeType } from './types'
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
const i18nPrefix = 'workflow.errorMsg' const i18nPrefix = 'workflow.errorMsg'
@ -22,7 +23,7 @@ const nodeDefault: NodeDefault<CodeNodeType> = {
const nodes = isChatMode ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS const nodes = isChatMode ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS
return nodes return nodes
}, },
checkValid(payload: CodeNodeType, t: any) { checkValid(payload: CodeNodeType, t: any, moreDataForCheckValid: { node: Node, availableVars: NodeOutPutVar[], availableNodes: Node[] }) {
let errorMessages = '' let errorMessages = ''
const { code, variables } = payload const { code, variables } = payload
if (!errorMessages && variables.filter(v => !v.variable).length > 0) if (!errorMessages && variables.filter(v => !v.variable).length > 0)
@ -31,7 +32,24 @@ const nodeDefault: NodeDefault<CodeNodeType> = {
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t(`${i18nPrefix}.fields.variableValue`) }) errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t(`${i18nPrefix}.fields.variableValue`) })
if (!errorMessages && !code) if (!errorMessages && !code)
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t(`${i18nPrefix}.fields.code`) }) errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t(`${i18nPrefix}.fields.code`) })
if (!errorMessages && moreDataForCheckValid) {
const availableVars = moreDataForCheckValid.availableVars
console.log('=======================节点名称:', moreDataForCheckValid.node.data.title)
console.log('=======================代码参数检查:', variables)
console.log('=======================可用变量:', moreDataForCheckValid.availableVars)
console.log('=======================可用节点:', moreDataForCheckValid.availableNodes)
console.log('=========================================================================================================')
variables.forEach((variable) => {
const isEnv = isENV(variable.value_selector)
const isConvVar = isConversationVar(variable.value_selector)
const isSysVar = isSystemVar(variable.value_selector)
if (!isEnv && !isConvVar && !isSysVar) {
const node = availableVars.find(v => v.nodeId === variable?.value_selector[0])
if (!node)
errorMessages = t(`${i18nPrefix}.invalidVariable`)
}
})
}
return { return {
isValid: !errorMessages, isValid: !errorMessages,
errorMessage: errorMessages, errorMessage: errorMessages,

Loading…
Cancel
Save