Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins
commit
c04a89d5b1
@ -0,0 +1,29 @@
|
||||
import { RiArrowRightLine } from '@remixicon/react'
|
||||
|
||||
type AgentLogTriggerProps = {
|
||||
onDetail?: () => void
|
||||
}
|
||||
const AgentLogTrigger = ({
|
||||
onDetail,
|
||||
}: AgentLogTriggerProps) => {
|
||||
return (
|
||||
<div className='bg-components-button-tertiary-bg rounded-[10px]'>
|
||||
<div className='flex items-center px-3 pt-2 system-2xs-medium-uppercase text-text-tertiary'>
|
||||
Agent strategy
|
||||
</div>
|
||||
<div className='flex items-center pl-3 pt-1 pr-2 pb-1.5'>
|
||||
<div className='shrink-0 w-5 h-5'></div>
|
||||
<div className='grow mx-0.5 px-1 system-xs-medium text-text-secondary'></div>
|
||||
<div
|
||||
className='shrink-0 flex items-center px-[1px] system-xs-regular-uppercase text-text-tertiary cursor-pointer'
|
||||
onClick={onDetail}
|
||||
>
|
||||
Detail
|
||||
<RiArrowRightLine className='ml-0.5 w-3.5 h-3.5' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AgentLogTrigger
|
||||
@ -0,0 +1,43 @@
|
||||
import Button from '@/app/components/base/button'
|
||||
import { RiArrowLeftLine } from '@remixicon/react'
|
||||
import TracingPanel from '../tracing-panel'
|
||||
|
||||
type AgentResultPanelProps = {
|
||||
onBack: () => void
|
||||
}
|
||||
const AgentResultPanel = ({
|
||||
onBack,
|
||||
}: AgentResultPanelProps) => {
|
||||
return (
|
||||
<div className='overflow-y-auto'>
|
||||
<div className='flex items-center p-1 pr-3 h-8'>
|
||||
<Button
|
||||
className='shrink-0 px-[5px]'
|
||||
size='small'
|
||||
variant='ghost-accent'
|
||||
onClick={onBack}
|
||||
>
|
||||
<RiArrowLeftLine className='mr-1 w-3.5 h-3.5' />
|
||||
Back
|
||||
</Button>
|
||||
<div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div>
|
||||
<div className='grow px-[5px] system-xs-medium-uppercase'>
|
||||
Agent strategy
|
||||
</div>
|
||||
<Button
|
||||
className='px-[5px]'
|
||||
size='small'
|
||||
variant='ghost-accent'
|
||||
onClick={onBack}
|
||||
>
|
||||
close
|
||||
</Button>
|
||||
</div>
|
||||
<TracingPanel
|
||||
list={[]}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AgentResultPanel
|
||||
@ -0,0 +1,45 @@
|
||||
import Button from '@/app/components/base/button'
|
||||
import { RiArrowLeftLine } from '@remixicon/react'
|
||||
import TracingPanel from '../tracing-panel'
|
||||
|
||||
type ToolCallResultPanelProps = {
|
||||
onBack: () => void
|
||||
onClose: () => void
|
||||
}
|
||||
const ToolCallResultPanel = ({
|
||||
onBack,
|
||||
onClose,
|
||||
}: ToolCallResultPanelProps) => {
|
||||
return (
|
||||
<div className='overflow-y-auto'>
|
||||
<div className='flex items-center p-1 pr-3 h-8'>
|
||||
<Button
|
||||
className='shrink-0 px-[5px]'
|
||||
size='small'
|
||||
variant='ghost-accent'
|
||||
onClick={onBack}
|
||||
>
|
||||
<RiArrowLeftLine className='mr-1 w-3.5 h-3.5' />
|
||||
Back
|
||||
</Button>
|
||||
<div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div>
|
||||
<div className='grow px-[5px] system-xs-medium-uppercase'>
|
||||
10 Logs
|
||||
</div>
|
||||
<Button
|
||||
className='px-[5px]'
|
||||
size='small'
|
||||
variant='ghost-accent'
|
||||
onClick={onClose}
|
||||
>
|
||||
close
|
||||
</Button>
|
||||
</div>
|
||||
<TracingPanel
|
||||
list={[]}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ToolCallResultPanel
|
||||
@ -0,0 +1,48 @@
|
||||
import {
|
||||
useCallback,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import type { IterationDurationMap, NodeTracing } from '@/types/workflow'
|
||||
|
||||
export const useLogs = () => {
|
||||
const [showRetryDetail, {
|
||||
setTrue: setShowRetryDetailTrue,
|
||||
setFalse: setShowRetryDetailFalse,
|
||||
}] = useBoolean(false)
|
||||
const [retryResultList, setRetryResultList] = useState<NodeTracing[]>([])
|
||||
const handleShowRetryResultList = useCallback((detail: NodeTracing[]) => {
|
||||
setShowRetryDetailTrue()
|
||||
setRetryResultList(detail)
|
||||
}, [setShowRetryDetailTrue, setRetryResultList])
|
||||
|
||||
const [showIteratingDetail, {
|
||||
setTrue: setShowIteratingDetailTrue,
|
||||
setFalse: setShowIteratingDetailFalse,
|
||||
}] = useBoolean(false)
|
||||
const [iterationResultList, setIterationResultList] = useState<NodeTracing[][]>([])
|
||||
const [iterationResultDurationMap, setIterationResultDurationMap] = useState<IterationDurationMap>({})
|
||||
const handleShowIterationResultList = useCallback((detail: NodeTracing[][], iterDurationMap: IterationDurationMap) => {
|
||||
setShowIteratingDetailTrue()
|
||||
setIterationResultList(detail)
|
||||
setIterationResultDurationMap(iterDurationMap)
|
||||
}, [setShowIteratingDetailTrue, setIterationResultList, setIterationResultDurationMap])
|
||||
|
||||
return {
|
||||
showSpecialResultPanel: !showRetryDetail && !showIteratingDetail,
|
||||
showRetryDetail,
|
||||
setShowRetryDetailTrue,
|
||||
setShowRetryDetailFalse,
|
||||
retryResultList,
|
||||
setRetryResultList,
|
||||
handleShowRetryResultList,
|
||||
showIteratingDetail,
|
||||
setShowIteratingDetailTrue,
|
||||
setShowIteratingDetailFalse,
|
||||
iterationResultList,
|
||||
setIterationResultList,
|
||||
iterationResultDurationMap,
|
||||
setIterationResultDurationMap,
|
||||
handleShowIterationResultList,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
import RetryResultPanel from './retry-result-panel'
|
||||
import IterationResultPanel from './iteration-result-panel'
|
||||
import type { IterationDurationMap, NodeTracing } from '@/types/workflow'
|
||||
|
||||
type SpecialResultPanelProps = {
|
||||
showRetryDetail: boolean
|
||||
setShowRetryDetailFalse: () => void
|
||||
retryResultList: NodeTracing[]
|
||||
|
||||
showIteratingDetail: boolean
|
||||
setShowIteratingDetailFalse: () => void
|
||||
iterationResultList: NodeTracing[][]
|
||||
iterationResultDurationMap: IterationDurationMap
|
||||
}
|
||||
const SpecialResultPanel = ({
|
||||
showRetryDetail,
|
||||
setShowRetryDetailFalse,
|
||||
retryResultList,
|
||||
|
||||
showIteratingDetail,
|
||||
setShowIteratingDetailFalse,
|
||||
iterationResultList,
|
||||
iterationResultDurationMap,
|
||||
}: SpecialResultPanelProps) => {
|
||||
return (
|
||||
<>
|
||||
{
|
||||
showRetryDetail && (
|
||||
<RetryResultPanel
|
||||
list={retryResultList}
|
||||
onBack={setShowRetryDetailFalse}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
showIteratingDetail && (
|
||||
<IterationResultPanel
|
||||
list={iterationResultList}
|
||||
onHide={setShowIteratingDetailFalse}
|
||||
onBack={setShowIteratingDetailFalse}
|
||||
iterDurationMap={iterationResultDurationMap}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SpecialResultPanel
|
||||
@ -1,11 +1,11 @@
|
||||
import format from '.'
|
||||
import { simpleIterationData } from './data'
|
||||
|
||||
describe('format api data to tracing panel data', () => {
|
||||
describe('iteration', () => {
|
||||
test('result should have no nodes in iteration node', () => {
|
||||
expect(format(simpleIterationData.in as any).find(item => !!(item as any).execution_metadata?.iteration_id)).toBeUndefined()
|
||||
})
|
||||
test('iteration should put nodes in details', () => {
|
||||
expect(format(simpleIterationData.in as any)).toEqual(simpleIterationData.output)
|
||||
expect(format(simpleIterationData.in as any)).toEqual(simpleIterationData.expect)
|
||||
})
|
||||
})
|
||||
|
||||
@ -0,0 +1,133 @@
|
||||
export const simpleRetryData = (() => {
|
||||
const startNode = {
|
||||
id: 'f7938b2b-77cd-43f0-814c-2f0ade7cbc60',
|
||||
index: 1,
|
||||
predecessor_node_id: null,
|
||||
node_id: '1735112903395',
|
||||
node_type: 'start',
|
||||
title: 'Start',
|
||||
inputs: {
|
||||
'sys.files': [],
|
||||
'sys.user_id': '6d8ad01f-edf9-43a6-b863-a034b1828ac7',
|
||||
'sys.app_id': '6180ead7-2190-4a61-975c-ec3bf29653da',
|
||||
'sys.workflow_id': 'eef6da45-244b-4c79-958e-f3573f7c12bb',
|
||||
'sys.workflow_run_id': 'fc8970ef-1406-484e-afde-8567dc22f34c',
|
||||
},
|
||||
process_data: null,
|
||||
outputs: {
|
||||
'sys.files': [],
|
||||
'sys.user_id': '6d8ad01f-edf9-43a6-b863-a034b1828ac7',
|
||||
'sys.app_id': '6180ead7-2190-4a61-975c-ec3bf29653da',
|
||||
'sys.workflow_id': 'eef6da45-244b-4c79-958e-f3573f7c12bb',
|
||||
'sys.workflow_run_id': 'fc8970ef-1406-484e-afde-8567dc22f34c',
|
||||
},
|
||||
status: 'succeeded',
|
||||
error: null,
|
||||
elapsed_time: 0.008715,
|
||||
execution_metadata: null,
|
||||
extras: {},
|
||||
created_at: 1735112940,
|
||||
created_by_role: 'account',
|
||||
created_by_account: {
|
||||
id: '6d8ad01f-edf9-43a6-b863-a034b1828ac7',
|
||||
name: '九彩拼盘',
|
||||
email: 'iamjoel007@gmail.com',
|
||||
},
|
||||
created_by_end_user: null,
|
||||
finished_at: 1735112940,
|
||||
}
|
||||
|
||||
const httpNode = {
|
||||
id: '50220407-3420-4ad4-89da-c6959710d1aa',
|
||||
index: 2,
|
||||
predecessor_node_id: '1735112903395',
|
||||
node_id: '1735112908006',
|
||||
node_type: 'http-request',
|
||||
title: 'HTTP Request',
|
||||
inputs: null,
|
||||
process_data: {
|
||||
request: 'GET / HTTP/1.1\r\nHost: 404\r\n\r\n',
|
||||
},
|
||||
outputs: null,
|
||||
status: 'failed',
|
||||
error: 'timed out',
|
||||
elapsed_time: 30.247757,
|
||||
execution_metadata: null,
|
||||
extras: {},
|
||||
created_at: 1735112940,
|
||||
created_by_role: 'account',
|
||||
created_by_account: {
|
||||
id: '6d8ad01f-edf9-43a6-b863-a034b1828ac7',
|
||||
name: '九彩拼盘',
|
||||
email: 'iamjoel007@gmail.com',
|
||||
},
|
||||
created_by_end_user: null,
|
||||
finished_at: 1735112970,
|
||||
}
|
||||
|
||||
const retry1 = {
|
||||
id: 'ed352b36-27fb-49c6-9e8f-cc755bfc25fc',
|
||||
index: 3,
|
||||
predecessor_node_id: '1735112903395',
|
||||
node_id: '1735112908006',
|
||||
node_type: 'http-request',
|
||||
title: 'HTTP Request',
|
||||
inputs: null,
|
||||
process_data: null,
|
||||
outputs: null,
|
||||
status: 'retry',
|
||||
error: 'timed out',
|
||||
elapsed_time: 10.011833,
|
||||
execution_metadata: {
|
||||
iteration_id: null,
|
||||
parallel_mode_run_id: null,
|
||||
},
|
||||
extras: {},
|
||||
created_at: 1735112940,
|
||||
created_by_role: 'account',
|
||||
created_by_account: {
|
||||
id: '6d8ad01f-edf9-43a6-b863-a034b1828ac7',
|
||||
name: '九彩拼盘',
|
||||
email: 'iamjoel007@gmail.com',
|
||||
},
|
||||
created_by_end_user: null,
|
||||
finished_at: 1735112950,
|
||||
}
|
||||
|
||||
const retry2 = {
|
||||
id: '74dfb3d3-dacf-44f2-8784-e36bfa2d6c4e',
|
||||
index: 4,
|
||||
predecessor_node_id: '1735112903395',
|
||||
node_id: '1735112908006',
|
||||
node_type: 'http-request',
|
||||
title: 'HTTP Request',
|
||||
inputs: null,
|
||||
process_data: null,
|
||||
outputs: null,
|
||||
status: 'retry',
|
||||
error: 'timed out',
|
||||
elapsed_time: 10.010368,
|
||||
execution_metadata: {
|
||||
iteration_id: null,
|
||||
parallel_mode_run_id: null,
|
||||
},
|
||||
extras: {},
|
||||
created_at: 1735112950,
|
||||
created_by_role: 'account',
|
||||
created_by_account: {
|
||||
id: '6d8ad01f-edf9-43a6-b863-a034b1828ac7',
|
||||
name: '九彩拼盘',
|
||||
email: 'iamjoel007@gmail.com',
|
||||
},
|
||||
created_by_end_user: null,
|
||||
finished_at: 1735112960,
|
||||
}
|
||||
|
||||
return {
|
||||
in: [startNode, httpNode, retry1, retry2],
|
||||
expect: [startNode, {
|
||||
...httpNode,
|
||||
retryDetail: [retry1, retry2],
|
||||
}],
|
||||
}
|
||||
})()
|
||||
@ -0,0 +1,11 @@
|
||||
import format from '.'
|
||||
import { simpleRetryData } from './data'
|
||||
|
||||
describe('retry', () => {
|
||||
test('should have no retry status nodes', () => {
|
||||
expect(format(simpleRetryData.in as any).find(item => (item as any).status === 'retry')).toBeUndefined()
|
||||
})
|
||||
test('should put retry nodes in retryDetail', () => {
|
||||
expect(format(simpleRetryData.in as any)).toEqual(simpleRetryData.expect)
|
||||
})
|
||||
})
|
||||
@ -1,7 +1,29 @@
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import type { NodeTracing } from '@/types/workflow'
|
||||
|
||||
const format = (list: NodeTracing[]): NodeTracing[] => {
|
||||
return list
|
||||
const retryNodes = list.filter((item) => {
|
||||
const { execution_metadata } = item
|
||||
const isInIteration = !!execution_metadata?.iteration_id
|
||||
if (isInIteration || item.node_type === BlockEnum.Iteration) return false
|
||||
return item.status === 'retry'
|
||||
})
|
||||
|
||||
const retryNodeIds = retryNodes.map(item => item.node_id)
|
||||
// move retry nodes to retryDetail
|
||||
const result = list.filter((item) => {
|
||||
return item.status !== 'retry'
|
||||
}).map((item) => {
|
||||
const isRetryBelongNode = retryNodeIds.includes(item.node_id)
|
||||
if (isRetryBelongNode) {
|
||||
return {
|
||||
...item,
|
||||
retryDetail: list.filter(node => node.status === 'retry' && node.node_id === item.node_id),
|
||||
}
|
||||
}
|
||||
return item
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
export default format
|
||||
|
||||
Loading…
Reference in New Issue