Commit 90ee7fe2 authored by JzoNg's avatar JzoNg

tracing

parent bc90fc88
...@@ -18,7 +18,7 @@ const DetailPanel: FC<ILogDetail> = ({ runID, onClose }) => { ...@@ -18,7 +18,7 @@ const DetailPanel: FC<ILogDetail> = ({ runID, onClose }) => {
<XClose className='w-4 h-4 text-gray-500' /> <XClose className='w-4 h-4 text-gray-500' />
</span> </span>
<h1 className='shrink-0 px-4 py-1 text-md font-semibold text-gray-900'>{t('appLog.runDetail.workflowTitle')}</h1> <h1 className='shrink-0 px-4 py-1 text-md font-semibold text-gray-900'>{t('appLog.runDetail.workflowTitle')}</h1>
<Run activeTab='TRACING' runID={runID}/> <Run runID={runID}/>
</div> </div>
) )
} }
......
...@@ -5,14 +5,13 @@ import { useTranslation } from 'react-i18next' ...@@ -5,14 +5,13 @@ import { useTranslation } from 'react-i18next'
import cn from 'classnames' import cn from 'classnames'
import Result from './result' import Result from './result'
import Tracing from './tracing' import Tracing from './tracing'
// import type { App } from '@/types/app'
type RunProps = { type RunProps = {
activeTab: 'RESULT' | 'TRACING' activeTab?: 'RESULT' | 'TRACING'
runID: string runID: string
} }
const RunPanel: FC<RunProps> = ({ activeTab, runID }) => { const RunPanel: FC<RunProps> = ({ activeTab = 'RESULT', runID }) => {
const { t } = useTranslation() const { t } = useTranslation()
const [currentTab, setCurrentTab] = useState<string>(activeTab) const [currentTab, setCurrentTab] = useState<string>(activeTab)
......
...@@ -3,24 +3,15 @@ import { useTranslation } from 'react-i18next' ...@@ -3,24 +3,15 @@ import { useTranslation } from 'react-i18next'
import type { FC } from 'react' import type { FC } from 'react'
import cn from 'classnames' import cn from 'classnames'
import BlockIcon from '../block-icon' import BlockIcon from '../block-icon'
import type { BlockEnum } from '../types'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
import { AlertCircle, AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' import { AlertCircle, AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
import { CheckCircle, Loading02 } from '@/app/components/base/icons/src/vender/line/general' import { CheckCircle, Loading02 } from '@/app/components/base/icons/src/vender/line/general'
import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows' import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
import type { NodeTracing } from '@/types/workflow'
export type NodeInfo = {
type: BlockEnum
title: string
time: number
tokens: number
status: string
error?: string
}
type Props = { type Props = {
nodeInfo: NodeInfo nodeInfo: NodeTracing
collapsed: boolean collapsed: boolean
collapseHandle: () => void collapseHandle: () => void
} }
...@@ -60,9 +51,9 @@ const NodePanel: FC<Props> = ({ nodeInfo, collapsed, collapseHandle }) => { ...@@ -60,9 +51,9 @@ const NodePanel: FC<Props> = ({ nodeInfo, collapsed, collapseHandle }) => {
!collapsed && 'rotate-90', !collapsed && 'rotate-90',
)} )}
/> />
<BlockIcon className='shrink-0 mr-2' type={nodeInfo.type} /> <BlockIcon className='shrink-0 mr-2' type={nodeInfo.node_type} />
<div className='grow text-gray-700 text-[13px] leading-[16px] font-semibold truncate' title={nodeInfo.title}>{nodeInfo.title}</div> <div className='grow text-gray-700 text-[13px] leading-[16px] font-semibold truncate' title={nodeInfo.title}>{nodeInfo.title}</div>
<div className='shrink-0 text-gray-500 text-xs leading-[18px]'>{`${getTime(nodeInfo.time)} · ${getTokenCount(nodeInfo.tokens)} tokens`}</div> <div className='shrink-0 text-gray-500 text-xs leading-[18px]'>{`${getTime(nodeInfo.elapsed_time)} · ${getTokenCount(nodeInfo.execution_metadata.total_tokens)} tokens`}</div>
{nodeInfo.status === 'succeeded' && ( {nodeInfo.status === 'succeeded' && (
<CheckCircle className='shrink-0 ml-2 w-3.5 h-3.5 text-[#12B76A]' /> <CheckCircle className='shrink-0 ml-2 w-3.5 h-3.5 text-[#12B76A]' />
)} )}
......
'use client' 'use client'
import type { FC } from 'react' import type { FC } from 'react'
import React, { useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
// import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector'
// import cn from 'classnames' import produce from 'immer'
import { BlockEnum } from '../types'
import NodePanel from './node' import NodePanel from './node'
import Loading from '@/app/components/base/loading'
import { fetchTracingList } from '@/service/log'
import { useStore as useAppStore } from '@/app/components/app/store'
import type { NodeTracing } from '@/types/workflow'
import { ToastContext } from '@/app/components/base/toast'
type TracingProps = { type TracingProps = {
runID: string runID: string
} }
const nodeInfoFake = {
type: BlockEnum.Start,
title: 'START',
time: 67.349,
tokens: 2708,
status: 'failed',
error: 'lvlvlvlv',
}
const Tracing: FC<TracingProps> = ({ runID }) => { const Tracing: FC<TracingProps> = ({ runID }) => {
// const { t } = useTranslation() const { notify } = useContext(ToastContext)
const [nodeCollapsed, setCurrentTab] = useState<boolean>(false) const { appDetail } = useAppStore()
const [loading, setLoading] = useState<boolean>(true)
const [list, setList] = useState<NodeTracing[]>([])
const [collapseState, setCollapseState] = useState<boolean[]>([])
const getTracingList = useCallback(async (appID: string, runID: string) => {
setLoading(true)
try {
const { data: nodeList } = await fetchTracingList({
url: `/apps/${appID}/workflow-runs/${runID}/node-executions`,
})
const collapseState = nodeList.map(node => node.status === 'succeeded')
setList(nodeList)
setCollapseState(collapseState)
setLoading(false)
}
catch (err) {
notify({
type: 'error',
message: `${err}`,
})
setLoading(false)
}
}, [notify])
useEffect(() => {
if (appDetail && runID)
getTracingList(appDetail.id, runID)
}, [appDetail, getTracingList, runID])
const collapseStateChange = (index: number) => {
const newCollapseState = produce(collapseState, (draft: boolean[]) => {
draft[index] = !draft[index]
})
setCollapseState(newCollapseState)
}
const collapseStateChange = () => { if (loading) {
setCurrentTab(!nodeCollapsed) return (
<div className='flex h-full items-center justify-center bg-white'>
<Loading />
</div>
)
} }
return ( return (
<div className='bg-gray-50 py-2'> <div className='bg-gray-50 py-2'>
<NodePanel nodeInfo={nodeInfoFake} collapsed={nodeCollapsed} collapseHandle={collapseStateChange} /> {list.map((node, index) => (
<NodePanel
key={node.id}
nodeInfo={node}
collapsed={collapseState[index]}
collapseHandle={() => collapseStateChange(index)}
/>
))}
</div> </div>
) )
} }
......
...@@ -19,6 +19,7 @@ import type { ...@@ -19,6 +19,7 @@ import type {
WorkflowLogsResponse, WorkflowLogsResponse,
WorkflowRunDetailResponse, WorkflowRunDetailResponse,
} from '@/models/log' } from '@/models/log'
import type { NodeTracingListResponse } from '@/types/workflow'
export const fetchConversationList: Fetcher<ConversationListResponse, { name: string; appId: string; params?: Record<string, any> }> = ({ appId, params }) => { export const fetchConversationList: Fetcher<ConversationListResponse, { name: string; appId: string; params?: Record<string, any> }> = ({ appId, params }) => {
return get<ConversationListResponse>(`/console/api/apps/${appId}/messages`, params) return get<ConversationListResponse>(`/console/api/apps/${appId}/messages`, params)
...@@ -68,3 +69,7 @@ export const fetchWorkflowLogs: Fetcher<WorkflowLogsResponse, { url: string; par ...@@ -68,3 +69,7 @@ export const fetchWorkflowLogs: Fetcher<WorkflowLogsResponse, { url: string; par
export const fetchRunDetail = ({ appID, runID }: { appID: string; runID: string }) => { export const fetchRunDetail = ({ appID, runID }: { appID: string; runID: string }) => {
return get<WorkflowRunDetailResponse>(`/apps/${appID}/workflow-run/${runID}`) return get<WorkflowRunDetailResponse>(`/apps/${appID}/workflow-run/${runID}`)
} }
export const fetchTracingList: Fetcher<NodeTracingListResponse, { url: string }> = ({ url }) => {
return get<NodeTracingListResponse>(url)
}
import type { Viewport } from 'reactflow' import type { Viewport } from 'reactflow'
import type { import type {
BlockEnum,
Edge, Edge,
Node, Node,
} from '@/app/components/workflow/types' } from '@/app/components/workflow/types'
export type NodeTracing = {
id: string
index: number
predecessor_node_id: string
node_id: string
node_type: BlockEnum
title: string
inputs: any
process_data: any
outputs?: any
status: string
error?: string
elapsed_time: number
execution_metadata: {
total_tokens: number
total_price: number
currency: string
}
created_at: number
created_by: {
id: string
name: string
email: string
}
finished_at: number
}
export type FetchWorkflowDraftResponse = { export type FetchWorkflowDraftResponse = {
id: string id: string
graph: { graph: {
...@@ -14,3 +42,7 @@ export type FetchWorkflowDraftResponse = { ...@@ -14,3 +42,7 @@ export type FetchWorkflowDraftResponse = {
features?: any features?: any
updated_at: number updated_at: number
} }
export type NodeTracingListResponse = {
data: NodeTracing[]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment