This commit is contained in:
2025-12-01 17:21:38 +08:00
parent 32fee2b8ab
commit fab8c13cb3
7511 changed files with 996300 additions and 0 deletions

View File

@@ -0,0 +1,126 @@
import {
useMemo,
useState,
} from 'react'
import {
RiArrowRightSLine,
RiListView,
} from '@remixicon/react'
import cn from '@/utils/classnames'
import Button from '@/app/components/base/button'
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
onShowAgentOrToolLog: (detail: AgentLogItemWithChildren) => void
}
const AgentLogItem = ({
item,
onShowAgentOrToolLog,
}: AgentLogItemProps) => {
const {
label,
status,
children,
data,
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 (
<div className='rounded-[10px] border-[0.5px] border-components-panel-border bg-background-default'>
<div
className={cn(
'flex cursor-pointer items-center pb-2 pl-1.5 pr-3 pt-2',
expanded && 'pb-1',
)}
onClick={() => setExpanded(!expanded)}
>
{
expanded
? <RiArrowRightSLine className='h-4 w-4 shrink-0 rotate-90 text-text-quaternary' />
: <RiArrowRightSLine className='h-4 w-4 shrink-0 text-text-quaternary' />
}
<BlockIcon
className='mr-1.5 shrink-0'
type={toolIcon ? BlockEnum.Tool : BlockEnum.Agent}
toolIcon={toolIcon}
/>
<div
className='system-sm-semibold-uppercase grow truncate text-text-secondary'
title={label}
>
{label}
</div>
{
metadata?.elapsed_time && (
<div className='system-xs-regular mr-2 shrink-0 text-text-tertiary'>{metadata?.elapsed_time?.toFixed(3)}s</div>
)
}
<NodeStatusIcon status={mergeStatus} />
</div>
{
expanded && (
<div className='p-1 pt-0'>
{
!!children?.length && (
<Button
className='mb-1 flex w-full items-center justify-between'
variant='tertiary'
onClick={() => onShowAgentOrToolLog(item)}
>
<div className='flex items-center'>
<RiListView className='mr-1 h-4 w-4 shrink-0 text-components-button-tertiary-text' />
{`${children.length} Action Logs`}
</div>
<div className='flex'>
<RiArrowRightSLine className='h-4 w-4 shrink-0 text-components-button-tertiary-text' />
</div>
</Button>
)
}
{
data && (
<CodeEditor
readOnly
title={<div>{'data'.toLocaleUpperCase()}</div>}
language={CodeLanguage.json}
value={data}
isJSONStringifyBeauty
/>
)
}
</div>
)
}
</div>
)
}
export default AgentLogItem

View File

@@ -0,0 +1,61 @@
import { useState } from 'react'
import { RiMoreLine } from '@remixicon/react'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import Button from '@/app/components/base/button'
import type { AgentLogItemWithChildren } from '@/types/workflow'
type AgentLogNavMoreProps = {
options: AgentLogItemWithChildren[]
onShowAgentOrToolLog: (detail?: AgentLogItemWithChildren) => void
}
const AgentLogNavMore = ({
options,
onShowAgentOrToolLog,
}: AgentLogNavMoreProps) => {
const [open, setOpen] = useState(false)
return (
<PortalToFollowElem
placement='bottom-start'
offset={{
mainAxis: 2,
crossAxis: -54,
}}
open={open}
onOpenChange={setOpen}
>
<PortalToFollowElemTrigger>
<Button
className='h-6 w-6'
variant='ghost-accent'
>
<RiMoreLine className='h-4 w-4' />
</Button>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent>
<div className='w-[136px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'>
{
options.map(option => (
<div
key={option.message_id}
className='system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 text-text-secondary hover:bg-state-base-hover'
onClick={() => {
onShowAgentOrToolLog(option)
setOpen(false)
}}
>
{option.label}
</div>
))
}
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
)
}
export default AgentLogNavMore

View File

@@ -0,0 +1,78 @@
import { RiArrowLeftLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import AgentLogNavMore from './agent-log-nav-more'
import Button from '@/app/components/base/button'
import type { AgentLogItemWithChildren } from '@/types/workflow'
type AgentLogNavProps = {
agentOrToolLogItemStack: AgentLogItemWithChildren[]
onShowAgentOrToolLog: (detail?: AgentLogItemWithChildren) => void
}
const AgentLogNav = ({
agentOrToolLogItemStack,
onShowAgentOrToolLog,
}: AgentLogNavProps) => {
const { t } = useTranslation()
const agentOrToolLogItemStackLength = agentOrToolLogItemStack.length
const first = agentOrToolLogItemStack[0]
const mid = agentOrToolLogItemStack.slice(1, -1)
const end = agentOrToolLogItemStack.at(-1)
return (
<div className='flex h-8 items-center bg-components-panel-bg p-1 pr-3'>
<Button
className='shrink-0 px-[5px]'
size='small'
variant='ghost-accent'
onClick={() => {
onShowAgentOrToolLog()
}}
>
<RiArrowLeftLine className='mr-1 h-3.5 w-3.5' />
AGENT
</Button>
<div className='system-xs-regular mx-0.5 shrink-0 text-divider-deep'>/</div>
{
agentOrToolLogItemStackLength > 1
? (
<Button
className='shrink-0 px-[5px]'
size='small'
variant='ghost-accent'
onClick={() => onShowAgentOrToolLog(first)}
>
{t('workflow.nodes.agent.strategy.label')}
</Button>
)
: (
<div className='system-xs-medium-uppercase flex items-center px-[5px] text-text-tertiary'>
{t('workflow.nodes.agent.strategy.label')}
</div>
)
}
{
!!mid.length && (
<>
<div className='system-xs-regular mx-0.5 shrink-0 text-divider-deep'>/</div>
<AgentLogNavMore
options={mid}
onShowAgentOrToolLog={onShowAgentOrToolLog}
/>
</>
)
}
{
!!end && agentOrToolLogItemStackLength > 1 && (
<>
<div className='system-xs-regular mx-0.5 shrink-0 text-divider-deep'>/</div>
<div className='system-xs-medium-uppercase flex items-center px-[5px] text-text-tertiary'>
{end.label}
</div>
</>
)
}
</div>
)
}
export default AgentLogNav

View File

@@ -0,0 +1,49 @@
import { RiArrowRightLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import type {
AgentLogItemWithChildren,
NodeTracing,
} from '@/types/workflow'
type AgentLogTriggerProps = {
nodeInfo: NodeTracing
onShowAgentOrToolLog: (detail?: AgentLogItemWithChildren) => void
}
const AgentLogTrigger = ({
nodeInfo,
onShowAgentOrToolLog,
}: AgentLogTriggerProps) => {
const { t } = useTranslation()
const { agentLog, execution_metadata } = nodeInfo
const agentStrategy = execution_metadata?.tool_info?.agent_strategy
return (
<div
className='cursor-pointer rounded-[10px] bg-components-button-tertiary-bg'
onClick={() => {
onShowAgentOrToolLog({ message_id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren)
}}
>
<div className='system-2xs-medium-uppercase flex items-center px-3 pt-2 text-text-tertiary'>
{t('workflow.nodes.agent.strategy.label')}
</div>
<div className='flex items-center pb-1.5 pl-3 pr-2 pt-1'>
{
agentStrategy && (
<div className='system-xs-medium grow text-text-secondary'>
{agentStrategy}
</div>
)
}
<div
className='system-xs-regular-uppercase flex shrink-0 cursor-pointer items-center px-[1px] text-text-tertiary'
>
{t('runLog.detail')}
<RiArrowRightLine className='ml-0.5 h-3.5 w-3.5' />
</div>
</div>
</div>
)
}
export default AgentLogTrigger

View File

@@ -0,0 +1,60 @@
import { RiAlertFill } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import AgentLogItem from './agent-log-item'
import AgentLogNav from './agent-log-nav'
import type { AgentLogItemWithChildren } from '@/types/workflow'
type AgentResultPanelProps = {
agentOrToolLogItemStack: AgentLogItemWithChildren[]
agentOrToolLogListMap: Record<string, AgentLogItemWithChildren[]>
onShowAgentOrToolLog: (detail?: AgentLogItemWithChildren) => void
}
const AgentResultPanel = ({
agentOrToolLogItemStack,
agentOrToolLogListMap,
onShowAgentOrToolLog,
}: AgentResultPanelProps) => {
const { t } = useTranslation()
const top = agentOrToolLogItemStack[agentOrToolLogItemStack.length - 1]
const list = agentOrToolLogListMap[top.message_id]
return (
<div className='overflow-y-auto bg-background-section'>
<AgentLogNav
agentOrToolLogItemStack={agentOrToolLogItemStack}
onShowAgentOrToolLog={onShowAgentOrToolLog}
/>
{
<div className='space-y-1 p-2'>
{
list.map(item => (
<AgentLogItem
key={item.message_id}
item={item}
onShowAgentOrToolLog={onShowAgentOrToolLog}
/>
))
}
</div>
}
{
top.hasCircle && (
<div className='mt-1 flex items-center rounded-xl border border-components-panel-border bg-components-panel-bg-blur px-3 pr-2 shadow-md'>
<div
className='absolute inset-0 rounded-xl opacity-[0.4]'
style={{
background: 'linear-gradient(92deg, rgba(247, 144, 9, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%)',
}}
></div>
<RiAlertFill className='mr-1.5 h-4 w-4 text-text-warning-secondary' />
<div className='system-xs-medium text-text-primary'>
{t('runLog.circularInvocationTip')}
</div>
</div>
)
}
</div>
)
}
export default AgentResultPanel

View File

@@ -0,0 +1,2 @@
export { default as AgentLogTrigger } from './agent-log-trigger'
export { default as AgentResultPanel } from './agent-result-panel'