refactore: Replace hardcoded null strategy strings with constant

This commit is contained in:
zhsama
2026-01-29 18:35:06 +08:00
parent 92731bffba
commit e47f690cd2
9 changed files with 87 additions and 17 deletions

View File

@ -15,6 +15,7 @@ import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/
import Field from '@/app/components/workflow/nodes/_base/components/field'
import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker'
import Tab, { TabType } from '@/app/components/workflow/nodes/_base/components/workflow-panel/tab'
import { NULL_STRATEGY } from '@/app/components/workflow/nodes/_base/constants'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
import { cn } from '@/utils/classnames'
@ -62,12 +63,12 @@ const ConfigPanel: FC<ConfigPanelProps> = ({
const [nullStrategyOpen, setNullStrategyOpen] = useState(false)
const whenOutputNoneOptions = useMemo(() => ([
{
value: 'raise_error' as const,
value: NULL_STRATEGY.RAISE_ERROR,
label: t('subGraphModal.whenOutputNone.error', { ns: 'workflow' }),
description: t('subGraphModal.whenOutputNone.errorDesc', { ns: 'workflow' }),
},
{
value: 'use_default' as const,
value: NULL_STRATEGY.USE_DEFAULT,
label: t('subGraphModal.whenOutputNone.default', { ns: 'workflow' }),
description: t('subGraphModal.whenOutputNone.defaultDesc', { ns: 'workflow' }),
},
@ -194,7 +195,7 @@ const ConfigPanel: FC<ConfigPanelProps> = ({
{selectedWhenOutputNoneOption.description}
</div>
)}
{nestedNodeConfig.null_strategy === 'use_default' && (
{nestedNodeConfig.null_strategy === NULL_STRATEGY.USE_DEFAULT && (
<div className={cn('overflow-hidden rounded-lg border border-components-input-border-active bg-components-input-bg-normal p-1')}>
<CodeEditor
noWrapper

View File

@ -22,6 +22,7 @@ import { WorkflowContext } from '@/app/components/workflow/context'
import { HooksStoreContext } from '@/app/components/workflow/hooks-store/provider'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker'
import { NULL_STRATEGY } from '@/app/components/workflow/nodes/_base/constants'
import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
import MixedVariableTextInput from '@/app/components/workflow/nodes/tool/components/mixed-variable-text-input'
@ -334,7 +335,7 @@ const FormInputItem: FC<Props> = ({
? (nestedNodeConfig ?? varInput?.nested_node_config ?? {
extractor_node_id: nodeId && variable ? `${nodeId}_ext_${variable}` : '',
output_selector: ['result'],
null_strategy: 'use_default',
null_strategy: NULL_STRATEGY.RAISE_ERROR,
default_value: '',
})
: undefined

View File

@ -0,0 +1,6 @@
export const NULL_STRATEGY = {
RAISE_ERROR: 'raise_error',
USE_DEFAULT: 'use_default',
} as const
export type NullStrategy = typeof NULL_STRATEGY[keyof typeof NULL_STRATEGY]

View File

@ -1,3 +1,4 @@
import type { NullStrategy } from '@/app/components/workflow/nodes/_base/constants'
import type { ValueSelector } from '@/app/components/workflow/types'
// Generic variable types for all resource forms
@ -11,7 +12,7 @@ export enum VarKindType {
export type NestedNodeConfig = {
extractor_node_id: string
output_selector: ValueSelector
null_strategy: 'raise_error' | 'use_default'
null_strategy: NullStrategy
default_value: unknown
}

View File

@ -23,6 +23,7 @@ type Props = {
codeNodeId: string
availableVars?: NodeOutPutVar[]
availableNodes?: Node[]
onOpenInternalViewAndRun?: () => void
}
export type ContextGenerateModalHandle = {
@ -59,6 +60,7 @@ const ContextGenerateModal = forwardRef<ContextGenerateModalHandle, Props>(({
codeNodeId,
availableVars,
availableNodes,
onOpenInternalViewAndRun,
}, ref) => {
const configsMap = useHooksStore(s => s.configsMap)
const nodes = useStore(s => s.nodes)
@ -179,13 +181,22 @@ const ContextGenerateModal = forwardRef<ContextGenerateModalHandle, Props>(({
return
if (current)
applyToNode(false)
const store = workflowStore.getState()
store.setInitShowLastRunTab(true)
store.setPendingSingleRun({
nodeId: codeNodeId,
action: 'run',
})
}, [applyToNode, codeNodeId, current, workflowStore])
if (onOpenInternalViewAndRun) {
// Close this modal and open internal view, then run
handleCloseModal()
onOpenInternalViewAndRun()
}
else {
// Fallback: direct run (for cases without internal view)
const store = workflowStore.getState()
store.setInitShowLastRunTab(true)
store.setPendingSingleRun({
nodeId: codeNodeId,
action: 'run',
})
}
}, [applyToNode, codeNodeId, current, handleCloseModal, onOpenInternalViewAndRun, workflowStore])
const isRunning = useMemo(() => {
const target = nodes.find(node => node.id === codeNodeId)

View File

@ -10,6 +10,7 @@ import type {
Node as WorkflowNode,
} from '@/app/components/workflow/types'
import { useCallback, useMemo } from 'react'
import { NULL_STRATEGY } from '@/app/components/workflow/nodes/_base/constants'
import { Type } from '@/app/components/workflow/nodes/llm/types'
import { BlockEnum, EditionType, isPromptMessageContext, PromptRole, VarType } from '@/app/components/workflow/types'
import { generateNewNode, getNodeCustomTypeByNodeDataType, mergeNodeDefaultData } from '@/app/components/workflow/utils'
@ -46,7 +47,7 @@ export const buildAssembleNestedNodeConfig = (
return {
extractor_node_id: extractorNodeId,
output_selector: defaultOutputKey ? [defaultOutputKey] : [],
null_strategy: 'use_default',
null_strategy: NULL_STRATEGY.RAISE_ERROR,
default_value: '',
}
}

View File

@ -13,6 +13,7 @@ import type {
import {
memo,
useCallback,
useEffect,
useMemo,
useRef,
useState,
@ -22,9 +23,10 @@ import { useNodes, useStoreApi } from 'reactflow'
import PromptEditor from '@/app/components/base/prompt-editor'
import { useNodesMetaData, useNodesSyncDraft } from '@/app/components/workflow/hooks'
import { useHooksStore } from '@/app/components/workflow/hooks-store'
import { NULL_STRATEGY } from '@/app/components/workflow/nodes/_base/constants'
import { VarKindType as VarKindTypeEnum } from '@/app/components/workflow/nodes/_base/types'
import { Type } from '@/app/components/workflow/nodes/llm/types'
import { useStore } from '@/app/components/workflow/store'
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
import { BlockEnum } from '@/app/components/workflow/types'
import { useGetLanguage } from '@/context/i18n'
import { useStrategyProviders } from '@/service/use-strategy'
@ -46,7 +48,7 @@ type WorkflowNodesMap = NonNullable<WorkflowVariableBlockType['workflowNodesMap'
const DEFAULT_NESTED_NODE_CONFIG: NestedNodeConfig = {
extractor_node_id: '',
output_selector: [],
null_strategy: 'use_default',
null_strategy: NULL_STRATEGY.RAISE_ERROR,
default_value: '',
}
@ -96,6 +98,8 @@ const MixedVariableTextInput = ({
const [isSubGraphModalOpen, setIsSubGraphModalOpen] = useState(false)
const [isContextGenerateModalOpen, setIsContextGenerateModalOpen] = useState(false)
const contextGenerateModalRef = useRef<ContextGenerateModalHandle>(null)
const [pendingRunAfterSubGraphOpen, setPendingRunAfterSubGraphOpen] = useState(false)
const workflowStore = useWorkflowStore()
const nodesByIdMap = useMemo(() => {
return availableNodes.reduce((acc, node) => {
@ -352,12 +356,39 @@ const MixedVariableTextInput = ({
const handleCloseSubGraphModal = useCallback(() => {
setIsSubGraphModalOpen(false)
setPendingRunAfterSubGraphOpen(false)
}, [])
const handleCloseContextGenerateModal = useCallback(() => {
setIsContextGenerateModalOpen(false)
}, [])
const handleOpenInternalViewAndRun = useCallback(() => {
setIsSubGraphModalOpen(true)
setPendingRunAfterSubGraphOpen(true)
}, [])
useEffect(() => {
if (!isSubGraphModalOpen || !pendingRunAfterSubGraphOpen)
return
const extractorNodeId = assembleExtractorNodeId || (toolNodeId && paramKey ? `${toolNodeId}_ext_${paramKey}` : '')
if (!extractorNodeId)
return
const timer = setTimeout(() => {
const store = workflowStore()
store.setInitShowLastRunTab(true)
store.setPendingSingleRun({
nodeId: extractorNodeId,
action: 'run',
})
setPendingRunAfterSubGraphOpen(false)
}, 300)
return () => clearTimeout(timer)
}, [isSubGraphModalOpen, pendingRunAfterSubGraphOpen, assembleExtractorNodeId, toolNodeId, paramKey, workflowStore])
const sourceVariable: ValueSelector | undefined = detectedAgentFromValue
? [detectedAgentFromValue.nodeId, 'context']
: undefined
@ -462,6 +493,7 @@ const MixedVariableTextInput = ({
codeNodeId={assembleExtractorNodeId || `${toolNodeId}_ext_${paramKey}`}
availableVars={nodesOutputVars}
availableNodes={availableNodes}
onOpenInternalViewAndRun={handleOpenInternalViewAndRun}
/>
)}
</div>

View File

@ -16,6 +16,7 @@ import { AssembleVariablesAlt } from '@/app/components/base/icons/src/vender/lin
import { Agent } from '@/app/components/base/icons/src/vender/workflow'
import { useIsChatMode, useNodesSyncDraft, useWorkflow, useWorkflowVariables } from '@/app/components/workflow/hooks'
import { useHooksStore } from '@/app/components/workflow/hooks-store'
import { NULL_STRATEGY } from '@/app/components/workflow/nodes/_base/constants'
import { VarKindType } from '@/app/components/workflow/nodes/_base/types'
import { useStore as useWorkflowStore } from '@/app/components/workflow/store'
import { EditionType, isPromptMessageContext, PromptRole, VarType } from '@/app/components/workflow/types'
@ -23,7 +24,15 @@ import SubGraphCanvas from './sub-graph-canvas'
const SubGraphModal: FC<SubGraphModalProps> = (props) => {
const { t } = useTranslation()
const { isOpen, onClose, variant, toolNodeId, paramKey } = props
const {
isOpen,
onClose,
variant,
toolNodeId,
paramKey,
pendingSingleRun,
onPendingSingleRunHandled,
} = props
const isAgentVariant = variant === 'agent'
const resolvedAgentNodeId = isAgentVariant ? props.agentNodeId : ''
const agentName = isAgentVariant ? props.agentName : ''
@ -93,7 +102,7 @@ const SubGraphModal: FC<SubGraphModalProps> = (props) => {
return {
extractor_node_id: current?.extractor_node_id || extractorNodeId,
output_selector: outputSelector.length > 0 ? outputSelector : defaultOutputSelector,
null_strategy: current?.null_strategy || 'use_default',
null_strategy: current?.null_strategy || NULL_STRATEGY.RAISE_ERROR,
default_value: current?.default_value ?? '',
}
}, [extractorNodeId, isAgentVariant, paramKey, toolParam?.nested_node_config])
@ -273,6 +282,9 @@ const SubGraphModal: FC<SubGraphModalProps> = (props) => {
variant="agent"
toolNodeId={toolNodeId}
paramKey={paramKey}
isOpen={isOpen}
pendingSingleRun={pendingSingleRun}
onPendingSingleRunHandled={onPendingSingleRunHandled}
sourceVariable={props.sourceVariable}
agentNodeId={props.agentNodeId}
agentName={props.agentName}
@ -292,6 +304,9 @@ const SubGraphModal: FC<SubGraphModalProps> = (props) => {
variant="assemble"
toolNodeId={toolNodeId}
paramKey={paramKey}
isOpen={isOpen}
pendingSingleRun={pendingSingleRun}
onPendingSingleRunHandled={onPendingSingleRunHandled}
title={props.title}
configsMap={configsMap}
nestedNodeConfig={nestedNodeConfig}

View File

@ -6,6 +6,8 @@ type BaseSubGraphModalProps = {
onClose: () => void
toolNodeId: string
paramKey: string
pendingSingleRun?: boolean
onPendingSingleRunHandled?: () => void
}
type AgentSubGraphModalProps = BaseSubGraphModalProps & {