Feat/plugins (#12547)

Co-authored-by: AkaraChen <akarachen@outlook.com>
Co-authored-by: Yi <yxiaoisme@gmail.com>
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: JzoNg <jzongcode@gmail.com>
Co-authored-by: twwu <twwu@dify.ai>
Co-authored-by: kurokobo <kuro664@gmail.com>
Co-authored-by: Hiroshi Fujita <fujita-h@users.noreply.github.com>
This commit is contained in:
zxhlyh
2025-01-09 18:47:41 +08:00
committed by GitHub
parent e4c4490175
commit 3c014f3ae5
719 changed files with 48585 additions and 8553 deletions

View File

@ -8,7 +8,7 @@ import RemoveButton from '@/app/components/workflow/nodes/_base/components/remov
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
type Props = {
interface Props {
isString: boolean
list: any[]
onChange: (list: any[]) => void

View File

@ -18,13 +18,13 @@ import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel
import cn from '@/utils/classnames'
import { checkKeys } from '@/utils/var'
export type ModalPropsType = {
export interface ModalPropsType {
chatVar?: ConversationVariable
onClose: () => void
onSave: (chatVar: ConversationVariable) => void
}
type ObjectValueItem = {
interface ObjectValueItem {
key: string
type: ChatVarType
value: string | number | undefined

View File

@ -22,7 +22,7 @@ import useTimestamp from '@/hooks/use-timestamp'
import { fetchCurrentValueOfConversationVariable } from '@/service/workflow'
import cn from '@/utils/classnames'
export type Props = {
export interface Props {
conversationID: string
onHide: () => void
}

View File

@ -27,7 +27,6 @@ import {
getProcessedFilesFromResponse,
} from '@/app/components/base/file-uploader/utils'
import type { FileEntity } from '@/app/components/base/file-uploader/types'
import type { NodeTracing } from '@/types/workflow'
type GetAbortController = (abortController: AbortController) => void
type SendCallback = {
@ -276,6 +275,7 @@ export const useChat = (
)
setSuggestQuestions(data)
}
// eslint-disable-next-line unused-imports/no-unused-vars
catch (error) {
setSuggestQuestions([])
}
@ -331,8 +331,7 @@ export const useChat = (
responseItem.workflowProcess!.tracing!.push({
...data,
status: NodeRunningStatus.Running,
details: [],
} as any)
})
handleUpdateChatList(produce(chatListRef.current, (draft) => {
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
draft[currentIndex] = {
@ -341,30 +340,21 @@ export const useChat = (
}
}))
},
onIterationNext: ({ data }) => {
const tracing = responseItem.workflowProcess!.tracing!
const iterations = tracing.find(item => item.node_id === data.node_id
&& (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id))!
iterations.details!.push([])
handleUpdateChatList(produce(chatListRef.current, (draft) => {
const currentIndex = draft.length - 1
draft[currentIndex] = responseItem
}))
},
onIterationFinish: ({ data }) => {
const tracing = responseItem.workflowProcess!.tracing!
const iterationsIndex = tracing.findIndex(item => item.node_id === data.node_id
&& (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id))!
tracing[iterationsIndex] = {
...tracing[iterationsIndex],
...data,
status: NodeRunningStatus.Succeeded,
} as any
handleUpdateChatList(produce(chatListRef.current, (draft) => {
const currentIndex = draft.length - 1
draft[currentIndex] = responseItem
}))
const currentTracingIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.id === data.id)
if (currentTracingIndex > -1) {
responseItem.workflowProcess!.tracing[currentTracingIndex] = {
...responseItem.workflowProcess!.tracing[currentTracingIndex],
...data,
}
handleUpdateChatList(produce(chatListRef.current, (draft) => {
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
draft[currentIndex] = {
...draft[currentIndex],
...responseItem,
}
}))
}
},
onNodeStarted: ({ data }) => {
if (data.iteration_id)
@ -386,16 +376,7 @@ export const useChat = (
if (data.iteration_id)
return
const currentIndex = responseItem.workflowProcess!.tracing!.findIndex((item) => {
if (!item.execution_metadata?.parallel_id)
return item.node_id === data.node_id
return item.node_id === data.node_id && (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id)
})
if (responseItem.workflowProcess!.tracing[currentIndex].retryDetail)
responseItem.workflowProcess!.tracing[currentIndex].retryDetail?.push(data as NodeTracing)
else
responseItem.workflowProcess!.tracing[currentIndex].retryDetail = [data as NodeTracing]
responseItem.workflowProcess!.tracing!.push(data)
handleUpdateChatList(produce(chatListRef.current, (draft) => {
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
draft[currentIndex] = {
@ -408,27 +389,77 @@ export const useChat = (
if (data.iteration_id)
return
const currentIndex = responseItem.workflowProcess!.tracing!.findIndex((item) => {
if (!item.execution_metadata?.parallel_id)
return item.node_id === data.node_id
return item.node_id === data.node_id && (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id)
})
responseItem.workflowProcess!.tracing[currentIndex] = {
...(responseItem.workflowProcess!.tracing[currentIndex]?.extras
? { extras: responseItem.workflowProcess!.tracing[currentIndex].extras }
: {}),
...(responseItem.workflowProcess!.tracing[currentIndex]?.retryDetail
? { retryDetail: responseItem.workflowProcess!.tracing[currentIndex].retryDetail }
: {}),
...data,
} as any
handleUpdateChatList(produce(chatListRef.current, (draft) => {
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
draft[currentIndex] = {
...draft[currentIndex],
...responseItem,
const currentTracingIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.id === data.id)
if (currentTracingIndex > -1) {
responseItem.workflowProcess!.tracing[currentTracingIndex] = {
...responseItem.workflowProcess!.tracing[currentTracingIndex],
...data,
}
}))
handleUpdateChatList(produce(chatListRef.current, (draft) => {
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
draft[currentIndex] = {
...draft[currentIndex],
...responseItem,
}
}))
}
},
onAgentLog: ({ data }) => {
const currentNodeIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.node_id === data.node_id)
if (currentNodeIndex > -1) {
const current = responseItem.workflowProcess!.tracing![currentNodeIndex]
if (current.execution_metadata) {
if (current.execution_metadata.agent_log) {
const currentLogIndex = current.execution_metadata.agent_log.findIndex(log => log.id === data.id)
if (currentLogIndex > -1) {
current.execution_metadata.agent_log[currentLogIndex] = {
...current.execution_metadata.agent_log[currentLogIndex],
...data,
}
}
else {
current.execution_metadata.agent_log.push(data)
}
}
else {
current.execution_metadata.agent_log = [data]
}
}
else {
current.execution_metadata = {
agent_log: [data],
} as any
}
// if (current.agentLog) {
// const currentLogIndex = current.agentLog.findIndex(log => log.id === data.id)
// if (currentLogIndex > -1) {
// current.agentLog[currentLogIndex] = {
// ...current.agentLog[currentLogIndex],
// ...data,
// }
// }
// else {
// current.agentLog.push(data)
// }
// }
// else {
// current.agentLog = [data]
// }
responseItem.workflowProcess!.tracing[currentNodeIndex] = {
...current,
}
handleUpdateChatList(produce(chatListRef.current, (draft) => {
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
draft[currentIndex] = {
...draft[currentIndex],
...responseItem,
}
}))
}
},
},
)

View File

@ -12,7 +12,7 @@ import type { EnvironmentVariable } from '@/app/components/workflow/types'
import cn from '@/utils/classnames'
import { checkKeys } from '@/utils/var'
export type ModalPropsType = {
export interface ModalPropsType {
env?: EnvironmentVariable
onClose: () => void
onSave: (env: EnvironmentVariable) => void

View File

@ -25,7 +25,7 @@ import {
} from '@/app/components/base/chat/chat/utils'
import { useCheckInputsForms } from '@/app/components/base/chat/chat/check-input-forms-hooks'
type Props = {
interface Props {
onRun: () => void
}

View File

@ -1,8 +1,6 @@
import {
memo,
useCallback,
useEffect,
// useRef,
useState,
} from 'react'
import {
@ -11,7 +9,6 @@ import {
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import copy from 'copy-to-clipboard'
import { useBoolean } from 'ahooks'
import ResultText from '../run/result-text'
import ResultPanel from '../run/result-panel'
import TracingPanel from '../run/tracing-panel'
@ -24,12 +21,9 @@ import {
} from '../types'
import { SimpleBtn } from '../../app/text-generate/item'
import Toast from '../../base/toast'
import IterationResultPanel from '../run/iteration-result-panel'
import RetryResultPanel from '../run/retry-result-panel'
import InputsPanel from './inputs-panel'
import cn from '@/utils/classnames'
import Loading from '@/app/components/base/loading'
import type { IterationDurationMap, NodeTracing } from '@/types/workflow'
const WorkflowPreview = () => {
const { t } = useTranslation()
@ -53,44 +47,6 @@ const WorkflowPreview = () => {
switchTab('DETAIL')
}, [workflowRunningData])
const [iterationRunResult, setIterationRunResult] = useState<NodeTracing[][]>([])
const [retryRunResult, setRetryRunResult] = useState<NodeTracing[]>([])
const [iterDurationMap, setIterDurationMap] = useState<IterationDurationMap>({})
const [isShowIterationDetail, {
setTrue: doShowIterationDetail,
setFalse: doHideIterationDetail,
}] = useBoolean(false)
const [isShowRetryDetail, {
setTrue: doShowRetryDetail,
setFalse: doHideRetryDetail,
}] = useBoolean(false)
const handleShowIterationDetail = useCallback((detail: NodeTracing[][], iterationDurationMap: IterationDurationMap) => {
setIterDurationMap(iterationDurationMap)
setIterationRunResult(detail)
doShowIterationDetail()
}, [doShowIterationDetail])
const handleRetryDetail = useCallback((detail: NodeTracing[]) => {
setRetryRunResult(detail)
doShowRetryDetail()
}, [doShowRetryDetail])
if (isShowIterationDetail) {
return (
<div className={`
flex flex-col w-[420px] h-full rounded-l-2xl border-[0.5px] border-gray-200 shadow-xl bg-white
`}>
<IterationResultPanel
list={iterationRunResult}
onHide={doHideIterationDetail}
onBack={doHideIterationDetail}
iterDurationMap={iterDurationMap}
/>
</div>
)
}
return (
<div className={`
flex flex-col w-[420px] h-full rounded-l-2xl border-[0.5px] border-gray-200 shadow-xl bg-white
@ -102,141 +58,117 @@ const WorkflowPreview = () => {
</div>
</div>
<div className='grow relative flex flex-col'>
{isShowIterationDetail
? (
<IterationResultPanel
list={iterationRunResult}
onHide={doHideIterationDetail}
onBack={doHideIterationDetail}
iterDurationMap={iterDurationMap}
/>
)
: (
<div className='shrink-0 flex items-center px-4 border-b-[0.5px] border-[rgba(0,0,0,0.05)]'>
{showInputsPanel && (
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'INPUT' && '!border-[rgb(21,94,239)] text-gray-700',
)}
onClick={() => switchTab('INPUT')}
>{t('runLog.input')}</div>
)}
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'RESULT' && '!border-[rgb(21,94,239)] text-gray-700',
!workflowRunningData && 'opacity-30 !cursor-not-allowed',
)}
onClick={() => {
if (!workflowRunningData)
return
switchTab('RESULT')
}}
>{t('runLog.result')}</div>
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'DETAIL' && '!border-[rgb(21,94,239)] text-gray-700',
!workflowRunningData && 'opacity-30 !cursor-not-allowed',
)}
onClick={() => {
if (!workflowRunningData)
return
switchTab('DETAIL')
}}
>{t('runLog.detail')}</div>
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'TRACING' && '!border-[rgb(21,94,239)] text-gray-700',
!workflowRunningData && 'opacity-30 !cursor-not-allowed',
)}
onClick={() => {
if (!workflowRunningData)
return
switchTab('TRACING')
}}
>{t('runLog.tracing')}</div>
</div>
<div className={cn(
'grow bg-components-panel-bg h-0 overflow-y-auto rounded-b-2xl',
(currentTab === 'RESULT' || currentTab === 'TRACING') && '!bg-background-section-burn',
)}>
{currentTab === 'INPUT' && showInputsPanel && (
<InputsPanel onRun={() => switchTab('RESULT')} />
)}
{currentTab === 'RESULT' && (
<>
<div className='shrink-0 flex items-center px-4 border-b-[0.5px] border-[rgba(0,0,0,0.05)]'>
{showInputsPanel && (
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'INPUT' && '!border-[rgb(21,94,239)] text-gray-700',
)}
onClick={() => switchTab('INPUT')}
>{t('runLog.input')}</div>
)}
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'RESULT' && '!border-[rgb(21,94,239)] text-gray-700',
!workflowRunningData && 'opacity-30 !cursor-not-allowed',
)}
<ResultText
isRunning={workflowRunningData?.result?.status === WorkflowRunningStatus.Running || !workflowRunningData?.result}
outputs={workflowRunningData?.resultText}
allFiles={workflowRunningData?.result?.files as any}
error={workflowRunningData?.result?.error}
onClick={() => switchTab('DETAIL')}
/>
{(workflowRunningData?.result.status === WorkflowRunningStatus.Succeeded && workflowRunningData?.resultText && typeof workflowRunningData?.resultText === 'string') && (
<SimpleBtn
className={cn('ml-4 mb-4 inline-flex space-x-1')}
onClick={() => {
if (!workflowRunningData)
return
switchTab('RESULT')
}}
>{t('runLog.result')}</div>
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'DETAIL' && '!border-[rgb(21,94,239)] text-gray-700',
!workflowRunningData && 'opacity-30 !cursor-not-allowed',
)}
onClick={() => {
if (!workflowRunningData)
return
switchTab('DETAIL')
}}
>{t('runLog.detail')}</div>
<div
className={cn(
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
currentTab === 'TRACING' && '!border-[rgb(21,94,239)] text-gray-700',
!workflowRunningData && 'opacity-30 !cursor-not-allowed',
)}
onClick={() => {
if (!workflowRunningData)
return
switchTab('TRACING')
}}
>{t('runLog.tracing')}</div>
</div>
<div className={cn(
'grow bg-components-panel-bg h-0 overflow-y-auto rounded-b-2xl',
(currentTab === 'RESULT' || currentTab === 'TRACING') && '!bg-background-section-burn',
)}>
{currentTab === 'INPUT' && showInputsPanel && (
<InputsPanel onRun={() => switchTab('RESULT')} />
)}
{currentTab === 'RESULT' && (
<>
<ResultText
isRunning={workflowRunningData?.result?.status === WorkflowRunningStatus.Running || !workflowRunningData?.result}
outputs={workflowRunningData?.resultText}
allFiles={workflowRunningData?.result?.files as any}
error={workflowRunningData?.result?.error}
onClick={() => switchTab('DETAIL')}
/>
{(workflowRunningData?.result.status === WorkflowRunningStatus.Succeeded && workflowRunningData?.resultText && typeof workflowRunningData?.resultText === 'string') && (
<SimpleBtn
className={cn('ml-4 mb-4 inline-flex space-x-1')}
onClick={() => {
const content = workflowRunningData?.resultText
if (typeof content === 'string')
copy(content)
else
copy(JSON.stringify(content))
Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') })
}}>
<RiClipboardLine className='w-3.5 h-3.5' />
<div>{t('common.operation.copy')}</div>
</SimpleBtn>
)}
</>
)}
{currentTab === 'DETAIL' && (
<ResultPanel
inputs={workflowRunningData?.result?.inputs}
outputs={workflowRunningData?.result?.outputs}
status={workflowRunningData?.result?.status || ''}
error={workflowRunningData?.result?.error}
elapsed_time={workflowRunningData?.result?.elapsed_time}
total_tokens={workflowRunningData?.result?.total_tokens}
created_at={workflowRunningData?.result?.created_at}
created_by={(workflowRunningData?.result?.created_by as any)?.name}
steps={workflowRunningData?.result?.total_steps}
exceptionCounts={workflowRunningData?.result?.exceptions_count}
/>
)}
{currentTab === 'DETAIL' && !workflowRunningData?.result && (
<div className='flex h-full items-center justify-center bg-components-panel-bg'>
<Loading />
</div>
)}
{currentTab === 'TRACING' && !isShowRetryDetail && (
<TracingPanel
className='bg-background-section-burn'
list={workflowRunningData?.tracing || []}
onShowIterationDetail={handleShowIterationDetail}
onShowRetryDetail={handleRetryDetail}
/>
)}
{currentTab === 'TRACING' && !workflowRunningData?.tracing?.length && (
<div className='flex h-full items-center justify-center !bg-background-section-burn'>
<Loading />
</div>
)}
{
currentTab === 'TRACING' && isShowRetryDetail && (
<RetryResultPanel
list={retryRunResult}
onBack={doHideRetryDetail}
/>
)
}
</div>
const content = workflowRunningData?.resultText
if (typeof content === 'string')
copy(content)
else
copy(JSON.stringify(content))
Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') })
}}>
<RiClipboardLine className='w-3.5 h-3.5' />
<div>{t('common.operation.copy')}</div>
</SimpleBtn>
)}
</>
)}
{currentTab === 'DETAIL' && (
<ResultPanel
inputs={workflowRunningData?.result?.inputs}
outputs={workflowRunningData?.result?.outputs}
status={workflowRunningData?.result?.status || ''}
error={workflowRunningData?.result?.error}
elapsed_time={workflowRunningData?.result?.elapsed_time}
total_tokens={workflowRunningData?.result?.total_tokens}
created_at={workflowRunningData?.result?.created_at}
created_by={(workflowRunningData?.result?.created_by as any)?.name}
steps={workflowRunningData?.result?.total_steps}
exceptionCounts={workflowRunningData?.result?.exceptions_count}
/>
)}
{currentTab === 'DETAIL' && !workflowRunningData?.result && (
<div className='flex h-full items-center justify-center bg-components-panel-bg'>
<Loading />
</div>
)}
{currentTab === 'TRACING' && (
<TracingPanel
className='bg-background-section-burn'
list={workflowRunningData?.tracing || []}
/>
)}
{currentTab === 'TRACING' && !workflowRunningData?.tracing?.length && (
<div className='flex h-full items-center justify-center !bg-background-section-burn'>
<Loading />
</div>
)}
</div>
</div>
</div>
)