chore(web): pre-align HITL frontend from build/feat/hitl

This commit is contained in:
yyh
2026-02-09 15:34:15 +08:00
parent b289e6a2b6
commit ca243d7efc
261 changed files with 14122 additions and 1328 deletions

View File

@ -82,7 +82,7 @@ const FormItem: FC<Props> = ({
<div className="p-[1px]">
<VarBlockIcon type={nodeType || BlockEnum.Start} />
</div>
<div className="mx-0.5 max-w-[150px] truncate text-xs font-medium text-gray-700" title={nodeName}>
<div className="mx-0.5 max-w-[150px] truncate text-xs font-medium text-text-secondary" title={nodeName}>
{nodeName}
</div>
<Line3 className="mr-0.5"></Line3>

View File

@ -3,7 +3,8 @@ import type { FC } from 'react'
import type { Props as FormProps } from './form'
import type { Emoji } from '@/app/components/tools/types'
import type { SpecialResultPanelProps } from '@/app/components/workflow/run/special-result-panel'
import type { BlockEnum, NodeRunningStatus } from '@/app/components/workflow/types'
import type { NodeRunningStatus } from '@/app/components/workflow/types'
import type { HumanInputFormData } from '@/types/workflow'
import * as React from 'react'
import { useEffect, useRef } from 'react'
import { useTranslation } from 'react-i18next'
@ -11,7 +12,8 @@ import Button from '@/app/components/base/button'
import { getProcessedFiles } from '@/app/components/base/file-uploader/utils'
import Toast from '@/app/components/base/toast'
import Split from '@/app/components/workflow/nodes/_base/components/split'
import { InputVarType } from '@/app/components/workflow/types'
import SingleRunForm from '@/app/components/workflow/nodes/human-input/components/single-run-form'
import { BlockEnum, InputVarType } from '@/app/components/workflow/types'
import { TransferMethod } from '@/types/app'
import { cn } from '@/utils/classnames'
import Form from './form'
@ -31,6 +33,12 @@ export type BeforeRunFormProps = {
showSpecialResultPanel?: boolean
existVarValuesInForms: Record<string, any>[]
filteredExistVarForms: FormProps[]
showGeneratedForm?: boolean
handleShowGeneratedForm?: (data: Record<string, any>) => void
handleHideGeneratedForm?: () => void
formData?: HumanInputFormData
handleSubmitHumanInputForm?: (data: any) => Promise<void>
handleAfterHumanInputStepRun?: () => void
} & Partial<SpecialResultPanelProps>
function formatValue(value: string | any, type: InputVarType) {
@ -62,14 +70,24 @@ function formatValue(value: string | any, type: InputVarType) {
}
const BeforeRunForm: FC<BeforeRunFormProps> = ({
nodeName,
nodeType,
onHide,
onRun,
forms,
filteredExistVarForms,
existVarValuesInForms,
showGeneratedForm = false,
handleShowGeneratedForm,
handleHideGeneratedForm,
formData,
handleSubmitHumanInputForm,
handleAfterHumanInputStepRun,
}) => {
const { t } = useTranslation()
const isHumanInput = nodeType === BlockEnum.HumanInput
const showBackButton = filteredExistVarForms.length > 0
const isFileLoaded = (() => {
if (!forms || forms.length === 0)
return true
@ -84,7 +102,8 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({
return true
})()
const handleRun = () => {
const handleRunOrGenerateForm = () => {
let errMsg = ''
forms.forEach((form, i) => {
const existVarValuesInForm = existVarValuesInForms[i]
@ -135,19 +154,30 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({
return
}
onRun(submitData)
if (isHumanInput)
handleShowGeneratedForm?.(submitData)
else
onRun(submitData)
}
const handleHumanInputFormSubmit = async (data: any) => {
await handleSubmitHumanInputForm?.(data)
handleAfterHumanInputStepRun?.()
}
const hasRun = useRef(false)
useEffect(() => {
// React 18 run twice in dev mode
if (hasRun.current)
return
hasRun.current = true
if (filteredExistVarForms.length === 0)
if (filteredExistVarForms.length === 0 && !isHumanInput)
onRun({})
}, [filteredExistVarForms, onRun])
if (filteredExistVarForms.length === 0 && isHumanInput)
handleShowGeneratedForm?.({})
}, [filteredExistVarForms, handleShowGeneratedForm, isHumanInput, onRun])
if (filteredExistVarForms.length === 0)
if (filteredExistVarForms.length === 0 && !isHumanInput)
return null
return (
@ -156,23 +186,43 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({
onHide={onHide}
>
<div className="h-0 grow overflow-y-auto pb-4">
<div className="mt-3 space-y-4 px-4">
{filteredExistVarForms.map((form, index) => (
<div key={index}>
<Form
key={index}
className={cn(index < forms.length - 1 && 'mb-4')}
{...form}
/>
{index < forms.length - 1 && <Split />}
</div>
))}
</div>
<div className="mt-4 flex justify-between space-x-2 px-4">
<Button disabled={!isFileLoaded} variant="primary" className="w-0 grow space-x-2" onClick={handleRun}>
<div>{t(`${i18nPrefix}.startRun`, { ns: 'workflow' })}</div>
</Button>
</div>
{!showGeneratedForm && (
<div className="mt-3 space-y-4 px-4">
{filteredExistVarForms.map((form, index) => (
<div key={index}>
<Form
key={index}
className={cn(index < forms.length - 1 && 'mb-4')}
{...form}
/>
{index < forms.length - 1 && <Split />}
</div>
))}
</div>
)}
{showGeneratedForm && formData && (
<SingleRunForm
nodeName={nodeName}
showBackButton={showBackButton}
handleBack={handleHideGeneratedForm}
data={formData}
onSubmit={handleHumanInputFormSubmit}
/>
)}
{!showGeneratedForm && (
<div className="mt-4 flex justify-between space-x-2 px-4">
{!isHumanInput && (
<Button disabled={!isFileLoaded} variant="primary" className="w-0 grow space-x-2" onClick={handleRunOrGenerateForm}>
<div>{t(`${i18nPrefix}.startRun`, { ns: 'workflow' })}</div>
</Button>
)}
{isHumanInput && (
<Button disabled={!isFileLoaded} variant="primary" className="w-0 grow space-x-2" onClick={handleRunOrGenerateForm}>
<div>{t('nodes.humanInput.singleRun.button', { ns: 'workflow' })}</div>
</Button>
)}
</div>
)}
</div>
</PanelWrap>
)

View File

@ -16,6 +16,7 @@ import type { QuestionClassifierNodeType } from '../../../question-classifier/ty
import type { TemplateTransformNodeType } from '../../../template-transform/types'
import type { ToolNodeType } from '../../../tool/types'
import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types'
import type { HumanInputNodeType } from '@/app/components/workflow/nodes/human-input/types'
import type { CaseItem, Condition } from '@/app/components/workflow/nodes/if-else/types'
import type { Field as StructField } from '@/app/components/workflow/nodes/llm/types'
import type { StartNodeType } from '@/app/components/workflow/nodes/start/types'
@ -43,6 +44,7 @@ import {
FILE_STRUCT,
getGlobalVars,
HTTP_REQUEST_OUTPUT_STRUCT,
HUMAN_INPUT_OUTPUT_STRUCT,
KNOWLEDGE_RETRIEVAL_OUTPUT_STRUCT,
LLM_OUTPUT_STRUCT,
PARAMETER_EXTRACTOR_COMMON_STRUCT,
@ -52,6 +54,7 @@ import {
TOOL_OUTPUT_STRUCT,
} from '@/app/components/workflow/constants'
import DataSourceNodeDefault from '@/app/components/workflow/nodes/data-source/default'
import HumanInputNodeDefault from '@/app/components/workflow/nodes/human-input/default'
import ToolNodeDefault from '@/app/components/workflow/nodes/tool/default'
import PluginTriggerNodeDefault from '@/app/components/workflow/nodes/trigger-plugin/default'
import {
@ -650,6 +653,17 @@ const formatItem = (
break
}
case BlockEnum.HumanInput: {
const outputSchema = HumanInputNodeDefault.getOutputVars?.(
data as HumanInputNodeType,
allPluginInfoList,
[],
{ schemaTypeDefinitions },
) || []
res.vars = [...outputSchema, ...HUMAN_INPUT_OUTPUT_STRUCT]
break
}
case 'env': {
res.vars = data.envList.map((env: EnvironmentVariable) => {
return {
@ -1515,6 +1529,13 @@ export const getNodeUsedVars = (node: Node): ValueSelector[] => {
res = valueSelectors
break
}
case BlockEnum.HumanInput: {
const payload = data as HumanInputNodeType
const formContent = payload.form_content
res = matchNotSystemVars([formContent])
break
}
}
return res || []
}
@ -1613,6 +1634,11 @@ export const getNodeUsedVarPassToServerKey = (
res = 'query'
break
}
case BlockEnum.HumanInput: {
res = `#${valueSelector.join('.')}#`
break
}
}
return res
}
@ -1963,6 +1989,15 @@ export const updateNodeVars = (
payload.variable = newVarSelector
break
}
case BlockEnum.HumanInput: {
const payload = data as HumanInputNodeType
payload.form_content = replaceOldVarInText(
payload.form_content,
oldVarSelector,
newVarSelector,
)
break
}
}
})
return newNode

View File

@ -71,6 +71,8 @@ type Props = {
availableNodes?: Node[]
availableVars?: NodeOutPutVar[]
isAddBtnTrigger?: boolean
trigger?: React.ReactNode
isJustShowValue?: boolean
schema?: Partial<CredentialFormSchema>
valueTypePlaceHolder?: string
isInTable?: boolean
@ -104,6 +106,8 @@ const VarReferencePicker: FC<Props> = ({
isFilterFileVar,
availableNodes: passedInAvailableNodes,
availableVars: passedInAvailableVars,
trigger,
isJustShowValue,
isAddBtnTrigger,
schema,
valueTypePlaceHolder,
@ -428,204 +432,207 @@ const VarReferencePicker: FC<Props> = ({
onOpenChange={setOpen}
placement={isAddBtnTrigger ? 'bottom-end' : 'bottom-start'}
>
<WrapElem
onClick={() => {
if (readonly)
return
if (!isConstant)
setOpen(!open)
else
setControlFocus(Date.now())
}}
className="group/picker-trigger-wrap relative !flex"
>
<>
{isAddBtnTrigger
? (
<div>
<AddButton onClick={noop}></AddButton>
</div>
)
: (
<div ref={!isSupportConstantValue ? triggerRef : null} className={cn((open || isFocus) ? 'border-gray-300' : 'border-gray-100', 'group/wrap relative flex h-8 w-full items-center', !isSupportConstantValue && 'rounded-lg bg-components-input-bg-normal p-1', isInTable && 'border-none bg-transparent', readonly && 'bg-components-input-bg-disabled')}>
{isSupportConstantValue
? (
<div
onClick={(e) => {
e.stopPropagation()
setOpen(false)
setControlFocus(Date.now())
}}
className="mr-1 flex h-full items-center space-x-1"
>
<TypeSelector
noLeft
trigger={(
<div className="radius-md flex h-8 items-center bg-components-input-bg-normal px-2">
<div className="system-sm-regular mr-1 text-components-input-text-filled">{varKindTypes.find(item => item.value === varKindType)?.label}</div>
<RiArrowDownSLine className="h-4 w-4 text-text-quaternary" />
</div>
)}
popupClassName="top-8"
readonly={readonly}
value={varKindType}
options={varKindTypes}
onChange={handleVarKindTypeChange}
showChecked
/>
</div>
)
: (!hasValue && (
<div className="ml-1.5 mr-1">
<Variable02 className={`h-4 w-4 ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'}`} />
</div>
))}
{isConstant
? (
<ConstantField
value={value as string}
onChange={onChange as ((value: string | number, varKindType: VarKindType, varInfo?: Var) => void)}
schema={schemaWithDynamicSelect as CredentialFormSchema}
readonly={readonly}
isLoading={isLoading}
/>
)
: (
<VarPickerWrap
onClick={() => {
if (readonly)
return
if (!isConstant)
setOpen(!open)
else
{!!trigger && <PortalToFollowElemTrigger onClick={() => setOpen(!open)}>{trigger}</PortalToFollowElemTrigger>}
{!trigger && (
<WrapElem
onClick={() => {
if (readonly)
return
if (!isConstant)
setOpen(!open)
else
setControlFocus(Date.now())
}}
className="group/picker-trigger-wrap relative !flex"
>
<>
{isAddBtnTrigger
? (
<div>
<AddButton onClick={noop}></AddButton>
</div>
)
: (
<div ref={!isSupportConstantValue ? triggerRef : null} className={cn((open || isFocus) ? 'border-gray-300' : 'border-gray-100', 'group/wrap relative flex h-8 w-full items-center', !isSupportConstantValue && 'rounded-lg bg-components-input-bg-normal p-1', isInTable && 'border-none bg-transparent', readonly && 'bg-components-input-bg-disabled', isJustShowValue && 'h-6 bg-transparent p-0')}>
{isSupportConstantValue
? (
<div
onClick={(e) => {
e.stopPropagation()
setOpen(false)
setControlFocus(Date.now())
}}
className="h-full grow"
>
<div ref={isSupportConstantValue ? triggerRef : null} className={cn('h-full', isSupportConstantValue && 'flex items-center rounded-lg bg-components-panel-bg py-1 pl-1')}>
<Tooltip noDecoration={isShowAPart} popupContent={tooltipPopup}>
<div className={cn('h-full items-center rounded-[5px] px-1.5', hasValue ? 'inline-flex bg-components-badge-white-to-dark' : 'flex')}>
{hasValue
? (
<>
{isShowNodeName && !isEnv && !isChatVar && !isGlobal && !isRagVar && (
<div
className="flex items-center"
onClick={(e) => {
if (e.metaKey || e.ctrlKey) {
e.stopPropagation()
handleVariableJump(outputVarNode?.id)
}
}}
>
<div className="h-3 px-[1px]">
{outputVarNode?.type && (
<VarBlockIcon
className="!text-text-primary"
type={outputVarNode.type}
/>
)}
</div>
}}
className="mr-1 flex h-full items-center space-x-1"
>
<TypeSelector
noLeft
trigger={(
<div className="radius-md flex h-8 items-center bg-components-input-bg-normal px-2">
<div className="system-sm-regular mr-1 text-components-input-text-filled">{varKindTypes.find(item => item.value === varKindType)?.label}</div>
<RiArrowDownSLine className="h-4 w-4 text-text-quaternary" />
</div>
)}
popupClassName="top-8"
readonly={readonly}
value={varKindType}
options={varKindTypes}
onChange={handleVarKindTypeChange}
showChecked
/>
</div>
)
: (!hasValue && (
<div className="ml-1.5 mr-1">
<Variable02 className={`h-4 w-4 ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'}`} />
</div>
))}
{isConstant
? (
<ConstantField
value={value as string}
onChange={onChange as ((value: string | number, varKindType: VarKindType, varInfo?: Var) => void)}
schema={schemaWithDynamicSelect as CredentialFormSchema}
readonly={readonly}
isLoading={isLoading}
/>
)
: (
<VarPickerWrap
onClick={() => {
if (readonly)
return
if (!isConstant)
setOpen(!open)
else
setControlFocus(Date.now())
}}
className="h-full grow"
>
<div ref={isSupportConstantValue ? triggerRef : null} className={cn('h-full', isSupportConstantValue && 'flex items-center rounded-lg bg-components-panel-bg py-1 pl-1')}>
<Tooltip noDecoration={isShowAPart} popupContent={tooltipPopup}>
<div className={cn('h-full items-center rounded-[5px] px-1.5', hasValue ? 'inline-flex bg-components-badge-white-to-dark' : 'flex')}>
{hasValue
? (
<>
{isShowNodeName && !isEnv && !isChatVar && !isGlobal && !isRagVar && (
<div
className="mx-0.5 truncate text-xs font-medium text-text-secondary"
title={outputVarNode?.title}
style={{
maxWidth: maxNodeNameWidth,
className="flex items-center"
onClick={(e) => {
if (e.metaKey || e.ctrlKey) {
e.stopPropagation()
handleVariableJump(outputVarNode?.id)
}
}}
>
{outputVarNode?.title}
<div className="h-3 px-[1px]">
{outputVarNode?.type && (
<VarBlockIcon
className="!text-text-primary"
type={outputVarNode.type}
/>
)}
</div>
<div
className="mx-0.5 truncate text-xs font-medium text-text-secondary"
title={outputVarNode?.title}
style={{
maxWidth: maxNodeNameWidth,
}}
>
{outputVarNode?.title}
</div>
<Line3 className="mr-0.5"></Line3>
</div>
)}
{isShowAPart && (
<div className="flex items-center">
<RiMoreLine className="h-3 w-3 text-text-secondary" />
<Line3 className="mr-0.5 text-divider-deep"></Line3>
</div>
)}
<div className="flex items-center text-text-accent">
{isLoading && <RiLoader4Line className="h-3.5 w-3.5 animate-spin text-text-secondary" />}
<VariableIconWithColor
variables={value as ValueSelector}
variableCategory={variableCategory}
isExceptionVariable={isException}
/>
<div
className={cn('ml-0.5 truncate text-xs font-medium', isEnv && '!text-text-secondary', isChatVar && 'text-util-colors-teal-teal-700', isException && 'text-text-warning', isGlobal && 'text-util-colors-orange-orange-600')}
title={varName}
style={{
maxWidth: maxVarNameWidth,
}}
>
{varName}
</div>
<Line3 className="mr-0.5"></Line3>
</div>
)}
{isShowAPart && (
<div className="flex items-center">
<RiMoreLine className="h-3 w-3 text-text-secondary" />
<Line3 className="mr-0.5 text-divider-deep"></Line3>
</div>
)}
<div className="flex items-center text-text-accent">
{isLoading && <RiLoader4Line className="h-3.5 w-3.5 animate-spin text-text-secondary" />}
<VariableIconWithColor
variables={value as ValueSelector}
variableCategory={variableCategory}
isExceptionVariable={isException}
/>
<div
className={cn('ml-0.5 truncate text-xs font-medium', isEnv && '!text-text-secondary', isChatVar && 'text-util-colors-teal-teal-700', isException && 'text-text-warning', isGlobal && 'text-util-colors-orange-orange-600')}
title={varName}
className="system-xs-regular ml-0.5 truncate text-center capitalize text-text-tertiary"
title={type}
style={{
maxWidth: maxVarNameWidth,
maxWidth: maxTypeWidth,
}}
>
{varName}
{type}
</div>
{!isValidVar && <RiErrorWarningFill className="ml-0.5 h-3 w-3 text-text-destructive" />}
</>
)
: (
<div className={`overflow-hidden ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'} system-sm-regular text-ellipsis`}>
{isLoading
? (
<div className="flex items-center">
<RiLoader4Line className="mr-1 h-3.5 w-3.5 animate-spin text-text-secondary" />
<span>{placeholder ?? t('common.setVarValuePlaceholder', { ns: 'workflow' })}</span>
</div>
)
: (
placeholder ?? t('common.setVarValuePlaceholder', { ns: 'workflow' })
)}
</div>
<div
className="system-xs-regular ml-0.5 truncate text-center capitalize text-text-tertiary"
title={type}
style={{
maxWidth: maxTypeWidth,
}}
>
{type}
</div>
{!isValidVar && <RiErrorWarningFill className="ml-0.5 h-3 w-3 text-text-destructive" />}
</>
)
: (
<div className={`overflow-hidden ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'} system-sm-regular text-ellipsis`}>
{isLoading
? (
<div className="flex items-center">
<RiLoader4Line className="mr-1 h-3.5 w-3.5 animate-spin text-text-secondary" />
<span>{placeholder ?? t('common.setVarValuePlaceholder', { ns: 'workflow' })}</span>
</div>
)
: (
placeholder ?? t('common.setVarValuePlaceholder', { ns: 'workflow' })
)}
</div>
)}
</div>
</Tooltip>
</div>
)}
</div>
</Tooltip>
</div>
</VarPickerWrap>
)}
{(hasValue && !readonly && !isInTable) && (
<div
className="group invisible absolute right-1 top-[50%] h-5 translate-y-[-50%] cursor-pointer rounded-md p-1 hover:bg-state-base-hover group-hover/wrap:visible"
onClick={handleClearVar}
>
<RiCloseLine className="h-3.5 w-3.5 text-text-tertiary group-hover:text-text-secondary" />
</div>
)}
{!hasValue && valueTypePlaceHolder && (
<Badge
className="absolute right-1 top-[50%] translate-y-[-50%] capitalize"
text={valueTypePlaceHolder}
uppercase={false}
/>
)}
</div>
)}
{!readonly && isInTable && (
<RemoveButton
className="absolute right-1 top-0.5 hidden group-hover/picker-trigger-wrap:block"
onClick={() => onRemove?.()}
/>
)}
</VarPickerWrap>
)}
{(hasValue && !readonly && !isInTable && !isJustShowValue) && (
<div
className="group invisible absolute right-1 top-[50%] h-5 translate-y-[-50%] cursor-pointer rounded-md p-1 hover:bg-state-base-hover group-hover/wrap:visible"
onClick={handleClearVar}
>
<RiCloseLine className="h-3.5 w-3.5 text-text-tertiary group-hover:text-text-secondary" />
</div>
)}
{!hasValue && valueTypePlaceHolder && (
<Badge
className="absolute right-1 top-[50%] translate-y-[-50%] capitalize"
text={valueTypePlaceHolder}
uppercase={false}
/>
)}
</div>
)}
{!readonly && isInTable && (
<RemoveButton
className="absolute right-1 top-0.5 hidden group-hover/picker-trigger-wrap:block"
onClick={() => onRemove?.()}
/>
)}
{!hasValue && typePlaceHolder && (
<Badge
className="absolute right-2 top-1.5"
text={typePlaceHolder}
uppercase={false}
/>
)}
</>
</WrapElem>
{!hasValue && typePlaceHolder && (
<Badge
className="absolute right-2 top-1.5"
text={typePlaceHolder}
uppercase={false}
/>
)}
</>
</WrapElem>
)}
<PortalToFollowElemContent
style={{
zIndex: zIndex || 100,

View File

@ -181,6 +181,7 @@ const Item: FC<ItemProps> = ({
}, [isHovering])
const handleChosen = (e: React.MouseEvent) => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
if (!isSupportFileVar && isFile)
return
@ -219,7 +220,11 @@ const Item: FC<ItemProps> = ({
)}
onClick={handleChosen}
onMouseEnter={onSetHighlight}
onMouseDown={e => e.preventDefault()}
onMouseDown={(e) => {
e.preventDefault()
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
}}
>
<div className="flex w-0 grow items-center">
{!isFlat && (

View File

@ -27,7 +27,7 @@ const VariableLabel = ({
rightSlot,
}: VariablePayload) => {
const varColorClassName = useVarColor(variables, isExceptionVariable)
const isHideNodeLabel = !(isENV(variables) || isConversationVar(variables) || isGlobalVar(variables) || isRagVariableVar(variables))
const isShowNodeLabel = !(isENV(variables) || isConversationVar(variables) || isGlobalVar(variables) || isRagVariableVar(variables))
return (
<div
className={cn(
@ -37,7 +37,7 @@ const VariableLabel = ({
onClick={onClick}
ref={ref}
>
{ isHideNodeLabel && (
{isShowNodeLabel && (
<VariableNodeLabel
nodeType={nodeType}
nodeTitle={nodeTitle}

View File

@ -370,7 +370,7 @@ const BasePanel: FC<BasePanelProps> = ({
const currentDataSource = useMemo(() => {
if (data.type === BlockEnum.DataSource && data.provider_type !== DataSourceClassification.localFile)
return dataSourceList?.find(item => item.plugin_id === data.plugin_id)
}, [dataSourceList, data.provider_id, data.type, data.provider_type])
}, [data.type, data.provider_type, data.plugin_id, dataSourceList])
const handleAuthorizationItemClick = useCallback((credential_id: string) => {
handleNodeDataUpdateWithSyncDraft({
@ -491,6 +491,7 @@ const BasePanel: FC<BasePanelProps> = ({
{...passedLogParams}
existVarValuesInForms={getExistVarValuesInForms(singleRunParams?.forms as any)}
filteredExistVarForms={getFilteredExistVarForms(singleRunParams?.forms as any)}
handleAfterHumanInputStepRun={handleAfterCustomSingleRun}
/>
)}

View File

@ -21,6 +21,7 @@ import useVariableAssignerSingleRunFormParams from '@/app/components/workflow/no
import useCodeSingleRunFormParams from '@/app/components/workflow/nodes/code/use-single-run-form-params'
import useDocExtractorSingleRunFormParams from '@/app/components/workflow/nodes/document-extractor/use-single-run-form-params'
import useHttpRequestSingleRunFormParams from '@/app/components/workflow/nodes/http/use-single-run-form-params'
import useHumanInputSingleRunFormParams from '@/app/components/workflow/nodes/human-input/hooks/use-single-run-form-params'
import useIfElseSingleRunFormParams from '@/app/components/workflow/nodes/if-else/use-single-run-form-params'
import useIterationSingleRunFormParams from '@/app/components/workflow/nodes/iteration/use-single-run-form-params'
import useKnowledgeBaseSingleRunFormParams from '@/app/components/workflow/nodes/knowledge-base/use-single-run-form-params'
@ -28,15 +29,16 @@ import useKnowledgeRetrievalSingleRunFormParams from '@/app/components/workflow/
import useLLMSingleRunFormParams from '@/app/components/workflow/nodes/llm/use-single-run-form-params'
import useLoopSingleRunFormParams from '@/app/components/workflow/nodes/loop/use-single-run-form-params'
import useParameterExtractorSingleRunFormParams from '@/app/components/workflow/nodes/parameter-extractor/use-single-run-form-params'
import useQuestionClassifierSingleRunFormParams from '@/app/components/workflow/nodes/question-classifier/use-single-run-form-params'
import useQuestionClassifierSingleRunFormParams from '@/app/components/workflow/nodes/question-classifier/use-single-run-form-params'
import useStartSingleRunFormParams from '@/app/components/workflow/nodes/start/use-single-run-form-params'
import useTemplateTransformSingleRunFormParams from '@/app/components/workflow/nodes/template-transform/use-single-run-form-params'
import useToolGetDataForCheckMore from '@/app/components/workflow/nodes/tool/use-get-data-for-check-more'
import useToolGetDataForCheckMore from '@/app/components/workflow/nodes/tool/use-get-data-for-check-more'
import useToolSingleRunFormParams from '@/app/components/workflow/nodes/tool/use-single-run-form-params'
import useTriggerPluginGetDataForCheckMore from '@/app/components/workflow/nodes/trigger-plugin/use-check-params'
import useVariableAggregatorSingleRunFormParams from '@/app/components/workflow/nodes/variable-assigner/use-single-run-form-params'
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
import { BlockEnum, isPromptMessageContext } from '@/app/components/workflow/types'
import { isSupportCustomRunForm } from '@/app/components/workflow/utils'
@ -71,6 +73,7 @@ const singleRunFormParamsHooks: Record<BlockEnum, any> = {
[BlockEnum.IterationStart]: undefined,
[BlockEnum.LoopStart]: undefined,
[BlockEnum.LoopEnd]: undefined,
[BlockEnum.HumanInput]: useHumanInputSingleRunFormParams,
[BlockEnum.DataSource]: undefined,
[BlockEnum.DataSourceEmpty]: undefined,
[BlockEnum.TriggerWebhook]: undefined,
@ -109,6 +112,7 @@ const getDataForCheckMoreHooks: Record<BlockEnum, any> = {
[BlockEnum.Assigner]: undefined,
[BlockEnum.LoopStart]: undefined,
[BlockEnum.LoopEnd]: undefined,
[BlockEnum.HumanInput]: undefined,
[BlockEnum.DataSource]: undefined,
[BlockEnum.DataSourceEmpty]: undefined,
[BlockEnum.KnowledgeBase]: undefined,
@ -153,6 +157,7 @@ const useLastRun = <T>({
const isLoopNode = blockType === BlockEnum.Loop
const isAggregatorNode = blockType === BlockEnum.VariableAggregator
const isCustomRunNode = isSupportCustomRunForm(blockType)
const isHumanInputNode = blockType === BlockEnum.HumanInput
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
const reactFlowStore = useStoreApi()
const {
@ -444,7 +449,7 @@ const useLastRun = <T>({
}
if (blockType === BlockEnum.TriggerWebhook || blockType === BlockEnum.TriggerPlugin || blockType === BlockEnum.TriggerSchedule)
setShowVariableInspectPanel(true)
if (isCustomRunNode) {
if (isCustomRunNode || isHumanInputNode) {
showSingleRun()
return
}

View File

@ -25,6 +25,7 @@ import Assigner from '@/app/components/workflow/nodes/assigner/default'
import CodeDefault from '@/app/components/workflow/nodes/code/default'
import DocumentExtractorDefault from '@/app/components/workflow/nodes/document-extractor/default'
import HTTPDefault from '@/app/components/workflow/nodes/http/default'
import HumanInputDefault from '@/app/components/workflow/nodes/human-input/default'
import IfElseDefault from '@/app/components/workflow/nodes/if-else/default'
import IterationDefault from '@/app/components/workflow/nodes/iteration/default'
import KnowledgeRetrievalDefault from '@/app/components/workflow/nodes/knowledge-retrieval/default'
@ -70,6 +71,7 @@ const { checkValid: checkParameterExtractorValid } = ParameterExtractorDefault
const { checkValid: checkIterationValid } = IterationDefault
const { checkValid: checkDocumentExtractorValid } = DocumentExtractorDefault
const { checkValid: checkLoopValid } = LoopDefault
const { checkValid: checkHumanInputValid } = HumanInputDefault
// eslint-disable-next-line ts/no-unsafe-function-type
const checkValidFns: Partial<Record<BlockEnum, Function>> = {
@ -87,6 +89,7 @@ const checkValidFns: Partial<Record<BlockEnum, Function>> = {
[BlockEnum.Iteration]: checkIterationValid,
[BlockEnum.DocExtractor]: checkDocumentExtractorValid,
[BlockEnum.Loop]: checkLoopValid,
[BlockEnum.HumanInput]: checkHumanInputValid,
}
type RequestError = {
@ -329,20 +332,7 @@ const useOneStepRun = <T>({
invalidateSysVarValues()
invalidateConversationVarValues() // loop, iteration, variable assigner node can update the conversation variables, but to simple the logic(some nodes may also can update in the future), all nodes refresh.
}
}, [
isRunAfterSingleRun,
runningStatus,
flowId,
id,
store,
appendNodeInspectVars,
updateNodeInspectRunningState,
invalidLastRun,
isStartNode,
isTriggerNode,
invalidateSysVarValues,
invalidateConversationVarValues,
])
}, [isRunAfterSingleRun, runningStatus, flowType, flowId, id, store, appendNodeInspectVars, updateNodeInspectRunningState, invalidLastRun, isStartNode, isTriggerNode, invalidateSysVarValues, invalidateConversationVarValues])
const { handleNodeDataUpdate }: { handleNodeDataUpdate: (data: any) => void } = useNodeDataUpdate()
const setNodeRunning = () => {

View File

@ -9,6 +9,7 @@ import {
RiCheckboxCircleFill,
RiErrorWarningFill,
RiLoader2Line,
RiPauseCircleFill,
} from '@remixicon/react'
import {
cloneElement,
@ -148,7 +149,7 @@ const BaseNode: FC<BaseNodeProps> = ({
showExceptionBorder,
} = useMemo(() => {
return {
showRunningBorder: data._runningStatus === NodeRunningStatus.Running && !showSelectedBorder,
showRunningBorder: (data._runningStatus === NodeRunningStatus.Running || data._runningStatus === NodeRunningStatus.Paused) && !showSelectedBorder,
showSuccessBorder: (data._runningStatus === NodeRunningStatus.Succeeded || hasVarValue) && !showSelectedBorder,
showFailedBorder: data._runningStatus === NodeRunningStatus.Failed && !showSelectedBorder,
showExceptionBorder: data._runningStatus === NodeRunningStatus.Exception && !showSelectedBorder,
@ -306,7 +307,7 @@ const BaseNode: FC<BaseNodeProps> = ({
)
}
{
data.type !== BlockEnum.IfElse && data.type !== BlockEnum.QuestionClassifier && data.type !== BlockEnum.Group && !data._isCandidate && (
data.type !== BlockEnum.IfElse && data.type !== BlockEnum.QuestionClassifier && data.type !== BlockEnum.Group && data.type !== BlockEnum.HumanInput && !data._isCandidate && (
<NodeSourceHandle
id={id}
data={data}
@ -382,15 +383,27 @@ const BaseNode: FC<BaseNodeProps> = ({
!!(data.type === BlockEnum.Loop && data._loopIndex) && LoopIndex
}
{
isLoading
? <RiLoader2Line className="h-3.5 w-3.5 animate-spin text-text-accent" />
: data._runningStatus === NodeRunningStatus.Failed
? <RiErrorWarningFill className="h-3.5 w-3.5 text-text-destructive" />
: data._runningStatus === NodeRunningStatus.Exception
? <RiAlertFill className="h-3.5 w-3.5 text-text-warning-secondary" />
: (data._runningStatus === NodeRunningStatus.Succeeded || hasVarValue)
? <RiCheckboxCircleFill className="h-3.5 w-3.5 text-text-success" />
: null
isLoading && <RiLoader2Line className="h-3.5 w-3.5 animate-spin text-text-accent" />
}
{
!isLoading && data._runningStatus === NodeRunningStatus.Failed && (
<RiErrorWarningFill className="h-3.5 w-3.5 text-text-destructive" />
)
}
{
!isLoading && data._runningStatus === NodeRunningStatus.Exception && (
<RiAlertFill className="h-3.5 w-3.5 text-text-warning-secondary" />
)
}
{
!isLoading && (data._runningStatus === NodeRunningStatus.Succeeded || hasVarValue) && (
<RiCheckboxCircleFill className="h-3.5 w-3.5 text-text-success" />
)
}
{
!isLoading && data._runningStatus === NodeRunningStatus.Paused && (
<RiPauseCircleFill className="h-3.5 w-3.5 text-text-warning-secondary" />
)
}
</div>
{