diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-finished.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-finished.ts index 1a39360d9a..b8490ff14c 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-finished.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-finished.ts @@ -6,8 +6,8 @@ import { BlockEnum, NodeRunningStatus, } from '@/app/components/workflow/types' -import { useWorkflowStore } from '@/app/components/workflow/store' import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' +import { useWorkflowStore } from '@/app/components/workflow/store' export const useWorkflowNodeFinished = () => { const store = useStoreApi() @@ -18,8 +18,6 @@ export const useWorkflowNodeFinished = () => { const { workflowRunningData, setWorkflowRunningData, - iterParallelLogMap, - setIterParallelLogMap, } = workflowStore.getState() const { getNodes, @@ -28,124 +26,45 @@ export const useWorkflowNodeFinished = () => { setEdges, } = store.getState() const nodes = getNodes() - const nodeParentId = nodes.find(node => node.id === data.node_id)!.parentId - if (nodeParentId) { - if (!data.execution_metadata.parallel_mode_run_id) { - setWorkflowRunningData(produce(workflowRunningData!, (draft) => { - const tracing = draft.tracing! - const iterations = tracing.find(trace => trace.node_id === nodeParentId) // the iteration node + setWorkflowRunningData(produce(workflowRunningData!, (draft) => { + const currentIndex = draft.tracing!.findIndex(item => item.id === data.id) + if (currentIndex > -1) { + draft.tracing![currentIndex] = { + ...draft.tracing![currentIndex], + ...data, + } + } + })) - if (iterations && iterations.details) { - const iterationIndex = data.execution_metadata?.iteration_index || 0 - if (!iterations.details[iterationIndex]) - iterations.details[iterationIndex] = [] - - const currIteration = iterations.details[iterationIndex] - const nodeIndex = currIteration.findIndex(node => - node.node_id === data.node_id && ( - node.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || node.parallel_id === data.execution_metadata?.parallel_id), - ) - if (nodeIndex !== -1) { - currIteration[nodeIndex] = { - ...currIteration[nodeIndex], - ...(currIteration[nodeIndex].retryDetail - ? { retryDetail: currIteration[nodeIndex].retryDetail } - : {}), - ...data, - } as any - } - else { - currIteration.push({ - ...data, - } as any) - } - } - })) + const newNodes = produce(nodes, (draft) => { + const currentNode = draft.find(node => node.id === data.node_id)! + currentNode.data._runningStatus = data.status + if (data.status === NodeRunningStatus.Exception) { + if (data.execution_metadata?.error_strategy === ErrorHandleTypeEnum.failBranch) + currentNode.data._runningBranchId = ErrorHandleTypeEnum.failBranch } else { - // open parallel mode - setWorkflowRunningData(produce(workflowRunningData!, (draft) => { - const tracing = draft.tracing! - const iterations = tracing.find(trace => trace.node_id === nodeParentId) // the iteration node + if (data.node_type === BlockEnum.IfElse) + currentNode.data._runningBranchId = data?.outputs?.selected_case_id - if (iterations && iterations.details) { - const iterRunID = data.execution_metadata?.parallel_mode_run_id - - const currIteration = iterParallelLogMap.get(iterations.node_id)?.get(iterRunID) - const nodeIndex = currIteration?.findIndex(node => - node.node_id === data.node_id && ( - node?.parallel_run_id === data.execution_metadata?.parallel_mode_run_id), - ) - if (currIteration) { - if (nodeIndex !== undefined && nodeIndex !== -1) { - currIteration[nodeIndex] = { - ...currIteration[nodeIndex], - ...data, - } as any - } - else { - currIteration.push({ - ...data, - } as any) - } - } - setIterParallelLogMap(iterParallelLogMap) - const iterLogMap = iterParallelLogMap.get(iterations.node_id) - if (iterLogMap) - iterations.details = Array.from(iterLogMap.values()) - } - })) + if (data.node_type === BlockEnum.QuestionClassifier) + currentNode.data._runningBranchId = data?.outputs?.class_id } - } - else { - setWorkflowRunningData(produce(workflowRunningData!, (draft) => { - const currentIndex = draft.tracing!.findIndex((trace) => { - if (!trace.execution_metadata?.parallel_id) - return trace.node_id === data.node_id - return trace.node_id === data.node_id && trace.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id - }) - if (currentIndex > -1 && draft.tracing) { - draft.tracing[currentIndex] = { - ...data, - ...(draft.tracing[currentIndex].extras - ? { extras: draft.tracing[currentIndex].extras } - : {}), - ...(draft.tracing[currentIndex].retryDetail - ? { retryDetail: draft.tracing[currentIndex].retryDetail } - : {}), - } as any - } - })) - const newNodes = produce(nodes, (draft) => { - const currentNode = draft.find(node => node.id === data.node_id)! - currentNode.data._runningStatus = data.status as any - if (data.status === NodeRunningStatus.Exception) { - if (data.execution_metadata.error_strategy === ErrorHandleTypeEnum.failBranch) - currentNode.data._runningBranchId = ErrorHandleTypeEnum.failBranch - } - else { - if (data.node_type === BlockEnum.IfElse) - currentNode.data._runningBranchId = data?.outputs?.selected_case_id - - if (data.node_type === BlockEnum.QuestionClassifier) - currentNode.data._runningBranchId = data?.outputs?.class_id + }) + setNodes(newNodes) + const newEdges = produce(edges, (draft) => { + const incomeEdges = draft.filter((edge) => { + return edge.target === data.node_id + }) + incomeEdges.forEach((edge) => { + edge.data = { + ...edge.data, + _targetRunningStatus: data.status as any, } }) - setNodes(newNodes) - const newEdges = produce(edges, (draft) => { - const incomeEdges = draft.filter((edge) => { - return edge.target === data.node_id - }) - incomeEdges.forEach((edge) => { - edge.data = { - ...edge.data, - _targetRunningStatus: data.status as any, - } - }) - }) - setEdges(newEdges) - } - }, [workflowStore, store]) + }) + setEdges(newEdges) + }, [store, workflowStore]) return { handleWorkflowNodeFinished, diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-finished.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-finished.ts index 2394df7516..fdf9e28587 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-finished.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-finished.ts @@ -3,7 +3,6 @@ import { useStoreApi } from 'reactflow' import produce from 'immer' import type { IterationFinishedResponse } from '@/types/workflow' import { useWorkflowStore } from '@/app/components/workflow/store' -import { NodeRunningStatus } from '@/app/components/workflow/types' import { DEFAULT_ITER_TIMES } from '@/app/components/workflow/constants' export const useWorkflowNodeIterationFinished = () => { @@ -22,15 +21,14 @@ export const useWorkflowNodeIterationFinished = () => { setNodes, } = store.getState() const nodes = getNodes() - setWorkflowRunningData(produce(workflowRunningData!, (draft) => { - const tracing = draft.tracing! - const currIterationNode = tracing.find(trace => trace.node_id === data.node_id) - if (currIterationNode) { - Object.assign(currIterationNode, { + const currentIndex = draft.tracing!.findIndex(item => item.id === data.id) + + if (currentIndex > -1) { + draft.tracing![currentIndex] = { + ...draft.tracing![currentIndex], ...data, - status: NodeRunningStatus.Succeeded, - }) + } } })) setIterTimes(DEFAULT_ITER_TIMES) diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-next.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-next.ts index 537ff63e09..454f822675 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-next.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-next.ts @@ -10,8 +10,6 @@ export const useWorkflowNodeIterationNext = () => { const handleWorkflowNodeIterationNext = useCallback((params: IterationNextResponse) => { const { - workflowRunningData, - setWorkflowRunningData, iterTimes, setIterTimes, } = workflowStore.getState() @@ -22,17 +20,6 @@ export const useWorkflowNodeIterationNext = () => { setNodes, } = store.getState() - setWorkflowRunningData(produce(workflowRunningData!, (draft) => { - const iteration = draft.tracing!.find(trace => trace.node_id === data.node_id) - if (iteration) { - if (iteration.iterDurationMap && data.duration) - iteration.iterDurationMap[data.parallel_mode_run_id ?? `${data.index - 1}`] = data.duration - if (iteration.details!.length >= iteration.metadata.iterator_length!) - return - } - if (!data.parallel_mode_run_id) - iteration?.details!.push([]) - })) const nodes = getNodes() const newNodes = produce(nodes, (draft) => { const currentNode = draft.find(node => node.id === data.node_id)! diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-started.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-started.ts index 9d6536ccc9..0308f62b31 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-started.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-started.ts @@ -35,15 +35,13 @@ export const useWorkflowNodeIterationStarted = () => { transform, } = store.getState() const nodes = getNodes() - setIterTimes(DEFAULT_ITER_TIMES) setWorkflowRunningData(produce(workflowRunningData!, (draft) => { draft.tracing!.push({ ...data, status: NodeRunningStatus.Running, - details: [], - iterDurationMap: {}, - } as any) + }) })) + setIterTimes(DEFAULT_ITER_TIMES) const { setViewport, diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-retry.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-retry.ts index 0061920eb4..a57bfbce39 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-retry.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-retry.ts @@ -3,7 +3,6 @@ import { useStoreApi } from 'reactflow' import produce from 'immer' import type { NodeFinishedResponse, - NodeTracing, } from '@/types/workflow' import { useWorkflowStore } from '@/app/components/workflow/store' @@ -16,8 +15,6 @@ export const useWorkflowNodeRetry = () => { const { workflowRunningData, setWorkflowRunningData, - iterParallelLogMap, - setIterParallelLogMap, } = workflowStore.getState() const { getNodes, @@ -25,65 +22,9 @@ export const useWorkflowNodeRetry = () => { } = store.getState() const nodes = getNodes() - const currentNode = nodes.find(node => node.id === data.node_id)! - const nodeParent = nodes.find(node => node.id === currentNode.parentId) - if (nodeParent) { - if (!data.execution_metadata.parallel_mode_run_id) { - setWorkflowRunningData(produce(workflowRunningData!, (draft) => { - const tracing = draft.tracing! - const iteration = tracing.find(trace => trace.node_id === nodeParent.id) - - if (iteration && iteration.details?.length) { - const currentNodeRetry = iteration.details[nodeParent.data._iterationIndex - 1]?.find(item => item.node_id === data.node_id) - - if (currentNodeRetry) { - if (currentNodeRetry?.retryDetail) - currentNodeRetry?.retryDetail.push(data as NodeTracing) - else - currentNodeRetry.retryDetail = [data as NodeTracing] - } - } - })) - } - else { - setWorkflowRunningData(produce(workflowRunningData!, (draft) => { - const tracing = draft.tracing! - const iteration = tracing.find(trace => trace.node_id === nodeParent.id) - - if (iteration && iteration.details?.length) { - const iterRunID = data.execution_metadata?.parallel_mode_run_id - - const currIteration = iterParallelLogMap.get(iteration.node_id)?.get(iterRunID) - const currentNodeRetry = currIteration?.find(item => item.node_id === data.node_id) - - if (currentNodeRetry) { - if (currentNodeRetry?.retryDetail) - currentNodeRetry?.retryDetail.push(data as NodeTracing) - else - currentNodeRetry.retryDetail = [data as NodeTracing] - } - setIterParallelLogMap(iterParallelLogMap) - const iterLogMap = iterParallelLogMap.get(iteration.node_id) - if (iterLogMap) - iteration.details = Array.from(iterLogMap.values()) - } - })) - } - } - else { - setWorkflowRunningData(produce(workflowRunningData!, (draft) => { - const tracing = draft.tracing! - const currentRetryNodeIndex = tracing.findIndex(trace => trace.node_id === data.node_id) - - if (currentRetryNodeIndex > -1) { - const currentRetryNode = tracing[currentRetryNodeIndex] - if (currentRetryNode.retryDetail) - draft.tracing![currentRetryNodeIndex].retryDetail!.push(data as NodeTracing) - else - draft.tracing![currentRetryNodeIndex].retryDetail = [data as NodeTracing] - } - })) - } + setWorkflowRunningData(produce(workflowRunningData!, (draft) => { + draft.tracing!.push(data) + })) const newNodes = produce(nodes, (draft) => { const currentNode = draft.find(node => node.id === data.node_id)! diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-started.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-started.ts index 446d942274..b537ccbb27 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-started.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-started.ts @@ -24,8 +24,6 @@ export const useWorkflowNodeStarted = () => { const { workflowRunningData, setWorkflowRunningData, - iterParallelLogMap, - setIterParallelLogMap, } = workflowStore.getState() const { getNodes, @@ -35,84 +33,54 @@ export const useWorkflowNodeStarted = () => { transform, } = store.getState() const nodes = getNodes() - const node = nodes.find(node => node.id === data.node_id) - if (node?.parentId) { - setWorkflowRunningData(produce(workflowRunningData!, (draft) => { - const tracing = draft.tracing! - const iterations = tracing.find(trace => trace.node_id === node?.parentId) - const currIteration = iterations?.details![node.data.iteration_index] || iterations?.details![iterations.details!.length - 1] - if (!data.parallel_run_id) { - currIteration?.push({ - ...data, - status: NodeRunningStatus.Running, - } as any) - } - else { - const nodeId = iterations?.node_id as string - if (!iterParallelLogMap.has(nodeId as string)) - iterParallelLogMap.set(iterations?.node_id as string, new Map()) - - const currentIterLogMap = iterParallelLogMap.get(nodeId)! - if (!currentIterLogMap.has(data.parallel_run_id)) - currentIterLogMap.set(data.parallel_run_id, [{ ...data, status: NodeRunningStatus.Running } as any]) - else - currentIterLogMap.get(data.parallel_run_id)!.push({ ...data, status: NodeRunningStatus.Running } as any) - setIterParallelLogMap(iterParallelLogMap) - if (iterations) - iterations.details = Array.from(currentIterLogMap.values()) - } - })) - } - else { - setWorkflowRunningData(produce(workflowRunningData!, (draft) => { - draft.tracing!.push({ - ...data, - status: NodeRunningStatus.Running, - } as any) - })) - - const { - setViewport, - } = reactflow - const currentNodeIndex = nodes.findIndex(node => node.id === data.node_id) - const currentNode = nodes[currentNodeIndex] - const position = currentNode.position - const zoom = transform[2] - - if (!currentNode.parentId) { - setViewport({ - x: (containerParams.clientWidth - 400 - currentNode.width! * zoom) / 2 - position.x * zoom, - y: (containerParams.clientHeight - currentNode.height! * zoom) / 2 - position.y * zoom, - zoom: transform[2], - }) - } - const newNodes = produce(nodes, (draft) => { - draft[currentNodeIndex].data._runningStatus = NodeRunningStatus.Running - draft[currentNodeIndex].data._waitingRun = false + setWorkflowRunningData(produce(workflowRunningData!, (draft) => { + draft.tracing!.push({ + ...data, + status: NodeRunningStatus.Running, }) - setNodes(newNodes) - const newEdges = produce(edges, (draft) => { - const incomeEdges = draft.filter((edge) => { - return edge.target === data.node_id - }) + })) - incomeEdges.forEach((edge) => { - const incomeNode = nodes.find(node => node.id === edge.source)! - if ( - (!incomeNode.data._runningBranchId && edge.sourceHandle === 'source') - || (incomeNode.data._runningBranchId && edge.sourceHandle === incomeNode.data._runningBranchId) - ) { - edge.data = { - ...edge.data, - _sourceRunningStatus: incomeNode.data._runningStatus, - _targetRunningStatus: NodeRunningStatus.Running, - _waitingRun: false, - } + const { + setViewport, + } = reactflow + const currentNodeIndex = nodes.findIndex(node => node.id === data.node_id) + const currentNode = nodes[currentNodeIndex] + const position = currentNode.position + const zoom = transform[2] + + if (!currentNode.parentId) { + setViewport({ + x: (containerParams.clientWidth - 400 - currentNode.width! * zoom) / 2 - position.x * zoom, + y: (containerParams.clientHeight - currentNode.height! * zoom) / 2 - position.y * zoom, + zoom: transform[2], + }) + } + const newNodes = produce(nodes, (draft) => { + draft[currentNodeIndex].data._runningStatus = NodeRunningStatus.Running + draft[currentNodeIndex].data._waitingRun = false + }) + setNodes(newNodes) + const newEdges = produce(edges, (draft) => { + const incomeEdges = draft.filter((edge) => { + return edge.target === data.node_id + }) + + incomeEdges.forEach((edge) => { + const incomeNode = nodes.find(node => node.id === edge.source)! + if ( + (!incomeNode.data._runningBranchId && edge.sourceHandle === 'source') + || (incomeNode.data._runningBranchId && edge.sourceHandle === incomeNode.data._runningBranchId) + ) { + edge.data = { + ...edge.data, + _sourceRunningStatus: incomeNode.data._runningStatus, + _targetRunningStatus: NodeRunningStatus.Running, + _waitingRun: false, } - }) + } }) - setEdges(newEdges) - } + }) + setEdges(newEdges) }, [workflowStore, store, reactflow]) return { 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 98564e6b16..4b38111b1d 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 @@ -127,6 +127,7 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => { agent_strategy_provider_name: tool!.provider_name, agent_parameters: tool!.params, agent_strategy_label: tool!.tool_label, + agent_output_schema: tool!.output_schema, }) setOpen(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 c8aa80431b..3f0b05b8e6 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -19,6 +19,7 @@ export type Strategy = { agent_strategy_name: string agent_strategy_label: string agent_parameters?: ToolVarInputs + agent_output_schema: Record } export type AgentStrategyProps = { @@ -37,44 +38,6 @@ type MultipleToolSelectorSchema = CustomSchema<'array[tools]'> type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema const devMockForm = [{ - name: 'model', - label: { - en_US: 'Model', - zh_Hans: '模型', - pt_BR: 'Model', - ja_JP: 'Model', - }, - placeholder: null, - scope: 'tool-call&llm', - auto_generate: null, - template: null, - required: true, - default: null, - min: null, - max: null, - options: [], - type: 'model-selector', -}, -{ - name: 'tools', - label: { - en_US: 'Tools list', - zh_Hans: '工具列表', - pt_BR: 'Tools list', - ja_JP: 'Tools list', - }, - placeholder: null, - scope: null, - auto_generate: null, - template: null, - required: true, - default: null, - min: null, - max: null, - options: [], - type: 'array[tools]', -}, -{ name: 'instruction', label: { en_US: 'Instruction', @@ -139,7 +102,12 @@ const devMockForm = [{ pt_BR: 'The maximum number of iterations to run', ja_JP: 'The maximum number of iterations to run', }, -}] +}].map((item) => { + return { + ...item, + variable: item.name, + } +}) export const AgentStrategy = (props: AgentStrategyProps) => { const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange } = props diff --git a/web/app/components/workflow/nodes/_base/components/collapse/field-collapse.tsx b/web/app/components/workflow/nodes/_base/components/collapse/field-collapse.tsx index 7d2698a6d0..71ca0d28ea 100644 --- a/web/app/components/workflow/nodes/_base/components/collapse/field-collapse.tsx +++ b/web/app/components/workflow/nodes/_base/components/collapse/field-collapse.tsx @@ -1,8 +1,9 @@ +import type { ReactNode } from 'react' import Collapse from '.' type FieldCollapseProps = { title: string - children: JSX.Element + children: ReactNode } const FieldCollapse = ({ title, diff --git a/web/app/components/workflow/nodes/_base/components/output-vars.tsx b/web/app/components/workflow/nodes/_base/components/output-vars.tsx index a0d7a25c07..4a265a5a5b 100644 --- a/web/app/components/workflow/nodes/_base/components/output-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/output-vars.tsx @@ -1,5 +1,5 @@ 'use client' -import type { FC } from 'react' +import type { FC, ReactNode } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse' @@ -7,7 +7,7 @@ import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/ type Props = { className?: string title?: string - children: JSX.Element + children: ReactNode } const OutputVars: FC = ({ diff --git a/web/app/components/workflow/nodes/_base/components/variable/utils.ts b/web/app/components/workflow/nodes/_base/components/variable/utils.ts index 7685c3e587..787ed890d9 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -318,17 +318,23 @@ const formatItem = ( case BlockEnum.Agent: { const payload = data as AgentNodeType - if (!payload.agent_parameters) { - res.vars = [] - break - } - res.vars = Object.keys(payload.agent_parameters).map((key) => { - return { - variable: key, - // TODO: is this correct? - type: payload.agent_parameters![key].type as unknown as VarType, - } + const outputs: Var[] = [] + Object.keys(payload.output_schema.properties).forEach((outputKey) => { + const output = payload.output_schema.properties[outputKey] + outputs.push({ + variable: outputKey, + type: output.type === 'array' + ? `Array[${output.items?.type.slice(0, 1).toLocaleUpperCase()}${output.items?.type.slice(1)}]` as VarType + : `${output.type.slice(0, 1).toLocaleUpperCase()}${output.type.slice(1)}` as VarType, + // TODO: is this required? + // @ts-expect-error todo added + description: output.description, + }) }) + res.vars = [ + ...outputs, + ...TOOL_OUTPUT_STRUCT, + ] break } diff --git a/web/app/components/workflow/nodes/agent/panel.tsx b/web/app/components/workflow/nodes/agent/panel.tsx index 4b177df8c5..4f1d208948 100644 --- a/web/app/components/workflow/nodes/agent/panel.tsx +++ b/web/app/components/workflow/nodes/agent/panel.tsx @@ -5,11 +5,24 @@ import Field from '../_base/components/field' import { AgentStrategy } from '../_base/components/agent-strategy' import useConfig from './use-config' import { useTranslation } from 'react-i18next' +import OutputVars, { VarItem } from '../_base/components/output-vars' +import type { StrategyParamItem } from '@/app/components/plugins/types' +import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations' + +const i18nPrefix = 'workflow.nodes.agent' + +function strategyParamToCredientialForm(param: StrategyParamItem): CredentialFormSchema { + return { + ...param as any, + variable: param.name, + show_on: [], + } +} const AgentPanel: FC> = (props) => { const { inputs, setInputs, currentStrategy } = useConfig(props.id, props.data) const { t } = useTranslation() - return
+ return
> = (props) => { agent_strategy_name: inputs.agent_strategy_name!, agent_parameters: inputs.agent_parameters, agent_strategy_label: inputs.agent_strategy_label!, + agent_output_schema: inputs.output_schema, } : undefined} onStrategyChange={(strategy) => { setInputs({ @@ -25,9 +39,10 @@ const AgentPanel: FC> = (props) => { agent_strategy_name: strategy?.agent_strategy_name, agent_parameters: strategy?.agent_parameters, agent_strategy_label: strategy?.agent_strategy_label, + output_schema: strategy!.agent_output_schema, }) }} - formSchema={currentStrategy?.parameters as any || []} + formSchema={currentStrategy?.parameters?.map(strategyParamToCredientialForm) || []} formValue={inputs.agent_parameters || {}} onFormValueChange={value => setInputs({ ...inputs, @@ -35,6 +50,33 @@ const AgentPanel: FC> = (props) => { })} /> +
+ + + + + {inputs.output_schema && Object.entries(inputs.output_schema).map(([name, schema]) => ( + + ))} + +
} diff --git a/web/app/components/workflow/nodes/agent/types.ts b/web/app/components/workflow/nodes/agent/types.ts index a3301bcd62..20da43f66a 100644 --- a/web/app/components/workflow/nodes/agent/types.ts +++ b/web/app/components/workflow/nodes/agent/types.ts @@ -7,4 +7,5 @@ export type AgentNodeType = CommonNodeType & { agent_strategy_label?: string agent_parameters?: ToolVarInputs, agent_configurations?: Record + output_schema: Record } diff --git a/web/app/components/workflow/panel/workflow-preview.tsx b/web/app/components/workflow/panel/workflow-preview.tsx index b4e4d4c5d1..aa64229e30 100644 --- a/web/app/components/workflow/panel/workflow-preview.tsx +++ b/web/app/components/workflow/panel/workflow-preview.tsx @@ -24,6 +24,7 @@ import Toast from '../../base/toast' import InputsPanel from './inputs-panel' import cn from '@/utils/classnames' import Loading from '@/app/components/base/loading' +import formatNodeList from '@/app/components/workflow/run/utils/format-log' const WorkflowPreview = () => { const { t } = useTranslation() @@ -160,7 +161,7 @@ const WorkflowPreview = () => { {currentTab === 'TRACING' && ( )} {currentTab === 'TRACING' && !workflowRunningData?.tracing?.length && ( diff --git a/web/app/components/workflow/run/agent-log/agent-log-nav-more.tsx b/web/app/components/workflow/run/agent-log/agent-log-nav-more.tsx new file mode 100644 index 0000000000..f3179492ce --- /dev/null +++ b/web/app/components/workflow/run/agent-log/agent-log-nav-more.tsx @@ -0,0 +1,54 @@ +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' + +type AgentLogNavMoreProps = { + options: { id: string; label: string }[] +} +const AgentLogNavMore = ({ + options, +}: AgentLogNavMoreProps) => { + const [open, setOpen] = useState(false) + + return ( + + + + + +
+ { + options.map(option => ( +
+ {option.label} +
+ )) + } +
+
+
+ ) +} + +export default AgentLogNavMore diff --git a/web/app/components/workflow/run/agent-log/agent-log-nav.tsx b/web/app/components/workflow/run/agent-log/agent-log-nav.tsx new file mode 100644 index 0000000000..00166b398b --- /dev/null +++ b/web/app/components/workflow/run/agent-log/agent-log-nav.tsx @@ -0,0 +1,39 @@ +import { RiArrowLeftLine } from '@remixicon/react' +import AgentLogNavMore from './agent-log-nav-more' +import Button from '@/app/components/base/button' + +const AgentLogNav = () => { + return ( +
+ +
/
+ +
/
+ +
/
+
+ Run Actions +
+
+ ) +} + +export default AgentLogNav diff --git a/web/app/components/workflow/run/agent-log/agent-result-panel.tsx b/web/app/components/workflow/run/agent-log/agent-result-panel.tsx index dfb5b557c4..79b207c657 100644 --- a/web/app/components/workflow/run/agent-log/agent-result-panel.tsx +++ b/web/app/components/workflow/run/agent-log/agent-result-panel.tsx @@ -1,6 +1,5 @@ -import Button from '@/app/components/base/button' -import { RiArrowLeftLine } from '@remixicon/react' import AgentLogItem from './agent-log-item' +import AgentLogNav from './agent-log-nav' import type { AgentLogItemWithChildren } from '@/types/workflow' type AgentResultPanelProps = { @@ -12,29 +11,7 @@ const AgentResultPanel = ({ }: AgentResultPanelProps) => { return (
-
- -
/
-
- Agent strategy -
- -
+ {
{ 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 8239f6ca33..6beae5eb2b 100644 --- a/web/app/components/workflow/run/utils/format-log/index.ts +++ b/web/app/components/workflow/run/utils/format-log/index.ts @@ -5,7 +5,7 @@ import formatRetryNode from './retry' import formatAgentNode from './agent' const formatToTracingNodeList = (list: NodeTracing[], t: any) => { - const allItems = [...list].reverse() + const allItems = [...list].sort((a, b) => a.index - b.index) /* * First handle not change list structure node * Because Handle struct node will put the node in different diff --git a/web/app/components/workflow/run/utils/format-log/parallel/index.backup.ts b/web/app/components/workflow/run/utils/format-log/parallel/index.backup.ts deleted file mode 100644 index f30d03c839..0000000000 --- a/web/app/components/workflow/run/utils/format-log/parallel/index.backup.ts +++ /dev/null @@ -1,132 +0,0 @@ -import type { NodeTracing } from '@/types/workflow' - -type TracingNodeProps = { - id: string - uniqueId: string - isParallel: boolean - data: NodeTracing | null - children: TracingNodeProps[] - parallelTitle?: string - branchTitle?: string - hideNodeInfo?: boolean - hideNodeProcessDetail?: boolean -} - -function buildLogTree(nodes: NodeTracing[], t: (key: string) => string): TracingNodeProps[] { - const rootNodes: TracingNodeProps[] = [] - const parallelStacks: { [key: string]: TracingNodeProps } = {} - const levelCounts: { [key: string]: number } = {} - const parallelChildCounts: { [key: string]: Set } = {} - let uniqueIdCounter = 0 - const getUniqueId = () => { - uniqueIdCounter++ - return `unique-${uniqueIdCounter}` - } - - const getParallelTitle = (parentId: string | null): string => { - const levelKey = parentId || 'root' - if (!levelCounts[levelKey]) - levelCounts[levelKey] = 0 - - levelCounts[levelKey]++ - - const parentTitle = parentId ? parallelStacks[parentId]?.parallelTitle : '' - const levelNumber = parentTitle ? Number.parseInt(parentTitle.split('-')[1]) + 1 : 1 - const letter = parallelChildCounts[levelKey]?.size > 1 ? String.fromCharCode(64 + levelCounts[levelKey]) : '' - return `${t('workflow.common.parallel')}-${levelNumber}${letter}` - } - - const getBranchTitle = (parentId: string | null, branchNum: number): string => { - const levelKey = parentId || 'root' - const parentTitle = parentId ? parallelStacks[parentId]?.parallelTitle : '' - const levelNumber = parentTitle ? Number.parseInt(parentTitle.split('-')[1]) + 1 : 1 - const letter = parallelChildCounts[levelKey]?.size > 1 ? String.fromCharCode(64 + levelCounts[levelKey]) : '' - const branchLetter = String.fromCharCode(64 + branchNum) - return `${t('workflow.common.branch')}-${levelNumber}${letter}-${branchLetter}` - } - - // Count parallel children (for figuring out if we need to use letters) - for (const node of nodes) { - const parent_parallel_id = node.parent_parallel_id ?? node.execution_metadata?.parent_parallel_id ?? null - const parallel_id = node.parallel_id ?? node.execution_metadata?.parallel_id ?? null - - if (parallel_id) { - const parentKey = parent_parallel_id || 'root' - if (!parallelChildCounts[parentKey]) - parallelChildCounts[parentKey] = new Set() - - parallelChildCounts[parentKey].add(parallel_id) - } - } - - for (const node of nodes) { - const parallel_id = node.parallel_id ?? node.execution_metadata?.parallel_id ?? null - const parent_parallel_id = node.parent_parallel_id ?? node.execution_metadata?.parent_parallel_id ?? null - const parallel_start_node_id = node.parallel_start_node_id ?? node.execution_metadata?.parallel_start_node_id ?? null - const parent_parallel_start_node_id = node.parent_parallel_start_node_id ?? node.execution_metadata?.parent_parallel_start_node_id ?? null - - if (!parallel_id || node.node_type === BlockEnum.End) { - rootNodes.push({ - id: node.id, - uniqueId: getUniqueId(), - isParallel: false, - data: node, - children: [], - }) - } - else { - if (!parallelStacks[parallel_id]) { - const newParallelGroup: TracingNodeProps = { - id: parallel_id, - uniqueId: getUniqueId(), - isParallel: true, - data: null, - children: [], - parallelTitle: '', - } - parallelStacks[parallel_id] = newParallelGroup - - if (parent_parallel_id && parallelStacks[parent_parallel_id]) { - const sameBranchIndex = parallelStacks[parent_parallel_id].children.findLastIndex(c => - c.data?.execution_metadata?.parallel_start_node_id === parent_parallel_start_node_id || c.data?.parallel_start_node_id === parent_parallel_start_node_id, - ) - parallelStacks[parent_parallel_id].children.splice(sameBranchIndex + 1, 0, newParallelGroup) - newParallelGroup.parallelTitle = getParallelTitle(parent_parallel_id) - } - else { - newParallelGroup.parallelTitle = getParallelTitle(parent_parallel_id) - rootNodes.push(newParallelGroup) - } - } - const branchTitle = parallel_start_node_id === node.node_id ? getBranchTitle(parent_parallel_id, parallelStacks[parallel_id].children.length + 1) : '' - if (branchTitle) { - parallelStacks[parallel_id].children.push({ - id: node.id, - uniqueId: getUniqueId(), - isParallel: false, - data: node, - children: [], - branchTitle, - }) - } - else { - let sameBranchIndex = parallelStacks[parallel_id].children.findLastIndex(c => - c.data?.execution_metadata?.parallel_start_node_id === parallel_start_node_id || c.data?.parallel_start_node_id === parallel_start_node_id, - ) - if (parallelStacks[parallel_id].children[sameBranchIndex + 1]?.isParallel) - sameBranchIndex++ - - parallelStacks[parallel_id].children.splice(sameBranchIndex + 1, 0, { - id: node.id, - uniqueId: getUniqueId(), - isParallel: false, - data: node, - children: [], - branchTitle, - }) - } - } - } - - return rootNodes -} diff --git a/web/app/components/workflow/run/utils/format-log/parallel/index.ts b/web/app/components/workflow/run/utils/format-log/parallel/index.ts index 6c276a1e05..a341362e10 100644 --- a/web/app/components/workflow/run/utils/format-log/parallel/index.ts +++ b/web/app/components/workflow/run/utils/format-log/parallel/index.ts @@ -137,9 +137,12 @@ const format = (list: NodeTracing[], t: any): NodeTracing[] => { }) // print node structure for debug - filteredInParallelSubNodes.forEach((node) => { - printNodeStructure(node, 0) - }) + // filteredInParallelSubNodes.forEach((node) => { + // const now = Date.now() + // console.log(`----- p: ${now} start -----`) + // printNodeStructure(node, 0) + // console.log(`----- p: ${now} end -----`) + // }) const parallelNumRecord: Record = { num: 0, diff --git a/web/app/components/workflow/run/utils/format-log/retry/index.ts b/web/app/components/workflow/run/utils/format-log/retry/index.ts index e2a61dd6b2..b8dd0bfa80 100644 --- a/web/app/components/workflow/run/utils/format-log/retry/index.ts +++ b/web/app/components/workflow/run/utils/format-log/retry/index.ts @@ -1,11 +1,7 @@ -import { BlockEnum } from '@/app/components/workflow/types' import type { NodeTracing } from '@/types/workflow' const format = (list: NodeTracing[]): NodeTracing[] => { const retryNodes = list.filter((item) => { - const { execution_metadata } = item - const isInIteration = !!execution_metadata?.iteration_id - if (isInIteration || item.node_type === BlockEnum.Iteration) return false return item.status === 'retry' }) @@ -14,11 +10,21 @@ const format = (list: NodeTracing[]): NodeTracing[] => { const result = list.filter((item) => { return item.status !== 'retry' }).map((item) => { - const isRetryBelongNode = retryNodeIds.includes(item.node_id) + const { execution_metadata } = item + const isInIteration = !!execution_metadata?.iteration_id + const nodeId = item.node_id + const isRetryBelongNode = retryNodeIds.includes(nodeId) + if (isRetryBelongNode) { return { ...item, - retryDetail: list.filter(node => node.status === 'retry' && node.node_id === item.node_id), + retryDetail: retryNodes.filter((node) => { + if (!isInIteration) + return node.node_id === nodeId + + // retry node in iteration + return node.node_id === nodeId && node.execution_metadata?.iteration_index === execution_metadata?.iteration_index + }), } } return item diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 30e13b527d..f58bc3fdd0 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -728,6 +728,17 @@ const translation = { modelSelectorTooltips: { deprecated: 'This model is deprecated', }, + outputVars: { + text: 'agent generated content', + files: { + title: 'agent generated files', + type: 'Support type. Now only support image', + transfer_method: 'Transfer method.Value is remote_url or local_file', + url: 'Image url', + upload_file_id: 'Upload file id', + }, + json: 'agent generated json', + }, }, }, tracing: { diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index b4291c64fc..82c92dc983 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -728,6 +728,17 @@ const translation = { modelSelectorTooltips: { deprecated: '此模型已弃用', }, + outputVars: { + text: 'agent 生成的内容', + files: { + title: 'agent 生成的文件', + type: '支持类型。现在只支持图片', + transfer_method: '传输方式。值为 remote_url 或 local_file', + url: '图片链接', + upload_file_id: '上传文件ID', + }, + json: 'agent 生成的json', + }, }, }, tracing: { diff --git a/web/types/workflow.ts b/web/types/workflow.ts index 29fc91b06b..1efde98b3d 100644 --- a/web/types/workflow.ts +++ b/web/types/workflow.ts @@ -147,18 +147,7 @@ export type NodeStartedResponse = { task_id: string workflow_run_id: string event: string - data: { - id: string - node_id: string - iteration_id?: string - parallel_run_id?: string - node_type: string - index: number - predecessor_node_id?: string - inputs: any - created_at: number - extras?: any - } + data: NodeTracing } export type FileResponse = { @@ -176,120 +165,42 @@ export type NodeFinishedResponse = { task_id: string workflow_run_id: string event: string - data: { - id: string - node_id: string - iteration_id?: string - node_type: string - index: number - predecessor_node_id?: string - inputs: any - process_data: any - outputs: any - status: string - error: string - elapsed_time: number - execution_metadata: { - total_tokens: number - total_price: number - currency: string - parallel_id?: string - parallel_start_node_id?: string - iteration_index?: number - iteration_id?: string - parallel_mode_run_id: string - error_strategy?: ErrorHandleTypeEnum - } - created_at: number - files?: FileResponse[] - retry_index?: number - } + data: NodeTracing } export type IterationStartedResponse = { task_id: string workflow_run_id: string event: string - data: { - id: string - node_id: string - metadata: { - iterator_length: number - iteration_id: string - iteration_index: number - } - created_at: number - extras?: any - } + data: NodeTracing } export type IterationNextResponse = { task_id: string workflow_run_id: string event: string - data: { - id: string - node_id: string - index: number - output: any - extras?: any - created_at: number - parallel_mode_run_id: string - execution_metadata: { - parallel_id?: string - iteration_index: number - parallel_mode_run_id?: string - } - duration?: number - } + data: NodeTracing } export type IterationFinishedResponse = { task_id: string workflow_run_id: string event: string - data: { - id: string - node_id: string - outputs: any - extras?: any - status: string - created_at: number - error: string - execution_metadata: { - parallel_id?: string - } - } + data: NodeTracing } export type ParallelBranchStartedResponse = { task_id: string workflow_run_id: string event: string - data: { - parallel_id: string - parallel_start_node_id: string - parent_parallel_id: string - parent_parallel_start_node_id: string - iteration_id?: string - created_at: number - } + data: NodeTracing } export type ParallelBranchFinishedResponse = { task_id: string workflow_run_id: string event: string - data: { - parallel_id: string - parallel_start_node_id: string - parent_parallel_id: string - parent_parallel_start_node_id: string - iteration_id?: string - status: string - created_at: number - error: string - } + data: NodeTracing } export type TextChunkResponse = {