feat: workflow continue on error (#11474)
parent
86dfdcb8ec
commit
bec5451f12
@ -0,0 +1,53 @@
|
||||
type CustomEdgeLinearGradientRenderProps = {
|
||||
id: string
|
||||
startColor: string
|
||||
stopColor: string
|
||||
position: {
|
||||
x1: number
|
||||
x2: number
|
||||
y1: number
|
||||
y2: number
|
||||
}
|
||||
}
|
||||
const CustomEdgeLinearGradientRender = ({
|
||||
id,
|
||||
startColor,
|
||||
stopColor,
|
||||
position,
|
||||
}: CustomEdgeLinearGradientRenderProps) => {
|
||||
const {
|
||||
x1,
|
||||
x2,
|
||||
y1,
|
||||
y2,
|
||||
} = position
|
||||
return (
|
||||
<defs>
|
||||
<linearGradient
|
||||
id={id}
|
||||
gradientUnits='userSpaceOnUse'
|
||||
x1={x1}
|
||||
y1={y1}
|
||||
x2={x2}
|
||||
y2={y2}
|
||||
>
|
||||
<stop
|
||||
offset='0%'
|
||||
style={{
|
||||
stopColor: startColor,
|
||||
stopOpacity: 1,
|
||||
}}
|
||||
/>
|
||||
<stop
|
||||
offset='100%'
|
||||
style={{
|
||||
stopColor,
|
||||
stopOpacity: 1,
|
||||
}}
|
||||
/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
)
|
||||
}
|
||||
|
||||
export default CustomEdgeLinearGradientRender
|
||||
@ -0,0 +1,26 @@
|
||||
import Collapse from '.'
|
||||
|
||||
type FieldCollapseProps = {
|
||||
title: string
|
||||
children: JSX.Element
|
||||
}
|
||||
const FieldCollapse = ({
|
||||
title,
|
||||
children,
|
||||
}: FieldCollapseProps) => {
|
||||
return (
|
||||
<div className='py-4'>
|
||||
<Collapse
|
||||
trigger={
|
||||
<div className='flex items-center h-6 system-sm-semibold-uppercase text-text-secondary cursor-pointer'>{title}</div>
|
||||
}
|
||||
>
|
||||
<div className='px-4'>
|
||||
{children}
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FieldCollapse
|
||||
@ -0,0 +1,56 @@
|
||||
import { useState } from 'react'
|
||||
import { RiArrowDropRightLine } from '@remixicon/react'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
export { default as FieldCollapse } from './field-collapse'
|
||||
|
||||
type CollapseProps = {
|
||||
disabled?: boolean
|
||||
trigger: JSX.Element
|
||||
children: JSX.Element
|
||||
collapsed?: boolean
|
||||
onCollapse?: (collapsed: boolean) => void
|
||||
}
|
||||
const Collapse = ({
|
||||
disabled,
|
||||
trigger,
|
||||
children,
|
||||
collapsed,
|
||||
onCollapse,
|
||||
}: CollapseProps) => {
|
||||
const [collapsedLocal, setCollapsedLocal] = useState(true)
|
||||
const collapsedMerged = collapsed !== undefined ? collapsed : collapsedLocal
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className='flex items-center'
|
||||
onClick={() => {
|
||||
if (!disabled) {
|
||||
setCollapsedLocal(!collapsedMerged)
|
||||
onCollapse?.(!collapsedMerged)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className='shrink-0 w-4 h-4'>
|
||||
{
|
||||
!disabled && (
|
||||
<RiArrowDropRightLine
|
||||
className={cn(
|
||||
'w-4 h-4 text-text-tertiary',
|
||||
!collapsedMerged && 'transform rotate-90',
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{trigger}
|
||||
</div>
|
||||
{
|
||||
!collapsedMerged && children
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Collapse
|
||||
@ -0,0 +1,89 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { DefaultValueForm } from './types'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { VarType } from '@/app/components/workflow/types'
|
||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
||||
|
||||
type DefaultValueProps = {
|
||||
forms: DefaultValueForm[]
|
||||
onFormChange: (form: DefaultValueForm) => void
|
||||
}
|
||||
const DefaultValue = ({
|
||||
forms,
|
||||
onFormChange,
|
||||
}: DefaultValueProps) => {
|
||||
const { t } = useTranslation()
|
||||
const getFormChangeHandler = useCallback(({ key, type }: DefaultValueForm) => {
|
||||
return (payload: any) => {
|
||||
let value
|
||||
if (type === VarType.string || type === VarType.number)
|
||||
value = payload.target.value
|
||||
|
||||
if (type === VarType.array || type === VarType.arrayNumber || type === VarType.arrayString || type === VarType.arrayObject || type === VarType.arrayFile || type === VarType.object)
|
||||
value = payload
|
||||
|
||||
onFormChange({ key, type, value })
|
||||
}
|
||||
}, [onFormChange])
|
||||
|
||||
return (
|
||||
<div className='px-4 pt-2'>
|
||||
<div className='mb-2 body-xs-regular text-text-tertiary'>
|
||||
{t('workflow.nodes.common.errorHandle.defaultValue.desc')}
|
||||
|
||||
<a
|
||||
href='https://docs.dify.ai/guides/workflow/error-handling'
|
||||
target='_blank'
|
||||
className='text-text-accent'
|
||||
>
|
||||
{t('workflow.common.learnMore')}
|
||||
</a>
|
||||
</div>
|
||||
<div className='space-y-1'>
|
||||
{
|
||||
forms.map((form, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className='py-1'
|
||||
>
|
||||
<div className='flex items-center mb-1'>
|
||||
<div className='mr-1 system-sm-medium text-text-primary'>{form.key}</div>
|
||||
<div className='system-xs-regular text-text-tertiary'>{form.type}</div>
|
||||
</div>
|
||||
{
|
||||
(form.type === VarType.string || form.type === VarType.number) && (
|
||||
<Input
|
||||
type={form.type}
|
||||
value={form.value || (form.type === VarType.string ? '' : 0)}
|
||||
onChange={getFormChangeHandler({ key: form.key, type: form.type })}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
(
|
||||
form.type === VarType.array
|
||||
|| form.type === VarType.arrayNumber
|
||||
|| form.type === VarType.arrayString
|
||||
|| form.type === VarType.arrayObject
|
||||
|| form.type === VarType.object
|
||||
) && (
|
||||
<CodeEditor
|
||||
language={CodeLanguage.json}
|
||||
value={form.value}
|
||||
onChange={getFormChangeHandler({ key: form.key, type: form.type })}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DefaultValue
|
||||
@ -0,0 +1,67 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useUpdateNodeInternals } from 'reactflow'
|
||||
import { NodeSourceHandle } from '../node-handle'
|
||||
import { ErrorHandleTypeEnum } from './types'
|
||||
import type { Node } from '@/app/components/workflow/types'
|
||||
import { NodeRunningStatus } from '@/app/components/workflow/types'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type ErrorHandleOnNodeProps = Pick<Node, 'id' | 'data'>
|
||||
const ErrorHandleOnNode = ({
|
||||
id,
|
||||
data,
|
||||
}: ErrorHandleOnNodeProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { error_strategy } = data
|
||||
const updateNodeInternals = useUpdateNodeInternals()
|
||||
|
||||
useEffect(() => {
|
||||
if (error_strategy === ErrorHandleTypeEnum.failBranch)
|
||||
updateNodeInternals(id)
|
||||
}, [error_strategy, id, updateNodeInternals])
|
||||
|
||||
if (!error_strategy)
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className='relative pt-1 pb-2 px-3'>
|
||||
<div className={cn(
|
||||
'relative flex items-center justify-between px-[5px] h-6 bg-workflow-block-parma-bg rounded-md',
|
||||
data._runningStatus === NodeRunningStatus.Exception && 'border-[0.5px] border-components-badge-status-light-warning-halo bg-state-warning-hover',
|
||||
)}>
|
||||
<div className='system-xs-medium-uppercase text-text-tertiary'>
|
||||
{t('workflow.common.onFailure')}
|
||||
</div>
|
||||
<div className={cn(
|
||||
'system-xs-medium text-text-secondary',
|
||||
data._runningStatus === NodeRunningStatus.Exception && 'text-text-warning',
|
||||
)}>
|
||||
{
|
||||
error_strategy === ErrorHandleTypeEnum.defaultValue && (
|
||||
t('workflow.nodes.common.errorHandle.defaultValue.output')
|
||||
)
|
||||
}
|
||||
{
|
||||
error_strategy === ErrorHandleTypeEnum.failBranch && (
|
||||
t('workflow.nodes.common.errorHandle.failBranch.title')
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
error_strategy === ErrorHandleTypeEnum.failBranch && (
|
||||
<NodeSourceHandle
|
||||
id={id}
|
||||
data={data}
|
||||
handleId={ErrorHandleTypeEnum.failBranch}
|
||||
handleClassName='!top-1/2 !-right-[21px] !-translate-y-1/2 after:!bg-workflow-link-line-failure-button-bg'
|
||||
nodeSelectorClassName='!bg-workflow-link-line-failure-button-bg'
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ErrorHandleOnNode
|
||||
@ -0,0 +1,90 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Collapse from '../collapse'
|
||||
import { ErrorHandleTypeEnum } from './types'
|
||||
import ErrorHandleTypeSelector from './error-handle-type-selector'
|
||||
import FailBranchCard from './fail-branch-card'
|
||||
import DefaultValue from './default-value'
|
||||
import {
|
||||
useDefaultValue,
|
||||
useErrorHandle,
|
||||
} from './hooks'
|
||||
import type { DefaultValueForm } from './types'
|
||||
import type {
|
||||
CommonNodeType,
|
||||
Node,
|
||||
} from '@/app/components/workflow/types'
|
||||
import Split from '@/app/components/workflow/nodes/_base/components/split'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
|
||||
type ErrorHandleProps = Pick<Node, 'id' | 'data'>
|
||||
const ErrorHandle = ({
|
||||
id,
|
||||
data,
|
||||
}: ErrorHandleProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { error_strategy, default_value } = data
|
||||
const {
|
||||
collapsed,
|
||||
setCollapsed,
|
||||
handleErrorHandleTypeChange,
|
||||
} = useErrorHandle(id, data)
|
||||
const { handleFormChange } = useDefaultValue(id)
|
||||
|
||||
const getHandleErrorHandleTypeChange = useCallback((data: CommonNodeType) => {
|
||||
return (value: ErrorHandleTypeEnum) => {
|
||||
handleErrorHandleTypeChange(value, data)
|
||||
}
|
||||
}, [handleErrorHandleTypeChange])
|
||||
|
||||
const getHandleFormChange = useCallback((data: CommonNodeType) => {
|
||||
return (v: DefaultValueForm) => {
|
||||
handleFormChange(v, data)
|
||||
}
|
||||
}, [handleFormChange])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Split />
|
||||
<div className='py-4'>
|
||||
<Collapse
|
||||
disabled={!error_strategy}
|
||||
collapsed={collapsed}
|
||||
onCollapse={setCollapsed}
|
||||
trigger={
|
||||
<div className='grow flex items-center justify-between pr-4'>
|
||||
<div className='flex items-center'>
|
||||
<div className='mr-0.5 system-sm-semibold-uppercase text-text-secondary'>
|
||||
{t('workflow.nodes.common.errorHandle.title')}
|
||||
</div>
|
||||
<Tooltip popupContent={t('workflow.nodes.common.errorHandle.tip')} />
|
||||
</div>
|
||||
<ErrorHandleTypeSelector
|
||||
value={error_strategy || ErrorHandleTypeEnum.none}
|
||||
onSelected={getHandleErrorHandleTypeChange(data)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<>
|
||||
{
|
||||
error_strategy === ErrorHandleTypeEnum.failBranch && !collapsed && (
|
||||
<FailBranchCard />
|
||||
)
|
||||
}
|
||||
{
|
||||
error_strategy === ErrorHandleTypeEnum.defaultValue && !collapsed && !!default_value?.length && (
|
||||
<DefaultValue
|
||||
forms={default_value}
|
||||
onFormChange={getHandleFormChange(data)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</>
|
||||
</Collapse>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ErrorHandle
|
||||
@ -0,0 +1,43 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiAlertFill } from '@remixicon/react'
|
||||
import { ErrorHandleTypeEnum } from './types'
|
||||
|
||||
type ErrorHandleTipProps = {
|
||||
type?: ErrorHandleTypeEnum
|
||||
}
|
||||
const ErrorHandleTip = ({
|
||||
type,
|
||||
}: ErrorHandleTipProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const text = useMemo(() => {
|
||||
if (type === ErrorHandleTypeEnum.failBranch)
|
||||
return t('workflow.nodes.common.errorHandle.failBranch.inLog')
|
||||
|
||||
if (type === ErrorHandleTypeEnum.defaultValue)
|
||||
return t('workflow.nodes.common.errorHandle.defaultValue.inLog')
|
||||
}, [])
|
||||
|
||||
if (!type)
|
||||
return null
|
||||
|
||||
return (
|
||||
<div
|
||||
className='relative flex p-2 pr-[52px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-xs'
|
||||
>
|
||||
<div
|
||||
className='absolute inset-0 opacity-40 rounded-lg'
|
||||
style={{
|
||||
background: 'linear-gradient(92deg, rgba(247, 144, 9, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%)',
|
||||
}}
|
||||
></div>
|
||||
<RiAlertFill className='shrink-0 mr-1 w-4 h-4 text-text-warning-secondary' />
|
||||
<div className='grow system-xs-medium text-text-primary'>
|
||||
{text}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ErrorHandleTip
|
||||
@ -0,0 +1,95 @@
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiArrowDownSLine,
|
||||
RiCheckLine,
|
||||
} from '@remixicon/react'
|
||||
import { ErrorHandleTypeEnum } from './types'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
||||
type ErrorHandleTypeSelectorProps = {
|
||||
value: ErrorHandleTypeEnum
|
||||
onSelected: (value: ErrorHandleTypeEnum) => void
|
||||
}
|
||||
const ErrorHandleTypeSelector = ({
|
||||
value,
|
||||
onSelected,
|
||||
}: ErrorHandleTypeSelectorProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
const options = [
|
||||
{
|
||||
value: ErrorHandleTypeEnum.none,
|
||||
label: t('workflow.nodes.common.errorHandle.none.title'),
|
||||
description: t('workflow.nodes.common.errorHandle.none.desc'),
|
||||
},
|
||||
{
|
||||
value: ErrorHandleTypeEnum.defaultValue,
|
||||
label: t('workflow.nodes.common.errorHandle.defaultValue.title'),
|
||||
description: t('workflow.nodes.common.errorHandle.defaultValue.desc'),
|
||||
},
|
||||
{
|
||||
value: ErrorHandleTypeEnum.failBranch,
|
||||
label: t('workflow.nodes.common.errorHandle.failBranch.title'),
|
||||
description: t('workflow.nodes.common.errorHandle.failBranch.desc'),
|
||||
},
|
||||
]
|
||||
const selectedOption = options.find(option => option.value === value)
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
placement='bottom-end'
|
||||
offset={4}
|
||||
>
|
||||
<PortalToFollowElemTrigger onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setOpen(v => !v)
|
||||
}}>
|
||||
<Button
|
||||
size='small'
|
||||
>
|
||||
{selectedOption?.label}
|
||||
<RiArrowDownSLine className='w-3.5 h-3.5' />
|
||||
</Button>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[11]'>
|
||||
<div className='p-1 w-[280px] border-[0.5px] border-components-panel-border rounded-xl bg-components-panel-bg-blur shadow-lg'>
|
||||
{
|
||||
options.map(option => (
|
||||
<div
|
||||
key={option.value}
|
||||
className='flex p-2 pr-3 rounded-lg hover:bg-state-base-hover cursor-pointer'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onSelected(option.value)
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<div className='mr-1 w-4 shrink-0'>
|
||||
{
|
||||
value === option.value && (
|
||||
<RiCheckLine className='w-4 h-4 text-text-accent' />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div className='grow'>
|
||||
<div className='mb-0.5 system-sm-semibold text-text-secondary'>{option.label}</div>
|
||||
<div className='system-xs-regular text-text-tertiary'>{option.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
)
|
||||
}
|
||||
|
||||
export default ErrorHandleTypeSelector
|
||||
@ -0,0 +1,32 @@
|
||||
import { RiMindMap } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const FailBranchCard = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className='pt-2 px-4'>
|
||||
<div className='p-4 rounded-[10px] bg-workflow-process-bg'>
|
||||
<div className='flex items-center justify-center mb-2 w-8 h-8 rounded-[10px] border-[0.5px] bg-components-card-bg shadow-lg'>
|
||||
<RiMindMap className='w-5 h-5 text-text-tertiary' />
|
||||
</div>
|
||||
<div className='mb-1 system-sm-medium text-text-secondary'>
|
||||
{t('workflow.nodes.common.errorHandle.failBranch.customize')}
|
||||
</div>
|
||||
<div className='system-xs-regular text-text-tertiary'>
|
||||
{t('workflow.nodes.common.errorHandle.failBranch.customizeTip')}
|
||||
|
||||
<a
|
||||
href='https://docs.dify.ai/guides/workflow/error-handling'
|
||||
target='_blank'
|
||||
className='text-text-accent'
|
||||
>
|
||||
{t('workflow.common.learnMore')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FailBranchCard
|
||||
@ -0,0 +1,123 @@
|
||||
import {
|
||||
useCallback,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { ErrorHandleTypeEnum } from './types'
|
||||
import type { DefaultValueForm } from './types'
|
||||
import { getDefaultValue } from './utils'
|
||||
import type {
|
||||
CommonNodeType,
|
||||
} from '@/app/components/workflow/types'
|
||||
import {
|
||||
useEdgesInteractions,
|
||||
useNodeDataUpdate,
|
||||
} from '@/app/components/workflow/hooks'
|
||||
|
||||
export const useDefaultValue = (
|
||||
id: string,
|
||||
) => {
|
||||
const { handleNodeDataUpdateWithSyncDraft } = useNodeDataUpdate()
|
||||
const handleFormChange = useCallback((
|
||||
{
|
||||
key,
|
||||
value,
|
||||
type,
|
||||
}: DefaultValueForm,
|
||||
data: CommonNodeType,
|
||||
) => {
|
||||
const default_value = data.default_value || []
|
||||
const index = default_value.findIndex(form => form.key === key)
|
||||
|
||||
if (index > -1) {
|
||||
const newDefaultValue = [...default_value]
|
||||
newDefaultValue[index].value = value
|
||||
handleNodeDataUpdateWithSyncDraft({
|
||||
id,
|
||||
data: {
|
||||
default_value: newDefaultValue,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
handleNodeDataUpdateWithSyncDraft({
|
||||
id,
|
||||
data: {
|
||||
default_value: [
|
||||
...default_value,
|
||||
{
|
||||
key,
|
||||
value,
|
||||
type,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
}, [handleNodeDataUpdateWithSyncDraft, id])
|
||||
|
||||
return {
|
||||
handleFormChange,
|
||||
}
|
||||
}
|
||||
|
||||
export const useErrorHandle = (
|
||||
id: string,
|
||||
data: CommonNodeType,
|
||||
) => {
|
||||
const initCollapsed = useMemo(() => {
|
||||
if (data.error_strategy === ErrorHandleTypeEnum.none)
|
||||
return true
|
||||
|
||||
return false
|
||||
}, [data.error_strategy])
|
||||
const [collapsed, setCollapsed] = useState(initCollapsed)
|
||||
const { handleNodeDataUpdateWithSyncDraft } = useNodeDataUpdate()
|
||||
const { handleEdgeDeleteByDeleteBranch } = useEdgesInteractions()
|
||||
|
||||
const handleErrorHandleTypeChange = useCallback((value: ErrorHandleTypeEnum, data: CommonNodeType) => {
|
||||
if (data.error_strategy === value)
|
||||
return
|
||||
|
||||
if (value === ErrorHandleTypeEnum.none) {
|
||||
handleNodeDataUpdateWithSyncDraft({
|
||||
id,
|
||||
data: {
|
||||
error_strategy: undefined,
|
||||
default_value: undefined,
|
||||
},
|
||||
})
|
||||
setCollapsed(true)
|
||||
handleEdgeDeleteByDeleteBranch(id, ErrorHandleTypeEnum.failBranch)
|
||||
}
|
||||
|
||||
if (value === ErrorHandleTypeEnum.failBranch) {
|
||||
handleNodeDataUpdateWithSyncDraft({
|
||||
id,
|
||||
data: {
|
||||
error_strategy: value,
|
||||
default_value: undefined,
|
||||
},
|
||||
})
|
||||
setCollapsed(false)
|
||||
}
|
||||
|
||||
if (value === ErrorHandleTypeEnum.defaultValue) {
|
||||
handleNodeDataUpdateWithSyncDraft({
|
||||
id,
|
||||
data: {
|
||||
error_strategy: value,
|
||||
default_value: getDefaultValue(data),
|
||||
},
|
||||
})
|
||||
setCollapsed(false)
|
||||
handleEdgeDeleteByDeleteBranch(id, ErrorHandleTypeEnum.failBranch)
|
||||
}
|
||||
}, [id, handleNodeDataUpdateWithSyncDraft, handleEdgeDeleteByDeleteBranch])
|
||||
|
||||
return {
|
||||
collapsed,
|
||||
setCollapsed,
|
||||
handleErrorHandleTypeChange,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import type { VarType } from '@/app/components/workflow/types'
|
||||
|
||||
export enum ErrorHandleTypeEnum {
|
||||
none = 'none',
|
||||
failBranch = 'fail-branch',
|
||||
defaultValue = 'default-value',
|
||||
}
|
||||
|
||||
export type DefaultValueForm = {
|
||||
key: string
|
||||
type: VarType
|
||||
value?: any
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
import type { CommonNodeType } from '@/app/components/workflow/types'
|
||||
import {
|
||||
BlockEnum,
|
||||
VarType,
|
||||
} from '@/app/components/workflow/types'
|
||||
import type { CodeNodeType } from '@/app/components/workflow/nodes/code/types'
|
||||
|
||||
const getDefaultValueByType = (type: VarType) => {
|
||||
if (type === VarType.string)
|
||||
return ''
|
||||
|
||||
if (type === VarType.number)
|
||||
return 0
|
||||
|
||||
if (type === VarType.object)
|
||||
return '{}'
|
||||
|
||||
if (type === VarType.arrayObject || type === VarType.arrayString || type === VarType.arrayNumber || type === VarType.arrayFile)
|
||||
return '[]'
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
export const getDefaultValue = (data: CommonNodeType) => {
|
||||
const { type } = data
|
||||
|
||||
if (type === BlockEnum.LLM) {
|
||||
return [{
|
||||
key: 'text',
|
||||
type: VarType.string,
|
||||
value: getDefaultValueByType(VarType.string),
|
||||
}]
|
||||
}
|
||||
|
||||
if (type === BlockEnum.HttpRequest) {
|
||||
return [
|
||||
{
|
||||
key: 'body',
|
||||
type: VarType.string,
|
||||
value: getDefaultValueByType(VarType.string),
|
||||
},
|
||||
{
|
||||
key: 'status_code',
|
||||
type: VarType.number,
|
||||
value: getDefaultValueByType(VarType.number),
|
||||
},
|
||||
{
|
||||
key: 'headers',
|
||||
type: VarType.object,
|
||||
value: getDefaultValueByType(VarType.object),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
if (type === BlockEnum.Tool) {
|
||||
return [
|
||||
{
|
||||
key: 'text',
|
||||
type: VarType.string,
|
||||
value: getDefaultValueByType(VarType.string),
|
||||
},
|
||||
{
|
||||
key: 'json',
|
||||
type: VarType.arrayObject,
|
||||
value: getDefaultValueByType(VarType.arrayObject),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
if (type === BlockEnum.Code) {
|
||||
const { outputs } = data as CodeNodeType
|
||||
|
||||
return Object.keys(outputs).map((key) => {
|
||||
return {
|
||||
key,
|
||||
type: outputs[key].type,
|
||||
value: getDefaultValueByType(outputs[key].type),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
Loading…
Reference in New Issue