11import json
2+
3+ from langchain .agents import AgentState
4+ from langchain .agents .middleware .types import before_model
25from langchain_core .messages import ToolMessage
6+ from langgraph .runtime import Runtime
7+
38from .config import Config
49
510INDIVIDUAL_MIN_LENGTH = 100
611
12+
713def collect_long_strings (obj ):
814 field_info = []
15+
916 def _collect (obj ):
1017 if isinstance (obj , dict ):
1118 for key , value in obj .items ():
1219 if isinstance (value , str ) and len (value ) > INDIVIDUAL_MIN_LENGTH :
13- field_info .append ({
14- 'length' : len (value ),
15- 'dict' : obj ,
16- 'key' : key ,
17- })
20+ field_info .append (
21+ {
22+ "length" : len (value ),
23+ "dict" : obj ,
24+ "key" : key ,
25+ }
26+ )
1827 elif isinstance (value , (dict , list )):
1928 _collect (value )
2029 elif isinstance (obj , list ):
2130 for item in obj :
2231 if isinstance (item , (dict , list )):
2332 _collect (item )
24-
33+
2534 _collect (obj )
2635 return field_info
2736
2837
2938def truncate_by_length (content , max_length ):
3039 """
31- Truncate JSON content by recursively truncating the longest fields until content is under limit.
40+ Truncate JSON content by recursively truncating the longest fields until content is under limit.
3241 Preserves structure and smaller fields with minimum loss of information.
3342 """
3443 try :
3544 data = json .loads (content )
36- field_info = sorted (collect_long_strings (data ), key = lambda x : x [' length' ])
45+ field_info = sorted (collect_long_strings (data ), key = lambda x : x [" length" ])
3746
3847 cur_length = len (json .dumps (data ))
39- while field_info and cur_length - max_length > 0 :
48+ while field_info and cur_length - max_length > 0 :
4049 longest = field_info .pop ()
4150 excess = cur_length - max_length
42- new_length = max (INDIVIDUAL_MIN_LENGTH , longest [' length' ] - excess )
43- cur_length -= longest [' length' ] - new_length
44- longest [' dict' ][longest [' key' ]] = (
45- longest [' dict' ][longest [' key' ]][:new_length ] +
46- f"... [TRUNCATED: { longest ['length' ] - new_length } chars removed]"
51+ new_length = max (INDIVIDUAL_MIN_LENGTH , longest [" length" ] - excess )
52+ cur_length -= longest [" length" ] - new_length
53+ longest [" dict" ][longest [" key" ]] = (
54+ longest [" dict" ][longest [" key" ]][:new_length ]
55+ + f"... [TRUNCATED: { longest ['length' ] - new_length } chars removed]"
4756 )
48-
57+
4958 if cur_length <= max_length :
5059 return json .dumps (data )
5160 except (json .JSONDecodeError , Exception ):
@@ -54,20 +63,28 @@ def truncate_by_length(content, max_length):
5463 return content [:max_length ] + "\n ... [TRUNCATED]"
5564
5665
57- def truncate_tool_messages (state ):
66+ @before_model (state_schema = AgentState )
67+ def truncate_tool_messages (state : AgentState , runtime : Runtime ) -> AgentState :
5868 """
5969 Modify large tool messages to prevent exceeding model's token limits.
6070 Truncate to a length such that it keeps messages within your token limit.
6171 """
6272 messages = state .get ("messages" , [])
6373 modified_messages = []
64-
65- for i ,msg in enumerate (messages ):
66- if isinstance (msg , ToolMessage ) and len (msg .content ) > Config .MAX_CONTENT_LENGTH :
67- truncated_msg = msg .model_copy (update = {
68- 'content' : truncate_by_length (msg .content , Config .MAX_CONTENT_LENGTH )
69- })
74+
75+ for i , msg in enumerate (messages ):
76+ if (
77+ isinstance (msg , ToolMessage )
78+ and len (msg .content ) > Config .MAX_CONTENT_LENGTH
79+ ):
80+ truncated_msg = msg .model_copy (
81+ update = {
82+ "content" : truncate_by_length (
83+ msg .content , Config .MAX_CONTENT_LENGTH
84+ )
85+ }
86+ )
7087 modified_messages .append (truncated_msg )
7188 else :
7289 modified_messages .append (msg )
73- return {"messages" : modified_messages }
90+ return {"messages" : modified_messages }
0 commit comments