From ec6103b855bc79d28cd515617f1f532fadf3ab71 Mon Sep 17 00:00:00 2001 From: Wendong-Fan Date: Wed, 21 Jan 2026 00:33:02 +0800 Subject: [PATCH] enhance: duplicate actions panel PR988 --- backend/app/utils/agent.py | 51 ++++++++++----------- backend/app/utils/listen/toolkit_listen.py | 52 +++------------------- 2 files changed, 28 insertions(+), 75 deletions(-) diff --git a/backend/app/utils/agent.py b/backend/app/utils/agent.py index afe5490d..95920af5 100644 --- a/backend/app/utils/agent.py +++ b/backend/app/utils/agent.py @@ -563,23 +563,19 @@ class ListenChatAgent(ChatAgent): f"Agent {self.agent_name} executing async tool: {func_name} from toolkit: {toolkit_name} with args: {json.dumps(args, ensure_ascii=False)}" ) - # Check if tool is wrapped by @listen_toolkit decorator - # If so, the decorator will handle activate/deactivate events - has_listen_decorator = hasattr(tool.func, "__wrapped__") - - # Only send activate event if tool is NOT wrapped by @listen_toolkit - if not has_listen_decorator: - await task_lock.put_queue( - ActionActivateToolkitData( - data={ - "agent_name": self.agent_name, - "process_task_id": self.process_task_id, - "toolkit_name": toolkit_name, - "method_name": func_name, - "message": json.dumps(args, ensure_ascii=False), - }, - ) + # Always send activate event from agent to ensure consistent logging + # This ensures all tool calls are logged, regardless of decorator detection issues + await task_lock.put_queue( + ActionActivateToolkitData( + data={ + "agent_name": self.agent_name, + "process_task_id": self.process_task_id, + "toolkit_name": toolkit_name, + "method_name": func_name, + "message": json.dumps(args, ensure_ascii=False), + }, ) + ) try: # Set process_task context for all tool executions with set_process_task(self.process_task_id): @@ -647,19 +643,18 @@ class ListenChatAgent(ChatAgent): else: result_msg = result_str - # Only send deactivate event if tool is NOT wrapped by @listen_toolkit - if not has_listen_decorator: - await task_lock.put_queue( - ActionDeactivateToolkitData( - data={ - "agent_name": self.agent_name, - "process_task_id": self.process_task_id, - "toolkit_name": toolkit_name, - "method_name": func_name, - "message": result_msg, - }, - ) + # Always send deactivate event from agent to ensure consistent logging + await task_lock.put_queue( + ActionDeactivateToolkitData( + data={ + "agent_name": self.agent_name, + "process_task_id": self.process_task_id, + "toolkit_name": toolkit_name, + "method_name": func_name, + "message": result_msg, + }, ) + ) return self._record_tool_calling( func_name, args, diff --git a/backend/app/utils/listen/toolkit_listen.py b/backend/app/utils/listen/toolkit_listen.py index f8cff0c3..1c69b5b1 100644 --- a/backend/app/utils/listen/toolkit_listen.py +++ b/backend/app/utils/listen/toolkit_listen.py @@ -126,17 +126,8 @@ def listen_toolkit( if not process_task_id: logger.warning(f"[toolkit_listen] Both ContextVar process_task and toolkit.api_task_id are empty for {toolkit_name}.{method_name}") - if not skip_workflow_display: - activate_data = ActionActivateToolkitData( - data={ - "agent_name": toolkit.agent_name, - "process_task_id": process_task_id, - "toolkit_name": toolkit_name, - "method_name": method_name, - "message": args_str, - }, - ) - await task_lock.put_queue(activate_data) + # Note: activate/deactivate events are now sent by agent._aexecute_tool + # to avoid duplicate events and ensure all tools are logged consistently error = None res = None try: @@ -167,20 +158,9 @@ def listen_toolkit( deactivate_timestamp = datetime.now().isoformat() status = "ERROR" if error is not None else "SUCCESS" - # Log toolkit deactivation (only send to WorkFlow if not skipped) + # Log toolkit deactivation for debugging purposes logger.info(f"[TOOLKIT DEACTIVATE] Toolkit: {toolkit_name} | Method: {method_name} | Task ID: {process_task_id} | Agent: {toolkit.agent_name} | Status: {status} | Timestamp: {deactivate_timestamp}") - if not skip_workflow_display: - deactivate_data = ActionDeactivateToolkitData( - data={ - "agent_name": toolkit.agent_name, - "process_task_id": process_task_id, - "toolkit_name": toolkit_name, - "method_name": method_name, - "message": res_msg, - }, - ) - await task_lock.put_queue(deactivate_data) if error is not None: raise error return res @@ -229,18 +209,8 @@ def listen_toolkit( if not process_task_id: logger.warning(f"[toolkit_listen] Both ContextVar process_task and toolkit.api_task_id are empty for {toolkit_name}.{method_name}") - if not skip_workflow_display: - activate_data = ActionActivateToolkitData( - data={ - "agent_name": toolkit.agent_name, - "process_task_id": process_task_id, - "toolkit_name": toolkit_name, - "method_name": method_name, - "message": args_str, - }, - ) - _safe_put_queue(task_lock, activate_data) - + # Note: activate/deactivate events are now sent by agent._execute_tool + # to avoid duplicate events and ensure all tools are logged consistently error = None res = None try: @@ -275,18 +245,6 @@ def listen_toolkit( else: res_msg = str(error) - if not skip_workflow_display: - deactivate_data = ActionDeactivateToolkitData( - data={ - "agent_name": toolkit.agent_name, - "process_task_id": process_task_id, - "toolkit_name": toolkit_name, - "method_name": method_name, - "message": res_msg, - }, - ) - _safe_put_queue(task_lock, deactivate_data) - if error is not None: raise error return res