Merge pull request #866 from SuanmoSuanyangTechnology/fix/v0.3.0_zy
Fix/v0.3.0 zy
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2025-12-30 13:59:36
|
* @Date: 2025-12-30 13:59:36
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-04-08 11:05:34
|
* @Last Modified time: 2026-04-13 12:16:00
|
||||||
*/
|
*/
|
||||||
import { forwardRef, useImperativeHandle, useState, useRef, useMemo } from 'react';
|
import { forwardRef, useImperativeHandle, useState, useRef, useMemo } from 'react';
|
||||||
import { Form, Input, Select, InputNumber, Button, Row, Col, Flex } from 'antd';
|
import { Form, Input, Select, InputNumber, Button, Row, Col, Flex } from 'antd';
|
||||||
@@ -345,15 +345,16 @@ const ChatVariableModal = forwardRef<ChatVariableModalRef, ChatVariableModalProp
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
name="defaultValue"
|
name="defaultValue"
|
||||||
label={t('workflow.config.parameter-extractor.default')}
|
label={t('workflow.config.parameter-extractor.default')}
|
||||||
rules={[
|
rules={(type === 'object' || type === 'array[object]')
|
||||||
(type === 'object' || type === 'array[object]') ? {
|
? [{
|
||||||
validator: (_, value) => {
|
validator: (_, value) => {
|
||||||
if (!value) return Promise.resolve();
|
if (!value) return Promise.resolve();
|
||||||
try { JSON.parse(value); return Promise.resolve(); }
|
try { JSON.parse(value); return Promise.resolve(); }
|
||||||
catch { return Promise.reject(t('workflow.invalidJSON')); }
|
catch { return Promise.reject(t('workflow.invalidJSON')); }
|
||||||
}
|
}
|
||||||
} : {}
|
}]
|
||||||
]}
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{type === 'number'
|
{type === 'number'
|
||||||
? <InputNumber placeholder={t('common.enter')} style={{ width: '100%' }} />
|
? <InputNumber placeholder={t('common.enter')} style={{ width: '100%' }} />
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ const specialValidators: Record<string, (val: any) => boolean> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isEmpty(val: any): boolean {
|
function isEmpty(val: any): boolean {
|
||||||
console.log('validateNode isEmpty', val, val === undefined || val === null || val === '')
|
|
||||||
if (val === undefined || val === null || val === '') return true
|
if (val === undefined || val === null || val === '') return true
|
||||||
if (Array.isArray(val)) return val.length === 0
|
if (Array.isArray(val)) return val.length === 0
|
||||||
return false
|
return false
|
||||||
@@ -98,7 +97,6 @@ function validateNode(type: string, config: Record<string, any>): CheckError[] {
|
|||||||
const specialKey = `${type}.${field}`
|
const specialKey = `${type}.${field}`
|
||||||
const specialValidator = specialValidators[specialKey]
|
const specialValidator = specialValidators[specialKey]
|
||||||
const isInvalid = specialValidator ? specialValidator(val) : isEmpty(val)
|
const isInvalid = specialValidator ? specialValidator(val) : isEmpty(val)
|
||||||
console.log('validateNode', val, specialKey, specialValidator, isEmpty(val))
|
|
||||||
if (isInvalid) errors.push({ key: specialKey, message: '' })
|
if (isInvalid) errors.push({ key: specialKey, message: '' })
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -114,62 +112,6 @@ function validateNode(type: string, config: Record<string, any>): CheckError[] {
|
|||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runCheckOnGraph(
|
|
||||||
graph: import('@antv/x6').Graph,
|
|
||||||
t: (key: string) => string
|
|
||||||
): Promise<NodeCheckResult[]> {
|
|
||||||
const nodes = graph.getNodes()
|
|
||||||
const edges = graph.getEdges()
|
|
||||||
const targetIds = new Set<string>()
|
|
||||||
const childTargetIds = new Set<string>()
|
|
||||||
edges.forEach(e => {
|
|
||||||
targetIds.add(e.getTargetCellId())
|
|
||||||
const srcData = graph.getCellById(e.getSourceCellId())?.getData()
|
|
||||||
const tgtData = graph.getCellById(e.getTargetCellId())?.getData()
|
|
||||||
if (srcData?.cycle && tgtData?.cycle && srcData.cycle === tgtData.cycle) {
|
|
||||||
childTargetIds.add(e.getTargetCellId())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const checked: NodeCheckResult[] = []
|
|
||||||
for (const node of nodes) {
|
|
||||||
const data = node.getData()
|
|
||||||
if (!data || ['add-node', 'notes', 'cycle-start', 'break'].includes(data.type)) continue
|
|
||||||
|
|
||||||
const errors: CheckError[] = []
|
|
||||||
const isChildNode = !!data.cycle
|
|
||||||
const hasIncoming = isChildNode ? childTargetIds.has(node.id) : !['start', 'cycle-start'].includes(data.type) ? targetIds.has(node.id) : true
|
|
||||||
if (!hasIncoming) errors.push({ key: 'notConnected', message: t('workflow.notConnected') })
|
|
||||||
|
|
||||||
const configErrors = validateNode(data.type, data.config ?? {})
|
|
||||||
configErrors.forEach(e => {
|
|
||||||
errors.push({ key: e.key, message: `${t(`workflow.checkListErrors.${e.key}`)} ${t('workflow.cannotBeEmpty')}`.trim() })
|
|
||||||
})
|
|
||||||
|
|
||||||
if (data.type === 'tool') {
|
|
||||||
const toolId = data.config?.tool_id?.defaultValue ?? data.config?.tool_id
|
|
||||||
const toolParameters = data.config?.tool_parameters?.defaultValue ?? data.config?.tool_parameters ?? {}
|
|
||||||
if (toolId) {
|
|
||||||
try {
|
|
||||||
const methods = await getToolMethods(toolId) as Array<{ name: string; parameters: Array<{ name: string; required: boolean }> }>
|
|
||||||
const operation = toolParameters?.operation
|
|
||||||
const method = operation ? methods.find(m => m.name === operation) : methods[0]
|
|
||||||
if (method) {
|
|
||||||
method.parameters
|
|
||||||
.filter(p => p.required && (toolParameters[p.name] === undefined || toolParameters[p.name] === null || toolParameters[p.name] === ''))
|
|
||||||
.forEach(p => errors.push({ key: 'tool.tool_parameters', message: `${p.name} ${t('workflow.cannotBeEmpty')}` }))
|
|
||||||
}
|
|
||||||
} catch { /* ignore */ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errors.length) {
|
|
||||||
checked.push({ id: node.id, name: data.name || t(`workflow.${data.type}`), type: data.type, icon: nodeIconMap[data.type] ?? '', errors })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return checked
|
|
||||||
}
|
|
||||||
|
|
||||||
const CheckList: FC<CheckListProps> = ({ workflowRef, appId }) => {
|
const CheckList: FC<CheckListProps> = ({ workflowRef, appId }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
@@ -222,7 +164,8 @@ const CheckList: FC<CheckListProps> = ({ workflowRef, appId }) => {
|
|||||||
if (data.type === 'tool') {
|
if (data.type === 'tool') {
|
||||||
const toolId = data.config?.tool_id?.defaultValue ?? data.config?.tool_id
|
const toolId = data.config?.tool_id?.defaultValue ?? data.config?.tool_id
|
||||||
const toolParameters = data.config?.tool_parameters?.defaultValue ?? data.config?.tool_parameters ?? {}
|
const toolParameters = data.config?.tool_parameters?.defaultValue ?? data.config?.tool_parameters ?? {}
|
||||||
if (toolId) {
|
|
||||||
|
if (typeof toolId === 'string') {
|
||||||
try {
|
try {
|
||||||
const methods = await getToolMethods(toolId) as Array<{ name: string; parameters: Array<{ name: string; required: boolean }> }>
|
const methods = await getToolMethods(toolId) as Array<{ name: string; parameters: Array<{ name: string; required: boolean }> }>
|
||||||
const operation = toolParameters?.operation
|
const operation = toolParameters?.operation
|
||||||
@@ -251,21 +194,27 @@ const CheckList: FC<CheckListProps> = ({ workflowRef, appId }) => {
|
|||||||
return checked
|
return checked
|
||||||
}, [workflowRef.current?.graphRef?.current, t])
|
}, [workflowRef.current?.graphRef?.current, t])
|
||||||
|
|
||||||
|
const scheduleCheckRef = useRef<() => void>()
|
||||||
|
|
||||||
const scheduleCheck = useCallback(() => {
|
const scheduleCheck = useCallback(() => {
|
||||||
clearTimeout(timerRef.current)
|
clearTimeout(timerRef.current)
|
||||||
timerRef.current = setTimeout(async () => {
|
timerRef.current = setTimeout(async () => {
|
||||||
setCheckResults(appId, await runCheck())
|
setCheckResults(appId, await runCheck())
|
||||||
}, 500)
|
}, 300)
|
||||||
}, [runCheck])
|
}, [runCheck])
|
||||||
|
|
||||||
|
scheduleCheckRef.current = scheduleCheck
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const graph = workflowRef.current?.graphRef?.current
|
const graph = workflowRef.current?.graphRef?.current
|
||||||
|
console.log('graph')
|
||||||
if (!graph) return
|
if (!graph) return
|
||||||
const events = ['node:added', 'node:removed', 'node:change:data', 'edge:added', 'edge:removed']
|
const handler = () => scheduleCheckRef.current?.()
|
||||||
events.forEach(e => graph.on(e, scheduleCheck))
|
const events = ['node:added', 'node:removed', 'node:change:data', 'edge:added', 'edge:removed', 'edge:connected', 'edge:changed']
|
||||||
scheduleCheck()
|
events.forEach(e => graph.on(e, handler))
|
||||||
|
scheduleCheckRef.current?.()
|
||||||
return () => {
|
return () => {
|
||||||
events.forEach(e => graph.off(e, scheduleCheck))
|
events.forEach(e => graph.off(e, handler))
|
||||||
clearTimeout(timerRef.current)
|
clearTimeout(timerRef.current)
|
||||||
}
|
}
|
||||||
}, [workflowRef.current?.graphRef?.current])
|
}, [workflowRef.current?.graphRef?.current])
|
||||||
|
|||||||
Reference in New Issue
Block a user