mirror of https://github.com/langgenius/dify.git
feat: enhance chat component to manage multiple human input forms and their submissions
This commit is contained in:
parent
d0a713e117
commit
5a92bbdab5
|
|
@ -0,0 +1,62 @@
|
|||
import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import BlockIcon from '@/app/components/workflow/block-icon'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type ContentWrapperProps = {
|
||||
nodeTitle: string
|
||||
children: React.ReactNode
|
||||
showExpandIcon?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
const ContentWrapper = ({
|
||||
nodeTitle,
|
||||
children,
|
||||
showExpandIcon = false,
|
||||
className,
|
||||
}: ContentWrapperProps) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
|
||||
const handleToggleExpand = useCallback(() => {
|
||||
setIsExpanded(!isExpanded)
|
||||
}, [isExpanded])
|
||||
|
||||
return (
|
||||
<div className={cn('rounded-2xl border-[0.5px] border-components-panel-border bg-background-section p-2 shadow-md', className)}>
|
||||
<div className="flex items-center gap-2 p-2">
|
||||
{/* node icon */}
|
||||
<BlockIcon type={BlockEnum.HumanInput} className="shrink-0" />
|
||||
{/* node name */}
|
||||
<div
|
||||
className="system-sm-semibold-uppercase grow truncate text-text-primary"
|
||||
title={nodeTitle}
|
||||
>
|
||||
{nodeTitle}
|
||||
</div>
|
||||
{showExpandIcon && (
|
||||
<div className="shrink-0 cursor-pointer" onClick={handleToggleExpand}>
|
||||
{
|
||||
isExpanded
|
||||
? (
|
||||
<RiArrowDownSLine className="size-4" />
|
||||
)
|
||||
: (
|
||||
<RiArrowRightSLine className="size-4" />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{(!showExpandIcon || isExpanded) && (
|
||||
<div className="px-2 py-1">
|
||||
{/* human input form content */}
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ContentWrapper
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import type { ExecutedAction as ExecutedActionType } from './type'
|
||||
import { memo } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow'
|
||||
|
||||
type ExecutedActionProps = {
|
||||
executedAction: ExecutedActionType
|
||||
}
|
||||
|
||||
const ExecutedAction = ({
|
||||
executedAction,
|
||||
}: ExecutedActionProps) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-y-1 py-1">
|
||||
<Divider className="mb-2 mt-1 w-[30px]" />
|
||||
<div className="system-xs-regular flex items-center gap-x-1 text-text-tertiary">
|
||||
<TriggerAll className="size-3.5 shrink-0" />
|
||||
<Trans
|
||||
i18nKey="nodes.humanInput.userActions.triggered"
|
||||
ns="workflow"
|
||||
components={{ strong: <span className="system-xs-medium text-text-secondary"></span> }}
|
||||
values={{ actionName: executedAction.title }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(ExecutedAction)
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
import type { HumanInputContentProps } from './type'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow'
|
||||
import { useSelector as useAppSelector } from '@/context/app-context'
|
||||
import ExpirationTime from './expiration-time'
|
||||
import HumanInputForm from './human-input-form'
|
||||
|
||||
const HumanInputContent = ({
|
||||
formData,
|
||||
showEmailTip = false,
|
||||
isEmailDebugMode = false,
|
||||
showDebugModeTip = false,
|
||||
showTimeout = false,
|
||||
executedAction,
|
||||
expirationTime,
|
||||
onSubmit,
|
||||
}: HumanInputContentProps) => {
|
||||
const { t } = useTranslation()
|
||||
const email = useAppSelector(s => s.userProfile.email)
|
||||
|
||||
return (
|
||||
<>
|
||||
<HumanInputForm
|
||||
formData={formData}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
{/* Tips */}
|
||||
{(showEmailTip || showDebugModeTip) && (
|
||||
<>
|
||||
<Divider className="!my-2 w-[30px]" />
|
||||
<div className="space-y-1 pt-1">
|
||||
{showEmailTip && !isEmailDebugMode && (
|
||||
<div className="system-xs-regular text-text-secondary">{t('common.humanInputEmailTip', { ns: 'workflow' })}</div>
|
||||
)}
|
||||
{showEmailTip && isEmailDebugMode && (
|
||||
<div className="system-xs-regular text-text-secondary">
|
||||
<Trans
|
||||
i18nKey="common.humanInputEmailTipInDebugMode"
|
||||
ns="workflow"
|
||||
components={{ email: <span className="system-xs-semibold"></span> }}
|
||||
values={{ email }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{showDebugModeTip && <div className="system-xs-medium text-text-warning">{t('common.humanInputWebappTip', { ns: 'workflow' })}</div>}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{/* Timeout */}
|
||||
{showTimeout && typeof expirationTime === 'number' && (
|
||||
<ExpirationTime expirationTime={expirationTime} />
|
||||
)}
|
||||
{/* Executed Action */}
|
||||
{executedAction && (
|
||||
<div className="flex flex-col gap-y-1 py-1">
|
||||
<Divider className="mb-2 mt-1 w-[30px]" />
|
||||
<div className="system-xs-regular flex items-center gap-x-1 text-text-tertiary">
|
||||
<TriggerAll className="size-3.5 shrink-0" />
|
||||
<Trans
|
||||
i18nKey="nodes.humanInput.userActions.triggered"
|
||||
ns="workflow"
|
||||
components={{ strong: <span className="system-xs-medium text-text-secondary"></span> }}
|
||||
values={{ actionName: executedAction.title }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default HumanInputContent
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import * as React from 'react'
|
||||
import { Markdown } from '@/app/components/base/markdown'
|
||||
|
||||
type SubmittedContentProps = {
|
||||
content: string
|
||||
}
|
||||
|
||||
const SubmittedContent = ({
|
||||
content,
|
||||
}: SubmittedContentProps) => {
|
||||
return (
|
||||
<Markdown content={content} />
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(SubmittedContent)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import type { SubmittedHumanInputContentProps } from './type'
|
||||
import { useMemo } from 'react'
|
||||
import ExecutedAction from './executed-action'
|
||||
import SubmittedContent from './submitted-content'
|
||||
|
||||
export const SubmittedHumanInputContent = ({
|
||||
formData,
|
||||
}: SubmittedHumanInputContentProps) => {
|
||||
const { rendered_content, action_id, action_text } = formData
|
||||
|
||||
const executedAction = useMemo(() => {
|
||||
return {
|
||||
id: action_id,
|
||||
title: action_text,
|
||||
}
|
||||
}, [action_id, action_text])
|
||||
|
||||
return (
|
||||
<>
|
||||
<SubmittedContent content={rendered_content} />
|
||||
{/* Executed Action */}
|
||||
<ExecutedAction executedAction={executedAction} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import { memo } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { useSelector as useAppSelector } from '@/context/app-context'
|
||||
|
||||
type TipsProps = {
|
||||
showEmailTip: boolean
|
||||
isEmailDebugMode: boolean
|
||||
showDebugModeTip: boolean
|
||||
}
|
||||
|
||||
const Tips = ({
|
||||
showEmailTip,
|
||||
isEmailDebugMode,
|
||||
showDebugModeTip,
|
||||
}: TipsProps) => {
|
||||
const { t } = useTranslation()
|
||||
const email = useAppSelector(s => s.userProfile.email)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Divider className="!my-2 w-[30px]" />
|
||||
<div className="space-y-1 pt-1">
|
||||
{showEmailTip && !isEmailDebugMode && (
|
||||
<div className="system-xs-regular text-text-secondary">{t('common.humanInputEmailTip', { ns: 'workflow' })}</div>
|
||||
)}
|
||||
{showEmailTip && isEmailDebugMode && (
|
||||
<div className="system-xs-regular text-text-secondary">
|
||||
<Trans
|
||||
i18nKey="common.humanInputEmailTipInDebugMode"
|
||||
ns="workflow"
|
||||
components={{ email: <span className="system-xs-semibold"></span> }}
|
||||
values={{ email }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{showDebugModeTip && <div className="system-xs-medium text-text-warning">{t('common.humanInputWebappTip', { ns: 'workflow' })}</div>}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(Tips)
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
import type { FormInputItem } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import type { HumanInputFormData } from '@/types/workflow'
|
||||
import type { HumanInputFilledFormData, HumanInputFormData } from '@/types/workflow'
|
||||
|
||||
export type ExecutedAction = {
|
||||
id: string
|
||||
title: string
|
||||
}
|
||||
|
||||
export type HumanInputContentProps = {
|
||||
export type UnsubmittedHumanInputContentProps = {
|
||||
formData: HumanInputFormData
|
||||
executedAction?: ExecutedAction
|
||||
showEmailTip?: boolean
|
||||
isEmailDebugMode?: boolean
|
||||
showDebugModeTip?: boolean
|
||||
|
|
@ -17,6 +16,10 @@ export type HumanInputContentProps = {
|
|||
onSubmit?: (formID: string, data: any) => Promise<void>
|
||||
}
|
||||
|
||||
export type SubmittedHumanInputContentProps = {
|
||||
formData: HumanInputFilledFormData
|
||||
}
|
||||
|
||||
export type HumanInputFormProps = {
|
||||
formData: HumanInputFormData
|
||||
onSubmit?: (formID: string, data: any) => Promise<void>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
import type { UnsubmittedHumanInputContentProps } from './type'
|
||||
import ExpirationTime from './expiration-time'
|
||||
import HumanInputForm from './human-input-form'
|
||||
import Tips from './tips'
|
||||
|
||||
export const UnsubmittedHumanInputContent = ({
|
||||
formData,
|
||||
showEmailTip = false,
|
||||
isEmailDebugMode = false,
|
||||
showDebugModeTip = false,
|
||||
showTimeout = false,
|
||||
expirationTime,
|
||||
onSubmit,
|
||||
}: UnsubmittedHumanInputContentProps) => {
|
||||
return (
|
||||
<>
|
||||
{/* Form */}
|
||||
<HumanInputForm
|
||||
formData={formData}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
{/* Tips */}
|
||||
{(showEmailTip || showDebugModeTip) && (
|
||||
<Tips
|
||||
showEmailTip={showEmailTip}
|
||||
isEmailDebugMode={isEmailDebugMode}
|
||||
showDebugModeTip={showDebugModeTip}
|
||||
/>
|
||||
)}
|
||||
{/* Timeout */}
|
||||
{showTimeout && typeof expirationTime === 'number' && (
|
||||
<ExpirationTime expirationTime={expirationTime} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import type { HumanInputFilledFormData } from '@/types/workflow'
|
||||
import ContentWrapper from './human-input-content/content-wrapper'
|
||||
import { SubmittedHumanInputContent } from './human-input-content/submitted'
|
||||
|
||||
type HumanInputFilledFormListProps = {
|
||||
humanInputFilledFormDataList: HumanInputFilledFormData[]
|
||||
}
|
||||
|
||||
const HumanInputFilledFormList = ({
|
||||
humanInputFilledFormDataList,
|
||||
}: HumanInputFilledFormListProps) => {
|
||||
return (
|
||||
<div className="mt-2">
|
||||
{
|
||||
humanInputFilledFormDataList.map(formData => (
|
||||
<ContentWrapper
|
||||
key={formData.node_id}
|
||||
nodeTitle="todo: replace with node title"
|
||||
showExpandIcon
|
||||
className="mb-2 last:mb-0"
|
||||
>
|
||||
<SubmittedHumanInputContent
|
||||
key={formData.node_id}
|
||||
formData={formData}
|
||||
/>
|
||||
</ContentWrapper>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HumanInputFilledFormList
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import type { DeliveryMethod } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import type { HumanInputFormData } from '@/types/workflow'
|
||||
import { useMemo } from 'react'
|
||||
import { DeliveryMethodType } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import ContentWrapper from './human-input-content/content-wrapper'
|
||||
import { UnsubmittedHumanInputContent } from './human-input-content/unsubmitted'
|
||||
|
||||
type HumanInputFormListProps = {
|
||||
humanInputFormDataList: HumanInputFormData[]
|
||||
onHumanInputFormSubmit?: (formID: string, formData: any) => Promise<void>
|
||||
getHumanInputNodeData?: (nodeID: string) => any
|
||||
}
|
||||
|
||||
const HumanInputFormList = ({
|
||||
humanInputFormDataList,
|
||||
onHumanInputFormSubmit,
|
||||
getHumanInputNodeData,
|
||||
}: HumanInputFormListProps) => {
|
||||
const deliveryMethodsConfig = useMemo((): Record<string, { showEmailTip: boolean, isEmailDebugMode: boolean, showDebugModeTip: boolean }> => {
|
||||
if (!humanInputFormDataList.length)
|
||||
return {}
|
||||
return humanInputFormDataList.reduce((acc, formData) => {
|
||||
const deliveryMethodsConfig = getHumanInputNodeData?.(formData.node_id)?.data.delivery_methods || []
|
||||
if (!deliveryMethodsConfig.length) {
|
||||
acc[formData.node_id] = {
|
||||
showEmailTip: false,
|
||||
isEmailDebugMode: false,
|
||||
showDebugModeTip: false,
|
||||
}
|
||||
return acc
|
||||
}
|
||||
const isWebappEnabled = deliveryMethodsConfig.some((method: DeliveryMethod) => method.type === DeliveryMethodType.WebApp && method.enabled)
|
||||
const isEmailEnabled = deliveryMethodsConfig.some((method: DeliveryMethod) => method.type === DeliveryMethodType.Email && method.enabled)
|
||||
const isEmailDebugMode = deliveryMethodsConfig.some((method: DeliveryMethod) => method.type === DeliveryMethodType.Email && method.config?.debug_mode)
|
||||
acc[formData.node_id] = {
|
||||
showEmailTip: isEmailEnabled,
|
||||
isEmailDebugMode,
|
||||
showDebugModeTip: !isWebappEnabled,
|
||||
}
|
||||
return acc
|
||||
}, {} as Record<string, { showEmailTip: boolean, isEmailDebugMode: boolean, showDebugModeTip: boolean }>)
|
||||
}, [getHumanInputNodeData, humanInputFormDataList])
|
||||
|
||||
return (
|
||||
<div className="mt-2">
|
||||
{
|
||||
humanInputFormDataList.map(formData => (
|
||||
<ContentWrapper
|
||||
key={formData.node_id}
|
||||
nodeTitle={formData.node_title}
|
||||
className="mb-2 last:mb-0"
|
||||
>
|
||||
<UnsubmittedHumanInputContent
|
||||
key={formData.node_id}
|
||||
formData={formData}
|
||||
showEmailTip={!!deliveryMethodsConfig[formData.node_id]?.showEmailTip}
|
||||
isEmailDebugMode={!!deliveryMethodsConfig[formData.node_id]?.isEmailDebugMode}
|
||||
showDebugModeTip={!!deliveryMethodsConfig[formData.node_id]?.showDebugModeTip}
|
||||
onSubmit={onHumanInputFormSubmit}
|
||||
/>
|
||||
</ContentWrapper>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HumanInputFormList
|
||||
|
|
@ -6,24 +6,21 @@ import type {
|
|||
ChatConfig,
|
||||
ChatItem,
|
||||
} from '../../types'
|
||||
import type { ExecutedAction } from './human-input-content/type'
|
||||
import type { DeliveryMethod } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import type { AppData } from '@/models/share'
|
||||
import type { HumanInputFormData } from '@/types/workflow'
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { memo, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
|
||||
import AnswerIcon from '@/app/components/base/answer-icon'
|
||||
import Citation from '@/app/components/base/chat/chat/citation'
|
||||
import LoadingAnim from '@/app/components/base/chat/chat/loading-anim'
|
||||
import { FileList } from '@/app/components/base/file-uploader'
|
||||
import { DeliveryMethodType } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import ContentSwitch from '../content-switch'
|
||||
import { useChatContext } from '../context'
|
||||
import AgentContent from './agent-content'
|
||||
import BasicContent from './basic-content'
|
||||
import HumanInputContent from './human-input-content'
|
||||
import HumanInputFilledFormList from './human-input-filled-form-list'
|
||||
import HumanInputFormList from './human-input-form-list'
|
||||
import More from './more'
|
||||
import Operation from './operation'
|
||||
import SuggestedQuestions from './suggested-questions'
|
||||
|
|
@ -71,8 +68,8 @@ const Answer: FC<AnswerProps> = ({
|
|||
workflowProcess,
|
||||
allFiles,
|
||||
message_files,
|
||||
humanInputFormData,
|
||||
humanInputFormFilledData,
|
||||
humanInputFormDataList,
|
||||
humanInputFilledFormDataList,
|
||||
} = item
|
||||
const hasAgentThoughts = !!agent_thoughts?.length
|
||||
|
||||
|
|
@ -85,49 +82,6 @@ const Answer: FC<AnswerProps> = ({
|
|||
getHumanInputNodeData,
|
||||
} = useChatContext()
|
||||
|
||||
const deliveryMethodsConfig = useMemo(() => {
|
||||
const deliveryMethodsConfig = getHumanInputNodeData?.(humanInputFormData?.node_id as any)?.data.delivery_methods || []
|
||||
if (!deliveryMethodsConfig.length) {
|
||||
return {
|
||||
showEmailTip: false,
|
||||
isEmailDebugMode: false,
|
||||
showDebugModeTip: false,
|
||||
}
|
||||
}
|
||||
const isWebappEnabled = deliveryMethodsConfig.some((method: DeliveryMethod) => method.type === DeliveryMethodType.WebApp && method.enabled)
|
||||
const isEmailEnabled = deliveryMethodsConfig.some((method: DeliveryMethod) => method.type === DeliveryMethodType.Email && method.enabled)
|
||||
const isEmailDebugMode = deliveryMethodsConfig.some((method: DeliveryMethod) => method.type === DeliveryMethodType.Email && method.config?.debug_mode)
|
||||
return {
|
||||
showEmailTip: isEmailEnabled,
|
||||
isEmailDebugMode,
|
||||
showDebugModeTip: !isWebappEnabled,
|
||||
}
|
||||
}, [getHumanInputNodeData, humanInputFormData?.node_id])
|
||||
|
||||
const filledFormData = useMemo((): HumanInputFormData | undefined => {
|
||||
if (!humanInputFormFilledData)
|
||||
return
|
||||
return {
|
||||
form_id: '',
|
||||
node_id: humanInputFormFilledData.node_id,
|
||||
node_title: '',
|
||||
form_content: humanInputFormFilledData.rendered_content,
|
||||
inputs: [],
|
||||
actions: [],
|
||||
web_app_form_token: '',
|
||||
resolved_placeholder_values: {},
|
||||
}
|
||||
}, [humanInputFormFilledData])
|
||||
|
||||
const executedAction = useMemo((): ExecutedAction | undefined => {
|
||||
if (!humanInputFormFilledData)
|
||||
return
|
||||
return {
|
||||
id: humanInputFormFilledData.action_id,
|
||||
title: humanInputFormFilledData.action_text,
|
||||
}
|
||||
}, [humanInputFormFilledData])
|
||||
|
||||
const getContainerWidth = () => {
|
||||
if (containerRef.current)
|
||||
setContainerWidth(containerRef.current?.clientWidth + 16)
|
||||
|
|
@ -228,21 +182,18 @@ const Answer: FC<AnswerProps> = ({
|
|||
)
|
||||
}
|
||||
{
|
||||
humanInputFormData && (
|
||||
<HumanInputContent
|
||||
formData={humanInputFormData}
|
||||
showEmailTip={deliveryMethodsConfig.showEmailTip}
|
||||
isEmailDebugMode={deliveryMethodsConfig.isEmailDebugMode}
|
||||
showDebugModeTip={deliveryMethodsConfig.showDebugModeTip}
|
||||
onSubmit={onHumanInputFormSubmit}
|
||||
humanInputFormDataList && humanInputFormDataList.length > 0 && (
|
||||
<HumanInputFormList
|
||||
humanInputFormDataList={humanInputFormDataList}
|
||||
onHumanInputFormSubmit={onHumanInputFormSubmit}
|
||||
getHumanInputNodeData={getHumanInputNodeData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
filledFormData && (
|
||||
<HumanInputContent
|
||||
formData={filledFormData}
|
||||
executedAction={executedAction}
|
||||
humanInputFilledFormDataList && humanInputFilledFormDataList.length > 0 && (
|
||||
<HumanInputFilledFormList
|
||||
humanInputFilledFormDataList={humanInputFilledFormDataList}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ const Operation: FC<OperationProps> = ({
|
|||
feedback,
|
||||
adminFeedback,
|
||||
agent_thoughts,
|
||||
humanInputFormData,
|
||||
humanInputFormDataList,
|
||||
} = item
|
||||
const [userLocalFeedback, setUserLocalFeedback] = useState(feedback)
|
||||
const [adminLocalFeedback, setAdminLocalFeedback] = useState(adminFeedback)
|
||||
|
|
@ -304,7 +304,7 @@ const Operation: FC<OperationProps> = ({
|
|||
<Log logItem={item} />
|
||||
</div>
|
||||
)}
|
||||
{!isOpeningStatement && !humanInputFormData && (
|
||||
{!isOpeningStatement && !humanInputFormDataList?.length && (
|
||||
<div className="ml-1 hidden items-center gap-0.5 rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-md backdrop-blur-sm group-hover:flex">
|
||||
{(config?.text_to_speech?.enabled) && (
|
||||
<NewAudioButton
|
||||
|
|
|
|||
|
|
@ -652,7 +652,18 @@ export const useChat = (
|
|||
})
|
||||
},
|
||||
onHumanInputRequired: ({ data: humanInputRequiredData }) => {
|
||||
responseItem.humanInputFormData = humanInputRequiredData
|
||||
if (!responseItem.humanInputFormDataList) {
|
||||
responseItem.humanInputFormDataList = [humanInputRequiredData]
|
||||
}
|
||||
else {
|
||||
const currentFormIndex = responseItem.humanInputFormDataList!.findIndex(item => item.node_id === humanInputRequiredData.node_id)
|
||||
if (currentFormIndex > -1) {
|
||||
responseItem.humanInputFormDataList[currentFormIndex] = humanInputRequiredData
|
||||
}
|
||||
else {
|
||||
responseItem.humanInputFormDataList.push(humanInputRequiredData)
|
||||
}
|
||||
}
|
||||
const currentTracingIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.node_id === humanInputRequiredData.node_id)
|
||||
if (currentTracingIndex > -1) {
|
||||
responseItem.workflowProcess!.tracing[currentTracingIndex].status = NodeRunningStatus.Paused
|
||||
|
|
@ -664,9 +675,17 @@ export const useChat = (
|
|||
})
|
||||
}
|
||||
},
|
||||
onHumanInputFormFilled: ({ data: humanInputFormFilledData }) => {
|
||||
delete responseItem.humanInputFormData
|
||||
responseItem.humanInputFormFilledData = humanInputFormFilledData
|
||||
onHumanInputFormFilled: ({ data: humanInputFilledFormData }) => {
|
||||
if (responseItem.humanInputFormDataList?.length) {
|
||||
const currentFormIndex = responseItem.humanInputFormDataList!.findIndex(item => item.node_id === humanInputFilledFormData.node_id)
|
||||
responseItem.humanInputFormDataList.splice(currentFormIndex, 1)
|
||||
}
|
||||
if (!responseItem.humanInputFilledFormDataList) {
|
||||
responseItem.humanInputFilledFormDataList = [humanInputFilledFormData]
|
||||
}
|
||||
else {
|
||||
responseItem.humanInputFilledFormDataList.push(humanInputFilledFormData)
|
||||
}
|
||||
updateCurrentQAOnTree({
|
||||
placeholderQuestionId,
|
||||
questionItem,
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import type { InputVarType } from '@/app/components/workflow/types'
|
|||
import type { Annotation, MessageRating } from '@/models/log'
|
||||
import type {
|
||||
FileResponse,
|
||||
HumanInputFilledFormData,
|
||||
HumanInputFormData,
|
||||
HumanInputFormFilledData,
|
||||
} from '@/types/workflow'
|
||||
|
||||
export type MessageMore = {
|
||||
|
|
@ -109,8 +109,8 @@ export type IChatItem = {
|
|||
prevSibling?: string
|
||||
nextSibling?: string
|
||||
// for human input
|
||||
humanInputFormData?: HumanInputFormData
|
||||
humanInputFormFilledData?: HumanInputFormFilledData
|
||||
humanInputFormDataList?: HumanInputFormData[]
|
||||
humanInputFilledFormDataList?: HumanInputFilledFormData[]
|
||||
}
|
||||
|
||||
export type Metadata = {
|
||||
|
|
|
|||
|
|
@ -249,6 +249,8 @@ export const useChat = (
|
|||
isAnswer: true,
|
||||
parentMessageId: questionItem.id,
|
||||
siblingIndex: parentMessage?.children?.length ?? chatTree.length,
|
||||
humanInputFormDataList: [],
|
||||
humanInputFilledFormDataList: [],
|
||||
}
|
||||
|
||||
handleResponding(true)
|
||||
|
|
@ -527,7 +529,18 @@ export const useChat = (
|
|||
}
|
||||
},
|
||||
onHumanInputRequired: ({ data }) => {
|
||||
responseItem.humanInputFormData = data
|
||||
if (!responseItem.humanInputFormDataList) {
|
||||
responseItem.humanInputFormDataList = [data]
|
||||
}
|
||||
else {
|
||||
const currentFormIndex = responseItem.humanInputFormDataList!.findIndex(item => item.node_id === data.node_id)
|
||||
if (currentFormIndex > -1) {
|
||||
responseItem.humanInputFormDataList[currentFormIndex] = data
|
||||
}
|
||||
else {
|
||||
responseItem.humanInputFormDataList.push(data)
|
||||
}
|
||||
}
|
||||
const currentTracingIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.node_id === data.node_id)
|
||||
if (currentTracingIndex > -1) {
|
||||
responseItem.workflowProcess!.tracing[currentTracingIndex].status = NodeRunningStatus.Paused
|
||||
|
|
@ -540,8 +553,16 @@ export const useChat = (
|
|||
}
|
||||
},
|
||||
onHumanInputFormFilled: ({ data }) => {
|
||||
delete responseItem.humanInputFormData
|
||||
responseItem.humanInputFormFilledData = data
|
||||
if (responseItem.humanInputFormDataList?.length) {
|
||||
const currentFormIndex = responseItem.humanInputFormDataList!.findIndex(item => item.node_id === data.node_id)
|
||||
responseItem.humanInputFormDataList.splice(currentFormIndex, 1)
|
||||
}
|
||||
if (!responseItem.humanInputFilledFormDataList) {
|
||||
responseItem.humanInputFilledFormDataList = [data]
|
||||
}
|
||||
else {
|
||||
responseItem.humanInputFilledFormDataList.push(data)
|
||||
}
|
||||
updateCurrentQAOnTree({
|
||||
placeholderQuestionId,
|
||||
questionItem,
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ export type HumanInputRequiredResponse = {
|
|||
data: HumanInputFormData
|
||||
}
|
||||
|
||||
export type HumanInputFormFilledData = {
|
||||
export type HumanInputFilledFormData = {
|
||||
node_id: string
|
||||
rendered_content: string
|
||||
action_id: string
|
||||
|
|
@ -342,7 +342,7 @@ export type HumanInputFormFilledResponse = {
|
|||
task_id: string
|
||||
workflow_run_id: string
|
||||
event: string
|
||||
data: HumanInputFormFilledData
|
||||
data: HumanInputFilledFormData
|
||||
}
|
||||
|
||||
export type WorkflowRunHistory = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue