mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 19:21:05 +08:00
feat: run with params from logs (#26787)
Co-authored-by: lyzno1 <yuanyouhuilyz@gmail.com> Co-authored-by: lyzno1 <92089059+lyzno1@users.noreply.github.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
import { RiCloseLine, RiPlayLargeLine } from '@remixicon/react'
|
||||
import Run from '@/app/components/workflow/run'
|
||||
import { useStore } from '@/app/components/app/store'
|
||||
import TooltipPlus from '@/app/components/base/tooltip'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
type ILogDetail = {
|
||||
runID: string
|
||||
@@ -13,13 +15,34 @@ type ILogDetail = {
|
||||
const DetailPanel: FC<ILogDetail> = ({ runID, onClose }) => {
|
||||
const { t } = useTranslation()
|
||||
const appDetail = useStore(state => state.appDetail)
|
||||
const router = useRouter()
|
||||
|
||||
const handleReplay = () => {
|
||||
if (!appDetail?.id) return
|
||||
router.push(`/app/${appDetail.id}/workflow?replayRunId=${runID}`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='relative flex grow flex-col pt-3'>
|
||||
<span className='absolute right-3 top-4 z-20 cursor-pointer p-1' onClick={onClose}>
|
||||
<RiCloseLine className='h-4 w-4 text-text-tertiary' />
|
||||
</span>
|
||||
<h1 className='system-xl-semibold shrink-0 px-4 py-1 text-text-primary'>{t('appLog.runDetail.workflowTitle')}</h1>
|
||||
<div className='flex items-center bg-components-panel-bg'>
|
||||
<h1 className='system-xl-semibold shrink-0 px-4 py-1 text-text-primary'>{t('appLog.runDetail.workflowTitle')}</h1>
|
||||
<TooltipPlus
|
||||
popupContent={t('appLog.runDetail.testWithParams')}
|
||||
popupClassName='rounded-xl'
|
||||
>
|
||||
<button
|
||||
type='button'
|
||||
className='mr-1 flex h-6 w-6 items-center justify-center rounded-md hover:bg-state-base-hover'
|
||||
aria-label={t('appLog.runDetail.testWithParams')}
|
||||
onClick={handleReplay}
|
||||
>
|
||||
<RiPlayLargeLine className='h-4 w-4 text-text-tertiary' />
|
||||
</button>
|
||||
</TooltipPlus>
|
||||
</div>
|
||||
<Run
|
||||
runDetailUrl={runID ? `/apps/${appDetail?.id}/workflow-runs/${runID}` : ''}
|
||||
tracingListUrl={runID ? `/apps/${appDetail?.id}/workflow-runs/${runID}/node-executions` : ''}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
useEffect,
|
||||
useMemo,
|
||||
} from 'react'
|
||||
import {
|
||||
@@ -23,8 +24,13 @@ import {
|
||||
WorkflowContextProvider,
|
||||
} from '@/app/components/workflow/context'
|
||||
import type { InjectWorkflowStoreSliceFn } from '@/app/components/workflow/store'
|
||||
import { useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { createWorkflowSlice } from './store/workflow/workflow-slice'
|
||||
import WorkflowAppMain from './components/workflow-main'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
|
||||
import { fetchRunDetail } from '@/service/log'
|
||||
import { useGetRunAndTraceUrl } from './hooks/use-get-run-and-trace-url'
|
||||
|
||||
const WorkflowAppWithAdditionalContext = () => {
|
||||
const {
|
||||
@@ -47,6 +53,71 @@ const WorkflowAppWithAdditionalContext = () => {
|
||||
return []
|
||||
}, [data])
|
||||
|
||||
const searchParams = useSearchParams()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const { getWorkflowRunAndTraceUrl } = useGetRunAndTraceUrl()
|
||||
const replayRunId = searchParams.get('replayRunId')
|
||||
|
||||
useEffect(() => {
|
||||
if (!replayRunId)
|
||||
return
|
||||
const { runUrl } = getWorkflowRunAndTraceUrl(replayRunId)
|
||||
if (!runUrl)
|
||||
return
|
||||
fetchRunDetail(runUrl).then((res) => {
|
||||
const { setInputs, setShowInputsPanel, setShowDebugAndPreviewPanel } = workflowStore.getState()
|
||||
const rawInputs = res.inputs
|
||||
let parsedInputs: Record<string, unknown> | null = null
|
||||
|
||||
if (typeof rawInputs === 'string') {
|
||||
try {
|
||||
const maybeParsed = JSON.parse(rawInputs) as unknown
|
||||
if (maybeParsed && typeof maybeParsed === 'object' && !Array.isArray(maybeParsed))
|
||||
parsedInputs = maybeParsed as Record<string, unknown>
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Failed to parse workflow run inputs', error)
|
||||
}
|
||||
}
|
||||
else if (rawInputs && typeof rawInputs === 'object' && !Array.isArray(rawInputs)) {
|
||||
parsedInputs = rawInputs as Record<string, unknown>
|
||||
}
|
||||
|
||||
if (!parsedInputs)
|
||||
return
|
||||
|
||||
const userInputs: Record<string, string> = {}
|
||||
Object.entries(parsedInputs).forEach(([key, value]) => {
|
||||
if (key.startsWith('sys.'))
|
||||
return
|
||||
|
||||
if (value == null) {
|
||||
userInputs[key] = ''
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
||||
userInputs[key] = value
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
userInputs[key] = JSON.stringify(value)
|
||||
}
|
||||
catch {
|
||||
userInputs[key] = String(value)
|
||||
}
|
||||
})
|
||||
|
||||
if (!Object.keys(userInputs).length)
|
||||
return
|
||||
|
||||
setInputs(userInputs)
|
||||
setShowInputsPanel(true)
|
||||
setShowDebugAndPreviewPanel(true)
|
||||
})
|
||||
}, [replayRunId, workflowStore, getWorkflowRunAndTraceUrl])
|
||||
|
||||
if (!data || isLoading || isLoadingCurrentWorkspace || !currentWorkspace.id) {
|
||||
return (
|
||||
<div className='relative flex h-full w-full items-center justify-center'>
|
||||
|
||||
@@ -4,8 +4,8 @@ import type {
|
||||
} from '@/app/components/workflow/types'
|
||||
|
||||
export type FormSliceShape = {
|
||||
inputs: Record<string, string>
|
||||
setInputs: (inputs: Record<string, string>) => void
|
||||
inputs: Record<string, string | number | boolean>
|
||||
setInputs: (inputs: Record<string, string | number | boolean>) => void
|
||||
files: RunFile[]
|
||||
setFiles: (files: RunFile[]) => void
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ const translation = {
|
||||
workflowTitle: 'Protokolldetail',
|
||||
fileListLabel: 'Details zur Datei',
|
||||
fileListDetail: 'Detail',
|
||||
testWithParams: 'Test mit Parametern',
|
||||
},
|
||||
promptLog: 'Prompt-Protokoll',
|
||||
agentLog: 'Agentenprotokoll',
|
||||
|
||||
@@ -83,6 +83,7 @@ const translation = {
|
||||
workflowTitle: 'Log Detail',
|
||||
fileListLabel: 'File Details',
|
||||
fileListDetail: 'Detail',
|
||||
testWithParams: 'Test With Params',
|
||||
},
|
||||
promptLog: 'Prompt Log',
|
||||
agentLog: 'Agent Log',
|
||||
|
||||
@@ -82,6 +82,7 @@ const translation = {
|
||||
workflowTitle: 'Detalle del Registro',
|
||||
fileListLabel: 'Detalles del archivo',
|
||||
fileListDetail: 'Detalle',
|
||||
testWithParams: 'Prueba con parámetros',
|
||||
},
|
||||
promptLog: 'Registro de Indicación',
|
||||
agentLog: 'Registro de Agente',
|
||||
|
||||
@@ -82,6 +82,7 @@ const translation = {
|
||||
workflowTitle: 'جزئیات لاگ',
|
||||
fileListLabel: 'جزئیات فایل',
|
||||
fileListDetail: 'جزئیات',
|
||||
testWithParams: 'تست با پارامترها',
|
||||
},
|
||||
promptLog: 'لاگ درخواست',
|
||||
agentLog: 'لاگ عامل',
|
||||
|
||||
@@ -82,6 +82,7 @@ const translation = {
|
||||
workflowTitle: 'Détail du journal',
|
||||
fileListDetail: 'Détail',
|
||||
fileListLabel: 'Détails du fichier',
|
||||
testWithParams: 'Test avec paramètres',
|
||||
},
|
||||
promptLog: 'Journal de consigne',
|
||||
agentLog: 'Journal des agents',
|
||||
|
||||
@@ -84,6 +84,7 @@ const translation = {
|
||||
workflowTitle: 'लॉग विवरण',
|
||||
fileListDetail: 'विस्तार',
|
||||
fileListLabel: 'फ़ाइल विवरण',
|
||||
testWithParams: 'पैरामीटर्स के साथ परीक्षण',
|
||||
},
|
||||
promptLog: 'प्रॉम्प्ट लॉग',
|
||||
agentLog: 'एजेंट लॉग',
|
||||
|
||||
@@ -74,6 +74,7 @@ const translation = {
|
||||
workflowTitle: 'Log Detail',
|
||||
title: 'Log Percakapan',
|
||||
fileListLabel: 'Rincian File',
|
||||
testWithParams: 'Uji Dengan Param',
|
||||
},
|
||||
agentLogDetail: {
|
||||
iterations: 'Iterasi',
|
||||
|
||||
@@ -86,6 +86,7 @@ const translation = {
|
||||
workflowTitle: 'Dettagli Registro',
|
||||
fileListDetail: 'Dettaglio',
|
||||
fileListLabel: 'Dettagli del file',
|
||||
testWithParams: 'Test con parametri',
|
||||
},
|
||||
promptLog: 'Registro Prompt',
|
||||
agentLog: 'Registro Agente',
|
||||
|
||||
@@ -83,6 +83,7 @@ const translation = {
|
||||
workflowTitle: 'ログの詳細',
|
||||
fileListLabel: 'ファイルの詳細',
|
||||
fileListDetail: '詳細',
|
||||
testWithParams: 'パラメータ付きテスト',
|
||||
},
|
||||
promptLog: 'プロンプトログ',
|
||||
agentLog: 'エージェントログ',
|
||||
|
||||
@@ -83,6 +83,7 @@ const translation = {
|
||||
workflowTitle: '로그 세부 정보',
|
||||
fileListDetail: '세부',
|
||||
fileListLabel: '파일 세부 정보',
|
||||
testWithParams: '매개변수로 테스트',
|
||||
},
|
||||
promptLog: '프롬프트 로그',
|
||||
agentLog: '에이전트 로그',
|
||||
|
||||
@@ -86,6 +86,7 @@ const translation = {
|
||||
workflowTitle: 'Szczegół dziennika',
|
||||
fileListDetail: 'Detal',
|
||||
fileListLabel: 'Szczegóły pliku',
|
||||
testWithParams: 'Test z parametrami',
|
||||
},
|
||||
promptLog: 'Dziennik monitów',
|
||||
agentLog: 'Dziennik agenta',
|
||||
|
||||
@@ -82,6 +82,7 @@ const translation = {
|
||||
workflowTitle: 'Detalhes do Registro',
|
||||
fileListLabel: 'Detalhes do arquivo',
|
||||
fileListDetail: 'Detalhe',
|
||||
testWithParams: 'Teste com parâmetros',
|
||||
},
|
||||
promptLog: 'Registro de Prompt',
|
||||
agentLog: 'Registro do agente',
|
||||
|
||||
@@ -82,6 +82,7 @@ const translation = {
|
||||
workflowTitle: 'Detalii jurnal',
|
||||
fileListDetail: 'Amănunt',
|
||||
fileListLabel: 'Detalii fișier',
|
||||
testWithParams: 'Test cu parametri',
|
||||
},
|
||||
promptLog: 'Jurnal prompt',
|
||||
agentLog: 'Jurnal agent',
|
||||
|
||||
@@ -82,6 +82,7 @@ const translation = {
|
||||
workflowTitle: 'Подробная информация о журнале',
|
||||
fileListLabel: 'Сведения о файле',
|
||||
fileListDetail: 'Подробность',
|
||||
testWithParams: 'Тест с параметрами',
|
||||
},
|
||||
promptLog: 'Журнал подсказок',
|
||||
agentLog: 'Журнал агента',
|
||||
|
||||
@@ -82,6 +82,7 @@ const translation = {
|
||||
workflowTitle: 'Podrobnosti dnevnika',
|
||||
fileListDetail: 'Podrobnosti',
|
||||
fileListLabel: 'Podrobnosti o datoteki',
|
||||
testWithParams: 'Preizkus s parametri',
|
||||
},
|
||||
promptLog: 'Dnevnik PROMPT-ov',
|
||||
agentLog: 'Dnevnik pomočnika',
|
||||
|
||||
@@ -82,6 +82,7 @@ const translation = {
|
||||
workflowTitle: 'รายละเอียดบันทึก',
|
||||
fileListDetail: 'รายละเอียด',
|
||||
fileListLabel: 'รายละเอียดไฟล์',
|
||||
testWithParams: 'ทดสอบด้วยพารามิเตอร์',
|
||||
},
|
||||
promptLog: 'บันทึกพร้อมท์',
|
||||
agentLog: 'บันทึกตัวแทน',
|
||||
|
||||
@@ -82,6 +82,7 @@ const translation = {
|
||||
workflowTitle: 'Günlük Detayı',
|
||||
fileListDetail: 'Ayrıntı',
|
||||
fileListLabel: 'Dosya Detayları',
|
||||
testWithParams: 'Parametrelerle Test',
|
||||
},
|
||||
promptLog: 'Prompt Günlüğü',
|
||||
agentLog: 'Agent Günlüğü',
|
||||
|
||||
@@ -82,6 +82,7 @@ const translation = {
|
||||
workflowTitle: 'Деталі Журналу',
|
||||
fileListDetail: 'Деталь',
|
||||
fileListLabel: 'Подробиці файлу',
|
||||
testWithParams: 'Тест з параметрами',
|
||||
},
|
||||
promptLog: 'Журнал Запитань',
|
||||
agentLog: 'Журнал агента',
|
||||
|
||||
@@ -82,6 +82,7 @@ const translation = {
|
||||
workflowTitle: 'Chi tiết nhật ký',
|
||||
fileListDetail: 'Chi tiết',
|
||||
fileListLabel: 'Chi tiết tệp',
|
||||
testWithParams: 'Kiểm tra với các tham số',
|
||||
},
|
||||
promptLog: 'Nhật ký lời nhắc',
|
||||
viewLog: 'Xem nhật ký',
|
||||
|
||||
@@ -83,6 +83,7 @@ const translation = {
|
||||
workflowTitle: '日志详情',
|
||||
fileListLabel: '文件详情',
|
||||
fileListDetail: '详情',
|
||||
testWithParams: '按此参数测试',
|
||||
},
|
||||
promptLog: 'Prompt 日志',
|
||||
agentLog: 'Agent 日志',
|
||||
|
||||
@@ -82,6 +82,7 @@ const translation = {
|
||||
workflowTitle: '日誌詳情',
|
||||
fileListDetail: '細節',
|
||||
fileListLabel: '檔詳細資訊',
|
||||
testWithParams: '使用參數測試',
|
||||
},
|
||||
promptLog: 'Prompt 日誌',
|
||||
agentLog: 'Agent 日誌',
|
||||
|
||||
Reference in New Issue
Block a user