feat: llm node support tools

This commit is contained in:
zxhlyh 2025-12-30 10:40:01 +08:00
parent d132abcdb4
commit bbd11c9e89
7 changed files with 146 additions and 0 deletions

View File

@ -0,0 +1,2 @@
export { default as LLMLogTrigger } from './llm-log-trigger'
export { default as LLMResultPanel } from './llm-result-panel'

View File

@ -0,0 +1,41 @@
import type { NodeTracing } from '@/types/workflow'
import {
RiArrowRightSLine,
RiRestartFill,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
type LLMLogTriggerProps = {
nodeInfo: NodeTracing
onShowLLMDetail: (detail: NodeTracing[]) => void
}
const LLMLogTrigger = ({
nodeInfo,
onShowLLMDetail,
}: LLMLogTriggerProps) => {
const { t } = useTranslation()
const { retryDetail } = nodeInfo
const handleShowRetryResultList = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
onShowLLMDetail(retryDetail || [])
}
return (
<Button
className="mb-1 flex w-full items-center justify-between"
variant="tertiary"
onClick={handleShowRetryResultList}
>
<div className="flex items-center">
<RiRestartFill className="mr-0.5 h-4 w-4 shrink-0 text-components-button-tertiary-text" />
{t('nodes.common.retry.retries', { ns: 'workflow', num: retryDetail?.length })}
</div>
<RiArrowRightSLine className="h-4 w-4 shrink-0 text-components-button-tertiary-text" />
</Button>
)
}
export default LLMLogTrigger

View File

@ -0,0 +1,46 @@
'use client'
import type { FC } from 'react'
import type { NodeTracing } from '@/types/workflow'
import {
RiArrowLeftLine,
} from '@remixicon/react'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import TracingPanel from '../tracing-panel'
type Props = {
list: NodeTracing[]
onBack: () => void
}
const LLMResultPanel: FC<Props> = ({
list,
onBack,
}) => {
const { t } = useTranslation()
return (
<div>
<div
className="system-sm-medium flex h-8 cursor-pointer items-center bg-components-panel-bg px-4 text-text-accent-secondary"
onClick={(e) => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
onBack()
}}
>
<RiArrowLeftLine className="mr-1 h-4 w-4" />
{t('singleRun.back', { ns: 'workflow' })}
</div>
<TracingPanel
list={list.map((item, index) => ({
...item,
title: `${t('nodes.common.retry.retry', { ns: 'workflow' })} ${index + 1}`,
}))}
className="bg-background-section-burn"
/>
</div>
)
}
export default memo(LLMResultPanel)

View File

