import hashlib import json from langchain_core.messages import ToolMessage from modules.nodes.state import ChatState def dedup_tool_call(state: ChatState): # ensure seen_tool_calls exists if state.get("seen_tool_calls") is None: state["seen_tool_calls"] = set() state["skip_tool"] = False # reset every time if not state.get("messages"): return state last_msg = state["messages"][-1] # only process messages that have tool_calls if hasattr(last_msg, "tool_calls") and last_msg.tool_calls: call = last_msg.tool_calls[0] tool_name = call["name"] raw_args = call.get("arguments") or {} tool_args = json.dumps(raw_args, sort_keys=True) sig = (tool_name, hashlib.md5(tool_args.encode()).hexdigest()) if sig in state["seen_tool_calls"]: # Duplicate detected → append a proper ToolMessage instead of a system message state["messages"].append( ToolMessage( content=f"Duplicate tool call skipped: {tool_name}({tool_args})", tool_call_id=call["id"], name=tool_name, additional_kwargs={}, ) ) state["skip_tool"] = True # remove the tool_calls from the last assistant message to prevent validation error last_msg.tool_calls = [] else: state["seen_tool_calls"].add(sig) state["skip_tool"] = False return state