mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 01:18:05 +08:00
chore(web): pre-align HITL frontend from build/feat/hitl
This commit is contained in:
@ -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>
|
||||
|
||||
@ -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>
|
||||
)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 && (
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 = () => {
|
||||
|
||||
@ -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>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user