@ -29,6 +29,7 @@ import { BlockEnum } from '../types'
import LargeDataAlert from '../variable-inspect/large-data-alert'
import { AgentLogTrigger } from './agent-log'
import { IterationLogTrigger } from './iteration-log'
import { LLMLogTrigger } from './llm-log'
import { LoopLogTrigger } from './loop-log'
import { RetryLogTrigger } from './retry-log'
@ -43,6 +44,7 @@ type Props = {
onShowLoopDetail?: (detail: NodeTracing[][], loopDurationMap: LoopDurationMap, loopVariableMap: LoopVariableMap) => void
onShowRetryDetail?: (detail: NodeTracing[]) => void
onShowAgentOrToolLog?: (detail?: AgentLogItemWithChildren) => void
onShowLLMDetail?: (detail: NodeTracing[]) => void
notShowIterationNav?: boolean
notShowLoopNav?: boolean
}
@ -58,6 +60,7 @@ const NodePanel: FC<Props> = ({
onShowLoopDetail,
onShowRetryDetail,
onShowAgentOrToolLog,
onShowLLMDetail,
notShowIterationNav,
notShowLoopNav,
}) => {
@ -96,6 +99,7 @@ const NodePanel: FC<Props> = ({
const isRetryNode = hasRetryNode(nodeInfo.node_type) && !!nodeInfo.retryDetail?.length
const isAgentNode = nodeInfo.node_type === BlockEnum.Agent && !!nodeInfo.agentLog?.length
const isToolNode = nodeInfo.node_type === BlockEnum.Tool && !!nodeInfo.agentLog?.length
const isLLMNode = nodeInfo.node_type === BlockEnum.LLM && !!nodeInfo.generation_detail
const inputsTitle = useMemo(() => {
let text = t('common.input', { ns: 'workflow' })
@ -193,6 +197,12 @@ const NodePanel: FC<Props> = ({
onShowRetryResultList={onShowRetryDetail}
/>
)}
{isLLMNode && onShowLLMDetail && (
<LLMLogTrigger
nodeInfo={nodeInfo}
onShowLLMDetail={onShowLLMDetail}
/>
)}
{
(isAgentNode || isToolNode) && onShowAgentOrToolLog && (
<AgentLogTrigger

View File

@ -15,6 +15,7 @@ import { RetryLogTrigger } from '@/app/components/workflow/run/retry-log'
import { BlockEnum } from '@/app/components/workflow/types'
import { hasRetryNode } from '@/app/components/workflow/utils'
import LargeDataAlert from '../variable-inspect/large-data-alert'
import { LLMLogTrigger } from './llm-log'
import MetaData from './meta'
import StatusPanel from './status'
@ -45,6 +46,7 @@ export type ResultPanelProps = {
handleShowLoopResultList?: (detail: NodeTracing[][], loopDurationMap: any) => void
onShowRetryDetail?: (detail: NodeTracing[]) => void
handleShowAgentOrToolLog?: (detail?: AgentLogItemWithChildren) => void
onShowLLMDetail?: (detail: NodeTracing[]) => void
}
const ResultPanel: FC<ResultPanelProps> = ({
@ -71,6 +73,7 @@ const ResultPanel: FC<ResultPanelProps> = ({
handleShowLoopResultList,
onShowRetryDetail,
handleShowAgentOrToolLog,
onShowLLMDetail,
}) => {
const { t } = useTranslation()
const isIterationNode = nodeInfo?.node_type === BlockEnum.Iteration && !!nodeInfo?.details?.length
@ -78,6 +81,7 @@ const ResultPanel: FC<ResultPanelProps> = ({
const isRetryNode = hasRetryNode(nodeInfo?.node_type) && !!nodeInfo?.retryDetail?.length
const isAgentNode = nodeInfo?.node_type === BlockEnum.Agent && !!nodeInfo?.agentLog?.length
const isToolNode = nodeInfo?.node_type === BlockEnum.Tool && !!nodeInfo?.agentLog?.length
const isLLMNode = nodeInfo?.node_type === BlockEnum.LLM && !!nodeInfo?.generation_detail
return (
<div className="bg-components-panel-bg py-2">
@ -116,6 +120,14 @@ const ResultPanel: FC<ResultPanelProps> = ({
/>
)
}
{
isLLMNode && onShowLLMDetail && (
<LLMLogTrigger
nodeInfo={nodeInfo}
onShowLLMDetail={onShowLLMDetail}
/>
)
}
{
(isAgentNode || isToolNode) && handleShowAgentOrToolLog && (
<AgentLogTrigger

View File

@ -7,6 +7,7 @@ import type {
} from '@/types/workflow'
import { AgentResultPanel } from './agent-log'
import { IterationResultPanel } from './iteration-log'
import { LLMResultPanel } from './llm-log'
import { LoopResultPanel } from './loop-log'
import { RetryResultPanel } from './retry-log'
@ -29,6 +30,10 @@ export type SpecialResultPanelProps = {
agentOrToolLogItemStack?: AgentLogItemWithChildren[]
agentOrToolLogListMap?: Record<string, AgentLogItemWithChildren[]>
handleShowAgentOrToolLog?: (detail?: AgentLogItemWithChildren) => void
showLLMDetail?: boolean
setShowLLMDetailFalse?: () => void
llmResultList?: NodeTracing[]
}
const SpecialResultPanel = ({
showRetryDetail,
@ -49,6 +54,10 @@ const SpecialResultPanel = ({
agentOrToolLogItemStack,
agentOrToolLogListMap,
handleShowAgentOrToolLog,
showLLMDetail,
setShowLLMDetailFalse,
llmResultList,
}: SpecialResultPanelProps) => {
return (
<div onClick={(e) => {
@ -64,6 +73,14 @@ const SpecialResultPanel = ({
/>
)
}
{
!!showLLMDetail && !!llmResultList?.length && setShowLLMDetailFalse && (
<LLMResultPanel
list={llmResultList}
onBack={setShowLLMDetailFalse}
/>
)
}
{
showIteratingDetail && !!iterationResultList?.length && setShowIteratingDetailFalse && (
<IterationResultPanel

View File

@ -28,6 +28,23 @@ export type AgentLogItemWithChildren = AgentLogItem & {
children: AgentLogItemWithChildren[]
}
export type ToolCallDetail = {
id: string
name: string
arguments: string
result: string
}
export type SequenceSegment
= | { type: 'context', start: number, end: number }
| { type: 'reasoning', index: number }
| { type: 'tool_call', index: number }
export type LLMLogItem = {
reasoning_content: string[]
tool_calls: ToolCallDetail[]
sequence: SequenceSegment[]
}
export type NodeTracing = {
id: string
index: number
@ -104,6 +121,7 @@ export type NodeTracing = {
parent_parallel_id?: string
parent_parallel_start_node_id?: string
agentLog?: AgentLogItemWithChildren[] // agent log
generation_detail?: LLMLogItem
}
export type FetchWorkflowDraftResponse = {