From 1d545fc85d3f1c8e6cb0b3fd5216b821812933f5 Mon Sep 17 00:00:00 2001 From: baonudesifeizhai Date: Fri, 4 Jul 2025 18:49:03 -0400 Subject: [PATCH] fix(agent): show agent run steps, fixes #21718 --- api/core/workflow/nodes/agent/agent_node.py | 32 ++++++++++++++++ api/core/workflow/nodes/tool/tool_node.py | 37 +++++++++++++++++-- api/services/enterprise/enterprise_service.py | 4 +- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/api/core/workflow/nodes/agent/agent_node.py b/api/core/workflow/nodes/agent/agent_node.py index 987f670acb..1cde010841 100644 --- a/api/core/workflow/nodes/agent/agent_node.py +++ b/api/core/workflow/nodes/agent/agent_node.py @@ -1,4 +1,5 @@ import json +import uuid from collections.abc import Generator, Mapping, Sequence from typing import Any, Optional, cast @@ -102,7 +103,37 @@ class AgentNode(ToolNode): try: # convert tool messages + agent_thoughts = [] + + from core.tools.entities.tool_entities import ToolInvokeMessage + + thought_log_message = ToolInvokeMessage( + type=ToolInvokeMessage.MessageType.LOG, + message=ToolInvokeMessage.LogMessage( + id=str(uuid.uuid4()), + label=f"Agent Strategy: {cast(AgentNodeData, self.node_data).agent_strategy_name}", + parent_id=None, + error=None, + status=ToolInvokeMessage.LogMessage.LogStatus.START, + data={ + "strategy": cast(AgentNodeData, self.node_data).agent_strategy_name, + "parameters": parameters_for_log, + "thought_process": "Agent strategy execution started", + }, + metadata={ + "icon": self.agent_strategy_icon, + "agent_strategy": cast(AgentNodeData, self.node_data).agent_strategy_name, + }, + ), + ) + + from core.tools.entities.tool_entities import ToolInvokeMessage + + def enhanced_message_stream(): + + yield thought_log_message + yield from message_stream yield from self._transform_message( message_stream, { @@ -110,6 +141,7 @@ class AgentNode(ToolNode): "agent_strategy": cast(AgentNodeData, self.node_data).agent_strategy_name, }, parameters_for_log, + agent_thoughts ) except PluginDaemonClientSideError as e: yield RunCompletedEvent( diff --git a/api/core/workflow/nodes/tool/tool_node.py b/api/core/workflow/nodes/tool/tool_node.py index a4be02d863..03942919c8 100644 --- a/api/core/workflow/nodes/tool/tool_node.py +++ b/api/core/workflow/nodes/tool/tool_node.py @@ -1,5 +1,5 @@ from collections.abc import Generator, Mapping, Sequence -from typing import Any, cast +from typing import Any, Optional, cast from sqlalchemy import select from sqlalchemy.orm import Session @@ -191,6 +191,7 @@ class ToolNode(BaseNode[ToolNodeData]): messages: Generator[ToolInvokeMessage, None, None], tool_info: Mapping[str, Any], parameters_for_log: dict[str, Any], + agent_thoughts: Optional[list] = None, ) -> Generator: """ Convert ToolInvokeMessages into tuple[plain_text, files] @@ -368,11 +369,41 @@ class ToolNode(BaseNode[ToolNodeData]): agent_logs.append(agent_log) yield agent_log - + # Add agent_logs to outputs['json'] to ensure frontend can access thinking process + json_output = json.copy() + if agent_logs: + if not json_output: + json_output = {} + elif isinstance(json_output, list) and len(json_output) == 1: + # If json is a list with only one element, convert it to a dictionary + json_output = json_output[0] if isinstance(json_output[0], dict) else {"data": json_output[0]} + elif isinstance(json_output, list): + # If json is a list with multiple elements, create a dictionary containing all data + json_output = {"data": json_output} + + # Ensure json_output is a dictionary type + if not isinstance(json_output, dict): + json_output = {"data": json_output} + + # Add agent_logs to json output + json_output["agent_logs"] = [ + { + "id": log.id, + "parent_id": log.parent_id, + "error": log.error, + "status": log.status, + "data": log.data, + "label": log.label, + "metadata": log.metadata, + "node_id": log.node_id, + } + for log in agent_logs + ] yield RunCompletedEvent( run_result=NodeRunResult( status=WorkflowNodeExecutionStatus.SUCCEEDED, - outputs={"text": text, "files": ArrayFileSegment(value=files), "json": json, **variables}, + outputs={"text": text, "files": ArrayFileSegment(value=files), + "json": json_output, **variables}, metadata={ **agent_execution_metadata, WorkflowNodeExecutionMetadataKey.TOOL_INFO: tool_info, diff --git a/api/services/enterprise/enterprise_service.py b/api/services/enterprise/enterprise_service.py index 8c06ee9386..54d45f45ea 100644 --- a/api/services/enterprise/enterprise_service.py +++ b/api/services/enterprise/enterprise_service.py @@ -29,7 +29,7 @@ class EnterpriseService: raise ValueError("No data found.") try: # parse the UTC timestamp from the response - return datetime.fromisoformat(data.replace("Z", "+00:00")) + return datetime.fromisoformat(data) except ValueError as e: raise ValueError(f"Invalid date format: {data}") from e @@ -40,7 +40,7 @@ class EnterpriseService: raise ValueError("No data found.") try: # parse the UTC timestamp from the response - return datetime.fromisoformat(data.replace("Z", "+00:00")) + return datetime.fromisoformat(data) except ValueError as e: raise ValueError(f"Invalid date format: {data}") from e