diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/index.ts b/web/app/components/workflow/hooks/use-workflow-run-event/index.ts index 61216017e0..70528f7e79 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/index.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/index.ts @@ -9,3 +9,4 @@ export * from './use-workflow-node-iteration-finished' export * from './use-workflow-node-retry' export * from './use-workflow-text-chunk' export * from './use-workflow-text-replace' +export * from './use-workflow-agent-log' diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-agent-log.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-agent-log.ts new file mode 100644 index 0000000000..9a9fa628c0 --- /dev/null +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-agent-log.ts @@ -0,0 +1,50 @@ +import { useCallback } from 'react' +import produce from 'immer' +import type { AgentLogResponse } from '@/types/workflow' +import { useWorkflowStore } from '@/app/components/workflow/store' + +export const useWorkflowAgentLog = () => { + const workflowStore = useWorkflowStore() + + const handleWorkflowAgentLog = useCallback((params: AgentLogResponse) => { + const { data } = params + const { + workflowRunningData, + setWorkflowRunningData, + } = workflowStore.getState() + + setWorkflowRunningData(produce(workflowRunningData!, (draft) => { + const currentIndex = draft.tracing!.findIndex(item => item.node_id === data.node_id) + if (currentIndex > -1) { + const current = draft.tracing![currentIndex] + + if (current.execution_metadata) { + if (current.execution_metadata.agent_log) { + const currentLogIndex = current.execution_metadata.agent_log.findIndex(log => log.id === data.id) + if (currentLogIndex > -1) { + current.execution_metadata.agent_log[currentLogIndex] = { + ...current.execution_metadata.agent_log[currentLogIndex], + ...data, + } + } + else { + current.execution_metadata.agent_log.push(data) + } + } + else { + current.execution_metadata.agent_log = [data] + } + } + else { + current.execution_metadata = { + agent_log: [data], + } as any + } + } + })) + }, [workflowStore]) + + return { + handleWorkflowAgentLog, + } +} diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-run-event.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-run-event.ts index 82b76da22d..8ba6220818 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-run-event.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-run-event.ts @@ -1,4 +1,5 @@ import { + useWorkflowAgentLog, useWorkflowFailed, useWorkflowFinished, useWorkflowNodeFinished, @@ -24,6 +25,7 @@ export const useWorkflowRunEvent = () => { const { handleWorkflowNodeRetry } = useWorkflowNodeRetry() const { handleWorkflowTextChunk } = useWorkflowTextChunk() const { handleWorkflowTextReplace } = useWorkflowTextReplace() + const { handleWorkflowAgentLog } = useWorkflowAgentLog() return { handleWorkflowStarted, @@ -37,5 +39,6 @@ export const useWorkflowRunEvent = () => { handleWorkflowNodeRetry, handleWorkflowTextChunk, handleWorkflowTextReplace, + handleWorkflowAgentLog, } } diff --git a/web/app/components/workflow/hooks/use-workflow-run.ts b/web/app/components/workflow/hooks/use-workflow-run.ts index 1224e8136e..00e7faeeed 100644 --- a/web/app/components/workflow/hooks/use-workflow-run.ts +++ b/web/app/components/workflow/hooks/use-workflow-run.ts @@ -37,6 +37,7 @@ export const useWorkflowRun = () => { handleWorkflowNodeIterationNext, handleWorkflowNodeIterationFinished, handleWorkflowNodeRetry, + handleWorkflowAgentLog, handleWorkflowTextChunk, handleWorkflowTextReplace, } = useWorkflowRunEvent() @@ -118,6 +119,7 @@ export const useWorkflowRun = () => { onIterationNext, onIterationFinish, onNodeRetry, + onAgentLog, onError, ...restCallback } = callback || {} @@ -234,6 +236,12 @@ export const useWorkflowRun = () => { if (onNodeRetry) onNodeRetry(params) }, + onAgentLog: (params) => { + handleWorkflowAgentLog(params) + + if (onAgentLog) + onAgentLog(params) + }, onTextChunk: (params) => { handleWorkflowTextChunk(params) }, @@ -252,7 +260,7 @@ export const useWorkflowRun = () => { ...restCallback, }, ) - }, [store, workflowStore, doSyncWorkflowDraft, handleWorkflowStarted, handleWorkflowFinished, handleWorkflowFailed, handleWorkflowNodeStarted, handleWorkflowNodeFinished, handleWorkflowNodeIterationStarted, handleWorkflowNodeIterationNext, handleWorkflowNodeIterationFinished, handleWorkflowNodeRetry, handleWorkflowTextChunk, handleWorkflowTextReplace, pathname]) + }, [store, workflowStore, doSyncWorkflowDraft, handleWorkflowStarted, handleWorkflowFinished, handleWorkflowFailed, handleWorkflowNodeStarted, handleWorkflowNodeFinished, handleWorkflowNodeIterationStarted, handleWorkflowNodeIterationNext, handleWorkflowNodeIterationFinished, handleWorkflowNodeRetry, handleWorkflowTextChunk, handleWorkflowTextReplace, handleWorkflowAgentLog, pathname]) const handleStopRun = useCallback((taskId: string) => { const appId = useAppStore.getState().appDetail?.id diff --git a/web/app/components/workflow/panel/debug-and-preview/hooks.ts b/web/app/components/workflow/panel/debug-and-preview/hooks.ts index d91cf1082d..81cccd9cc7 100644 --- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts +++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts @@ -404,6 +404,63 @@ export const useChat = ( })) } }, + onAgentLog: ({ data }) => { + const currentNodeIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.node_id === data.node_id) + if (currentNodeIndex > -1) { + const current = responseItem.workflowProcess!.tracing![currentNodeIndex] + + if (current.execution_metadata) { + if (current.execution_metadata.agent_log) { + const currentLogIndex = current.execution_metadata.agent_log.findIndex(log => log.id === data.id) + if (currentLogIndex > -1) { + current.execution_metadata.agent_log[currentLogIndex] = { + ...current.execution_metadata.agent_log[currentLogIndex], + ...data, + } + } + else { + current.execution_metadata.agent_log.push(data) + } + } + else { + current.execution_metadata.agent_log = [data] + } + } + else { + current.execution_metadata = { + agent_log: [data], + } as any + } + // if (current.agentLog) { + // const currentLogIndex = current.agentLog.findIndex(log => log.id === data.id) + + // if (currentLogIndex > -1) { + // current.agentLog[currentLogIndex] = { + // ...current.agentLog[currentLogIndex], + // ...data, + // } + // } + // else { + // current.agentLog.push(data) + // } + // } + // else { + // current.agentLog = [data] + // } + + responseItem.workflowProcess!.tracing[currentNodeIndex] = { + ...current, + } + + handleUpdateChatList(produce(chatListRef.current, (draft) => { + const currentIndex = draft.findIndex(item => item.id === responseItem.id) + draft[currentIndex] = { + ...draft[currentIndex], + ...responseItem, + } + })) + } + }, }, ) }, [handleRun, handleResponding, handleUpdateChatList, notify, t, updateCurrentQA, config.suggested_questions_after_answer?.enabled, formSettings]) diff --git a/web/app/components/workflow/run/agent-log/agent-log-item.tsx b/web/app/components/workflow/run/agent-log/agent-log-item.tsx index 49c279d58a..0b84827500 100644 --- a/web/app/components/workflow/run/agent-log/agent-log-item.tsx +++ b/web/app/components/workflow/run/agent-log/agent-log-item.tsx @@ -1,4 +1,7 @@ -import { useState } from 'react' +import { + useMemo, + useState, +} from 'react' import { RiArrowRightSLine, RiListView, @@ -9,6 +12,9 @@ import type { AgentLogItemWithChildren } from '@/types/workflow' import NodeStatusIcon from '@/app/components/workflow/nodes/_base/components/node-status-icon' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' +import BlockIcon from '@/app/components/workflow/block-icon' +import { BlockEnum } from '@/app/components/workflow/types' +import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' type AgentLogItemProps = { item: AgentLogItemWithChildren @@ -26,6 +32,26 @@ const AgentLogItem = ({ metadata, } = item const [expanded, setExpanded] = useState(false) + const { getIconUrl } = useGetIcon() + const toolIcon = useMemo(() => { + const icon = metadata?.icon + + if (icon) { + if (icon.includes('http')) + return icon + + return getIconUrl(icon) + } + + return '' + }, [getIconUrl, metadata?.icon]) + + const mergeStatus = useMemo(() => { + if (status === 'start') + return 'running' + + return status + }, [status]) return (
@@ -41,7 +67,11 @@ const AgentLogItem = ({ ? : } -
+
{metadata?.elapsed_time?.toFixed(3)}s
) } - +
{ expanded && ( diff --git a/web/app/components/workflow/run/agent-log/agent-log-trigger.tsx b/web/app/components/workflow/run/agent-log/agent-log-trigger.tsx index 589624f559..86418a1c69 100644 --- a/web/app/components/workflow/run/agent-log/agent-log-trigger.tsx +++ b/web/app/components/workflow/run/agent-log/agent-log-trigger.tsx @@ -14,21 +14,29 @@ const AgentLogTrigger = ({ onShowAgentOrToolLog, }: AgentLogTriggerProps) => { const { t } = useTranslation() - const { agentLog } = nodeInfo + const { agentLog, execution_metadata } = nodeInfo + const agentStrategy = execution_metadata?.tool_info?.agent_strategy return ( -
+
{ + onShowAgentOrToolLog({ id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren) + }} + >
{t('workflow.nodes.agent.strategy.label')}
-
-
+ { + agentStrategy && ( +
+ {agentStrategy} +
+ ) + }
{ - onShowAgentOrToolLog({ id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren) - }} > {t('runLog.detail')} diff --git a/web/service/base.ts b/web/service/base.ts index c34a1f0e9c..38aaae0b1c 100644 --- a/web/service/base.ts +++ b/web/service/base.ts @@ -4,6 +4,7 @@ import Toast from '@/app/components/base/toast' import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type' import type { VisionFile } from '@/types/app' import type { + AgentLogResponse, IterationFinishedResponse, IterationNextResponse, IterationStartedResponse, @@ -53,6 +54,7 @@ export type IOnTextChunk = (textChunk: TextChunkResponse) => void export type IOnTTSChunk = (messageId: string, audioStr: string, audioType?: string) => void export type IOnTTSEnd = (messageId: string, audioStr: string, audioType?: string) => void export type IOnTextReplace = (textReplace: TextReplaceResponse) => void +export type IOnAgentLog = (agentLog: AgentLogResponse) => void export type IOtherOptions = { isPublicAPI?: boolean @@ -84,6 +86,7 @@ export type IOtherOptions = { onTTSChunk?: IOnTTSChunk onTTSEnd?: IOnTTSEnd onTextReplace?: IOnTextReplace + onAgentLog?: IOnAgentLog } function unicodeToChar(text: string) { @@ -129,6 +132,7 @@ const handleStream = ( onTTSChunk?: IOnTTSChunk, onTTSEnd?: IOnTTSEnd, onTextReplace?: IOnTextReplace, + onAgentLog?: IOnAgentLog, ) => { if (!response.ok) throw new Error('Network response was not ok') @@ -229,6 +233,9 @@ const handleStream = ( else if (bufferObj.event === 'text_replace') { onTextReplace?.(bufferObj as TextReplaceResponse) } + else if (bufferObj.event === 'agent_log') { + onAgentLog?.(bufferObj as AgentLogResponse) + } else if (bufferObj.event === 'tts_message') { onTTSChunk?.(bufferObj.message_id, bufferObj.audio, bufferObj.audio_type) } @@ -322,6 +329,7 @@ export const ssePost = ( onTTSChunk, onTTSEnd, onTextReplace, + onAgentLog, onError, getAbortController, } = otherOptions @@ -392,7 +400,7 @@ export const ssePost = ( return } onData?.(str, isFirstMessage, moreInfo) - }, onCompleted, onThought, onMessageEnd, onMessageReplace, onFile, onWorkflowStarted, onWorkflowFinished, onNodeStarted, onNodeFinished, onIterationStart, onIterationNext, onIterationFinish, onNodeRetry, onParallelBranchStarted, onParallelBranchFinished, onTextChunk, onTTSChunk, onTTSEnd, onTextReplace) + }, onCompleted, onThought, onMessageEnd, onMessageReplace, onFile, onWorkflowStarted, onWorkflowFinished, onNodeStarted, onNodeFinished, onIterationStart, onIterationNext, onIterationFinish, onNodeRetry, onParallelBranchStarted, onParallelBranchFinished, onTextChunk, onTTSChunk, onTTSEnd, onTextReplace, onAgentLog) }).catch((e) => { if (e.toString() !== 'AbortError: The user aborted a request.' && !e.toString().errorMessage.includes('TypeError: Cannot assign to read only property')) Toast.notify({ type: 'error', message: e }) diff --git a/web/types/workflow.ts b/web/types/workflow.ts index 9cc1cbac15..31ea759582 100644 --- a/web/types/workflow.ts +++ b/web/types/workflow.ts @@ -6,6 +6,7 @@ import type { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/ export type AgentLogItem = { node_execution_id: string, id: string, + node_id: string, parent_id?: string, label: string, data: object, // debug data @@ -14,6 +15,7 @@ export type AgentLogItem = { metadata?: { elapsed_time?: number provider?: string + icon?: string }, } @@ -51,6 +53,10 @@ export type NodeTracing = { iteration_duration_map?: IterationDurationMap error_strategy?: ErrorHandleTypeEnum agent_log?: AgentLogItem[] + tool_info?: { + agent_strategy?: string + icon?: string + } } metadata: { iterator_length: number @@ -230,6 +236,12 @@ export type TextReplaceResponse = { } } +export type AgentLogResponse = { + task_id: string + event: string + data: AgentLogItemWithChildren +} + export type WorkflowRunHistory = { id: string sequence_number: number