mirror of
http://112.124.100.131/huang.ze/ebiz-dify-ai.git
synced 2025-12-10 11:26:52 +08:00
feat: llm text stream support for workflow app (#3798)
Co-authored-by: JzoNg <jzongcode@gmail.com>
This commit is contained in:
@@ -124,6 +124,7 @@ export const useWorkflowRun = () => {
|
||||
status: WorkflowRunningStatus.Running,
|
||||
},
|
||||
tracing: [],
|
||||
resultText: '',
|
||||
})
|
||||
|
||||
ssePost(
|
||||
@@ -284,6 +285,27 @@ export const useWorkflowRun = () => {
|
||||
if (onNodeFinished)
|
||||
onNodeFinished(params)
|
||||
},
|
||||
onTextChunk: (params) => {
|
||||
const { data: { text } } = params
|
||||
const {
|
||||
workflowRunningData,
|
||||
setWorkflowRunningData,
|
||||
} = workflowStore.getState()
|
||||
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
|
||||
draft.resultTabActive = true
|
||||
draft.resultText += text
|
||||
}))
|
||||
},
|
||||
onTextReplace: (params) => {
|
||||
const { data: { text } } = params
|
||||
const {
|
||||
workflowRunningData,
|
||||
setWorkflowRunningData,
|
||||
} = workflowStore.getState()
|
||||
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
|
||||
draft.resultText = text
|
||||
}))
|
||||
},
|
||||
...restCallback,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import {
|
||||
memo,
|
||||
useEffect,
|
||||
useRef,
|
||||
// useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import cn from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import OutputPanel from '../run/output-panel'
|
||||
import ResultText from '../run/result-text'
|
||||
import ResultPanel from '../run/result-panel'
|
||||
import TracingPanel from '../run/tracing-panel'
|
||||
import {
|
||||
@@ -32,22 +32,15 @@ const WorkflowPreview = () => {
|
||||
setCurrentTab(tab)
|
||||
}
|
||||
|
||||
const [height, setHieght] = useState(0)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (showDebugAndPreviewPanel && showInputsPanel)
|
||||
setCurrentTab('INPUT')
|
||||
}, [showDebugAndPreviewPanel, showInputsPanel])
|
||||
|
||||
const adjustResultHeight = () => {
|
||||
if (ref.current)
|
||||
setHieght(ref.current?.clientHeight - 16 - 16 - 2 - 1)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
adjustResultHeight()
|
||||
}, [])
|
||||
if ((workflowRunningData?.result.status === WorkflowRunningStatus.Succeeded || workflowRunningData?.result.status === WorkflowRunningStatus.Failed) && !workflowRunningData.resultText)
|
||||
switchTab('DETAIL')
|
||||
}, [workflowRunningData])
|
||||
|
||||
return (
|
||||
<div className={`
|
||||
@@ -107,7 +100,7 @@ const WorkflowPreview = () => {
|
||||
}}
|
||||
>{t('runLog.tracing')}</div>
|
||||
</div>
|
||||
<div ref={ref} className={cn(
|
||||
<div className={cn(
|
||||
'grow bg-white h-0 overflow-y-auto rounded-b-2xl',
|
||||
(currentTab === 'RESULT' || currentTab === 'TRACING') && '!bg-gray-50',
|
||||
)}>
|
||||
@@ -115,11 +108,11 @@ const WorkflowPreview = () => {
|
||||
<InputsPanel onRun={() => switchTab('RESULT')} />
|
||||
)}
|
||||
{currentTab === 'RESULT' && (
|
||||
<OutputPanel
|
||||
<ResultText
|
||||
isRunning={workflowRunningData?.result?.status === WorkflowRunningStatus.Running || !workflowRunningData?.result}
|
||||
outputs={workflowRunningData?.result?.outputs}
|
||||
outputs={workflowRunningData?.resultText}
|
||||
error={workflowRunningData?.result?.error}
|
||||
height={height}
|
||||
onClick={() => switchTab('DETAIL')}
|
||||
/>
|
||||
)}
|
||||
{currentTab === 'DETAIL' && (
|
||||
|
||||
56
web/app/components/workflow/run/result-text.tsx
Normal file
56
web/app/components/workflow/run/result-text.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ImageIndentLeft } from '@/app/components/base/icons/src/vender/line/editor'
|
||||
import { Markdown } from '@/app/components/base/markdown'
|
||||
import LoadingAnim from '@/app/components/app/chat/loading-anim'
|
||||
|
||||
type ResultTextProps = {
|
||||
isRunning?: boolean
|
||||
outputs?: any
|
||||
error?: string
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
const ResultText: FC<ResultTextProps> = ({
|
||||
isRunning,
|
||||
outputs,
|
||||
error,
|
||||
onClick,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className='bg-gray-50 py-2'>
|
||||
{isRunning && !outputs && (
|
||||
<div className='pt-4 pl-[26px]'>
|
||||
<LoadingAnim type='text' />
|
||||
</div>
|
||||
)}
|
||||
{!isRunning && error && (
|
||||
<div className='px-4'>
|
||||
<div className='px-3 py-[10px] rounded-lg !bg-[#fef3f2] border-[0.5px] border-[rbga(0,0,0,0.05)] shadow-xs'>
|
||||
<div className='text-xs leading-[18px] text-[#d92d20]'>{error}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isRunning && !outputs && !error && (
|
||||
<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>
|
||||
<div>
|
||||
{t('runLog.resultEmpty.tipLeft')}
|
||||
<span onClick={onClick} className='cursor-pointer text-primary-600'>{t('runLog.resultEmpty.link')}</span>
|
||||
{t('runLog.resultEmpty.tipRight')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{outputs && (
|
||||
<div className='px-4 py-2'>
|
||||
<Markdown content={outputs} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ResultText
|
||||
@@ -19,11 +19,16 @@ import type {
|
||||
} from './types'
|
||||
import { WorkflowContext } from './context'
|
||||
|
||||
type PreviewRunningData = WorkflowRunningData & {
|
||||
resultTabActive?: boolean
|
||||
resultText?: string
|
||||
}
|
||||
|
||||
type Shape = {
|
||||
appId: string
|
||||
panelWidth: number
|
||||
workflowRunningData?: WorkflowRunningData
|
||||
setWorkflowRunningData: (workflowData?: WorkflowRunningData) => void
|
||||
workflowRunningData?: PreviewRunningData
|
||||
setWorkflowRunningData: (workflowData: PreviewRunningData) => void
|
||||
historyWorkflowData?: HistoryWorkflowData
|
||||
setHistoryWorkflowData: (historyWorkflowData?: HistoryWorkflowData) => void
|
||||
showRunHistory: boolean
|
||||
|
||||
Reference in New Issue
Block a user