diff --git a/web/app/components/workflow/nodes/_base/components/editor/base.tsx b/web/app/components/workflow/nodes/_base/components/editor/base.tsx index dd1ae811de..2f9ca7dfd9 100644 --- a/web/app/components/workflow/nodes/_base/components/editor/base.tsx +++ b/web/app/components/workflow/nodes/_base/components/editor/base.tsx @@ -39,6 +39,7 @@ type Props = { tip?: React.JSX.Element nodesOutputVars?: NodeOutPutVar[] availableNodes?: Node[] + footer?: React.ReactNode } const Base: FC = ({ @@ -57,6 +58,7 @@ const Base: FC = ({ showFileList, showCodeGenerator = false, tip, + footer, }) => { const ref = useRef(null) const { @@ -128,6 +130,7 @@ const Base: FC = ({ {showFileList && fileList.length > 0 && ( )} + {footer} ) diff --git a/web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx b/web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx index 36e47a6bfe..691e079b4e 100644 --- a/web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx @@ -39,6 +39,7 @@ export type Props = { showCodeGenerator?: boolean className?: string tip?: React.JSX.Element + footer?: React.ReactNode } export const languageMap = { @@ -67,6 +68,7 @@ const CodeEditor: FC = ({ showCodeGenerator = false, className, tip, + footer, }) => { const [isFocus, setIsFocus] = React.useState(false) const [isMounted, setIsMounted] = React.useState(false) @@ -191,6 +193,7 @@ const CodeEditor: FC = ({ showFileList={showFileList} showCodeGenerator={showCodeGenerator} tip={tip} + footer={footer} > {main} diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx index e7bcb6f20d..384776f671 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx @@ -17,6 +17,7 @@ type CodeEditorProps = { hideTopMenu?: boolean onFocus?: () => void onBlur?: () => void + topContent?: React.ReactNode } & React.HTMLAttributes const CodeEditor: FC = ({ @@ -26,6 +27,7 @@ const CodeEditor: FC = ({ editorWrapperClassName, readOnly = false, hideTopMenu = false, + topContent, className, onFocus, onBlur, @@ -139,6 +141,7 @@ const CodeEditor: FC = ({ )} + {topContent}
void onBlur?: () => void + isTruncated?: boolean } const SchemaEditor: FC = ({ @@ -20,6 +22,7 @@ const SchemaEditor: FC = ({ readonly = false, onFocus, onBlur, + isTruncated, }) => { return ( = ({ hideTopMenu={hideTopMenu} onFocus={onFocus} onBlur={onBlur} + topContent={isTruncated && } /> ) } diff --git a/web/app/components/workflow/panel/workflow-preview.tsx b/web/app/components/workflow/panel/workflow-preview.tsx index 2c797e05d6..5c1152cfbd 100644 --- a/web/app/components/workflow/panel/workflow-preview.tsx +++ b/web/app/components/workflow/panel/workflow-preview.tsx @@ -184,7 +184,12 @@ const WorkflowPreview = () => { {currentTab === 'DETAIL' && ( = ({ {!loading && currentTab === 'DETAIL' && runDetail && ( = ({ language={CodeLanguage.json} value={nodeInfo.inputs} isJSONStringifyBeauty + footer={nodeInfo.inputs_truncated && } />
)} @@ -254,6 +256,7 @@ const NodePanel: FC = ({ value={nodeInfo.outputs} isJSONStringifyBeauty tip={} + footer={nodeInfo.outputs_truncated && } /> )} diff --git a/web/app/components/workflow/run/result-panel.tsx b/web/app/components/workflow/run/result-panel.tsx index 14260e646b..0712d5209e 100644 --- a/web/app/components/workflow/run/result-panel.tsx +++ b/web/app/components/workflow/run/result-panel.tsx @@ -16,12 +16,19 @@ import { IterationLogTrigger } from '@/app/components/workflow/run/iteration-log import { LoopLogTrigger } from '@/app/components/workflow/run/loop-log' import { RetryLogTrigger } from '@/app/components/workflow/run/retry-log' import { AgentLogTrigger } from '@/app/components/workflow/run/agent-log' +import LargeDataAlert from '../variable-inspect/large-data-alert' export type ResultPanelProps = { nodeInfo?: NodeTracing inputs?: string + inputs_truncated?: boolean process_data?: string + process_data_truncated?: boolean outputs?: string | Record + outputs_truncated?: boolean + outputs_full_content?: { + download_url: string + } status: string error?: string elapsed_time?: number @@ -42,8 +49,12 @@ export type ResultPanelProps = { const ResultPanel: FC = ({ nodeInfo, inputs, + inputs_truncated, process_data, + process_data_truncated, outputs, + outputs_truncated, + outputs_full_content, status, error, elapsed_time, @@ -118,6 +129,7 @@ const ResultPanel: FC = ({ language={CodeLanguage.json} value={inputs} isJSONStringifyBeauty + footer={inputs_truncated && } /> {process_data && ( = ({ language={CodeLanguage.json} value={process_data} isJSONStringifyBeauty + footer={process_data_truncated && } /> )} {(outputs || status === 'running') && ( @@ -136,6 +149,7 @@ const ResultPanel: FC = ({ value={outputs} isJSONStringifyBeauty tip={} + footer={outputs_truncated && } /> )} diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index e6866e00cc..d44eea5a37 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -396,8 +396,14 @@ export type WorkflowRunningData = { result: { workflow_id?: string inputs?: string + inputs_truncated: boolean process_data?: string + process_data_truncated: boolean outputs?: string + outputs_truncated: boolean + outputs_full_content?: { + download_url: string + } status: string error?: string elapsed_time?: number diff --git a/web/app/components/workflow/variable-inspect/large-data-alert.tsx b/web/app/components/workflow/variable-inspect/large-data-alert.tsx new file mode 100644 index 0000000000..35b3648154 --- /dev/null +++ b/web/app/components/workflow/variable-inspect/large-data-alert.tsx @@ -0,0 +1,33 @@ +'use client' +import { RiInformation2Fill } from '@remixicon/react' +import type { FC } from 'react' +import React from 'react' +import cn from '@/utils/classnames' +import { useTranslation } from 'react-i18next' + +type Props = { + textHasNoExport?: boolean + downloadUrl?: string + className?: string +} + +const LargeDataAlert: FC = ({ + textHasNoExport, + downloadUrl, + className, +}) => { + const { t } = useTranslation() + const text = textHasNoExport ? t('workflow.debug.variableInspect.largeDataNoExport') : t('workflow.debug.variableInspect.largeData') + return ( +
+
+ +
{text}
+
+ {downloadUrl && ( +
{t('workflow.debug.variableInspect.export')}
+ )} +
+ ) +} +export default React.memo(LargeDataAlert) diff --git a/web/app/components/workflow/variable-inspect/right.tsx b/web/app/components/workflow/variable-inspect/right.tsx index d86280a888..76d14940c5 100644 --- a/web/app/components/workflow/variable-inspect/right.tsx +++ b/web/app/components/workflow/variable-inspect/right.tsx @@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next' import { RiArrowGoBackLine, RiCloseLine, + RiFileDownloadFill, RiMenuLine, RiSparklingFill, } from '@remixicon/react' @@ -53,6 +54,13 @@ const Right = ({ const setShowVariableInspectPanel = useStore(s => s.setShowVariableInspectPanel) const setCurrentFocusNodeId = useStore(s => s.setCurrentFocusNodeId) const toolIcon = useToolIcon(currentNodeVar?.nodeData) + const isTruncated = currentNodeVar?.var.is_truncated + const fullContent = currentNodeVar?.var.full_content + // const isTruncated = true + // const fullContent = { + // size_bytes: 11289600, + // download_url: 'https://upload.dify.ai/files/222bc6e7-40bd-4433-9ba8-4b9ecda88b14/file-preview?timestamp=1754976824&nonce=d970eb39b119f76ec94a9b026f2825b3&sign=ltJO4vS0jrwxuBl4GU74E1Sg_Tia2Y4g2LoBoPh3970=&as_attachment=true', + // } const { resetConversationVar, @@ -185,7 +193,16 @@ const Right = ({ )}
{currentNodeVar.var.name}
-
{currentNodeVar.var.value_type}
+
+ {currentNodeVar.var.value_type} + {isTruncated && ( + <> + · + {((fullContent?.size_bytes || 0) / 1024 / 1024).toFixed(1)}MB + + )} +
+ )} @@ -202,20 +219,32 @@ const Right = ({ )} - {currentNodeVar.var.edited && ( + {isTruncated && ( + + + + + + + + )} + {!isTruncated && currentNodeVar.var.edited && ( {t('workflow.debug.variableInspect.edited')} )} - {currentNodeVar.var.edited && currentNodeVar.var.type !== VarInInspectType.conversation && ( + {!isTruncated && currentNodeVar.var.edited && currentNodeVar.var.type !== VarInInspectType.conversation && ( )} - {currentNodeVar.var.edited && currentNodeVar.var.type === VarInInspectType.conversation && ( + {!isTruncated && currentNodeVar.var.edited && currentNodeVar.var.type === VarInInspectType.conversation && ( @@ -240,7 +269,7 @@ const Right = ({ )} - {currentNodeVar && !isValueFetching && } + {currentNodeVar && !isValueFetching && } {isShowPromptGenerator && ( isCodeBlock diff --git a/web/app/components/workflow/variable-inspect/value-content.tsx b/web/app/components/workflow/variable-inspect/value-content.tsx index 089cb06424..65265904a2 100644 --- a/web/app/components/workflow/variable-inspect/value-content.tsx +++ b/web/app/components/workflow/variable-inspect/value-content.tsx @@ -24,6 +24,7 @@ import { SupportUploadFileTypes } from '@/app/components/workflow/types' import type { VarInInspect } from '@/types/workflow' import { VarInInspectType } from '@/types/workflow' import cn from '@/utils/classnames' +import LargeDataAlert from './large-data-alert' import BoolValue from '../panel/chat-variable-panel/components/bool-value' import { useStore } from '@/app/components/workflow/store' import { ChunkCardList } from '@/app/components/rag-pipeline/components/chunk-card-list' @@ -114,11 +115,13 @@ const DisplayContent = (props: DisplayContentProps) => { type Props = { currentVar: VarInInspect handleValueChange: (varId: string, value: any) => void + isTruncated: boolean } const ValueContent = ({ currentVar, handleValueChange, + isTruncated, }: Props) => { const contentContainerRef = useRef(null) const errorMessageRef = useRef(null) @@ -173,6 +176,8 @@ const ValueContent = ({ }, [currentVar.id, currentVar.value]) const handleTextChange = (value: string) => { + if (isTruncated) + return if (currentVar.value_type === 'string') setValue(value) @@ -222,6 +227,8 @@ const ValueContent = ({ } const handleEditorChange = (value: string) => { + if (isTruncated) + return setJson(value) if (jsonValueValidate(value, currentVar.value_type)) { const parsed = JSON.parse(value) @@ -265,22 +272,25 @@ const ValueContent = ({ ref={contentContainerRef} className='flex h-full flex-col' > -
+
{showTextEditor && ( - currentVar.value_type === 'string' ? ( + <> + {isTruncated && } + currentVar.value_type === 'string' ? ( - ) :