Compare commits

...

2 Commits

Author SHA1 Message Date
JzoNg
4cbc59d92f fix file display in log 2024-12-04 16:47:16 +08:00
JzoNg
323a6f8ca4 support file download in workflow result 2024-12-04 16:17:13 +08:00
9 changed files with 60 additions and 17 deletions

View File

@@ -334,7 +334,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({
</SimpleBtn>
)
}
{(currentTab === 'RESULT' || !isWorkflow) && (
{((currentTab === 'RESULT' && workflowProcessData?.resultText) || !isWorkflow) && (
<SimpleBtn
isDisabled={isError || !messageId}
className={cn(isMobile && '!px-1.5', 'space-x-1')}

View File

@@ -27,15 +27,15 @@ const ResultTab = ({
onCurrentTabChange(tab)
}
useEffect(() => {
if (data?.resultText)
if (data?.resultText || !!data?.files?.length)
switchTab('RESULT')
else
switchTab('DETAIL')
}, [data?.resultText])
}, [data?.files?.length, data?.resultText])
return (
<div className='grow relative flex flex-col'>
{data?.resultText && (
{(data?.resultText || !!data?.files?.length) && (
<div className='shrink-0 flex items-center mb-2 border-b-[0.5px] border-[rgba(0,0,0,0.05)]'>
<div
className={cn(
@@ -56,7 +56,7 @@ const ResultTab = ({
<div className={cn('grow bg-white')}>
{currentTab === 'RESULT' && (
<>
<Markdown content={data?.resultText || ''} />
{data?.resultText && <Markdown content={data?.resultText || ''} />}
{!!data?.files?.length && (
<FileList
files={data?.files}

View File

@@ -1,4 +1,5 @@
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RiArrowRightSLine } from '@remixicon/react'
import FileImageRender from './file-image-render'
import FileTypeIcon from './file-type-icon'
@@ -16,18 +17,18 @@ type Props = {
}
const FileListInLog = ({ fileList }: Props) => {
const { t } = useTranslation()
const [expanded, setExpanded] = useState(false)
if (!fileList.length)
return null
return (
<div className={cn('border-t border-divider-subtle px-3 py-2', expanded && 'py-3')}>
<div className='flex justify-between gap-1'>
{expanded && (
<div></div>
<div className='text-text-tertiary system-xs-medium-uppercase'>{t('appLog.runDetail.fileListLabel')}</div>
)}
{!expanded && (
<div className='flex'>
<div className='flex gap-1'>
{fileList.map((file) => {
const { id, name, type, supportFileType, base64Url, url } = file
const isImageFile = supportFileType === SupportUploadFileTypes.image
@@ -63,7 +64,7 @@ const FileListInLog = ({ fileList }: Props) => {
</div>
)}
<div className='flex items-center gap-1 cursor-pointer' onClick={() => setExpanded(!expanded)}>
{!expanded && <div className='text-text-tertiary system-xs-medium-uppercase'>DETAIL</div>}
{!expanded && <div className='text-text-tertiary system-xs-medium-uppercase'>{t('appLog.runDetail.fileListDetail')}</div>}
<RiArrowRightSLine className={cn('w-4 h-4 text-text-tertiary', expanded && 'rotate-90')} />
</div>
</div>

View File

@@ -162,7 +162,7 @@ export const getFilesInLogs = (rawData: any) => {
if (typeof rawData[key] === 'object' || Array.isArray(rawData[key]))
return rawData[key]
return undefined
}).filter(Boolean)).filter(item => item?.model_identity === '__dify__file__')
}).filter(Boolean)).filter(item => item?.dify_model_identity === '__dify__file__')
return getProcessedFilesFromResponse(originalFiles)
}

View File

@@ -48,7 +48,7 @@ const WorkflowPreview = () => {
}, [showDebugAndPreviewPanel, showInputsPanel])
useEffect(() => {
if ((workflowRunningData?.result.status === WorkflowRunningStatus.Succeeded || workflowRunningData?.result.status === WorkflowRunningStatus.Failed) && !workflowRunningData.resultText)
if ((workflowRunningData?.result.status === WorkflowRunningStatus.Succeeded || workflowRunningData?.result.status === WorkflowRunningStatus.Failed) && !workflowRunningData.resultText && !workflowRunningData.result.files?.length)
switchTab('DETAIL')
}, [workflowRunningData])

View File

@@ -1,10 +1,13 @@
'use client'
import type { FC } from 'react'
import { useMemo } from 'react'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
import { Markdown } from '@/app/components/base/markdown'
import LoadingAnim from '@/app/components/base/chat/chat/loading-anim'
import { FileList } from '@/app/components/base/file-uploader'
import StatusContainer from '@/app/components/workflow/run/status-container'
import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
type OutputPanelProps = {
isRunning?: boolean
@@ -19,6 +22,30 @@ const OutputPanel: FC<OutputPanelProps> = ({
error,
height,
}) => {
const isTextOutput = useMemo(() => {
return outputs && Object.keys(outputs).length === 1 && typeof outputs[Object.keys(outputs)[0]] === 'string'
}, [outputs])
const fileList = useMemo(() => {
const fileList: any[] = []
if (!outputs)
return fileList
if (Object.keys(outputs).length > 1)
return fileList
for (const key in outputs) {
if (Array.isArray(outputs[key])) {
outputs[key].map((output: any) => {
if (output.dify_model_identity === '__dify__file__')
fileList.push(output)
return null
})
}
else if (outputs[key].dify_model_identity === '__dify__file__') {
fileList.push(outputs[key])
}
}
return getProcessedFilesFromResponse(fileList)
}, [outputs])
return (
<div className='py-2'>
{isRunning && (
@@ -36,20 +63,31 @@ const OutputPanel: FC<OutputPanelProps> = ({
<Markdown content='No Output' />
</div>
)}
{outputs && Object.keys(outputs).length === 1 && (
{isTextOutput && (
<div className='px-4 py-2'>
<Markdown content={outputs[Object.keys(outputs)[0]] || ''} />
</div>
)}
{fileList.length > 0 && (
<div className='px-4 py-2'>
<FileList
files={fileList}
showDeleteAction={false}
showDownloadAction
canPreview
/>
</div>
)}
{outputs && Object.keys(outputs).length > 1 && height! > 0 && (
<div className='px-4 py-2 flex flex-col gap-2'>
<div className='flex flex-col gap-2'>
<CodeEditor
showFileList
readOnly
title={<div></div>}
language={CodeLanguage.json}
value={outputs}
isJSONStringifyBeauty
height={height}
height={height ? height - 50 - 16 : undefined}
/>
</div>
)}

View File

@@ -38,7 +38,7 @@ const ResultText: FC<ResultTextProps> = ({
</StatusContainer>
</div>
)}
{!isRunning && !outputs && !error && (
{!isRunning && !outputs && !error && !allFiles?.length && (
<div className='mt-[120px] px-4 py-2 flex flex-col items-center text-[13px] leading-[18px] text-gray-500'>
<ImageIndentLeft className='w-6 h-6 text-gray-400' />
<div className='mr-2'>{t('runLog.resultEmpty.title')}</div>
@@ -49,9 +49,9 @@ const ResultText: FC<ResultTextProps> = ({
</div>
</div>
)}
{outputs && (
{(outputs || !!allFiles?.length) && (
<div className='px-4 py-2'>
<Markdown content={outputs} />
{outputs && <Markdown content={outputs} />}
{!!allFiles?.length && (
<FileList
files={allFiles}

View File

@@ -79,6 +79,8 @@ const translation = {
runDetail: {
title: 'Conversation Log',
workflowTitle: 'Log Detail',
fileListLabel: 'File List',
fileListDetail: 'Detail',
},
promptLog: 'Prompt Log',
agentLog: 'Agent Log',

View File

@@ -79,6 +79,8 @@ const translation = {
runDetail: {
title: '对话日志',
workflowTitle: '日志详情',
fileListLabel: '文件列表',
fileListDetail: '详情',
},
promptLog: 'Prompt 日志',
agentLog: 'Agent 日志',