feat: llm node support tools

This commit is contained in:
zxhlyh 2026-01-07 16:28:41 +08:00
parent 04f40303fd
commit 1d93f41fcf
23 changed files with 282 additions and 76 deletions

View File

@ -1,5 +1,5 @@
import type { ToolCallItem } from '../../type'
import ToolCallsItem from './item'
import type { ToolCallItem } from '@/types/workflow'
import ToolCallItemComponent from '@/app/components/workflow/run/llm-log/tool-call-item'
type ToolCallsProps = {
toolCalls: ToolCallItem[]
@ -8,9 +8,13 @@ const ToolCalls = ({
toolCalls,
}: ToolCallsProps) => {
return (
<div>
<div className="my-1 space-y-1">
{toolCalls.map((toolCall: ToolCallItem) => (
<ToolCallsItem key={toolCall.tool_call_id} payload={toolCall} />
<ToolCallItemComponent
key={toolCall.tool_call_id}
payload={toolCall}
className="bg-background-gradient-bg-fill-chat-bubble-bg-2 shadow-none"
/>
))}
</div>
)

View File

@ -45,7 +45,7 @@ const WorkflowProcessItem = ({
return (
<div
className={cn(
'-mx-1 rounded-xl px-2.5',
'rounded-xl px-2.5',
collapse ? 'border-l-[0.25px] border-components-panel-border py-[7px]' : 'border-[0.5px] border-components-panel-border-subtle px-1 pb-1 pt-[7px]',
running && !collapse && 'bg-background-section-burn',
succeeded && !collapse && 'bg-state-success-hover',

View File

@ -2,7 +2,7 @@ import type { FileEntity } from '@/app/components/base/file-uploader/types'
import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { InputVarType } from '@/app/components/workflow/types'
import type { Annotation, MessageRating } from '@/models/log'
import type { FileResponse } from '@/types/workflow'
import type { FileResponse, ToolCallItem } from '@/types/workflow'
export type MessageMore = {
time: string
@ -64,15 +64,9 @@ export type CitationItem = {
word_count: number
}
export type ToolCallItem = {
is_thought?: boolean
tool_call_id?: string
tool_name?: string
tool_arguments?: string
tool_files?: string[]
tool_error?: string
tool_output?: string
tool_elapsed_time?: number
export type IconObject = {
background: string
content: string
}
export type IChatItem = {

View File

@ -0,0 +1,4 @@
<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 9.49479C0.782372 8.51826 0 7.01768 0 5.33333C0 2.38782 2.38782 0 5.33333 0C8.20841 0 10.5503 2.27504 10.6608 5.12305L11.888 6.96354C12.0843 7.25794 12.0161 7.65424 11.7331 7.86654L10.6667 8.66602V10C10.6667 10.7364 10.0697 11.3333 9.33333 11.3333H8V13.3333H6.66667V10.6667C6.66667 10.2985 6.96514 10 7.33333 10H9.33333V8.33333C9.33333 8.12349 9.43239 7.92603 9.60026 7.80013L10.4284 7.17838L9.44531 5.70312C9.3723 5.59361 9.33333 5.46495 9.33333 5.33333C9.33333 3.1242 7.54248 1.33333 5.33333 1.33333C3.1242 1.33333 1.33333 3.1242 1.33333 5.33333C1.33333 6.69202 2.0103 7.89261 3.04818 8.61654C3.2269 8.74119 3.33329 8.94552 3.33333 9.16341V13.3333H2V9.49479Z" fill="#354052"/>
<path d="M6.04367 4.24012L5.6504 3.21778C5.59993 3.08657 5.47393 3 5.33333 3C5.19273 3 5.06673 3.08657 5.01627 3.21778L4.62303 4.24012C4.55531 4.41618 4.41618 4.55531 4.24012 4.62303L3.21778 5.01624C3.08657 5.0667 3 5.19276 3 5.33333C3 5.47393 3.08657 5.59993 3.21778 5.6504L4.24012 6.04367C4.41618 6.11133 4.55531 6.25047 4.62303 6.42653L5.01627 7.44887C5.06673 7.58007 5.19273 7.66667 5.33333 7.66667C5.47393 7.66667 5.59993 7.58007 5.6504 7.44887L6.04367 6.42653C6.11133 6.25047 6.25047 6.11133 6.42653 6.04367L7.44887 5.6504C7.58007 5.59993 7.66667 5.47393 7.66667 5.33333C7.66667 5.19276 7.58007 5.0667 7.44887 5.01624L6.42653 4.62303C6.25047 4.55531 6.11133 4.41618 6.04367 4.24012Z" fill="#354052"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,35 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "12",
"height": "14",
"viewBox": "0 0 12 14",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"d": "M2 9.49479C0.782372 8.51826 0 7.01768 0 5.33333C0 2.38782 2.38782 0 5.33333 0C8.20841 0 10.5503 2.27504 10.6608 5.12305L11.888 6.96354C12.0843 7.25794 12.0161 7.65424 11.7331 7.86654L10.6667 8.66602V10C10.6667 10.7364 10.0697 11.3333 9.33333 11.3333H8V13.3333H6.66667V10.6667C6.66667 10.2985 6.96514 10 7.33333 10H9.33333V8.33333C9.33333 8.12349 9.43239 7.92603 9.60026 7.80013L10.4284 7.17838L9.44531 5.70312C9.3723 5.59361 9.33333 5.46495 9.33333 5.33333C9.33333 3.1242 7.54248 1.33333 5.33333 1.33333C3.1242 1.33333 1.33333 3.1242 1.33333 5.33333C1.33333 6.69202 2.0103 7.89261 3.04818 8.61654C3.2269 8.74119 3.33329 8.94552 3.33333 9.16341V13.3333H2V9.49479Z",
"fill": "currentColor"
},
"children": []
},
{
"type": "element",
"name": "path",
"attributes": {
"d": "M6.04367 4.24012L5.6504 3.21778C5.59993 3.08657 5.47393 3 5.33333 3C5.19273 3 5.06673 3.08657 5.01627 3.21778L4.62303 4.24012C4.55531 4.41618 4.41618 4.55531 4.24012 4.62303L3.21778 5.01624C3.08657 5.0667 3 5.19276 3 5.33333C3 5.47393 3.08657 5.59993 3.21778 5.6504L4.24012 6.04367C4.41618 6.11133 4.55531 6.25047 4.62303 6.42653L5.01627 7.44887C5.06673 7.58007 5.19273 7.66667 5.33333 7.66667C5.47393 7.66667 5.59993 7.58007 5.6504 7.44887L6.04367 6.42653C6.11133 6.25047 6.25047 6.11133 6.42653 6.04367L7.44887 5.6504C7.58007 5.59993 7.66667 5.47393 7.66667 5.33333C7.66667 5.19276 7.58007 5.0667 7.44887 5.01624L6.42653 4.62303C6.25047 4.55531 6.11133 4.41618 6.04367 4.24012Z",
"fill": "currentColor"
},
"children": []
}
]
},
"name": "Thinking"
}

View File

@ -0,0 +1,20 @@
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import type { IconData } from '@/app/components/base/icons/IconBase'
import * as React from 'react'
import IconBase from '@/app/components/base/icons/IconBase'
import data from './Thinking.json'
const Icon = (
{
ref,
...props
}: React.SVGProps<SVGSVGElement> & {
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>
},
) => <IconBase {...props} ref={ref} data={data as IconData} />
Icon.displayName = 'Thinking'
export default Icon

View File

@ -24,6 +24,7 @@ export { default as ParameterExtractor } from './ParameterExtractor'
export { default as QuestionClassifier } from './QuestionClassifier'
export { default as Schedule } from './Schedule'
export { default as TemplatingTransform } from './TemplatingTransform'
export { default as Thinking } from './Thinking'
export { default as TriggerAll } from './TriggerAll'
export { default as VariableX } from './VariableX'
export { default as WebhookLine } from './WebhookLine'

View File

@ -2,6 +2,7 @@ import type { FC } from 'react'
import {
RiFileTextLine,
RiFilmAiLine,
RiHammerLine,
RiImageCircleAiLine,
RiVoiceAiFill,
} from '@remixicon/react'
@ -38,17 +39,33 @@ const FeatureIcon: FC<FeatureIconProps> = ({
// )
// }
// if (feature === ModelFeatureEnum.toolCall) {
// return (
// <Tooltip
// popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.toolCall })}
// >
// <ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}>
// <MagicWand className='w-3 h-3' />
// </ModelBadge>
// </Tooltip>
// )
// }
if (feature === ModelFeatureEnum.toolCall) {
if (showFeaturesLabel) {
return (
<ModelBadge className={cn('gap-x-0.5', className)}>
<RiHammerLine className="size-3" />
<span>{ModelFeatureTextEnum.toolCall}</span>
</ModelBadge>
)
}
return (
<Tooltip
popupContent={t('modelProvider.featureSupported', { ns: 'common', feature: ModelFeatureTextEnum.toolCall })}
>
<div className="inline-block cursor-help">
<ModelBadge
className={cn(
'w-[18px] justify-center !px-0',
className,
)}
>
<RiHammerLine className="size-3" />
</ModelBadge>
</div>
</Tooltip>
)
}
// if (feature === ModelFeatureEnum.multiToolCall) {
// return (

View File

@ -96,6 +96,14 @@ const PopupItem: FC<PopupItemProps> = ({
<div className='text-text-tertiary system-xs-regular'>{currentProvider?.description?.[language] || currentProvider?.description?.en_US}</div>
)} */}
<div className="flex flex-wrap gap-1">
{
modelItem.features?.includes(ModelFeatureEnum.toolCall) && (
<FeatureIcon
feature={ModelFeatureEnum.toolCall}
showFeaturesLabel
/>
)
}
{modelItem.model_type && (
<ModelBadge>
{modelTypeFormat(modelItem.model_type)}
@ -118,7 +126,7 @@ const PopupItem: FC<PopupItemProps> = ({
<div className="pt-2">
<div className="system-2xs-medium-uppercase mb-1 text-text-tertiary">{t('model.capabilities', { ns: 'common' })}</div>
<div className="flex flex-wrap gap-1">
{modelItem.features?.map(feature => (
{modelItem.features?.filter(feature => feature !== ModelFeatureEnum.toolCall).map(feature => (
<FeatureIcon
key={feature}
feature={feature}

View File

@ -275,6 +275,8 @@ export const useChat = (
messageId,
taskId,
chunk_type,
tool_icon,
tool_icon_dark,
tool_call_id,
tool_name,
tool_arguments,
@ -288,9 +290,12 @@ export const useChat = (
if (!responseItem.toolCalls)
responseItem.toolCalls = []
responseItem.toolCalls?.push({
type: 'tool',
tool_call_id,
tool_name,
tool_arguments,
tool_icon,
tool_icon_dark,
tool_files,
tool_error,
tool_elapsed_time,
@ -305,16 +310,22 @@ export const useChat = (
}
if (chunk_type === 'thought_start') {
console.log(message, 'xx1')
responseItem.toolCalls?.push({
is_thought: true,
type: 'thought',
tool_elapsed_time,
})
}
if (chunk_type === 'thought_end') {
console.log(message, 'xx2')
// const currentThoughtIndex = responseItem.toolCalls?.findIndex(item => item.is_thought) ?? -1
// if (currentThoughtIndex > -1)
// responseItem.toolCalls![currentThoughtIndex].tool_output = message
}
if (chunk_type === 'thought') {
const currentThoughtIndex = responseItem.toolCalls?.findIndex(item => item.is_thought) ?? -1
if (currentThoughtIndex > -1)
responseItem.toolCalls![currentThoughtIndex].tool_output = message
console.log(message, 'xx3')
}
if (messageId && !hasSetResponseId) {

View File

@ -1,6 +1,7 @@
import type {
AgentLogItemWithChildren,
IterationDurationMap,
LLMTraceItem,
LoopDurationMap,
LoopVariableMap,
NodeTracing,
@ -79,8 +80,18 @@ export const useLogs = () => {
}
}, [setAgentOrToolLogItemStack, setAgentOrToolLogListMap])
const [showLLMDetail, {
setTrue: setShowLLMDetailTrue,
setFalse: setShowLLMDetailFalse,
}] = useBoolean(false)
const [llmResultList, setLLMResultList] = useState<LLMTraceItem[]>([])
const handleShowLLMDetail = useCallback((detail: LLMTraceItem[]) => {
setShowLLMDetailTrue()
setLLMResultList(detail)
}, [setShowLLMDetailTrue, setLLMResultList])
return {
showSpecialResultPanel: showRetryDetail || showIteratingDetail || showLoopingDetail || !!agentOrToolLogItemStack.length,
showSpecialResultPanel: showRetryDetail || showIteratingDetail || showLoopingDetail || !!agentOrToolLogItemStack.length || showLLMDetail,
showRetryDetail,
setShowRetryDetailTrue,
setShowRetryDetailFalse,
@ -111,5 +122,12 @@ export const useLogs = () => {
agentOrToolLogItemStack,
agentOrToolLogListMap,
handleShowAgentOrToolLog,
showLLMDetail,
setShowLLMDetailTrue,
setShowLLMDetailFalse,
llmResultList,
setLLMResultList,
handleShowLLMDetail,
}
}

View File

@ -153,7 +153,7 @@ const RunPanel: FC<RunProps> = ({
</div>
</div>
{/* panel detail */}
<div ref={ref} className={cn('relative h-0 grow overflow-y-auto rounded-b-xl bg-components-panel-bg')}>
<div ref={ref} className={cn('relative h-0 grow overflow-y-auto rounded-b-xl bg-background-section')}>
{loading && (
<div className="flex h-full items-center justify-center bg-components-panel-bg">
<Loading />
@ -192,7 +192,7 @@ const RunPanel: FC<RunProps> = ({
)}
{!loading && currentTab === 'TRACING' && (
<TracingPanel
className="bg-background-section-burn"
className="bg-background-section"
list={list}
/>
)}

View File

@ -1,37 +1,37 @@
import type { NodeTracing } from '@/types/workflow'
import type { LLMTraceItem, NodeTracing } from '@/types/workflow'
import {
RiArrowRightSLine,
RiRestartFill,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import { Thinking } from '@/app/components/base/icons/src/vender/workflow'
type LLMLogTriggerProps = {
nodeInfo: NodeTracing
onShowLLMDetail: (detail: NodeTracing[]) => void
onShowLLMDetail: (detail: LLMTraceItem[]) => void
}
const LLMLogTrigger = ({
nodeInfo,
onShowLLMDetail,
}: LLMLogTriggerProps) => {
const { t } = useTranslation()
const { retryDetail } = nodeInfo
const llmTrace = nodeInfo?.execution_metadata?.llm_trace || []
const handleShowRetryResultList = (e: React.MouseEvent<HTMLButtonElement>) => {
const handleShowLLMDetail = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
onShowLLMDetail(retryDetail || [])
onShowLLMDetail(llmTrace || [])
}
return (
<Button
className="mb-1 flex w-full items-center justify-between"
variant="tertiary"
onClick={handleShowRetryResultList}
onClick={handleShowLLMDetail}
>
<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 })}
<Thinking className="mr-[5px] h-4 w-4 shrink-0 text-components-button-tertiary-text" />
{t('detail', { ns: 'runLog' })}
</div>
<RiArrowRightSLine className="h-4 w-4 shrink-0 text-components-button-tertiary-text" />
</Button>

View File

@ -1,16 +1,19 @@
'use client'
import type { FC } from 'react'
import type { NodeTracing } from '@/types/workflow'
import type {
LLMTraceItem,
ToolCallItem,
} from '@/types/workflow'
import {
RiArrowLeftLine,
} from '@remixicon/react'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import TracingPanel from '../tracing-panel'
import ToolCallItemComponent from '@/app/components/workflow/run/llm-log/tool-call-item'
type Props = {
list: NodeTracing[]
list: LLMTraceItem[]
onBack: () => void
}
@ -19,6 +22,18 @@ const LLMResultPanel: FC<Props> = ({
onBack,
}) => {
const { t } = useTranslation()
const formattedList = list.map(item => ({
type: item.type,
tool_call_id: item.provider,
tool_name: item.name,
tool_arguments: item.type === 'tool' ? item.output.arguments : undefined,
tool_icon: item.icon,
tool_icon_dark: item.icon_dark,
tool_files: [],
tool_error: item.error,
tool_output: item.type === 'tool' ? item.output.output : item.output,
tool_elapsed_time: item.duration,
}))
return (
<div>
@ -33,13 +48,13 @@ const LLMResultPanel: FC<Props> = ({
<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 className="space-y-1 p-2">
{
formattedList.map((item, index) => (
<ToolCallItemComponent key={index} payload={item as ToolCallItem} />
))
}
</div>
</div>
)
}

View File

@ -1,30 +1,40 @@
import type { ToolCallItem } from '../../type'
import type { ToolCallItem } from '@/types/workflow'
import {
RiArrowDownSLine,
} from '@remixicon/react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import BlockIcon from '@/app/components/workflow/block-icon'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
import { BlockEnum } from '@/app/components/workflow/types'
import { cn } from '@/utils/classnames'
type ToolCallsItemProps = {
type ToolCallItemComponentProps = {
className?: string
payload: ToolCallItem
}
const ToolCallsItem = ({
const ToolCallItemComponent = ({
className,
payload,
}: ToolCallsItemProps) => {
}: ToolCallItemComponentProps) => {
const { t } = useTranslation()
const [expand, setExpand] = useState(false)
return (
<div
className="rounded-xl bg-background-gradient-bg-fill-chat-bubble-bg-1 px-2 pb-1 pt-2"
className={cn('rounded-[10px] border-[0.5px] border-components-panel-border bg-background-default-subtle px-2 pb-1 pt-2 shadow-xs', className)}
>
<div className="mb-1 flex cursor-pointer items-center hover:bg-background-gradient-bg-fill-chat-bubble-bg-2" onClick={() => setExpand(!expand)}>
<div className="mr-1 h-5 w-5 grow truncate" title={payload.tool_name}>{payload.tool_name}</div>
<BlockIcon
type={BlockEnum.Tool}
toolIcon={payload.tool_icon}
className="mr-1 h-4 w-4 shrink-0"
/>
<div className="mr-1 grow truncate" title={payload.tool_name}>{payload.tool_name}</div>
{
!!payload.tool_elapsed_time && (
<div className="system-xs-regular mr-1 shrink-0 text-text-tertiary">
{payload.tool_elapsed_time?.toFixed(3)}
{payload.tool_elapsed_time?.toFixed(1)}
s
</div>
)
@ -36,31 +46,40 @@ const ToolCallsItem = ({
<div className="relative px-2 pl-9">
<div className="absolute bottom-1 left-2 top-1 w-[1px] bg-divider-regular"></div>
{
payload.is_thought && (
payload.type === 'thought' && typeof payload.tool_output === 'string' && (
<div className="body-sm-medium text-text-tertiary">{payload.tool_output}</div>
)
}
{
!payload.is_thought && (
payload.type === 'model' && (
<CodeEditor
readOnly
title={<div>{t('common.input', { ns: 'workflow' })}</div>}
title={<div>{t('common.data', { ns: 'workflow' })}</div>}
language={CodeLanguage.json}
value={JSON.parse(payload.tool_arguments || '{}')}
value={payload.tool_output}
isJSONStringifyBeauty
/>
)
}
{
!payload.is_thought && (
payload.type === 'tool' && (
<CodeEditor
readOnly
title={<div>{t('common.input', { ns: 'workflow' })}</div>}
language={CodeLanguage.json}
value={payload.tool_arguments}
isJSONStringifyBeauty
/>
)
}
{
payload.type === 'tool' && (
<CodeEditor
readOnly
className="mt-1"
title={<div>{t('common.output', { ns: 'workflow' })}</div>}
language={CodeLanguage.json}
value={{
answer: payload.tool_output,
}}
value={payload.tool_output}
isJSONStringifyBeauty
/>
)
@ -72,4 +91,4 @@ const ToolCallsItem = ({
)
}
export default ToolCallsItem
export default ToolCallItemComponent

View File

@ -3,6 +3,7 @@ import type { FC } from 'react'
import type {
AgentLogItemWithChildren,
IterationDurationMap,
LLMTraceItem,
LoopDurationMap,
LoopVariableMap,
NodeTracing,
@ -44,7 +45,7 @@ type Props = {
onShowLoopDetail?: (detail: NodeTracing[][], loopDurationMap: LoopDurationMap, loopVariableMap: LoopVariableMap) => void
onShowRetryDetail?: (detail: NodeTracing[]) => void
onShowAgentOrToolLog?: (detail?: AgentLogItemWithChildren) => void
onShowLLMDetail?: (detail: NodeTracing[]) => void
onShowLLMDetail?: (detail: LLMTraceItem[]) => void
notShowIterationNav?: boolean
notShowLoopNav?: boolean
}
@ -99,7 +100,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 isLLMNode = nodeInfo.node_type === BlockEnum.LLM && !!nodeInfo.execution_metadata?.llm_trace?.length
const inputsTitle = useMemo(() => {
let text = t('common.input', { ns: 'workflow' })

View File

@ -2,6 +2,7 @@
import type { FC } from 'react'
import type {
AgentLogItemWithChildren,
LLMTraceItem,
NodeTracing,
} from '@/types/workflow'
import { useTranslation } from 'react-i18next'
@ -46,7 +47,7 @@ export type ResultPanelProps = {
handleShowLoopResultList?: (detail: NodeTracing[][], loopDurationMap: any) => void
onShowRetryDetail?: (detail: NodeTracing[]) => void
handleShowAgentOrToolLog?: (detail?: AgentLogItemWithChildren) => void
onShowLLMDetail?: (detail: NodeTracing[]) => void
onShowLLMDetail?: (detail: LLMTraceItem[]) => void
}
const ResultPanel: FC<ResultPanelProps> = ({
@ -81,7 +82,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
const isLLMNode = nodeInfo?.node_type === BlockEnum.LLM && !!nodeInfo?.execution_metadata?.llm_trace?.length
return (
<div className="bg-components-panel-bg py-2">

View File

@ -1,6 +1,7 @@
import type {
AgentLogItemWithChildren,
IterationDurationMap,
LLMTraceItem,
LoopDurationMap,
LoopVariableMap,
NodeTracing,
@ -33,7 +34,7 @@ export type SpecialResultPanelProps = {
showLLMDetail?: boolean
setShowLLMDetailFalse?: () => void
llmResultList?: NodeTracing[]
llmResultList?: LLMTraceItem[]
}
const SpecialResultPanel = ({
showRetryDetail,

View File

@ -91,6 +91,11 @@ const TracingPanel: FC<TracingPanelProps> = ({
agentOrToolLogItemStack,
agentOrToolLogListMap,
handleShowAgentOrToolLog,
showLLMDetail,
setShowLLMDetailFalse,
llmResultList,
handleShowLLMDetail,
} = useLogs()
const renderNode = (node: NodeTracing) => {
@ -153,6 +158,7 @@ const TracingPanel: FC<TracingPanelProps> = ({
onShowLoopDetail={handleShowLoopResultList}
onShowRetryDetail={handleShowRetryResultList}
onShowAgentOrToolLog={handleShowAgentOrToolLog}
onShowLLMDetail={handleShowLLMDetail}
hideInfo={hideNodeInfo}
hideProcessDetail={hideNodeProcessDetail}
/>
@ -182,6 +188,10 @@ const TracingPanel: FC<TracingPanelProps> = ({
agentOrToolLogItemStack={agentOrToolLogItemStack}
agentOrToolLogListMap={agentOrToolLogListMap}
handleShowAgentOrToolLog={handleShowAgentOrToolLog}
showLLMDetail={showLLMDetail}
setShowLLMDetailFalse={setShowLLMDetailFalse}
llmResultList={llmResultList}
/>
)
}

View File

@ -126,6 +126,7 @@
"common.currentDraftUnpublished": "Current Draft Unpublished",
"common.currentView": "Current View",
"common.currentWorkflow": "Current Workflow",
"common.data": "Data",
"common.debugAndPreview": "Preview",
"common.disconnect": "Disconnect",
"common.duplicate": "Duplicate",

View File

@ -126,6 +126,7 @@
"common.currentDraftUnpublished": "当前草稿未发布",
"common.currentView": "当前视图",
"common.currentWorkflow": "整个工作流",
"common.data": "数据",
"common.debugAndPreview": "预览",
"common.disconnect": "断开连接",
"common.duplicate": "复制",

View File

@ -34,16 +34,24 @@ import { getWebAppPassport } from './webapp-auth'
const TIME_OUT = 100000
export type IconObject = {
background: string
content: string
}
export type IOnDataMoreInfo = {
conversationId?: string
taskId?: string
messageId: string
errorMessage?: string
errorCode?: string
chunk_type?: 'text' | 'tool_call' | 'tool_result' | 'thought' | 'thought_start'
chunk_type?: 'text' | 'tool_call' | 'tool_result' | 'thought' | 'thought_start' | 'thought_end'
tool_call_id?: string
tool_name?: string
tool_arguments?: string
tool_icon?: string | IconObject
tool_icon_dark?: string | IconObject
tool_files?: string[]
tool_error?: string
tool_elapsed_time?: number
@ -245,6 +253,8 @@ export const handleStream = (
tool_call_id: bufferObj.tool_call_id,
tool_name: bufferObj.tool_name,
tool_arguments: bufferObj.tool_arguments,
tool_icon: bufferObj.tool_icon,
tool_icon_dark: bufferObj.tool_icon_dark,
tool_files: bufferObj.tool_files,
tool_error: bufferObj.tool_error,
tool_elapsed_time: bufferObj.tool_elapsed_time,

View File

@ -28,11 +28,33 @@ export type AgentLogItemWithChildren = AgentLogItem & {
children: AgentLogItemWithChildren[]
}
export type IconObject = {
background: string
content: string
}
export type ToolCallItem = {
type: 'model' | 'tool' | 'thought'
tool_call_id?: string
tool_name?: string
tool_arguments?: string
tool_icon?: string | IconObject
tool_icon_dark?: string | IconObject
tool_files?: string[]
tool_error?: string
tool_output?: Record<string, any> | string
tool_elapsed_time?: number
}
export type ToolCallDetail = {
id: string
name: string
arguments: string
result: string
output: string
files: string[]
error: string
elapsed_time?: number
status: string
}
export type SequenceSegment
= | { type: 'context', start: number, end: number }
@ -45,6 +67,18 @@ export type LLMLogItem = {
sequence: SequenceSegment[]
}
export type LLMTraceItem = {
type: 'model' | 'tool'
duration: number
output: Record<string, any>
provider?: string
name: string
icon?: string | IconObject
icon_dark?: string | IconObject
error?: string
status?: 'success' | 'error'
}
export type NodeTracing = {
id: string
index: number
@ -89,6 +123,7 @@ export type NodeTracing = {
icon?: string
}
loop_variable_map?: Record<string, any>
llm_trace?: LLMTraceItem[]
}
metadata: {
iterator_length: number