11"""Utility functions for the agent graph."""
22
3- import json
4- from typing import List , Dict , Any , Optional
5- from langchain_core .messages import ToolMessage , FunctionMessage
3+ from copy import copy
4+ from typing import List , Dict , Any , Sequence
5+ from langchain_core .messages import BaseMessage , ToolMessage , AnyMessage , SystemMessage
66
77
8- def has_too_many_consecutive_tool_calls (messages : list , max_calls : int = 3 , look_back : int = 10 ) -> bool :
8+ def has_too_many_consecutive_tool_calls (
9+ messages : Sequence [AnyMessage ],
10+ max_calls : int = 3 ,
11+ look_back : int = 10
12+ ) -> bool :
913 """Check if there are too many consecutive tool calls to prevent infinite loops."""
1014 recent_messages = messages [- look_back :] if len (
1115 messages ) > look_back else messages
@@ -22,23 +26,16 @@ def has_too_many_consecutive_tool_calls(messages: list, max_calls: int = 3, look
2226 return consecutive_tool_calls >= max_calls
2327
2428
25- def message_has_tool_calls (message ) -> bool :
26- """Check if a message contains tool calls in either format ."""
27- # Check new tool_calls format
28- if hasattr ( message , " tool_calls" ) and getattr (message , "tool_calls" , None ):
29- return True
29+ def message_has_tool_calls (message : AnyMessage ) -> bool :
30+ """Check if a message contains tool calls."""
31+ # Check for tool_calls format (modern standard)
32+ tool_calls = getattr (message , "tool_calls" , None )
33+ return bool ( tool_calls )
3034
31- # Check old function_call format
32- if hasattr (message , "additional_kwargs" ):
33- additional_kwargs = getattr (message , "additional_kwargs" , {})
34- return "function_call" in additional_kwargs
3535
36- return False
37-
38-
39- def is_tool_response (message ) -> bool :
40- """Check if a message is a tool or function response."""
41- return hasattr (message , "__class__" ) and message .__class__ .__name__ in ["ToolMessage" , "FunctionMessage" ]
36+ def is_tool_response (message : AnyMessage ) -> bool :
37+ """Check if a message is a tool response."""
38+ return isinstance (message , ToolMessage )
4239
4340
4441def execute_tool (tool , tool_name : str , tool_input : Dict [str , Any ]) -> str :
@@ -61,8 +58,8 @@ def find_tool_by_name(tools: List, tool_name: str):
6158 return next ((t for t in tools if t .name == tool_name ), None )
6259
6360
64- def process_new_format_tool_calls (tool_calls : List [Dict ], tools : List ) -> List [ToolMessage ]:
65- """Process tool calls in the new format and return ToolMessage list."""
61+ def process_tool_calls (tool_calls : List [Dict ], tools : List ) -> List [ToolMessage ]:
62+ """Process tool calls and return ToolMessage list."""
6663 messages_to_add = []
6764
6865 for tool_call in tool_calls :
@@ -82,15 +79,26 @@ def process_new_format_tool_calls(tool_calls: List[Dict], tools: List) -> List[T
8279 return messages_to_add
8380
8481
85- def process_old_format_function_call (function_call : Dict , tools : List ) -> FunctionMessage :
86- """Process function call in the old format and return FunctionMessage."""
87- tool_name = function_call ["name" ]
88- tool_input = json .loads (function_call ["arguments" ])
89-
90- tool = find_tool_by_name (tools , tool_name )
91- if tool :
92- result_content = execute_tool (tool , tool_name , tool_input )
93- else :
94- result_content = f"Tool '{ tool_name } ' not found"
82+ def filter_empty_content_messages (messages : List [AnyMessage ]) -> List [AnyMessage ]:
83+ """Filter messages to ensure no message has empty content (prevents Gemini errors)."""
84+ processed_messages = []
85+ for message in messages :
86+ content = getattr (message , "content" , "" )
87+
88+ # If content is empty or just whitespace, provide minimal content
89+ if not content or (isinstance (content , str ) and not content .strip ()):
90+ # Create a copy with minimal non-empty content
91+ new_message = copy (message )
92+ if isinstance (message , ToolMessage ):
93+ new_message .content = "Completed"
94+ elif isinstance (message , SystemMessage ):
95+ # Keep system message as is (it should have content)
96+ processed_messages .append (message )
97+ continue
98+ else :
99+ new_message .content = "..."
100+ processed_messages .append (new_message )
101+ else :
102+ processed_messages .append (message )
95103
96- return FunctionMessage ( content = result_content , name = tool_name )
104+ return processed_messages
0 commit comments