diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx index ed506c63f3..151e0db451 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx @@ -3,7 +3,6 @@ import { useMemo, useState } from 'react' import type { Strategy } from './agent-strategy' import classNames from '@/utils/classnames' import { RiArrowDownSLine, RiArrowRightUpLine, RiErrorWarningFill } from '@remixicon/react' -import { useAllBuiltInTools } from '@/service/use-tools' import Tooltip from '@/app/components/base/tooltip' import Link from 'next/link' import { InstallPluginButton } from './install-plugin-button' @@ -12,6 +11,10 @@ import SearchInput from '@/app/components/base/search-input' import { MARKETPLACE_URL_PREFIX } from '@/config' import Tools from '../../../block-selector/tools' import { useTranslation } from 'react-i18next' +import { useStrategyProviders } from '@/service/use-strategy' +import type { StrategyPluginDetail } from '@/app/components/plugins/types' +import type { ToolWithProvider } from '../../../types' +import { CollectionType } from '@/app/components/tools/types' const ExternalNotInstallWarn = () => { const { t } = useTranslation() @@ -31,6 +34,36 @@ const ExternalNotInstallWarn = () => { } +function formatStrategy(input: StrategyPluginDetail[]): ToolWithProvider[] { + return input.map((item) => { + const res: ToolWithProvider = { + id: item.provider, + // TODO: replace this + author: item.declaration.identity.author, + name: item.declaration.identity.name, + description: item.declaration.identity.description as any, + plugin_id: item.plugin_id, + icon: item.declaration.identity.icon, + label: item.declaration.identity.label as any, + type: CollectionType.all, + tools: item.declaration.strategies.map(strategy => ({ + name: strategy.identity.name, + author: strategy.identity.author, + label: strategy.identity.label as any, + description: strategy.description, + parameters: strategy.parameters as any, + output_schema: strategy.output_schema, + labels: [], + })), + team_credentials: {}, + is_team_authorization: true, + allow_delete: false, + labels: [], + } + return res + }) +} + export type AgentStrategySelectorProps = { value?: Strategy, onChange: (value?: Strategy) => void, @@ -39,13 +72,15 @@ export type AgentStrategySelectorProps = { export const AgentStrategySelector = (props: AgentStrategySelectorProps) => { const { value, onChange } = props const [open, setOpen] = useState(false) - const list = useAllBuiltInTools() const [viewType, setViewType] = useState(ViewType.flat) const [query, setQuery] = useState('') + const stra = useStrategyProviders() + const list = stra.data ? formatStrategy(stra.data) : undefined + console.log('list', list, 'stra', stra) const filteredTools = useMemo(() => { - if (!list.data) return [] - return list.data.filter(tool => tool.name.toLowerCase().includes(query.toLowerCase())) - }, [query, list.data]) + if (!list) return [] + return list.filter(tool => tool.name.toLowerCase().includes(query.toLowerCase())) + }, [query, list]) // TODO: should be replaced by real data const isExternalInstalled = true const { t } = useTranslation() @@ -53,9 +88,9 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => {
setOpen(o => !o)}> {/* eslint-disable-next-line @next/next/no-img-element */} - {list.data && value && coll, + {list && value && coll.tools?.find(tool => tool.name === value.agent_strategy_name), )?.icon as string} width={20} height={20} @@ -65,7 +100,7 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => {

- {value?.agent_strategy_name || t('workflow.nodes.agent.strategy.selectTip')} + {value?.agent_strategy_label || t('workflow.nodes.agent.strategy.selectTip')}

{value &&
e.stopPropagation()} size={'small'} /> @@ -85,10 +120,12 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => { viewType={viewType} onSelect={(_, tool) => { onChange({ - agent_strategy_name: tool!.title, + agent_strategy_name: tool!.tool_name, agent_strategy_provider_name: tool!.provider_name, agent_parameters: tool!.params, + agent_strategy_label: tool!.tool_label, }) + console.log(tool, 'tool') setOpen(false) }} hasSearchText={false} diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx index 50bf260311..fbd9463582 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -10,7 +10,7 @@ import { Agent } from '@/app/components/base/icons/src/vender/workflow' export type Strategy = { agent_strategy_provider_name: string agent_strategy_name: string - agent_strategy_label?: string + agent_strategy_label: string agent_parameters?: ToolVarInputs } diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx index 667263c102..fe18290ebf 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx @@ -18,7 +18,7 @@ import Toast from '@/app/components/base/toast' import { TransferMethod } from '@/types/app' import { getProcessedFiles } from '@/app/components/base/file-uploader/utils' import type { NodeTracing } from '@/types/workflow' -import RetryResultPanel from '@/app/components/workflow/run/retry-result-panel' +import { RetryResultPanel } from '@/app/components/workflow/run/retry-log' import type { BlockEnum } from '@/app/components/workflow/types' import type { Emoji } from '@/app/components/tools/types' diff --git a/web/app/components/workflow/nodes/agent/node.tsx b/web/app/components/workflow/nodes/agent/node.tsx index ea51cbb0ff..fca34623ec 100644 --- a/web/app/components/workflow/nodes/agent/node.tsx +++ b/web/app/components/workflow/nodes/agent/node.tsx @@ -17,10 +17,10 @@ const AgentNode: FC> = (props) => { label={t('workflow.nodes.agent.strategy.shortLabel')} status='error' tooltip={t('workflow.nodes.agent.strategyNotInstallTooltip', { - strategy: inputs.agent_strategy_name, + strategy: inputs.agent_strategy_label, })} > - {inputs.agent_strategy_name} + {inputs.agent_strategy_label} : } const AgentPanel: FC> = (props) => { - const { inputs, setInputs } = useConfig(props.id, props.data) + const { inputs, setInputs, currentStrategy } = useConfig(props.id, props.data) const { t } = useTranslation() const [iter, setIter] = [inputs.max_iterations, (value: number) => { setInputs({ @@ -63,6 +24,7 @@ const AgentPanel: FC> = (props) => { agent_strategy_provider_name: inputs.agent_strategy_provider_name!, agent_strategy_name: inputs.agent_strategy_name!, agent_parameters: inputs.agent_parameters, + agent_strategy_label: inputs.agent_strategy_label!, } : undefined} onStrategyChange={(strategy) => { setInputs({ @@ -70,9 +32,10 @@ const AgentPanel: FC> = (props) => { agent_strategy_provider_name: strategy?.agent_strategy_provider_name, agent_strategy_name: strategy?.agent_strategy_name, agent_parameters: strategy?.agent_parameters, + agent_strategy_label: strategy?.agent_strategy_label, }) }} - formSchema={mockSchema as any} + formSchema={currentStrategy?.parameters as any || []} formValue={inputs.agent_parameters || {}} onFormValueChange={value => setInputs({ ...inputs, diff --git a/web/app/components/workflow/nodes/agent/use-config.ts b/web/app/components/workflow/nodes/agent/use-config.ts index 078d2b1796..721bea9188 100644 --- a/web/app/components/workflow/nodes/agent/use-config.ts +++ b/web/app/components/workflow/nodes/agent/use-config.ts @@ -1,3 +1,4 @@ +import { useStrategyProviderDetail } from '@/service/use-strategy' import useNodeCrud from '../_base/hooks/use-node-crud' import useVarList from '../_base/hooks/use-var-list' import type { AgentNodeType } from './types' @@ -13,13 +14,20 @@ const useConfig = (id: string, payload: AgentNodeType) => { inputs, setInputs, }) - + const strategies = useStrategyProviderDetail( + inputs.agent_strategy_provider_name || '', + ) + const currentStrategy = strategies.data?.declaration.strategies.find( + str => str.identity.name === inputs.agent_strategy_name, + ) + console.log('currentStrategy', currentStrategy, 'strategies', strategies, 'inputs', inputs) return { readOnly, inputs, setInputs, handleVarListChange, handleAddVariable, + currentStrategy, } } diff --git a/web/app/components/workflow/nodes/iteration/panel.tsx b/web/app/components/workflow/nodes/iteration/panel.tsx index 9b6b3d3790..2ad2cac2d9 100644 --- a/web/app/components/workflow/nodes/iteration/panel.tsx +++ b/web/app/components/workflow/nodes/iteration/panel.tsx @@ -7,7 +7,7 @@ import { import VarReferencePicker from '../_base/components/variable/var-reference-picker' import Split from '../_base/components/split' import ResultPanel from '../../run/result-panel' -import IterationResultPanel from '../../run/iteration-result-panel' +import { IterationResultPanel } from '../../run/iteration-log' import { MAX_ITERATION_PARALLEL_NUM, MIN_ITERATION_PARALLEL_NUM } from '../../constants' import type { IterationNodeType } from './types' import useConfig from './use-config' @@ -123,7 +123,7 @@ const Panel: FC> = ({ onChange={changeParallelNums} max={MAX_ITERATION_PARALLEL_NUM} min={MIN_ITERATION_PARALLEL_NUM} - className=' flex-shrink-0 flex-1 mt-4' + className=' shrink-0 flex-1 mt-4' />
diff --git a/web/app/components/workflow/panel/workflow-preview.tsx b/web/app/components/workflow/panel/workflow-preview.tsx index 210a95f1f8..2e7f859b93 100644 --- a/web/app/components/workflow/panel/workflow-preview.tsx +++ b/web/app/components/workflow/panel/workflow-preview.tsx @@ -24,8 +24,8 @@ import { } from '../types' import { SimpleBtn } from '../../app/text-generate/item' import Toast from '../../base/toast' -import IterationResultPanel from '../run/iteration-result-panel' -import RetryResultPanel from '../run/retry-result-panel' +import { IterationResultPanel } from '../run/iteration-log' +import { RetryResultPanel } from '../run/retry-log' import InputsPanel from './inputs-panel' import cn from '@/utils/classnames' import Loading from '@/app/components/base/loading' diff --git a/web/app/components/workflow/run/agent-log/index.tsx b/web/app/components/workflow/run/agent-log/index.tsx new file mode 100644 index 0000000000..26a43ca8c5 --- /dev/null +++ b/web/app/components/workflow/run/agent-log/index.tsx @@ -0,0 +1,3 @@ +export { default as AgentLogTrigger } from './agent-log-trigger' +export { default as AgentResultPanel } from './agent-result-panel' +export { default as AgentToolCallResultPanel } from './tool-call-result-panel' diff --git a/web/app/components/workflow/run/hooks.ts b/web/app/components/workflow/run/hooks.ts index 07534b911d..74ee9719b5 100644 --- a/web/app/components/workflow/run/hooks.ts +++ b/web/app/components/workflow/run/hooks.ts @@ -28,14 +28,20 @@ export const useLogs = () => { setIterationResultDurationMap(iterDurationMap) }, [setShowIteratingDetailTrue, setIterationResultList, setIterationResultDurationMap]) + const [showAgentDetail, { + setTrue: setShowAgentDetailTrue, + setFalse: setShowAgentDetailFalse, + }] = useBoolean(false) + return { - showSpecialResultPanel: !showRetryDetail && !showIteratingDetail, + showSpecialResultPanel: showRetryDetail || showIteratingDetail, showRetryDetail, setShowRetryDetailTrue, setShowRetryDetailFalse, retryResultList, setRetryResultList, handleShowRetryResultList, + showIteratingDetail, setShowIteratingDetailTrue, setShowIteratingDetailFalse, @@ -44,5 +50,9 @@ export const useLogs = () => { iterationResultDurationMap, setIterationResultDurationMap, handleShowIterationResultList, + + showAgentDetail, + setShowAgentDetailTrue, + setShowAgentDetailFalse, } } diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx index f81c2defe7..b4de75fa64 100644 --- a/web/app/components/workflow/run/index.tsx +++ b/web/app/components/workflow/run/index.tsx @@ -106,16 +106,21 @@ const RunPanel: FC = ({ hideResult, activeTab = 'RESULT', runID, getRe }, [loading]) const { + showSpecialResultPanel, + showRetryDetail, setShowRetryDetailFalse, retryResultList, handleShowRetryResultList, + showIteratingDetail, setShowIteratingDetailFalse, iterationResultList, iterationResultDurationMap, handleShowIterationResultList, - showSpecialResultPanel, + + showAgentDetail, + setShowAgentDetailFalse, } = useLogs() return ( @@ -193,6 +198,9 @@ const RunPanel: FC = ({ hideResult, activeTab = 'RESULT', runID, getRe setShowIteratingDetailFalse={setShowIteratingDetailFalse} iterationResultList={iterationResultList} iterationResultDurationMap={iterationResultDurationMap} + + showAgentDetail={showAgentDetail} + setShowAgentDetailFalse={setShowAgentDetailFalse} /> ) } diff --git a/web/app/components/workflow/run/iteration-log/index.tsx b/web/app/components/workflow/run/iteration-log/index.tsx new file mode 100644 index 0000000000..5cbe70fece --- /dev/null +++ b/web/app/components/workflow/run/iteration-log/index.tsx @@ -0,0 +1,2 @@ +export { default as IterationLogTrigger } from './iteration-log-trigger' +export { default as IterationResultPanel } from './iteration-result-panel' diff --git a/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx b/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx new file mode 100644 index 0000000000..350c15c2b8 --- /dev/null +++ b/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx @@ -0,0 +1,72 @@ +import { useTranslation } from 'react-i18next' +import { RiArrowRightSLine } from '@remixicon/react' +import Button from '@/app/components/base/button' +import type { + IterationDurationMap, + NodeTracing, +} from '@/types/workflow' +import { Iteration } from '@/app/components/base/icons/src/vender/workflow' +import Split from '@/app/components/workflow/nodes/_base/components/split' + +type IterationLogTriggerProps = { + nodeInfo: NodeTracing + onShowIterationResultList: (iterationResultList: NodeTracing[][], iterationResultDurationMap: IterationDurationMap) => void + justShowIterationNavArrow?: boolean +} +const IterationLogTrigger = ({ + nodeInfo, + onShowIterationResultList, + justShowIterationNavArrow, +}: IterationLogTriggerProps) => { + const { t } = useTranslation() + const getErrorCount = (details: NodeTracing[][] | undefined) => { + if (!details || details.length === 0) + return 0 + + return details.reduce((acc, iteration) => { + if (iteration.some(item => item.status === 'failed')) + acc++ + return acc + }, 0) + } + const getCount = (iteration_curr_length: number | undefined, iteration_length: number) => { + if ((iteration_curr_length && iteration_curr_length < iteration_length) || !iteration_length) + return iteration_curr_length + + return iteration_length + } + const handleOnShowIterationDetail = (e: React.MouseEvent) => { + e.stopPropagation() + e.nativeEvent.stopImmediatePropagation() + onShowIterationResultList(nodeInfo.details || [], nodeInfo?.iterDurationMap || nodeInfo.execution_metadata?.iteration_duration_map || {}) + } + return ( +
+ + +
+ ) +} + +export default IterationLogTrigger diff --git a/web/app/components/workflow/run/iteration-result-panel.tsx b/web/app/components/workflow/run/iteration-log/iteration-result-panel.tsx similarity index 95% rename from web/app/components/workflow/run/iteration-result-panel.tsx rename to web/app/components/workflow/run/iteration-log/iteration-result-panel.tsx index 282c75880c..07e0db969f 100644 --- a/web/app/components/workflow/run/iteration-result-panel.tsx +++ b/web/app/components/workflow/run/iteration-log/iteration-result-panel.tsx @@ -8,10 +8,10 @@ import { RiErrorWarningLine, RiLoader2Line, } from '@remixicon/react' -import { ArrowNarrowLeft } from '../../base/icons/src/vender/line/arrows' -import { NodeRunningStatus } from '../types' -import TracingPanel from './tracing-panel' -import RetryResultPanel from './retry-result-panel' +import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows' +import { NodeRunningStatus } from '@/app/components/workflow/types' +import TracingPanel from '@/app/components/workflow/run/tracing-panel' +import { RetryResultPanel } from '@/app/components/workflow/run/retry-log' import { Iteration } from '@/app/components/base/icons/src/vender/workflow' import cn from '@/utils/classnames' import type { IterationDurationMap, NodeTracing } from '@/types/workflow' diff --git a/web/app/components/workflow/run/node.tsx b/web/app/components/workflow/run/node.tsx index 25e3802107..6e5d99fb34 100644 --- a/web/app/components/workflow/run/node.tsx +++ b/web/app/components/workflow/run/node.tsx @@ -8,16 +8,15 @@ import { RiCheckboxCircleFill, RiErrorWarningLine, RiLoader2Line, - RiRestartFill, } from '@remixicon/react' import BlockIcon from '../block-icon' import { BlockEnum } from '../types' -import Split from '../nodes/_base/components/split' -import { Iteration } from '@/app/components/base/icons/src/vender/workflow' +import { RetryLogTrigger } from './retry-log' +import { IterationLogTrigger } from './iteration-log' +import { AgentLogTrigger } from './agent-log' import cn from '@/utils/classnames' import StatusContainer from '@/app/components/workflow/run/status-container' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' -import Button from '@/app/components/base/button' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import type { IterationDurationMap, NodeTracing } from '@/types/workflow' import ErrorHandleTip from '@/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip' @@ -72,38 +71,14 @@ const NodePanel: FC = ({ return `${Number.parseFloat((tokens / 1000000).toFixed(3))}M` } - const getCount = (iteration_curr_length: number | undefined, iteration_length: number) => { - if ((iteration_curr_length && iteration_curr_length < iteration_length) || !iteration_length) - return iteration_curr_length - - return iteration_length - } - const getErrorCount = (details: NodeTracing[][] | undefined) => { - if (!details || details.length === 0) - return 0 - - return details.reduce((acc, iteration) => { - if (iteration.some(item => item.status === 'failed')) - acc++ - return acc - }, 0) - } useEffect(() => { setCollapseState(!nodeInfo.expand) }, [nodeInfo.expand, setCollapseState]) const isIterationNode = nodeInfo.node_type === BlockEnum.Iteration const isRetryNode = hasRetryNode(nodeInfo.node_type) && nodeInfo.retryDetail - const handleOnShowIterationDetail = (e: React.MouseEvent) => { - e.stopPropagation() - e.nativeEvent.stopImmediatePropagation() - onShowIterationDetail?.(nodeInfo.details || [], nodeInfo?.iterDurationMap || nodeInfo.execution_metadata?.iteration_duration_map || {}) - } - const handleOnShowRetryDetail = (e: React.MouseEvent) => { - e.stopPropagation() - e.nativeEvent.stopImmediatePropagation() - onShowRetryDetail?.(nodeInfo.retryDetail || []) - } + const isAgentNode = nodeInfo.node_type === BlockEnum.Agent + return (
@@ -153,46 +128,24 @@ const NodePanel: FC = ({ {!collapseState && !hideProcessDetail && (
{/* The nav to the iteration detail */} - {isIterationNode && !notShowIterationNav && ( -
- - -
+ {isIterationNode && !notShowIterationNav && onShowIterationDetail && ( + )} - {isRetryNode && ( - + {isRetryNode && onShowRetryDetail && ( + )} + { + isAgentNode && ( + + ) + }
{(nodeInfo.status === 'stopped') && ( diff --git a/web/app/components/workflow/run/retry-log/index.tsx b/web/app/components/workflow/run/retry-log/index.tsx new file mode 100644 index 0000000000..ee83f1a151 --- /dev/null +++ b/web/app/components/workflow/run/retry-log/index.tsx @@ -0,0 +1,2 @@ +export { default as RetryLogTrigger } from './retry-log-trigger' +export { default as RetryResultPanel } from './retry-result-panel' diff --git a/web/app/components/workflow/run/retry-log/retry-log-trigger.tsx b/web/app/components/workflow/run/retry-log/retry-log-trigger.tsx new file mode 100644 index 0000000000..9c4a987c42 --- /dev/null +++ b/web/app/components/workflow/run/retry-log/retry-log-trigger.tsx @@ -0,0 +1,41 @@ +import { useTranslation } from 'react-i18next' +import { + RiArrowRightSLine, + RiRestartFill, +} from '@remixicon/react' +import Button from '@/app/components/base/button' +import type { NodeTracing } from '@/types/workflow' + +type RetryLogTriggerProps = { + nodeInfo: NodeTracing + onShowRetryResultList: (detail: NodeTracing[]) => void +} +const RetryLogTrigger = ({ + nodeInfo, + onShowRetryResultList, +}: RetryLogTriggerProps) => { + const { t } = useTranslation() + const { retryDetail } = nodeInfo + + const handleShowRetryResultList = (e: React.MouseEvent) => { + e.stopPropagation() + e.nativeEvent.stopImmediatePropagation() + onShowRetryResultList(retryDetail || []) + } + + return ( + + ) +} + +export default RetryLogTrigger diff --git a/web/app/components/workflow/run/retry-result-panel.tsx b/web/app/components/workflow/run/retry-log/retry-result-panel.tsx similarity index 96% rename from web/app/components/workflow/run/retry-result-panel.tsx rename to web/app/components/workflow/run/retry-log/retry-result-panel.tsx index 3b177b1623..a8d171a4fc 100644 --- a/web/app/components/workflow/run/retry-result-panel.tsx +++ b/web/app/components/workflow/run/retry-log/retry-result-panel.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next' import { RiArrowLeftLine, } from '@remixicon/react' -import TracingPanel from './tracing-panel' +import TracingPanel from '../tracing-panel' import type { NodeTracing } from '@/types/workflow' type Props = { diff --git a/web/app/components/workflow/run/special-result-panel.tsx b/web/app/components/workflow/run/special-result-panel.tsx index c8918e897e..0995ecf1ef 100644 --- a/web/app/components/workflow/run/special-result-panel.tsx +++ b/web/app/components/workflow/run/special-result-panel.tsx @@ -1,5 +1,6 @@ -import RetryResultPanel from './retry-result-panel' -import IterationResultPanel from './iteration-result-panel' +import { RetryResultPanel } from './retry-log' +import { IterationResultPanel } from './iteration-log' +import { AgentResultPanel } from './agent-log' import type { IterationDurationMap, NodeTracing } from '@/types/workflow' type SpecialResultPanelProps = { @@ -11,6 +12,9 @@ type SpecialResultPanelProps = { setShowIteratingDetailFalse: () => void iterationResultList: NodeTracing[][] iterationResultDurationMap: IterationDurationMap + + showAgentDetail: boolean + setShowAgentDetailFalse: () => void } const SpecialResultPanel = ({ showRetryDetail, @@ -21,6 +25,9 @@ const SpecialResultPanel = ({ setShowIteratingDetailFalse, iterationResultList, iterationResultDurationMap, + + showAgentDetail, + setShowAgentDetailFalse, }: SpecialResultPanelProps) => { return ( <> @@ -42,6 +49,13 @@ const SpecialResultPanel = ({ /> ) } + { + showAgentDetail && ( + + ) + } ) } diff --git a/web/app/components/workflow/run/utils/format-log/agent/data.ts b/web/app/components/workflow/run/utils/format-log/agent/data.ts new file mode 100644 index 0000000000..2e0c23b5fb --- /dev/null +++ b/web/app/components/workflow/run/utils/format-log/agent/data.ts @@ -0,0 +1,91 @@ +import { BlockEnum } from '@/app/components/workflow/types' + +export const agentNodeData = (() => { + const node = { + node_type: BlockEnum.Agent, + execution_metadata: { + agent_log: [ + { id: '1', label: 'Root 1' }, + { id: '2', parent_id: '1', label: 'Child 1.2' }, + { id: '3', parent_id: '1', label: 'Child 1.3' }, + { id: '4', parent_id: '2', label: 'Child 2.4' }, + { id: '5', parent_id: '2', label: 'Child 2.5' }, + { id: '6', parent_id: '3', label: 'Child 3.6' }, + { id: '7', parent_id: '4', label: 'Child 4.7' }, + { id: '8', parent_id: '4', label: 'Child 4.8' }, + { id: '9', parent_id: '5', label: 'Child 5.9' }, + { id: '10', parent_id: '5', label: 'Child 5.10' }, + { id: '11', parent_id: '7', label: 'Child 7.11' }, + { id: '12', parent_id: '7', label: 'Child 7.12' }, + { id: '13', parent_id: '9', label: 'Child 9.13' }, + { id: '14', parent_id: '9', label: 'Child 9.14' }, + { id: '15', parent_id: '9', label: 'Child 9.15' }, + ], + }, + } + + return { + in: [node], + expect: [{ + ...node, + agentLog: [ + { + id: '1', + label: 'Root 1', + children: [ + { + id: '2', + parent_id: '1', + label: 'Child 1.2', + children: [ + { + id: '4', + parent_id: '2', + label: 'Child 2.4', + children: [ + { + id: '7', + parent_id: '4', + label: 'Child 4.7', + children: [ + { id: '11', parent_id: '7', label: 'Child 7.11' }, + { id: '12', parent_id: '7', label: 'Child 7.12' }, + ], + }, + { id: '8', parent_id: '4', label: 'Child 4.8' }, + ], + }, + { + id: '5', + parent_id: '2', + label: 'Child 2.5', + children: [ + { + id: '9', + parent_id: '5', + label: 'Child 5.9', + children: [ + { id: '13', parent_id: '9', label: 'Child 9.13' }, + { id: '14', parent_id: '9', label: 'Child 9.14' }, + { id: '15', parent_id: '9', label: 'Child 9.15' }, + ], + }, + { id: '10', parent_id: '5', label: 'Child 5.10' }, + ], + }, + ], + }, + { + id: '3', + parent_id: '1', + label: 'Child 1.3', + children: [ + { id: '6', parent_id: '3', label: 'Child 3.6' }, + ], + }, + ], + }, + ], + }], + } +})() diff --git a/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts b/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts new file mode 100644 index 0000000000..0fd871c41d --- /dev/null +++ b/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts @@ -0,0 +1,9 @@ +import format from '.' +import { agentNodeData } from './data' + +describe('agent', () => { + test('list should transform to tree', () => { + // console.log(format(agentNodeData.in as any)) + expect(format(agentNodeData.in as any)).toEqual(agentNodeData.expect) + }) +}) diff --git a/web/app/components/workflow/run/utils/format-log/agent/index.ts b/web/app/components/workflow/run/utils/format-log/agent/index.ts new file mode 100644 index 0000000000..7bbe0aa57f --- /dev/null +++ b/web/app/components/workflow/run/utils/format-log/agent/index.ts @@ -0,0 +1,36 @@ +import { BlockEnum } from '@/app/components/workflow/types' +import type { AgentLogItem, AgentLogItemWithChildren, NodeTracing } from '@/types/workflow' + +const listToTree = (logs: AgentLogItem[]) => { + if (!logs || logs.length === 0) + return [] + + const tree: AgentLogItemWithChildren[] = [] + logs.forEach((log) => { + const hasParent = !!log.parent_id + if (hasParent) { + const parent = logs.find(item => item.id === log.parent_id) as AgentLogItemWithChildren + if (parent) { + if (!parent.children) + parent.children = [] + parent.children.push(log as AgentLogItemWithChildren) + } + } + else { + tree.push(log as AgentLogItemWithChildren) + } + }) + return tree +} +const format = (list: NodeTracing[]): NodeTracing[] => { + const result: NodeTracing[] = list.map((item) => { + if (item.node_type === BlockEnum.Agent && item.execution_metadata?.agent_log && item.execution_metadata?.agent_log.length > 0) + item.agentLog = listToTree(item.execution_metadata.agent_log) + + return item + }) + + return result +} + +export default format diff --git a/web/app/components/workflow/run/utils/format-log/index.spec.ts b/web/app/components/workflow/run/utils/format-log/index.spec.ts index b4e513cc88..5ebaf8c20a 100644 --- a/web/app/components/workflow/run/utils/format-log/index.spec.ts +++ b/web/app/components/workflow/run/utils/format-log/index.spec.ts @@ -1,8 +1,10 @@ import formatToTracingNodeList from '.' -import { simpleIterationData } from '../spec-test-data' +import { simpleIterationData } from './iteration/data' +import { simpleRetryData } from './retry/data' describe('format api data to tracing panel data', () => { - test('iteration should put nodes in details', () => { - expect(formatToTracingNodeList(simpleIterationData.in as any)).toEqual(simpleIterationData.output) + test('integration', () => { + expect(formatToTracingNodeList(simpleIterationData.in.reverse() as any)).toEqual(simpleIterationData.expect) + expect(formatToTracingNodeList(simpleRetryData.in.reverse() as any)).toEqual(simpleRetryData.expect) }) }) diff --git a/web/app/components/workflow/run/utils/format-log/index.ts b/web/app/components/workflow/run/utils/format-log/index.ts index f35e029490..4f996e2f26 100644 --- a/web/app/components/workflow/run/utils/format-log/index.ts +++ b/web/app/components/workflow/run/utils/format-log/index.ts @@ -1,101 +1,15 @@ import type { NodeTracing } from '@/types/workflow' -import { BlockEnum } from '../../../types' +import formatIterationNode from './iteration' +import formatRetryNode from './retry' +import formatAgentNode from './agent' -type IterationNodeId = string -type RunIndex = string -type IterationGroupMap = Map> - -const processIterationNode = (item: NodeTracing) => { - return { - ...item, - details: [], // to add the sub nodes in the iteration - } -} - -const updateParallelModeGroup = (nodeGroupMap: IterationGroupMap, runIndex: string, item: NodeTracing, iterationNode: NodeTracing) => { - if (!nodeGroupMap.has(iterationNode.node_id)) - nodeGroupMap.set(iterationNode.node_id, new Map()) - - const groupMap = nodeGroupMap.get(iterationNode.node_id)! - - if (!groupMap.has(runIndex)) - groupMap.set(runIndex, [item]) - - else - groupMap.get(runIndex)!.push(item) - - if (item.status === 'failed') { - iterationNode.status = 'failed' - iterationNode.error = item.error - } - - iterationNode.details = Array.from(groupMap.values()) -} - -const updateSequentialModeGroup = (runIndex: number, item: NodeTracing, iterationNode: NodeTracing) => { - const { details } = iterationNode - if (details) { - if (!details[runIndex]) - details[runIndex] = [item] - else - details[runIndex].push(item) - } - - if (item.status === 'failed') { - iterationNode.status = 'failed' - iterationNode.error = item.error - } -} - -const addRetryDetail = (result: NodeTracing[], item: NodeTracing) => { - const retryNode = result.find(node => node.node_id === item.node_id) - - if (retryNode) { - if (retryNode?.retryDetail) - retryNode.retryDetail.push(item) - else - retryNode.retryDetail = [item] - } -} - -const processNonIterationNode = (result: NodeTracing[], nodeGroupMap: IterationGroupMap, item: NodeTracing) => { - const { execution_metadata } = item - if (!execution_metadata?.iteration_id) { - if (item.status === 'retry') { - addRetryDetail(result, item) - return - } - result.push(item) - return - } - - const parentIterationNode = result.find(node => node.node_id === execution_metadata.iteration_id) - const isInIteration = !!parentIterationNode && Array.isArray(parentIterationNode.details) - if (!isInIteration) - return - - // the parallel in the iteration in mode. - const { parallel_mode_run_id, iteration_index = 0 } = execution_metadata - const isInParallel = !!parallel_mode_run_id - - if (isInParallel) - updateParallelModeGroup(nodeGroupMap, parallel_mode_run_id, item, parentIterationNode) - else - updateSequentialModeGroup(iteration_index, item, parentIterationNode) -} - -// list => tree. Put the iteration node's children into the details field. const formatToTracingNodeList = (list: NodeTracing[]) => { const allItems = [...list].reverse() - const result: NodeTracing[] = [] - const iterationGroupMap = new Map>() - - allItems.forEach((item) => { - item.node_type === BlockEnum.Iteration - ? result.push(processIterationNode(item)) - : processNonIterationNode(result, iterationGroupMap, item) - }) + const formattedIterationList = formatIterationNode(allItems) + const formattedRetryList = formatRetryNode(formattedIterationList) + const formattedAgentList = formatAgentNode(formattedRetryList) + const result = formattedAgentList // console.log(allItems) // console.log(result) diff --git a/web/app/components/workflow/run/utils/spec-test-data.ts b/web/app/components/workflow/run/utils/spec-test-data.ts deleted file mode 100644 index 7e85004736..0000000000 --- a/web/app/components/workflow/run/utils/spec-test-data.ts +++ /dev/null @@ -1,481 +0,0 @@ -export const simpleIterationData = { - // start -> code(output: [1, 2, 3]) -> iteration(output: ['aaa', 'aaa', 'aaa']) -> end(output: ['aaa', 'aaa', 'aaa']) - in: [ - { - id: '36c9860a-39e6-4107-b750-655b07895f47', - index: 1, - predecessor_node_id: null, - node_id: '1735023354069', - node_type: 'start', - title: 'Start', - inputs: { - 'sys.files': [], - 'sys.user_id': '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - 'sys.app_id': '8a5e87f8-6433-40f4-a67a-4be78a558dc7', - 'sys.workflow_id': 'bb5e2b89-40ac-45c9-9ccb-4f2cd926e080', - 'sys.workflow_run_id': '76adf675-a7d3-4cc1-9282-ed7ecfe4f65d', - }, - process_data: null, - outputs: { - 'sys.files': [], - 'sys.user_id': '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - 'sys.app_id': '8a5e87f8-6433-40f4-a67a-4be78a558dc7', - 'sys.workflow_id': 'bb5e2b89-40ac-45c9-9ccb-4f2cd926e080', - 'sys.workflow_run_id': '76adf675-a7d3-4cc1-9282-ed7ecfe4f65d', - }, - status: 'succeeded', - error: null, - elapsed_time: 0.011458, - execution_metadata: null, - extras: {}, - created_at: 1735023510, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023510, - }, - { - id: 'a3105c5d-ff9e-44ea-9f4c-ab428958af20', - index: 2, - predecessor_node_id: '1735023354069', - node_id: '1735023361224', - node_type: 'code', - title: 'Code', - inputs: null, - process_data: null, - outputs: { - result: [ - 1, - 2, - 3, - ], - }, - status: 'succeeded', - error: null, - elapsed_time: 0.103333, - execution_metadata: null, - extras: {}, - created_at: 1735023510, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023511, - }, - { - id: 'a823134d-9f1a-45a4-8977-db838d076316', - index: 3, - predecessor_node_id: '1735023361224', - node_id: '1735023391914', - node_type: 'iteration', - title: 'Iteration', - inputs: null, - process_data: null, - outputs: { - output: [ - 'aaa', - 'aaa', - 'aaa', - ], - }, - status: 'succeeded', - error: null, - elapsed_time: 0.408383, - execution_metadata: { - iteration_duration_map: { - 0: 0.118153, - 1: 0.135956, - 2: 0.128251, - }, - total_tokens: 0, - }, - extras: {}, - created_at: 1735023511, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023511, - }, - { - id: 'a84a22d8-0f08-4006-bee2-fa7a7aef0420', - index: 4, - predecessor_node_id: '1735023391914start', - node_id: '1735023409906', - node_type: 'code', - title: 'Code 2', - inputs: null, - process_data: null, - outputs: { - result: 'aaa', - }, - status: 'succeeded', - error: null, - elapsed_time: 0.112688, - execution_metadata: { - iteration_id: '1735023391914', - iteration_index: 0, - }, - extras: {}, - created_at: 1735023511, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023511, - }, - { - id: 'ff71d773-a916-4513-960f-d7dcc4fadd86', - index: 5, - predecessor_node_id: '1735023391914start', - node_id: '1735023409906', - node_type: 'code', - title: 'Code 2', - inputs: null, - process_data: null, - outputs: { - result: 'aaa', - }, - status: 'succeeded', - error: null, - elapsed_time: 0.126034, - execution_metadata: { - iteration_id: '1735023391914', - iteration_index: 1, - }, - extras: {}, - created_at: 1735023511, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023511, - }, - { - id: 'd91c3ef9-0162-4013-9272-d4cc7fb1f188', - index: 6, - predecessor_node_id: '1735023391914start', - node_id: '1735023409906', - node_type: 'code', - title: 'Code 2', - inputs: null, - process_data: null, - outputs: { - result: 'aaa', - }, - status: 'succeeded', - error: null, - elapsed_time: 0.122716, - execution_metadata: { - iteration_id: '1735023391914', - iteration_index: 2, - }, - extras: {}, - created_at: 1735023511, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023511, - }, - { - id: 'e6ad6560-1aa3-43f3-89e3-e5287c9ea272', - index: 7, - predecessor_node_id: '1735023391914', - node_id: '1735023417757', - node_type: 'end', - title: 'End', - inputs: { - output: [ - 'aaa', - 'aaa', - 'aaa', - ], - }, - process_data: null, - outputs: { - output: [ - 'aaa', - 'aaa', - 'aaa', - ], - }, - status: 'succeeded', - error: null, - elapsed_time: 0.017552, - execution_metadata: null, - extras: {}, - created_at: 1735023511, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023511, - }, - ].reverse(), - output: [ - { - id: '36c9860a-39e6-4107-b750-655b07895f47', - index: 1, - predecessor_node_id: null, - node_id: '1735023354069', - node_type: 'start', - title: 'Start', - inputs: { - 'sys.files': [], - 'sys.user_id': '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - 'sys.app_id': '8a5e87f8-6433-40f4-a67a-4be78a558dc7', - 'sys.workflow_id': 'bb5e2b89-40ac-45c9-9ccb-4f2cd926e080', - 'sys.workflow_run_id': '76adf675-a7d3-4cc1-9282-ed7ecfe4f65d', - }, - process_data: null, - outputs: { - 'sys.files': [], - 'sys.user_id': '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - 'sys.app_id': '8a5e87f8-6433-40f4-a67a-4be78a558dc7', - 'sys.workflow_id': 'bb5e2b89-40ac-45c9-9ccb-4f2cd926e080', - 'sys.workflow_run_id': '76adf675-a7d3-4cc1-9282-ed7ecfe4f65d', - }, - status: 'succeeded', - error: null, - elapsed_time: 0.011458, - execution_metadata: null, - extras: {}, - created_at: 1735023510, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023510, - }, - { - id: 'a3105c5d-ff9e-44ea-9f4c-ab428958af20', - index: 2, - predecessor_node_id: '1735023354069', - node_id: '1735023361224', - node_type: 'code', - title: 'Code', - inputs: null, - process_data: null, - outputs: { - result: [ - 1, - 2, - 3, - ], - }, - status: 'succeeded', - error: null, - elapsed_time: 0.103333, - execution_metadata: null, - extras: {}, - created_at: 1735023510, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023511, - }, - { - id: 'a823134d-9f1a-45a4-8977-db838d076316', - index: 3, - predecessor_node_id: '1735023361224', - node_id: '1735023391914', - node_type: 'iteration', - title: 'Iteration', - inputs: null, - process_data: null, - outputs: { - output: [ - 'aaa', - 'aaa', - 'aaa', - ], - }, - status: 'succeeded', - error: null, - elapsed_time: 0.408383, - execution_metadata: { - iteration_duration_map: { - 0: 0.118153, - 1: 0.135956, - 2: 0.128251, - }, - total_tokens: 0, - }, - extras: {}, - created_at: 1735023511, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023511, - details: [ - [ - { - id: 'a84a22d8-0f08-4006-bee2-fa7a7aef0420', - index: 4, - predecessor_node_id: '1735023391914start', - node_id: '1735023409906', - node_type: 'code', - title: 'Code 2', - inputs: null, - process_data: null, - outputs: { - result: 'aaa', - }, - status: 'succeeded', - error: null, - elapsed_time: 0.112688, - execution_metadata: { - iteration_id: '1735023391914', - iteration_index: 0, - }, - extras: {}, - created_at: 1735023511, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023511, - }, - ], - [ - { - id: 'ff71d773-a916-4513-960f-d7dcc4fadd86', - index: 5, - predecessor_node_id: '1735023391914start', - node_id: '1735023409906', - node_type: 'code', - title: 'Code 2', - inputs: null, - process_data: null, - outputs: { - result: 'aaa', - }, - status: 'succeeded', - error: null, - elapsed_time: 0.126034, - execution_metadata: { - iteration_id: '1735023391914', - iteration_index: 1, - }, - extras: {}, - created_at: 1735023511, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023511, - }, - ], - [ - { - id: 'd91c3ef9-0162-4013-9272-d4cc7fb1f188', - index: 6, - predecessor_node_id: '1735023391914start', - node_id: '1735023409906', - node_type: 'code', - title: 'Code 2', - inputs: null, - process_data: null, - outputs: { - result: 'aaa', - }, - status: 'succeeded', - error: null, - elapsed_time: 0.122716, - execution_metadata: { - iteration_id: '1735023391914', - iteration_index: 2, - }, - extras: {}, - created_at: 1735023511, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023511, - }, - ], - ], - }, - { - id: 'e6ad6560-1aa3-43f3-89e3-e5287c9ea272', - index: 7, - predecessor_node_id: '1735023391914', - node_id: '1735023417757', - node_type: 'end', - title: 'End', - inputs: { - output: [ - 'aaa', - 'aaa', - 'aaa', - ], - }, - process_data: null, - outputs: { - output: [ - 'aaa', - 'aaa', - 'aaa', - ], - }, - status: 'succeeded', - error: null, - elapsed_time: 0.017552, - execution_metadata: null, - extras: {}, - created_at: 1735023511, - created_by_role: 'account', - created_by_account: { - id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - name: 'Joel', - email: 'iamjoel007@gmail.com', - }, - created_by_end_user: null, - finished_at: 1735023511, - }, - ], -} diff --git a/web/service/use-strategy.ts b/web/service/use-strategy.ts index 6ef11e15ee..e6f6c4b607 100644 --- a/web/service/use-strategy.ts +++ b/web/service/use-strategy.ts @@ -24,7 +24,8 @@ export const useInvalidateStrategyProviders = () => { export const useStrategyProviderDetail = (agentProvider: string) => { return useQuery({ queryKey: [NAME_SPACE, 'detail', agentProvider], - queryFn: () => get(`/workspaces/current/agent-providers/${agentProvider}`), + queryFn: () => get(`/workspaces/current/agent-provider/${agentProvider}`), + enabled: !!agentProvider, }) } diff --git a/web/types/workflow.ts b/web/types/workflow.ts index cd6e9cfa5f..c89f5536a0 100644 --- a/web/types/workflow.ts +++ b/web/types/workflow.ts @@ -9,6 +9,20 @@ import type { import type { TransferMethod } from '@/types/app' import type { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' +export type AgentLogItem = { + node_execution_id: string, + id: string, + parent_id?: string, + label: string, + data: object, // debug data + error?: string, + status: string, +} + +export type AgentLogItemWithChildren = AgentLogItem & { + children: AgentLogItemWithChildren[] +} + export type NodeTracing = { id: string index: number @@ -36,6 +50,7 @@ export type NodeTracing = { parallel_mode_run_id?: string iteration_duration_map?: IterationDurationMap error_strategy?: ErrorHandleTypeEnum + agent_log?: AgentLogItem[] } metadata: { iterator_length: number @@ -53,6 +68,7 @@ export type NodeTracing = { expand?: boolean // for UI details?: NodeTracing[][] // iteration detail retryDetail?: NodeTracing[] // retry detail + agentLog?: AgentLogItemWithChildren[] parallel_id?: string parallel_start_node_id?: string parent_parallel_id?: string